Posts tagged ‘tech’

Middle Click Close For Safari

I love Safari on my shiny Mac Pro, particularly because it’s so much faster than Firefox. But one thing I missed from Firefox was being able to do a middle-click on a tab and have it close. After several failed attempts, I finally got some help from Mark Rowe who pointed me to a proper method-swizzling implementation. After switching my code to use that swizzler, everything fell into place. I’ve been happily using it ever since, I just never released it. I got an email the other day asking for it, so here it is.

Unlike my Export to Archive plugin for iPhoto, I didn’t build an installer for this one. It’s easy to install, but you still have to do it yourself. And there’s a dependency: SIMBL. Now, before Leopard shipped the word was that Input Managers would no longer be supported. After Leopard shipped we saw that wasn’t entirely true. So, once you get SIMBL installed and working, using my plugin is easy. This should work on both Tiger and Leopard. I’ve only tested it recently on Leopard, but I originally wrote it on a Tiger system.

  1. Get the binary package: MiddleClickClose.zip
  2. Create ~/Library/Application Support/SIMBL/Plugins if it doesn’t exist
  3. Unzip the MiddleClickClose.zip into this directory. You should end up with a directory called MiddleClickClose.bundle
  4. Restart Safari

If all goes well, you should now be able to use your middle mouse button to close Safari tabs. It works for me. If it doesn’t work for you, run the OSX Console and look for “MiddleClickClose loaded” in the “Console Messages” section.

If you want the source code, download it here.

I should mention that this really is a dirty hack, and may not work with future versions of Leopard. It works for me, but your mileage may vary. I hope it works for you, but it may not.

This code is distributed under the GPL v2.

Ordered: 13-port USB Hub!

I have a ton of USB devices and I’m am constantly running out of ports. I hate having to unplug one device to plug in another (much like Norm Abram has multiple routers so he doesn’t have to change the bits), but with only four ports on my Mac Pro and five on an external hub, that’s what I’ve been reduced to.

Until now. I just discovered Synchrotech’s 13 Port USB 2.0 Hub, which looks like exactly what I’ve been wanting. I just placed the order, and I should have it in about a week. This will give me a total of twenty-two ports, which should last me for a while. :-)

Are My Local Krogers Blocking EDGE Data?

Since getting my iPhone, I’ve visited two Kroger stores near my house and in both stores I was unable to access the Internet over the EDGE network. In both stores I had 5 bars of signal, and in both stores I was able to place calls, so it wasn’t a signal problem. But in both stores, trying to access any website using Safari failed with a message about not being able to find the server. Now, in both stores the iPhone popped up its list of available WiFi networks and in both stores that list contained a single network called “nomad5.” In both cases the network was private, so I didn’t try to connect.

Am I seeing something nefarious where there is nothing? Is it just coincidence that in both of these Kroger stores I was unable to get on the EDGE network, despite the fact that I had a perfect signal? Is there some technical aspect that I don’t understand? I’d love an answer to this. There is a third Kroger that is about 5 minutes from my house, so I will try to get to it and run a test to see what happens there.

QuickTime Player Annoys Me

The QuickTime player that Apple ships really, really annoys me. There are two reasons why I hate it: volume and geometry defaults. By this I mean that the player defaults to 100% volume, which is really loud, and top-left-of-window placement, with no way to set either of these in preferences. I don’t know who on the QT team at Apple thought these were good/valid defaults, but regardless of stupid defaults, I should be able to set preferences for these and have them respected. I would prefer the player to startup with a volume in the 25 - 50% range, and centered on my screen, or at least closer to the middle than the top-left. It’s absurd that these things can’t be changed by the user. I notice from Apple’s docs that if you want to embed a QT movie in a webpage you can set the volume…

I wrote an Automator script that changes the volume once the player starts, but there’s no way to set this workflow as a default. In other words, the only way I could use it was to drag a movie I wanted to watch onto the workflow’s icon, rather than just double-clicking on the movie file.

Is anyone else annoyed by this?

Monitor Log Using Console.app

When I’m working on a Linux desktop, I will frequently open a new shell window with tail -f on a log file I need to watch, such as my JBoss log. When I switched to my glorious Mac Pro I did the same thing, but it wasn’t exactly what I wanted.

I discovered that you can execute Console.app from the command line, so I have created a Bash alias that I run when I need to. Here’s what I put in my ~/.alias file:

alias jbtail=’/Applications/Utilities/Console.app/Contents/MacOS/Console /opt/jboss/server/default/log/server.log &’

(Make certain all of that is on one line!) Obviously you should change the path to the log to be something you want to monitor. You can change the alias name to something else if you like. After editing the file, either logout or type . ./.alias (notice the dots) and that will load up the alias. Now you can type jbtail (or whatever you chose to call it) and Console.app will open for you watching changes to that log file.

