21

I just updated to macOS Mojave, and immediately noticed a couple of things:

  • My custom login screen wallpaper is gone.
  • When you click on a user's name in the login screen, it switches to their personal background (their usual wallpaper for the first space on the primary monitor).

I assumed it had just overwritten my cached image file. But when I went to replace it, nothing happened. It turns out that com.apple.desktop.admin.png is gone entirely!

no-cached-image

Right after taking that screenshot, I decided to poke into Desktop Pictures and found my personal login screen background, which looks promising. It contains one other folder, which  probably  (edit: confirmed) contains the login screen background of my administrator account.

16

I've fixed it! You'll have to edit the dune HEIC picture though. If you're willing, follow these steps:

​1) Go To: /Library/Desktop Pictures/

2) Find the file called "Mojave.heic"

3) Save a copy as a backup somewhere else

4) Select the picture you want to have instead

5) Edit image values (DPI, size, etc.) to fit

6) Rename this edited picture as Mojave.heic

  • Good thinking, except this "removes" the Mojave background in the Desktop Background preference pane. – juniorRubyist Sep 26 '18 at 5:11
  • That definitely might be acceptable! I take it SIP won't trigger, since it's in /Library? And it has to be HEIF, right? Most of my images are JPEGs, so I'll have to figure out a way to convert them for this. Maybe sip. – SilverWolf Sep 26 '18 at 13:01
  • You can just rename a whole jpg file as Mojave.heic and it works just fine. – Leonard Sep 26 '18 at 14:41
  • @Leonard Interesting. You've tested that, and it works? I'm quite surprised, but that's fantastic! – SilverWolf Sep 27 '18 at 16:21
  • @BlackPearl tried this, and it didn't work. I don't know all the specifics of the attempt, though. – SilverWolf Sep 27 '18 at 16:53
5

Expanding on Leonard's answer:

You can do this by replacing the Mojave.heic default desktop background. This does not require disabling SIP, as it's in /Library.

  • Back up /Library/Desktop Pictures/Mojave.heic by copying it to Mojave.heic.orig or similar.
  • Get your new image and scale/crop it to exactly fit the display. If you don't know your screen resolution, you can go to  > About This Mac.
  • Replace Mojave.heic with your new file. Don't worry if it's JPG or similar, it'll still work even after you rename it to Mojave.heic.*

  • If you have FileVault enabled, change a login option in System Preferences. For example, whether to show a list of users or name and password fields. Just change it back if you don't actually want it changed.

    This is because when you boot with FileVault, at the login screen your system hasn't really booted up all the way! It's actually running a tiny system on your EFI partition, since your main partition is encrypted. Changing a login option will make System Preferences change the EFI system's settings, including picking up the wallpaper change. See this answer.

  • Reboot and enjoy!

* I've only tested this with JPEG images, but it may work for other types.


Completely Unnecessary Timesaver

I've made a small Swift program that does all this for you (it detects the OS version and works both on Mojave and earlier versions). You'll need Xcode to compile it.

It shouldn't break your system, but I can't guarantee anything -- make sure you have backups first!

This is now also available on GitHub. It may or may not be updated here in the future.

//
// loginwindowbgconverter
// by SilverWolf
// 2018-09-27
//

import Foundation
import AppKit

func printUsage() {
    print("""
    usage: \(CommandLine.arguments[0]) \u{1B}[4mimage-file\u{1B}[0m
    It needs to be run as root, as it saves to /Library/Desktop Pictures.
    """)
}

guard CommandLine.arguments.indices.contains(1) else {
    printUsage()
    exit(1)
}
let inputFile = CommandLine.arguments[1]

guard let inputImage = NSImage(contentsOfFile: inputFile) else {
    print("\(CommandLine.arguments[0]): can't load image from \(inputFile)")
    exit(2)
}

let iw = inputImage.size.width
let ih = inputImage.size.height
let iaspect = Double(iw) / Double(ih)

// use System Profiler to get screen size

var sw = 0, sh = 0

enum ScreenSizeError: Error {
    case foundNil
}
do {
    let task = Process()
    if #available(macOS 10.13, *) {
        task.executableURL = URL(fileURLWithPath: "/bin/zsh")
    } else {
        task.launchPath = "/bin/zsh"
    }
    task.arguments = ["-f", "-c", "system_profiler SPDisplaysDataType | awk '/Resolution/{print $2, $4}' | head -n 1"]

    let stdoutPipe = Pipe()
    task.standardOutput = stdoutPipe

    if #available(macOS 10.13, *) {
        try task.run()
    } else {
        task.launch()
    }
    task.waitUntilExit()

    let data = stdoutPipe.fileHandleForReading.readDataToEndOfFile()
    guard let text = String(data: data, encoding: .utf8) else {
        throw ScreenSizeError.foundNil
    }
    let sizes = (text as NSString).replacingOccurrences(of: "\n", with: "").components(separatedBy: " ")
    sw = Int(sizes[0]) ?? 0
    sh = Int(sizes[1]) ?? 0
    guard sw != 0 && sh != 0 else {
        throw ScreenSizeError.foundNil
    }
} catch {
    print("\(CommandLine.arguments[0]): can't get screen resolution")
    exit(3)
}

print("Screen size: \(sw)x\(sh)")

var nw = 0, nh = 0
var x = 0, y = 0 // offsets

