Solipsism Gradient

Rainer Brockerhoff’s blog

Browsing Posts tagged Klicko

Yay! An update!

No comments

So, a long-delayed update.

A few days ago, and exactly 365 days after the last version (1.0.2, I dimly recall), I submitted RB App Checker Lite 1.0.3 to the Mac App Store. It should be out before the end of the week, I hope; watch this space for news. As soon as it’s on the store the developer ID-signed version will also be available for direct download.

There are a few new features and some bug fixes – the exact list will be out with the update. There are, also, many new features not directly visible by the user. In particular, RB App Checker (non-Lite) is now being built on almost exactly the same codebase. This will be a paid application, probably around US$16, with a new UI and explanations for the non-technical user – but the very detailed geeky stuff will still be visible with a click, don’t worry. It will also be able to scan the user’s Application folder.

Both versions of the App Checker build on a generic application framework that will make it easy for me publish more file-twiddling utilities. Two of them – one to count and scan folder contents, one to generate various types of aliases and file links – are already in alpha and should be available before the end of the year.

In parallel, I hope to, very soon, restart work on my next-generation System Preferences panel – the one that will obsolete and subsume my previous apps like Quay and (perhaps) Klicko. If all works out as I hope, this panel will be able to leverage the RB Utilities  to get extra funcionality not allowed by the Mac App Store, and centralize preferences and auto-updating for my non-App Store utilities.

In a day we’ll leave for a short vacation in Ireland, followed by a visit to lovely Köln and the Objective-Cologne conference, where I’ll present a short talk on (ahem) “Coding Secrets of the Ancients”. Well, the subject is a little misleading – there will be a section about history and reminiscences about early computing, but there’ll be a very practical and up-to-date section about protecting applications in the Mac App Store; tricky stuff like receipt and certificate checking. More details should be up soon at the ObjCgn website.

We’ll also seize the opportunity to visit friends, relatives and developers in Germany, and should be back early in October. I’ll post updates here whenever possible.

Lion is coming

1 comment

With the new apartment mostly ready (though the external/common areas are still unfinished), a return to blogging and coding may now be possible.

While I’ve pondered about the general direction that I wish to take my software, details are still a little unclear. Yes, the Mac App Store does figure in my plans for updating/replacing XRay, but System Preference panels are not accepted – so Quay and Klicko will continue being distributed over this website.

The developer preview release of Mac OS X 10.7 “Lion” was a surprise to me. My expectation was that it would be released at (or just after) next June’s WWDC, which I hadn’t planned to attend. I’ve just installed the Lion preview and had a fast look; it’s farther along than such previews usually are, and there are sufficient new UI details and API changes that I decided to study those first, before commiting to design details on my own software.

A surprising amount of detail about Lion has already been published, NDAs notwithstanding, with only token sallies from Apple Legal. I’ve been somewhat out of touch with the developer community, so I can only speculate. Reducing the price of developer access to $99 – 20% of what it cost the last time around – may be a factor.

One aspect which will impact me immediately is that PowerPC support over Rosetta will no longer be available. There are some PowerPC apps I still use a lot: among them are Resorcerer, DMG Maker, Plain Clip, my own XRay, and – the one that’s open all the time – Eudora; as well as a bunch of utilities and games that I open very rarely. I suppose I’ll have to relegate them to my old Mac mini Core Solo, which – being a 32-bit machine – will also not be supported by Lion.

The exception is Eudora; I’ve used it since 1.0b5 or thereabouts. I suppose I’ll finally have to try out the Eudora OSE version; some fellow oldsters tell me it’s not too bad. None of the other email clients seem attractive, especially Apple’s Mail, which I actually tried out last year and didn’t like.

I just found out that an errant script is messing up the automatic version checker in Quay and Klicko – a consequence of the server migration which happened several days ago. Please hold on while our trained server gnomes return from their burrows and fix it (it might be one or two more days, I’m really stretched thin here). My apologies.

Update: fixed. Again, sorry about the delay.

While reading the links pertaining to the previous post about a “back” button for the iPad – and, incidentally, for the Mac – it occurred to me that such a capability might be built into a future version of my Klicko utility. Which is Mac-only, of course. For now, Apple doesn’t allow any sort of utilities for the iPhone/iPad, more’s the pity.

I’m rather overloaded at the moment, moving out of my apartment only a few days before going off to WWDC, and I’ve got an iPad app prototype to work on, too; but I’ll definitely look into this as soon as possible. I think that most of the infrastructure, in fact, is already in place inside Klicko.

I did download the Universal Back Button App, but didn’t have time to check out how it works; at any rate, it seems to be quite different in implementation.

10.6 is early …and circumstances conspired to make me late in checking (or, at least, ensuring) compatibility with it.

I just posted on the Quay support forum about the situation with Quay. Briefly, an interim 1.1.2 version should be out soon.

Klicko installs and runs with no problem on 10.6. However, it being a 32-bit control panel, System Preferences will restart every time it is run. Once you’ve set the preferences, that shouldn’t be too onerous, but I still plan to do a 64-bit version as soon as possible.

XRay will mostly work if you have Rosetta installed, but with the same restrictions as on Leopard: the file browser may crash (though, oddly enough, less than on 10.5); and changing permissions on folder contents will probably fail without warning. XRay is, unfortunately, recommended for 10.4 (Tiger) users only, and most of its functionality will be reincarnated in some form or other in the delayed-but-upcoming Quay 1.2.

Zingg! and Nudge are Finder Contextual Menus, which are not supported by Snow Leopard. Their functionality will also be implemented through plug-ins for Quay 1.2.

My US International keyboard layout has finally been incorporated by Apple into their standard list of layouts, so you won’t need to get it from here anymore. Yay!

Stay tuned for further announcements regarding Snow Leopard…

Hold on

No comments

Well, since I last wrote, a reasonably definite version of Klicko has been published (1.1.1 build 207) and I deemed the supporting code to be mature enough to serve as basis for the next version of Quay. So, since then, I’ve been busy on that.

On June 4th I’ll be arriving in San Francisco – WWDC starts on June 8 – and hopefully by then I’ll have an early alpha version of Quay 1.2 (or maybe 2? II?), and of the Quay Plugin Developer Kit as well. Stay tuned.

Also, yikes. 26 days without posting! I plead temporary insanity brought on by tax filing time, and Twitter – the latter being a more convenient outlet for short links and thoughts.

Anyway, taxes are filed and we’re now having a short working vacation in the hills of Petrópolis, an old town north of Rio de Janeiro. Dorinha is taking a short English immersion course (excellent BTW), and I’m coding again, yay!

I’m patching up some loose ends in Klicko in preparation to cloning its preference panel for the next version of Quay, as I’ve mentioned before. While doing that, I’m also trying to refactor my code into a tighter and more readable form. Some of that might be interesting…

For instance, the automatic update checker has a dialog button to “Open System Preferences” and this should go to the Klicko preference panel. Now, System Preferences may already be running but with another panel selected; in any event, the Klicko panel should be opened and ready for the user to see update details. There are several ways to accomplish this.

Most people probably will consider, at first, writing an AppleScript to open System Preferences and then select the Klicko preferences panel. This is unnecessarily complex, and I’ve looked at several solutions. The simplest one-liner to do so from Cocoa would be:

[[NSWorkspace sharedWorkspace] openFile:@"/full/path/to/my.prefPane"];

There’s a non-obvious down-side to that: NSPreferencePane is generic and may be implemented by other apps for their preference plug-ins. Someone’s application might use it and declare .prefPane in its Info.plist. This would in my opinion be a mistake, in that double-clicking or running the code above might (or not) open that other app instead of System Preferences!

The solution I finally hit upon uses Launch Services to open the correct application with the preference panel, like this:

FSRef ref;
if (LSFindApplicationForInfo(0, CFSTR("com.apple.systempreferences"), NULL, &ref, NULL)==noErr) {
   LSApplicationParameters parms = {0,kLSLaunchDefaults,&ref,NULL,NULL,NULL,NULL};
   NSArray* args = [NSArray arrayWithObject:[NSURL fileURLWithPath:@"/full/path/to/my.prefPane"]];
   if (LSOpenURLsWithRole((CFArrayRef)args, kLSRolesAll, NULL, &parms, NULL, 0)==noErr) {
      // success!
   }
}

This code first finds the System Preferences app by its bundle ID, and makes a FSRef for it. The FSRef is then pointed to from the LSApplicationParameters structure, and passed to LSOpenURLsWithRole; this will run System Preferences if it’s not already running, and tell it to open the panel.

It’s tempting to pass the panel’s path as an argument inside of LSApplicationParameters. This does indeed work if System Preferences is not already running, but unfortunately it’s ignored if it is.

One feature of Klicko which is approaching buglessness asymptotically is auto-update.

I frequently get asked why I’m not just using Sparkle. There are two main reasons: first, it’s somewhat bulky and generic. Look at the latest version of ClickToFlash: it’s 1.4MB, of which 1.1MB are used by Sparkle. (Why this matters to me may best be explained in another post; the same goes for my generic dislike of in-app frameworks.)

Second, Sparkle does not have automatic updating as such; it normally checks when its application (or plug-in, or whatever) is run. Klicko, as a System Preferences panel, is mostly a set-it-and-forget-it piece of software; my experience was that few users remember to check for updates regularly, or re-open the panel at all. So I need periodic checking in the background process; however, Klicko’s background process is constrained to not use AppKit or show any UI, so it can’t use Sparkle either.

That said, updating applications or preference panels is in many ways a tricky business. One of the problems is that, unlike Classic applications, Mac OS X applications or bundles are composed of a folder hierarchy, and finding items in that hierarchy is usually done by APIs that turn out to be path-based. If you move (or, worse, substitute) a running application or plug-in, it may fail in interesting ways: things like images or executable code may be cached from the old version, and others will be pulled in from the new version.

In other words, it’s not advisable to have a running app delete itself and replace its bundle by a freshly-downloaded one. In the case of a preferences panel, System Preferences (at least in Leopard) doesn’t properly unload and uncache a panel when installing a new one. The solution is to quit the application and have a different process do the swap-in and immediate re-execution of the updated application.

The swap-in part is easily handled by FSReplaceObject() or its cousin, FSPathReplaceObject(); just make sure of passing kFSReplaceObjectDoNotCheckObjectWriteAccess|kFSReplaceObjectPreservePermissionInfo in the options argument, and everything will work. (However, this may require user authorization for replacing bundles in, say, /Library/PreferencePanes.)

For simplicity, let’s show only the re-execution part. Sparkle uses a shell script to do so. Slightly simplified, it looks like this:

 setenv ("Executable_PATH", path, 1);
 system ("/bin/bash -c '{ for (( i = 0; i < 3000 && $(echo $(/bin/ps -xp $PPID|/usr/bin/wc -l))-1; i++ )); do\n"
    /bin/sleep .2;\n"
         "  done\n"
         "  if [[ $(/bin/ps -xp $PPID|/usr/bin/wc -l) -ne 2 ]]; then\n"
         "    /usr/bin/open \"${Executable_PATH}\"\n"
         "  fi\n"
         "} &>/dev/null &'");

Don’t expect me to explain line-for-line what this does; I’m not a shell scripting guru. From what I understand, it runs the ps utility to wait until the parent process quits, then opens the path passed in. This solution has the advantage of not requiring a separate tool to do its work. The disadvantage is that spawns several auxiliary processes; a shell, the ps tool, and so forth; often it also logs some cryptic errors to the system log.

In Klicko, since I already had to write an auxiliary tool for other purposes, like installing and uninstalling, it was easy to just add another function to it. But, for simplicity, let’s assume the tool just does wait for its parent process to quit and opens its argument path. There’s a simple way of doing so, by using a pipe. Pipes are already used for communicating between parent and child processes, anyway. Here’s how you could launch the tool using NSTask:

   NSTask* task = [[NSTask alloc] init];
   [task setLaunchPath:@"/path/to/tool"];
   [task setArguments:[NSArray arrayWithObject:[[NSBundle mainBundle] bundlePath]]];
   [task setStandardInput:[NSPipe pipe]];
   [task launch];
   [NSApp terminate:nil];

and the tool would look like this:

int main(int argc, char **argv) {
   char dummy;
   read(STDIN_FILENO, &dummy, 1);
   CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (UInt8*)argv[1], strlen(argv[1]), FALSE);
   LSOpenCFURLRef(url, NULL);
}

Notice that we launch the NSTask with a dummy NSPipe, and terminate immediately afterwards without waiting for the tool to complete. This means that the sending end of the pipe is shut down when the application terminates. On the tool side, we have a read() call trying to get a single byte from the pipe. The parent application never sends anything, so it hangs there until the application terminates and the pipe is shut down; the read statement will return an error (which is ignored) and the tool then calls LaunchServices to re-launch the application.

If you already have all the paths in C string format, and don’t want to use Cocoa for calling the tool, here’s an alternative solution on the application side:

   int fildes[2] = {0,0};
   if (!pipe(fildes)) {
      if (!vfork()) {
         close(fildes[1]);
         if (dup2(fildes[0],STDIN_FILENO)!=STDIN_FILENO) {
            close(fildes[0]);
         }
         err = execl(pathToTool,pathToTool,pathToApplication,NULL);
         _exit(EXIT_FAILURE);
      }
      close(fildes[0]);
   }
   [NSApp terminate:nil];

which does essentially the same thing using BSD APIs.

Many thanks to Mike Ash for explaining the pipe trick to me, and correcting my misunderstanding about child process lifetimes; I originally thought that a child process wouldn’t survive the termination of its parent. Apparently this is true only for shell processes.

Photos licensed by Creative Commons license. Unless otherwise noted, content © 2002-2017 by Rainer Brockerhoff. Iravan child theme by Rainer Brockerhoff, based on Arjuna-X, a WordPress Theme by SRS Solutions. jQuery UI based on Aristo.