What’s really nice about this method as opposed to just using Terminal.app is that Console.app remembers things like window geometry, fonts and, most importantly, which monitor it last ran on. This is really nice for people like me who have two monitors. I always want this log to show up on my second monitor, and once I’ve placed it there, future invocations of my alias will open it right where I left it.

If you already have a .bash_profile or .profile file in your home directory, look for a line that looks like this

test -r ~/.alias && . ~/.alias

If you see that, you’re all set. If not, adding it will ensure that your aliases get loaded when you next login. You could add the alias=… line directly to .profile or .bash_profile if you like, and skip the .alias file altogether. I have several aliases and I like to keep them in their own file.

No Flash By Default In Fedora 8? Huh?

I saw that Fedora 8 had been released, so I thought I’d install it inside Parallels and check it out. After a long install, I booted up in 800×600 mode… Lovely. I googled to find out how to change the screen resolution and discovered I needed to edit /etc/X11/xorg.conf, so I typed sudo vi /etc/X11/xorg.conf and was promptly told that my userid was not in the sudoers file, and that “this incident will be reported.” I’ve just gotten used to OSX and Ubuntu that handle that sort of thing, so this was a bit of an annoyance. Of course, I was able to just su without issue…

So already, I’m not overly impressed. The WTF? moment came when I ran Firefox and discovered that there’s no Flash plugin installed! Who thought that was a good idea? Flash is used on 80%, 90% of all websites, and the Fedora people thought it would be a good idea not to ship the plugin?

OK, so it should be easy to install, right? I clicked on the “click here to download plugin” button and after it chugged for about 10 seconds I was told that it failed. So I went to the Adobe website to download the installer. I’ve now got it installed, but what a joke. The Linux zealots love to claim that “linux is ready for the desktop” but it’s stupid things like this that really show that it isn’t. Granny isn’t going to know how to manually install the Flash plugin.

Objective-C Strings Are Not C Strings

I got bitten today by the fact that Objective-C strings in Cocoa programming are not the same things as plain old C strings. The problem is that Objective-C is essentially an object-oriented veneer on top of plain old C; sometimes it matters that you remember this, and other times it doesn’t. This was one of the times it mattered.

A little background. A while back I wrote and released ExportToArchive, a somewhat useful plugin for iPhoto. It allows you to select photos from your iPhoto library and export them into a few different archive formats. About three days after releasing it, I got an email from a guy who had just tried to export his entire library, 756 photos, into a single archive. He was perplexed because iPhoto just seemed to hang/lock up. The problem was that when I wrote the thing, I never considered that anyone would try to archive more than a few photos at a time. Thus, once you made your selections and started the export, I copied each file to a temporary directory, and then archived the copies. That works great for 10 or 20 photos, but not so well for 756.

The answer to that problem was not to copy the files, but to make a symlink of each photo into that temporary directory and then archive the files by dereferencing the links. It’s still going to take a while to archive the photos, but there will no longer be a copy phase, which should make things faster.

Which leads to today’s adventure. There’s not a native Obj-C or Cocoa method to create a symlink. There is a method on NSFileManager called linkPath:toPath:handler: but that creates a hard link, which won’t span file systems. So I was forced to use the C function symlink which takes two arguments: the source path and the destination path. This seemed easy. I would pass the absolute path name to the original file as the source argument, and the generated name for the link as the second. Easy-peasy.

Well, not really. Since symlink is a C function it, like most other C functions, tells you bugger-all about why a failure occurs. The return value when I tried to call it was -1 which means, “something bad happened.” I then had to consult the C value errno to get more info. The value of errno was 22 which, according to the header file errno.h means:

#define EINVAL 22 /* Invalid argument */

An invalid argument. OK. How about telling me which argument is invalid since I did, after all, pass in two arguments.

I tried escaping spaces and quoting the entire string, but nothing worked. Finally, it dawned on me: Objective-C strings are not C strings. symlink was expecting a plain old C string, but I was passing in something completely different: instances of the Obj-C class, NSString. So, the way to fix this was to pass in C strings, and how do you get C strings from NSString instances? Right, call UTF8string on them. Thus my call to symlink went from

rc = symlink([source UTF8String], [dest UTF8String]);

to

rc = symlink([source UTF8String], [dest UTF8String]);

and all seems to be working properly now.

I’m going to do a bit more testing on this, but I will probably release this new version tomorrow.

Export to Archive iPhoto Plugin

Ever since I switched to my exquisite Mac Pro, I’ve been using iPhoto. A few weeks ago when Apple announced the new iLife ‘08, I bought it. Last week while using iPhoto, I wanted to zip up a bunch of photos so I could move them to another machine. But I couldn’t find anything like this in iPhoto. There’s an exporter, but it doesn’t support zip files. You can right-click on a photo and select “Show File”, but that’s clunky and wouldn’t work well if you had multiple files you wanted to zip up.

So I wrote an exporter plugin to handle this. It’s called Export To Archive, and it supports three archive types: Zip, GZip and BZip2, depending on which flavor you prefer. I personally like bzip2, because the compression is much better than the others, but YMMV.

Once you install the plugin, you select the photos and/or events you want to export, then select Export… from the File menu (if using an older iPhoto, the Export… menu item is under the Share menu). The “Export Photos” dialog opens with a new tab labeled “Archive.” Selecting this tab reveals a pane that looks like this.

The dropdown reveals the other two archive types. Select the type you want and then press the Export button. You will be presented with a “Save As” dialog where you can enter the name of the new archive file. You don’t need to specify an extension as the plugin will append the correct extension based on the archive type. After you enter the name you want and press OK, you should see a progress dialog and then you are taken back to your photo library. If everything went as planned, you have a shiny new archive where you told the plugin to put one. If not, you should get an error message telling you what happened.

The plugin does not keep any directory paths that might have existed in your iPhoto library. I considered making a directory for each “event” (iPhoto 7.x only) but I believe a photo can be in multiple events, so this seemed like a bad idea. Maybe later.

Also, if you select multiple photos with the same name (but from different directories, obviously), then I handle it like Safari does when downloading the same file twice. I append an underscore with a number to the filename before the extension. So if you had two files called 100_1234.jpg, then one would be called 100_1234.jpg and the other would be 100_1234_1.jpg. If there were three files, the second would have _2 appended, etc. This seems like a reasonable solution to this issue.

I should mention one more thing about the compression methods. If you use Zip, then any resource fork info is lost. This probably isn’t a problem, but I felt I should mention it. It’s how the /usr/bin/zip program works. If you choose either GZip or BZip2, then the photos are first put into a tar file and then compressed. The /usr/bin/tar program that comes with OSX does preserve resource fork info into the archive. If you look in the archive, you will see that for every file, there is a similarly-named file that is vastly smaller than the original. If you expand the archive on a Mac, then you only end up with the files you asked for. But on other systems, the resource fork “phantom files” will be expanded to the disk along with the photo files. The file names start with ._ so on Unix systems they will be hidden. On Windows, you will probably see them. Either way, it’s extra crud. I tried to figure out a way to get rid of these files, but there doesn’t seem to be one. And maybe we don’t want to get rid of them, anyway. Thus, if you are going to move your archive to another Mac, you might want to choose GZip or BZip2. If you’re going to it to some other system, choose Zip.

Want it? There are a couple of ways to get it, depending on which version of iPhoto you have.

Both of these installers install the plugin for everyone on the system. PackageMaker.app that Apple provides doesn’t seem to provide a way to install it for just the curent user. This means that the plugin will go to one of two places, depending again on your iPhoto version

  • iPhoto 7.x: /Library/Application Support/iPhoto/Plugins
  • iPhoto < 7.x: /Applications/iPhoto.app/Contents/PlugIns

If you’re running iPhoto 7.x and you’d rather install it just for yourself, you can download this zip file and install it by hand. You should put it in ~/Library/Application Support/iPhoto/Plugins.

This is a universal binary. I have tested this on a Mac Pro with iPhoto 7.x, and on an iBook G4, with iPhoto 5.x. If you have some other config and it doesn’t work for you, let me know, providing a crash dump, if you have one. I don’t know that I can make it work, but I could look into it.

If you’d like the source code, get it here. You’ll need Xcode 2.4.1 to open the project.

That’s about it. This is free software, released under the GNU GPL. If you like it, tell your friends. If there’s a feature you want, let me know, or implement it yourself and provide me a patch to include in the source.

09/12/2007 Update: I’ve now had two three reports of the plugin not working with iPhoto 6.x. That’s the version I was unable to test with, so if you have iPhoto 6, use caution. And if you have iPhoto 6 and it crashes on you, would you send me the crash dump? It can be found in ~/Library/Logs/CrashReporter/iPhoto.crash.log

09/13/2007 Update: I’ve now had several reports that the plugin doesn’t work with iPhoto 6.x, so if you’re using that version, don’t install it. I am trying to figure out how I can test against 6.x, since I don’t have a machine with that version. Stay tuned for updates.

Uninstallation Instructions
If you need to uninstall, delete the ExportToArchive.iPhotoExporter directory. Depending on the version of iPhoto you are using, it will be in one of two places:

iPhoto 7
/Library/Application Support/iPhoto/Plugins
iPhoto 5 or 6
/Applications/iPhoto.app/Contents/PlugIns

Delete the ExportToArchive.iPhotoExporter directory from whichever place it’s been installed to, and once you restart iPhoto, you should be fine.

iWork vs. CVS

I recently bought the new iWork ‘08 suite from Apple and have started using the tools for stuff I used to use NeoOffice for. But I noticed something yesterday that is disconcerting.

Like a lot of people, I store my documents in a CVS repository that is backed up to another disk. I checked in a few documents created with Pages and Numbers, and everything seemed fine. That is, until I re-saved any of the documents.

The problem lies in the fact that a “document” for Pages or Numbers (and probably for Keynote as well) is not a monolithic file like a .doc file from Word. They are directory structures (”bundles” is the Apple term) that the Finder and the applications that use them treat specially. Any program in the /Applications folder is the same type of thing. When you check something into a CVS repository, CVS creates a hidden CVS directory in each sub-directory of the thing being checked in. After checking in one of these documents, I went into the document through Terminal and verified that the CVS directories had been created. So far, so good.

When things went awry was when I re-saved a document. Instead of just changing the necessary files inside the “document,” Numbers deleted and re-created the entire directory structure. Thus, those CVS directories were toast. There’s no good way to recover from this, because those files now look like non-CVS files, but the server already knows about them. To my knowledge, there’s no painless way to handle this situation.

I don’t believe that Subversion is in any better position. When using SVN, you get a .svn directory created in each sub-directory of the document, but those will also get whacked when the document is re-saved. I haven’t tested that assumption, but it seems logical.

I tried to come up with a solution to this, but I’m stumped. I looked into cvswrappers just to see if it could help, but it doesn’t look like it. I also considered a pre-commit script and a post-checkout analog (if there is one), but this didn’t seem like it would really get us there.

The only solution I see is for Apple to stop whacking the directory structure and just change the files inside it that it needs to, and stop molesting the version-control special files. Maybe they can implement this behavior when they add support for OpenOffice and the OpenDocument format…

Capturing Middle-Mouse Click in Safari

I have been a consistent user of the nightly builds of the WebKit project for some time now. For those of you who don’t know, WebKit is essentially the work-in-progress that will be the next production version of Safari. I like the nightlies because they are extremely fast, and while they occasionally have problems, that’s OK.

The point of that discussion was to say that I have stopped using Firefox on my Mac, because the nightly builds of WebKit are so much faster. But there’s one problem. In Firefox I could click a tab with my middle-mouse button, and it would close. Safari doesn’t have that feature, and that’s the one feature from Firefox that I really miss.

So, I’ve been trying to solve this problem using SIMBL. What SIMBL does is let you write a standard Cocoa bundle and have it load into another program, like Safari. Once loaded, you can replace methods in the application with your own versions, in a way known as “method swizzling.” I have successfully written a Cocoa bundle, made the approrpriate changes to make it loadable by SIMBL, and have loaded it into Safari/WebKit. I have logging statements at various points in the bundle, and I can see these on the system console, thus I know it’s loading.

Once the bundle was loading, I needed to pick the objects and their methods that I thought would be most likely to let me do what I needed, and then swizzle in my changes. Using F-Script Anywhere I was able to identify a single tab as an instance of TabButton. Using class-dump I was able to generate header files for Safari that would let me see the methods on TabButton and it’s parents. My first thought was to override mouseUp:. This works, and I can now trap mouse events when you click on any tab. According to the Apple docs, once I get a mouse event, I should be able to call [theEvent buttonNumber] to figure out which button was pressed. Well, maybe. No matter if I clicked with the left- or middle-mouse button, [theEvent buttonNumber] always returned 0. Further digging in the docs turned up an event called NSOtherMouseUp that is sent when a button “other” than left- or right-mouse is clicked. Supposedly, I should be able to override otherMouseUp: to get those events. I have successfully swizzled this method, but it never gets called. I know that TabButton had a version of this method, because when I swizzle, I can tell if there was already a method there, and there was. I’m just not sure why it isn’t being called.

So what did I get working? Well, after working a long time trying to get the middle-mouse detection working, I decided to punt for the moment. I added some code to the mouseUp method to check the event for modifiers and if the user held down Command while clicking, then I will close the tab. This is close to what I want, but nearly as sexy as just middle-mouse clicking. In case you’re interested, my TabButton.m looks like this:

 	 1 #import "TabButton.h" 	 2 #import "WebKit/WebKit.h" 	 3  	 4 @implementation TabButton (MCCSwizzle) 	 5 - (void)_mcc_mouseUp:(NSEvent *) theEvent 	 6 { 	 7   NSLog(@"_mcc_mouseDown"); 	 8   int buttonNumber = [theEvent buttonNumber]; 	 9  	10   NSLog(@"buttonNumber: %d", buttonNumber); 	11   NSLog(@"type: %d", [theEvent type]); 	12   NSLog(@"modifierFlags: %d", [theEvent modifierFlags]); 	13  	14   if ([theEvent modifierFlags] & NSCommandKeyMask) { 	15     NSLog(@"Cmd-Click!"); 	16     [self closeTab: theEvent]; 	17   } else { 	18     [self _safari_mouseUp: theEvent]; 	19   } 	20 } 	21 @end 	

and then the swizzling looks like this:

 	 1 #import  	 2 #import "AppController.h" 	 3 #import "MiddleClickClose.h" 	 4  	 5 typedef struct objc_method *Method; 	 6  	 7 struct objc_method { 	 8   SEL method_name; 	 9   char *method_types; 	10   IMP method_imp; 	11 }; 	12  	13 BOOL MCCRenameSelector(Class _class, SEL _oldSelector, SEL _newSelector) 	14 { 	15   NSLog(@"OLD: %s", _oldSelector); 	16   NSLog(@"NEW: %s", _newSelector); 	17  	18   Method method = nil; 	19  	20   // Look for the methods 	21   method = (Method)class_getInstanceMethod(_class, _oldSelector); 	22   if (method == nil) 	23     return NO; 	24  	25   // Point the method to a new function 	26   method->method_name = _newSelector; 	27   return YES; 	28 } 	29  	30 @implementation MiddleClickClose 	31 + (void) load 	32 { 	33   int rc; 	34  	35   rc = MCCRenameSelector([TabButton class], @selector(mouseUp:), 	36                                    @selector (_safari_mouseUp:)); 	37   NSLog(@"RC: %d", rc); 	38  	39   rc = MCCRenameSelector([TabButton class], @selector(_mcc_mouseUp:), 	40                                                  @selector(mouseUp:)); 	41   NSLog(@"RC: %d", rc); 	42  	43   rc = MCCRenameSelector([TabButton class], @selector(rightMouseUp:), 	44                                    @selector (_safari_rightMouseUp:)); 	45   NSLog(@"RC: %d", rc); 	46  	47   rc = MCCRenameSelector([TabButton class], @selector(_mcc_rightMouseUp:), 	48                                                  @selector(rightMouseUp:)); 	49   NSLog(@"RC: %d", rc); 	50  	51   NSLog(@"MiddleClickClose loaded"); 	52 } 	53  	54 + (MiddleClickClose*) sharedInstance 	55 { 	56   static MiddleClickClose* plugin = nil; 	57  	58   if (plugin == nil) 	59   { 	60     plugin = [[MiddleClickClose alloc] init]; 	61   } 	62  	63   return plugin; 	64 } 	65 @end 	

Line 35 renames Safari’s original mouseUp: method to _safari_mouseUp: and then line 39 renames my _mcc_mouseUp: method to mouseUp:. The otherMouseUp: method is handled on lines 43 and 47, but as I said otherMouseUp never gets called.

What’s especially frustrating about this is that I know Safari knows how to bag a middle-click, because I frequently will middle-click on a link in a web page, and Safari will open that link in a new tab. So, why can’t I get a middle-click in the TabButton instance? Does anyone have any ideas on this? I’d appreciate any pointers. I feel like I’m thiiiiiiiiiiis close to getting this working, but there’s some small piece of info that is eluding me.

03/03/2008 Update: I got this working and have released it under the GPL. Read about it and get it over here.

Third Time’s The Charm on Mac RAM — I Hope

Being at the end of a UPS route is hard. Tuesday morning I saw from the UPS website that my third set of RAM from Crucial was “out for delivery” from the local hub. What this means is that it’s on a truck, heading for my house. Unfortunately, we’re at the tail-end of said route, and I have yet to receive a UPS delivery before 4:00 PM. We were going to be leaving around 5:00 and since UPS is a “drop and run” courier, if it got there after we left, it would have been sitting on the porch for several hours until we got home. Fortunately, it arrived about 4:45. So it got inside the house, but wouldn’t get inside the Mac until later.

When I got home Tuesday night, I installed the RAM. So far, it’s working perfectly. Of course, the first set worked perfectly for a week or so, so I’ll have to just keep an eye on it. Thus, once again, my Activity Monitor looks like this

Mac Pro RAM Woes

You may remember how excited I was about a month ago when I got the 2GB RAM upgrade from Crucial for my Mac Pro. Well, about two weeks ago I happened to notice in Activity Monitor that I only had 3GB of RAM. That wasn’t right; I had 4GB. I started testing, and discovered a problem with the RAM. System Profiler was reporting 6 512MB sticks of RAM, instead of 4 512MB and 2 1GB sticks. I tried moving the sticks around to various banks, but never got it to see more than 3GB. Also, there are four LED’s on each of the risers that correspond to the four RAM banks. When you boot the system, these lights come on for about 2 seconds, then go out. There was one Crucial stick whose LED stayed on. No matter which bank I put that stick in, its light stayed on.

Saddened by this, I called Crucial. I spent just a few minutes on the phone with them before they agreed that there was clearly a problem, and that they would replace them. They have excellent customer and technical support. I shipped the two sticks back to them and waited.

Two days ago UPS arrived with my replacement sticks. I shut down my Mac and put the two new sticks in banks 3 and 4 of riser A, leaving the 512MB sticks in banks 1 and 2 of risers A and B, and booted. I logged-in, brought up Activity Monitor, only to see 3GB reported. System Profiler says 6 512MB sticks, just like before. The new RAM is doing exactly what the first set of RAM did. I tried every combination of placement of the sticks I could think of, including removing ALL the Apple RAM, and just putting the Crucial sticks into banks 1 and 2 of riser A. In that case, the Mac only saw 1G of RAM.

So it was back to Crucial tech support. They agreed it sounded like a bad stick (again) and are going to replace them again. I just shipped back the two latest sticks, and am once again waiting on a shipment from Crucial. I hope this one works. If it doesn’t, I’m just going to pony up the 2x extra and buy direct from Apple.

The Crucial guy said it was “hard to believe” that I got two bad sticks in a row, and I agree. But what else could it be? If I only had two banks for RAM in the computer, then you could blame it on the computer itself. But I’ve got eight banks to play with, and have tried them all. I don’t think it’s a problem with the Mac itself. I could be wrong, but I don’t think so.

My Mac Pro Goes Boogety Boogety-er

I said before that my Mac Pro was fast. But lately it’s been bogging down a bit when I have lots of apps open. Especially if I am using Parallels.

But UPS just arrived with a little shipment from Crucial: another 2GB of RAM. O, glorious day! Notice the screenshot of my activity meter:

AppleScript and iTerm: Sweet!

I use iTerm for all my command-line needs. I really like it, especially the tabbed interface. I typically have two to three terminals open for my local box, plus two to three for various systems in the company rack. iTerm has a nice bookmark feature that lets you save commands (like ssh me@foo) to open these various windows, but it’s not exactly what I want. The reason for this is that I either have to leave the bookmark drawer open all the time, or I have to click to open it, then double-click the bookmark I want to launch, then close the bookmark drawer. And that’s a real drag for me, because I’m not really a mouse guy.

So yesterday I started thinking that it had to be possible to do this using AppleScript, and indeed it is. My solution is not optimal, as I had to use two files, but it’s close to optimal. It’s approaching optimal. Here’s the AppleScript file, which is called it.scpt:

 		    1 on run argv 		    2     tell application "iTerm" 		    3             activate 		    4  		    5             tell the first terminal 		    6                     launch session (item 1 of argv) 		    7             end tell 		    8     end tell 		    9 end run 	

Next is a regular shell script, called it that calls it.scpt:

 		    1 #!/bin/bash 		    2  		    3 if [[ $# == 0 ]] 		    4 then     		    5     echo "Usage: it " 		    6     exit 		    7 fi 		    8  		    9 osascript ~/bin/it.scpt $* 	

You can see that the shell script checks to see if you’ve specified a bookmark name to launch. If you haven’t, it tells you how to run it. Once it’s established that you have specified a name, it calls the AppleScript file it.scpt, which I’ve placed in ~/bin for convenience. (Both files are in ~/bin on my system.) The AppleScript tells the currently-running iTerm to activate (probably not needed) and then tells it to launch the requested bookmark in a new tab. I don’t need to worry about the case where iTerm isn’t running, because I would execute this script from within iTerm. If you specify a non-existent bookmark, it just opens a new shell on your local system, which is OK, I guess.

So, to run it, I would type something like

it web1

to open the bookmark called “web1.” And that’s what I wanted.

I was a bit surprised that I was unable to just have one file. I should have been able to put a she-bang line in it.scpt and have it work. In other words, I should have been able to have

 		    1 #!/usr/bin/osascript 	

and then the rest of the script, but I got an error when I tried that.

If anyone knows an easier way to do this, please let me know.

04/11/2007 14:20 Update: A reader sent in how you can combine the two scripts into one, using osascript’s -e switch. I knew about this switch, which let’s you specify the program on the command line, but I’ve seen so many horrible (ab)uses of the same option in Perl that I didn’t even try it. What I didn’t know was that you can have embedded line feeds inside the quotes, so you can still have a nicely formatted script. Here’s the new and improved, single-file version of it:

 		    1 #!/bin/bash 		    2  		    3 if [[ $# == 0 ]] ; then 		    4     echo "Usage: $0 " >&2 		    5     exit 1 		    6 fi 		    7  		    8 osascript -e 'on run argv 		    9     tell application "iTerm" 		   10             activate 		   11  		   12             tell the first terminal 		   13                     launch session (item 1 of argv) 		   14             end tell 		   15     end tell 		   16 end run' $@ 	

04/12/2007 11:46 Update: This tip got posted over at MacOSXHints.com and has gotten some comments. Based on those comments, below is the latest version, which includes the ability to get a list of available bookmarks to launch by typing it list. Here it is:

 		    1 #!/bin/bash 		    2  		    3 if [[ "$#" = "0" ]]; then 		    4     echo "Usage: 'it bookmarkname' or 'it list'" && exit 1 		    5 elif [[ "$1" = "list" ]]; then 		    6     defaults read ~/Library/Preferences/iTerm|grep Name 		|grep -v NSColorPicker|awk '{$1="";$2=""; print $0}'|tr -d ';' 		    7 else  		    8 osascript <<ENDSCRIPT 		    9 on run argv 		   10   tell application "iTerm" 		   11     activate 		   12     tell the current terminal 		   13       launch session "$1" 		   14     end tell 		   15   end tell 		   16 end run 		   17 ENDSCRIPT 		   18 fi 	

Is This Click-Fraud?

I’ve been seeing lots (and lots) of traffic at work from two particular domains

  • e-mailpaysu.com
  • cashmoneyemail.com

which both sound like click-fraud houses to me.

I have looked at their sites and both offer to “pay you to read email,” which, again, sounds like nothing more than click-fraud. My guess is that they show you an email with an AdSense ad, or Y!’s ads, or whatever, and then you click on it. They get the revenue from the click and share it with the person doing the clicking. But the advertiser is paying for ad impressions/clicks to people who have no desire to buy the product or service, and are only clicking on the ads because they are being paid to do so. How can this not be fraud?

Am I wrong on this? If I am, then my apologies to the two websites. But I don’t think I’m wrong.

My Mac Pro Goes Boogety Boogety

I have a Mac Pro. It goes fast. A full dump of my production MySQL database is about 481 MB. To load that into one of my development machines (dual 1.4MHz 64-bit AMD’s) takes about 40 minutes.

On my Mac Pro, it takes 7.5 minutes.

iTunes Script Menu on Context-Menu?

Does anyone know if it’s possible to duplicate the iTunes Scripts menu in the context-menu? IOW, I want to right-click on a track and have a submenu on that context-menu called “Scripts” and all my installed scripts would be there. It doesn’t have to be called “Scripts” but you get the idea.

The reason this is important is that I have two monitors. I leave iTunes running on the second monitor which works perfectly 99 2/3% of the time. But since OSX has only a single menubar for all applications, and since that menubar only lives on the primary monitor, when I want to do something in iTunes, the menubar is actually on the other monitor. This makes using scripts from the main Scripts menu a pain. Thus my desire to context-ify the Scripts menu.

Is this possible? Anyone?

03/15/2007 17:53:23 Update: I did come up with something that works pretty well, though it’s quite different from what I asked for. The script I was really wanting to easily run sets the last played date on the currently-playing track to the current date and time, increments its play count and then skips to the next track. This is something I’ve been wanting for a long time, so that songs that Party Shuffle hits me with that I don’t like will be buried for all time. Anyway, the script works, but I wanted an easier way to get to it. What I really wanted was a global hotkey that would invoke the script, no matter if iTunes was up front of not. What I did was opened the script in the AppleScript editor, and then saved it as an application. Once I had the app, I added it to the dock. Now, whenever I click on it, it does what it’s supposed to do, then exists. It works pretty well.

03/19/2007 11:13:23 Update: I now have an even better solution. I installed QuickSilver which allows you to map just about anything to global hotkeys. I replaced iMote, which I had been using to give me global iTunes hotkeys, with the functionality in QuickSilver, but I also got more. I was able to map Opt-Cmd-PgDn to my “Skip and Update Play Count” script, which executes within iTunes itself. This is much faster than launching the program I mentioned in the update from Thursday. I’m still digging into QuickSilver, but even if it didn’t do anything else, I’m happy with it.

Vim Syntax-Highlighting Works In a Shell (I Didn’t Know That!)

I’ve been using the great Vim editor for many years (and vi even longer), but I just learned something about it this morning. Usually when I use Vim, I’m using gvim, which means it opens in its own GUI window. I had a stanza inside my .vimrc that would set some things only if Vim was running in GUI mode, and one of those things was syntax-highlighting. The bummer was that whenever I’d have to use Vim inside a shell (typically via ssh) I didn’t get the pretty pretty colors.

This morning I was looking at the FAQ for iTerm and noticed a screenshot section. I clicked on it, and one of the shots was of Vim, running inside iTerm, with full syntax-highlighting. I was surprised. I had never even tried to enable syntax-highlighting inside a shell. For some reason it never occurred to me that it would work, or even to try it. So I Cmd-Tabbed over to a shell, opened Vim on an XML file and typed :syntax on and BLAMMO! full-color XML. Very nice! I then edited my .vimrc to move the syntax on directive out of the GUI-only section and into the common area on all the machines I work on. It works perfectly. I can’t believe I’ve lived so many years without this feature turned on, but I’m quite happy now.

First Mac “Exploit” from MOAB — Yawn…

Yesterday was the start of the Month of Apple Bugs, and I have to say it was a big snooze. The “exploit” supposedly allows a malicious QuickTime “movie” file to run arbitrary code on your Mac. Sounds scary, doesn’t it? Yep. The only problem is you have to really work at it to get the exploit to actually do anything. Supposedly, following this link will demonstrate the exploit and will speak the oh-so-classy message “Happy new year shit bag” using the Mac’s speech synthesizer. Unfortunately, I couldn’t get the exploit to work.

I tried following the link with Firefox and two versions of Safari. In all three cases I got a page with some garbled text on it, but nothing exciting. It’s always funny when someone reveals a supposed “exploit” and includes instructions for “if it doesn’t work for you, then try this.” And that’s what we have here. They have included a Ruby program to generate a file that is just like what you can download, and after running it, you should then “open pwnage.qtl”. I did this, and while QuickTime did open and crash, I didn’t hear the message. To be fair, and to ensure that I didn’t just miss hearing the message, I changed what the “exploit” should try to do. Instead of speaking the message, I changed it to open a terminal window, using “/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal”. Guess what happened. Nothing.

All of this tells me this “exploit” is nothing more than a Mac-hater trying to make people think that Mac’s are “just as vulnerable” as crappy Windows machines.

Let’s see if the rest of the month is as scary and exploitable as yesterday was…

Pasteboard Op Hosed My Mac

Today, during lunch, I continued to work through Aaron Hillegass’s book, Cocoa Programming for Mac OS X. I was finishing up the chapter on using the Pasteboard for cut & paste, and my sample app was working as it should. And on my shiny Mac Pro, it ran very fast, indeed.

Then I got to the “Challenge” section… The challenge was to have the app copy its data to the pasteboard in both text and PDF formats. I got this working in about 5 minutes; no sweat.

Then I got creative… In the “For the More Curious: Lazy Copying” section, he explained how to have an application tell the pasteboard that it could provide data in various formats, just like usual, but to only put the data on the pasteboard when specifically asked by another app. I thought to myself, “Self, you should implement that with the text + PDF copying you’ve already got working.” And so I did. I got the code written in just a few minutes, referring to the API docs for various help. I ran my app, and copied the data. So far, so good. Then I went into the Preview app, selected File>New From Clipboard… and that’s when things started going South. I got the spinning beach-ball on Preview, and never a window opening. I toggled back over to my app, and it, too, was spinning. I killed Preview and my app, but trying to restart my app resulted in the window showing up, but the spinning beach-ball, and nothing else.

At this point, I figured I had hosed something, so I killed off Xcode and tried to restart it. Same thing as with the others: menubar + spinning beach-ball. Now, I figured a reboot was going to be required. And so it was, but actually rebooting proved to be a problem. I started killing off all my apps and while some would actually quit, some wouldn’t. I tried killing them from Activity Monitor, which worked on some, but not others. I then went to the Apple menu and selected Restart… which eventually timed out trying to kill the running apps. I tried again, but it still wouldn’t restart. I killed some more apps, and tried restarting again, but no dice. I finally killed the power to the box and turned it back on.

If anyone is interested, here are the relevant pieces of code that were involved in this fiasco.

The class is called BigLetterView, and it’s a subclass of NSView. The first method to see is copy:

 	- (IBAction) copy: (id) sender 	{ 	    NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; 	    [self declarePasteboardTypes: pasteboard];  	    NSLog(@"Backing up data"); 	    [self backupDataForCopy]; 	    NSLog("@Backed up data");  	    // [self writeStringToPasteboard: pasteboard]; 	    // [self writePdfToPasteboard: pasteboard]; 	} 	

Next is the method called from copy: that makes a copy of the data so that if the current values changes, a paste from another app will get the data as they were when the copy was invoked.

 	- (void) backupDataForCopy 	{ 	    backupString = [string copy]; 	    backupPdf = [self generatePdf]; 	} 	

Moving right along, we come to the method that is invoked when another app tries to paste our data from the pasteboard.

 	- (void) pasteboard: (NSPasteboard *) pasteboard provideDataForType: (NSString *) type 	{ 	    NSLog(@"Pasteboard requesting data of type: %@", type);  	    if ([type isEqual: NSStringPboardType]) 	    { 	        [self writeStringToPasteboard: pasteboard]; 	    } else if ([type isEqual: NSPDFPboardType]) 	    { 	        [self writePdfToPasteboard: pasteboard]; 	    } else 	    { 	        NSBeep(); 	    } 	} 	

And, finally, the two methods that write the data to the pasteboard when asked

 	- (void) writePdfToPasteboard: (NSPasteboard *) pasteboard 	{ 	    // NSData *data = [self generatePdf];     	    // [pasteboard setData: data forType: NSPDFPboardType]; 	    [pasteboard setData: backupPdf forType: NSPDFPboardType]; 	}  	- (void) writeStringToPasteboard: (NSPasteboard *) pasteboard 	{ 	    // copy data to the pasteboard 	    [pasteboard setString: backupString forType: NSStringPboardType]; 	    // [pasteboard setString: string forType: NSStringPboardType]; 	} 	

The problem seems to be in pasteboard:provideDataForType:, though I can’t see what it is. When I hit Cmd-C, the copy works OK. When I switch to Preview and select File>New From Pasteboard is when everything locks up. What looks to be happening is that the pasteboard service (pbs) is getting hosed, and it’s considered a “critical service” by the OS (I read that in some OSX docs), so if it’s dead/dying other apps who try to contact it are locking up. Makes sense. I tried restarting pbs, but that didn’t seem to make a difference.

So, has anyone else fought with this situation? If so

  • Do you see a problem with the code snippets above?
  • How can you recover from hosing pbs?
  • Any other suggestions?