let saspect = Double(sw) / Double(sh)
if saspect > iaspect { // screen is wider
    nw = sw
    nh = Int(Double(sw) / iaspect) // keep input image aspect ratio
    y = -1 * (nh - sh) / 2 // half the difference
} else { // screen is narrower
    nh = sh
    nw = Int(Double(sh) * iaspect)
    x = -1 * (nw - sw) / 2
}

// draw into new image
guard let newImage = NSBitmapImageRep(bitmapDataPlanes: nil,
                                pixelsWide: Int(sw),
                                pixelsHigh: Int(sh),
                                bitsPerSample: 8,
                                samplesPerPixel: 4,
                                hasAlpha: true,
                                isPlanar: false,
                                colorSpaceName: .deviceRGB,
                                bytesPerRow: sw * 4,
                                bitsPerPixel: 32) else {
    print("\(CommandLine.arguments[0]): can't create bitmap image to draw into!")
    exit(2)
}

NSGraphicsContext.saveGraphicsState()
let graphicsContext = NSGraphicsContext(bitmapImageRep: newImage)
NSGraphicsContext.current = graphicsContext
graphicsContext?.imageInterpolation = .high
let r = NSMakeRect(CGFloat(x), CGFloat(y), CGFloat(nw), CGFloat(nh))
print("drawing rect: \(r)")
inputImage.draw(in: r)

graphicsContext?.flushGraphics()
NSGraphicsContext.restoreGraphicsState()

print("image size: \(newImage.size)")

// write to file
if #available(macOS 10.14, *) { // macOS Mojave has a completely different system
    let targetFile = "/Library/Desktop Pictures/Mojave.heic"
    let origFile =  "/Library/Desktop Pictures/Mojave.heic.orig"
    if !FileManager.default.fileExists(atPath: origFile) { // no backup of original Mojave.heic
        print("Backing up original Mojave.heic (this should only happen once)")
        do {
            try FileManager.default.copyItem(atPath: targetFile, toPath: origFile)
        } catch {
            print("\(CommandLine.arguments[0]): \u{1B}[1mbackup failed, aborting!\u{1B}[0m \(error.localizedDescription)")
            exit(1)
        }
    }

    print("Saving to \(targetFile)")
    // actual writing
    let imageData = newImage.representation(using: .jpeg, properties: [:])!
    do {
        try imageData.write(to: URL(fileURLWithPath: targetFile))
    } catch {
        print("\(CommandLine.arguments[0]): can't write image data: \(error)")
        print("(are you root?)")
        exit(1)
    }
} else {
    let targetFile = "/Library/Caches/com.apple.desktop.admin.png"
    print("Saving to \(targetFile)")
    let pngData = newImage.representation(using: .png, properties: [:])!
    do {
        try pngData.write(to: URL(fileURLWithPath: targetFile))
    } catch {
        print("\(CommandLine.arguments[0]): can't write image data: \(error)")
        print("(are you root?)")
        exit(1)
    }
}

//
// This is free and unencumbered software released into the public domain.
//
// Anyone is free to copy, modify, publish, use, compile, sell, or
// distribute this software, either in source code form or as a compiled
// binary, for any purpose, commercial or non-commercial, and by any
// means.
//
// In jurisdictions that recognize copyright laws, the author or authors
// of this software dedicate any and all copyright interest in the
// software to the public domain. We make this dedication for the benefit
// of the public at large and to the detriment of our heirs and
// successors. We intend this dedication to be an overt act of
// relinquishment in perpetuity of all present and future rights to this
// software under copyright law.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// For more information, please refer to <https://unlicense.org/>.
//
  • To get the screen dimensions you can use NSScreen frame property developer.apple.com/documentation/appkit/nsscreen/1388387-frame – Mateusz Szlosek Oct 29 '18 at 17:37
  • I tried that. It returns the "fake" screen size used for layout, not the real, physical screen size. – SilverWolf Oct 29 '18 at 17:40
  • And multiplying it by the backing scale factor doesn't work either: it returns a flat 2 for me, even though I'm using higher density scaling. So it can be larger or smaller than the physical size, depending on your scaling settings. (: – SilverWolf Oct 29 '18 at 17:44
  • Ah OK, I get what you mean:) – Mateusz Szlosek Oct 29 '18 at 19:07
  • 1
    Unfortunately, this didn't work for me. I even tried the program but with no success. I'm still getting the default dune instead of my picture. I have FileVault enabled but I changed login option as you described. Any ideas? – Artem M Jan 4 at 22:07
1

I receive a weird image when I just replace the file with a JPG, renaming it too HEIC. However, when I take the image I want as the background and export it in the HEIC format in Preview, it all works great. I was working with a 5333×3333 image to start with:

  1. Open image you want as your background in Preview
  2. In Preview, select File > Export...
  3. Set the Format to HEIC and Quality to best (I got a completely blank image when I tried a quality less than "Best")
  4. Rename the exported file as Mojave (the extension should already be .heic)
  5. Copy exported image to /Library/Desktop\ Pictures

When you logout, you should see your new background. Try restarting, if don't see the image show up right away.

If you run into issues with exporting the file as .heic, try adjusting the size of the image using Preview: Tools > Adjust Size. As a start, set it to the size of your screen as seen in System Information > Graphics/Displays.

  • Thank you, good to know! Also, I wrote a small program to do all this automatically if you don't want to resize it with Preview, and it can now export to HEIF. You can find it over on GitHub. – SilverWolf May 15 at 16:32

You must log in to answer this question.

protected by Community Oct 1 '18 at 3:55

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged .