Regression: Adobe CS4

So I was talking with my friends a while back, deep into one of my frequent rants about ineptitude in the software industry, when it struck me that I should start blogging this stuff. Also, my friends pointed out that the manner in which I deliver my criticism is particularly funny, and that I should post audio clips of this to accompany the rant.

I’ve decided to start a new section on this site devoted to these rants. The section is called “Regressions” and will contain my rants on various software issues that have been solved correctly before but, for some reason, are (still) present in some piece of software. These rants are a bit harsh: I’m trying to write it how I spit it when I get in the mood, and so if you find intense criticism and profanity offensive, I suggest you stop reading.

So, lets kick this bitch off with Adobe’s Creative Suite 4!

First, the installer is called “Setup.” On a Mac. See guys, on the Mac, we name things after what they really are. The correct name for this application is “Adobe CS4 Installer.” “Setup” is a) Windows-esque and b) useless for determining what this thing actually installs. Consider this regression #1.

Not only is the installer misnamed, but it’s in a folder with some other files. On the disk you see a folder with an icon that looks vaguely archive-like called “Adobe CS4 Master Collection.” I assumed this was the installer; what else would you put on the disk? Instead of the installer, however I see another Finder window open with 6 files: Bootstrapper.dmg, deployment, extensions, payloads, resources, Setup. What is all this crap? I just wanna install the apps dude, I don’t care about any of this junk.

After prompting for a password, the installer then proceeds to open a copy of the installer app running as root. This then makes another couple of child processes, which do something for a bit. Despite Apple’s consistent recommendation for and implementation of installers that use privilege-escalated tools only for the actual file copying portion, it seems that this part is too hard for Adobe, so they just launch the entire installer(s) as root. Nice job guys! Lets hope there’s no security flaws in your installer :) Regression #2.

I then get a dialog about some applications that need to be closed for installation. Photoshop of course is on there duh, so I close that. But what the fuck? Safari and Excel? What does that have to do with Adobe products? Presumably, Adobe thinks that because it can install its horrible PDF plugin for Safari and Office programs, you need to close these programs before you can install! Of course I don’t actually use these plugins because this is Mac OS X, where the graphics subsystem has had PDF support for 7 fucking years, and neither does anyone else who hasn’t been living under a rock. Regression #3.

Finally I get to the list of applications to install. I choose a custom install to see what there is and damn. That’s a LOT of crap to install. And fucking huge too! Acrobat 9 Pro is 1.2GB. How on earth you manage to make a PDF editor that big I have no idea, but clearly the folks at Adobe are good at it. Fortunately, Photoshop, which is about 2000 times more awesome and useful, is about half the size. Clearly Acrobat 9 has some absolutely AMAZING features that warrant this ridiculously huge installation size. Perhaps we should all aspire to make excessively large applications. Regression #4.

So I select some of these components and click install. The install finishes and I check my Applications folder to see the new apps. Wow. There’s a folder for each of the apps I wanted, of course, but there’s also a folder for some of the apps I didn’t want, containing an application container with nothing in it. Clearly along the packaging process Adobe thought that littering my hard drive with crap was acceptable. Regression #5.

You would think a big company like Adobe with a Mac userbase that is (or was) a significant portion of their customer base would be able to put more effort into their presentation, but perhaps to a big company these things are not such critical issues. With no serious competitor to Photoshop, customers are likely to overlook issues like these, if only because they have to.

[Note: Apologies if this seems a bit old -- I wrote it several months ago and completely forgot about it until today]

Posted in Regressions | 1 Comment

Units of mach_absolute_time()

As programmers on OS X are well aware, the units of mach_absolute_time() have been nanoseconds since 10.2, although this was only documented since 10.5. As the iPhone runs a slimmed down version of OS X, I thought it reasonable that the units would remain the same on the device.

[Update] As Justin points out below, this was never the case. A complete misread of the documentation on my part. I managed to work on only machines for which the units of mach_absolute_time() were nanoseconds.

So if you’re trying to get some timing from the iPhone using mach_absolute_time() and seeing times that indicate faster results on the iPhone, (not that this happened to me…), make sure to account for the difference in timebase.

Here’s the code for conversion between the mach_absolute_time() units and nanoseconds:

#include <mach/mach_time.h>

/* Get the timebase info */
mach_timebase_info_data_t info;
mach_timebase_info(&info);

uint64_t start = mach_absolute_time();

/* Do some code */

uint64_t duration = mach_absolute_time() - start;

/* Convert to nanoseconds */
duration *= info.numer;
duration /= info.denom;

/* Log the time */
NSLog(@"My amazing code took %lld nanoseconds!", duration);

Posted in Programming | 8 Comments

Time Machine: Switching Computers

My main Mac is out of service for a few days as the screen gets replaced, so I performed a Time Machine restore onto another computer to use in the meantime. I’ll let this computer continue backing up to the same time machine volume, then do a TM restore back onto my main machine when it’s fixed.

On the temporary machine, Time Machine didn’t want to continue backing up to the same backup folder. From Time Machine’s perspective, this is a different computer and so it creates a separate folder to perform the backups in. The MAC address of the computer is set as an extended attribute (xattr) on this folder so TM knows which folder to use.

Allowing the temp machine to use the old machine’s backup folder is simple, but not as easy as it should be. The goal is to put the temp machine’s MAC address in the place of the original’s in the xattr on the backup folder. However, using xattr -w com.apple.backupd.BackupMachineAddress 00:00:00:00:00:00 doesn’t work. Instead, you need a little program that can write the address using the xattr API.

I’ve written a little program that performs the switch. It turns off acls on the backup volume, sets the new xattr, logs the old MAC address, then turns the acls back on. You’ll need to run it with root privileges.

It’s available here, or in svn at http://svn.shiftedbits.org/public/backupswitch/trunk.

Posted in Leopard | 2 Comments

Key Value Observing Improvements v1.2.2

Quick bug fix update:

1.2.2 Fixes a crash due to a missing implementation of __KVOAdditions__dealloc__original__ on NSObject.

1.2.1 now runs on the iPhone.

The new updates are in SVN at http://svn.shiftedbits.org/public/KVOAdditions/trunk and tagged at http://svn.shiftedbits.org/public/KVOAdditions/tags/1.2.2

Posted in Programming | Leave a comment

Key Value Observing Improvements v1.2

Hot on the heels of the 1.1 bug fix release comes version a freshly rewritten 1.2 with API cleanup, bug fixes, and a great new feature.

Lately, I’ve been using observers to allow an object to observe changes to its own properties. This means I don’t have to override the setter method (I’m using synthesized properties) and the observation is managed the same way that it would be externally. The current KVO implementation (at least, in non-GC land,) however, doesn’t allow you to remove observations from yourself in your -dealloc method. This is due to the way KVO works, by interposing a KVODeallocate function before your -dealloc method. It expects that all observers of the object have been removed at this point, and warns if they are not. This is, of course, blindingly annoying — you now have to create a method that gets called on your objects by their owner before they dealloc so that they can remove their observers.

To fix this, I’ve done a little interposing of my own, putting in another dealloc method before KVODealloc that will remove self observers and call a -KVODealloc method on the deallocating observer object before KVODeallocate is called. If you don’t want automatic removal, simply return NO from +automaticallyRemoveSelfObservations on the class of your choice.

The new updates are in SVN at http://svn.shiftedbits.org/public/KVOAdditions/trunk and tagged at http://svn.shiftedbits.org/public/KVOAdditions/tags/1.2
Continue reading

Posted in Leopard, Programming | 2 Comments

Key Value Observing Improvements

Key value observing is quite a useful tool, no doubt about it. But it has a singularly annoying manner of informing the observer of a change. The -[NSObject observeValueForKeyPath:ofObject:change:context:] method is sent to the observer when a change occurs. It’s up to the implementor to parse the `change’ dictionary to figure out what changed.

When I use observers, I usually want a method called on my class when a change occurs, similar to target/action with UI elements. So my -[NSObject observeValueForKeyPath:ofObject:change:context:] is basically a set of if’s for each key I’m observing that calls the appropriate method.

This turns out to be pretty nasty, especially when you have multiple classes in a hierarchy implementing the method. It wasn’t obvious to me that I needed to call super in this method. Thirty minutes of debugging later, and I was convinced there had to be a better way of doing it.

To this effort, I present two new APIs. To NSObject, I add -[NSObject addObserver:forKeyPath:options:selector:] and to NSArray, -[NSArray addObserver:toObjectsAtIndexes:forKeyPath:options:selector]. The code is available here, or keep reading for the details.

Update 1.1: Two critical bug fixes. Please update to the new version if you have the old one. Also, anonymous svn is now available if you want to use an external. http://svn.shiftedbits.org/public/KVOAdditions/trunk. Continue reading

Posted in Programming | 4 Comments

SSE Optimized Compositing

As part of a graphics API I’ve been working on (for my own use, it’s hardly ready for production,) I decided to try learning SSE optimization by making the compositing routine faster.

I came up with an implementation which, according to my tests, is 3-5X faster than the non-optimized version. The optimized version below composites a single color starting at a destination pixel buffer for a specified run of pixels. It uses source over compositing on a RGBA, 8bpc, integer pixel buffer.

The optimized version is presented below:

Continue reading

Posted in Programming | Leave a comment

Transparency Layer Slowdowns

As the documentation mentions, a transparency layer allows subsequent drawing to be rendered in a separate, fully transparent buffer before being composited to the destination context. In the absence of a clipping region, this buffer is the same size as the destination context, requiring a context-sized buffer regardless of the actual drawing bounds. Creating a transparency layer for a small section of content, then drawing this layer in a window, for example, results in a window-sized buffer for the layer. When the layer is composited into the destination, the entire buffer must be composited — CoreGraphics doesn’t keep track of where the actual drawing is.

This, of course, is slow, especially for several transparency layers.

To help speed things up, clip the context to the bounds of any drawing in a transparency layer before beginning the transparency layer, such as below:

CGRect artRect = CGRectMake(50,50,100,50);
CGContextClipToRect(context, artRect);

CGContextBeginTransparencyLayer(context, NULL);

/* Draw layer content here */

CGContextEndTransparencyLayer(context);

I’ve created a demonstration program to illustrate the effects of clipped vs. unclipped transparency layers. To see the effects, open Quartz Debug (/Developer/Applications/Graphics Tools/Quartz Debug.app) and turn on “Flash screen updates.” Then, open the Transparency Layer program. There are two windows, the left showing the drawing without clipping and the right clipping to the approximate art bounds. The views are redrawing four times per second. Notice that the unclipped view redraws its entire content while the clipped view redraws only the actual art.

Finally, using -[NSView setNeedsDisplayInRect:] clips the drawing to the specified rect.

Posted in Programming | Leave a comment

Leopard CGWindowList* APIs

Leopard brings new APIs at the CoreGraphics layer to gather information about windows on the system. These functions let you find windows relative to a known window, find all windows on the system, including those offscreen, and obtain images of one or more windows as composited by the Window Server.

However, these API’s have an interesting peculiarity: when the documentation says “a CFArray of CGWindowID values” they mean a CFArray of uint32s, not CFNumbers as programmers familiar with CoreFoundation might expect. This holds true for the CGWindowListCreate(), CGWindowListCreateDescriptionFromArray(), and CGWindowListCreateImageFromArray functions, but the two functions that return window information (CGWindowListCopyWindowInfo() and CGWindowListCreateDescriptionFromArray()) wrap all numeric values in CFNumbers.

To create such an array, you’ll have to create it with CFArrayCreateMutable(), such as the following:

/* Make an array with no callbacks */
CFMutableArrayRef windows = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);

/* myWindow is a NSWindow */
CFArrayAppendValue(windows, (void *)[myWindow windowNumber]);

To get the CGWindowID of a NSWindow, you’ll want to use -[NSWindow windowNumber]. Even though the documentation specifies that this isn’t the same number as assigned by the WindowServer, it turns out that it works fine with the CGWindow* APIs.

Posted in Leopard, Programming | 1 Comment

Time Machine Menu

The Time Machine application, when in the dock, enables access to functions for showing Time Machine, backing up now, stopping an existing backup, browsing other Time Machine disks and showing Time Machine preferences. If you’re like me, you’d like access to these features without the app in the dock; unfortunately, you can’t — by default.

I’ve done a bit of digging and was able to implement all but one of these features in a menu extra. Download it and the source here.

And yes, the menu icon doesn’t scale nicely :)

Posted in Leopard, Programming | 4 Comments