Dali Clock 2.24 out now

Dali Clock 2.24 out now for MacOS 10.4, PalmOS, and X11. This release includes a MacOS screen saver version of the clock, and there are a few minor display-glitch fixes to the PalmOS version. Also the PalmOS version has a color application icon now, ooooooh.

So, I tried to add a preference to the Mac version to let you hide the dock icon, but <lj-cut text="I couldn't make that work..."> I couldn't make that work.

So, apparently there's no way for a running application to turn its dock icon off. But, you can configure the app to be one that doesn't have a dock icon when it is launched, and then after that, you can turn the dock icon back on. So the way you do this is, you put LSBackgroundOnly=1 in the Info.plist (LSUIElement=1 does not work) and then if you want to have a dock icon after all, you call TransformProcessType (&psn, kProcessTransformToForegroundApplication);. So far so good. Except, LSBackgroundOnly applications never get a menubar: when you select the window, the menubar stays the menubar of the previous application. So when the dock icon is hidden, there's no way to bring up the Preferences dialog so that you can say "actually I do want a dock icon after all." Keyboard shortcuts don't work either. So that's kinda lame.

Also, the way preferences and bindings work is still complete insanity. How can such a basic, fundamental part of the OS be so completely, incomprehensibly insane? And then screen savers take the baseline insanity of it and layer even more crazy on top, because of the moronic way that previewed screen savers run in the same process as the System Preferences application. Madness, I tell you.

Tags: , , , , , , ,

9 Responses:

  1. tiff_seattle says:

    What OS would Dali run on his phone though?

  2. chanson says:

    What is it that you find insane about NSUserDefaults and Cocoa Bindings?

    • jwz says:

      What's crazy about them? Just about everything.

      Since you use bindings by passing strings around, there's no error checking and no easy way to tell when things are actually hooked up properly. If you need to create views manually instead of doing it all in the nib file, hooking things up to them is a big hassle. The order that things happen is extremely sensitive and undocumented: for example, it appears that any kind of setting of defaults must happen in class-initialize methods, not lazily in instance-creation methods, because if you don't set all that crap up before the nib file is loaded, nothing gets hooked up properly. E.g., your color selectors in the panels in the nib file will be black, even though you haven't actually put that panel on the screen yet, because the default color wasn't put in the preferences before the stuff in the nib was deserialized.

      This also means that, without the kind of extremely verbose dictionary-fiddling gyrations that I went through, the "easy" way is to put all of your defaults in one place, e.g., in an initialize method of an app controller class, and far away from the places those preferences are actually used, e.g., a rendering view. Especially if you have an app with multiple distinct renderers that all happen to use distinct preferences of their own. You have to bend over backwards to not break modularity.

      I have no idea why there are both a NSUserDefaults and a NSUserDefaultsController. When you use one and not the other seems completely arbitrary. I still have no idea when you're supposed to say "fieldName" and when you're supposed to say "values.fieldName", or what that even means. The difference between setInitialValues and registerDefaults is what? The difference between withKey and withKeyPath is what?

      Some of these problems might (merely!) be that the documentation sucks, but that in itself is indicative of the problem: all this is for is saving key/value pairs in a preferences file and getting notified when they change! It shouldn't even require more than a paragraph of documentation, but it does, oh it does.

      And the obviously-an-afterthought wart known as ScreenSaverDefaults just screws everything up even more, not the least because Interface Builder doesn't know about it. IB has hardcoded knowledge of "Shared Defaults" all over the place, but oops, if you want to have a screen saver that actually has preferences, you have to hook everything up by hand. I found a way to do it, but frankly it probably would have been easier to just code the panel by hand instead of doing it in IB at all.

      The only demo I've seen for doing defaults with a screen saver encourages you to access the slider values in the preferences panel directly by polling them. You know why? Because doing anything more abstract than that in a screen saver is hellaciously difficult!

      And the only reason ScreenSaverDefaults exists at all is because they moronically decided that screen savers should be modules loaded into the same address space as either System Preferences or the screen-blanker daemon (depending on how it's launched), instead of having them run as distinct executables that are launched and killed as needed. So without ScreenSaverDefaults, the usual preferences mechanism would end up saving all your preferences into SystemPreferences.plist instead of YourSaver.plist. The only reason SSD exists is to change that file name. But all the other code in the world, including IB, uses sharedUserDefaultsController, so you can't reuse anything that hadn't been written from the beginning with screen-saverness in mind. The mere fact that you're using a different instance of the defaults controller than everyone else means all the code has to change.

      But hey, if they ran savers in their own subprocess, then in addition to not needing SSD, they'd also lose the feature that a buggy screen saver can perma-lock your screen so that you have to reboot.

      Oh wait, that's not a feature at all.

      • blasdelf says:

        LSUIElement=1 doesn't work? Did you try rebooting to re-initialize LaunchServices? (I feel like this guy just saying that). Or is there something specific to screensaving that fucks that up?

        I think that Apple's "Screensaver Roadmap" is to move everything into Quartz Composer. In the last couple builds of Leopard, they added a bunch of new documentation and sample code for it, and have added a number of default screensavers implemented in it.

        Preferences were super-easy to implement in the screensavers I wrote in QC a few months ago: all I had to do was have my top-level patch publish the inputs that I wanted to expose, and everything was handled perfectly.

        Perhaps your DaliClock code could be ported to be a QC patch (my old roommate has written several), but in really it would be better done inside QC. I could probably write a prettier GPU-rendered version in QC in about an hour, but I guess this endeavor is really about you cherishing your familiar old code.

        As a side note: another hilarious feature? ScreenSaver Preferences (at least for QC and Apple savers) are stored in the ByHost preferences domain! (one of the only things it's used for). That way, your screensaver preferences are different for each computer you use that home directory on. It sort of makes sense, but forces me to use scripts to make a copy of my prefs with the new MAC when I reimage my Computer Labs.

        • jwz says:

          TransformProcessType() only works with LSBackgroundOnly; with LSUIElement, it returns error -50.

          I've only used QC a little bit (I played with it when I was working on the photo booth) and it was pretty weird. But mostly, it was an exercise in, "only use this if you want to ensure that everything uses software rendering on any machine that's more than 6 months old, even if normal GL programs run accelerated there." QC is all-or-nothing, and falls back to all software rendering if your video card is missing even one feature that it wants, even if you aren't using that feature at all.

          If that's really Apple's plan, I guess it means that Quartz graphics are never going to start running at a reasonable speed, so the ports of the older X11 (non-GL) xscreensaver modules are never going to run at a reasonable speed. Sigh.

          • blasdelf says:

            You can port your existing C drawing code as an image generating 'patch' for QC pretty easily though, and only use QC to handle preferences and interfacing with the OS. My friend wrote a number of C patches for his own use: one was to buffer, play, and switch between raw MJPEG streams from disk; another was a full OSC (networked MIDI overhaul) implementation.

            The only all-or-nothing thing you'll run into if you're using your own patches is when running on really old machines that have Rage128s in them. In that situation they won't run at all.

      • darkshadow2 says:

        There's NSUserDefaults and NSUserDefaultsController because bindings are relatively new. So before that, there was only NSUserDefaults. And it's still around because not everyone uses bindings. I don't - they're a pain in the ass to get working with most of the things you want to do with them. If you want to keep a text field in sync with a slider, sure, use bindings - that works great. Anything more is just a pain. Plus, having the bindings saved with the nib file is just strange to me.

  3. darkshadow2 says:

    Hmm, LSUIElement should work fine. Did you mean it doesn't work, as in it has no effect, or that it doesn't work in that you have to relaunch the app for it to activate? If it's the former, one thing you need to do in addition to setting the key in the Info.plist file is to touch the bundle as well (just use NSFileManager to change the modification date). Otherwise, the system doesn't pick up on the change (or not immediately, anyway). Also, if you have multiple copies of it on your drive, LaunchServices usually picks one of the older versions to get it's info from.

    LSBackgroundOnly is OK, but it's a pain in that it's FORCED to be background. Meaning, if you have a window with controls, all of the controls will be in their inactive state even if you click on the window. You can't force them to be active, either. They'll work, but it looks wrong. In one of my apps I give the users the ability to change the LSUIElement key, and considered changing that to LSBackgroundOnly because of the TransformProcessType() call, but the fact that everything is rendered in its inactive state was a deal buster there.

    To note, you lose the menu bar as well as the Dock icon with either method. If you have your keyboard shortcut for showing your Preferences to "Command-," most people know about that though it does confuse some of 'em to have no menu bar.