One thing that's always bothered me about MacOS X's Terminal.app is that you can't really run multiple applications. Technically you can but it's not right having to switch to Terminal and then cycle through the windows to get to the one you want.

And I'm not talking about being able to have multiple shells running so keep your iTerm has tabs bullshit to yourself. I use GNU/Screen for that and am quite happy with it.

There's a handful of textmode tools I'd like to be able to run as if they were all out Mac applications like Mutt, irssi, elinks, and Vim.

Some of the more popular textmode tools have graphical versions. Mac Vim is solid. AquaEmacs was a little wonky the last time I played with it. MacIrssi sucks because you can't use plugins and, really, what's the point of using a shitty textmode anything if you can't customize the piss out of it?

We need Terminal.app to be a generic container for all of the textmode tools we'd like to pull up to first-class application land. The good news is that, after reading this article, you'll be able to do just that. The bad news is that it requires quite a bit of work and the technique is pretty nasty. I would even go so far as to call it a big giant hack.

Top.app Dock Icon

To illustrate, we're going to turn top into a standalone application: Top.app. Yes, even a tool as humble as top ought to have its own bouncy Dock icon and Command+Tab target. We'll use it as a backup for Activity Monitor the next time it goes all beach-ball on us.

We need another .app

The first thing I don't like about this approach is that you have to copy Terminal.app in its entirety. I recommend the Applications directory under your $HOME:

$ mkdir -p ~/Applications  # this may already exists
$ cp -r /Applications/Utilities/Terminal.app ~/Applications/Top.app

Without doing anything else, you can run Top.app and get a separate item in The Dock and Command+Tab.

Open it from Finder, or:

$ open ~/Applications/Top.app

Changing The Menu

The menu still says "Terminal", which is kind of annoying. We can change this without too much hassle by editing the InfoPlist.strings file under your locale's directory. Check this out:

$ cd ~/Applications/Top.app/Contents/Resources/English.lproj
$ cat InfoPlist.strings
"CFBundleHelpBookName" = "Terminal Help";
"CFBundleName" = "Terminal";
"NSHumanReadableCopyright" = "? Copyright 1995-2003 Apple Compute ...

We need to open the InfoPlist.strings file in an editor that's UTF-16 aware and change the CFBundleName value from "Terminal" to "Top". Vim worked swimmingly for this task and OmniOutliner actually has a special graphical mode for strings files. But you can open it in TextEdit if neither of those is available to you:

$ open -a TextEdit InfoPlist.strings

Once saved, you can quit and start Top.app again and you should now see "Top" in the menu instead of Terminal.

Changing The Icon

The hard part about this step is finding an .icns file for the tool you're running. top is the command-line equivalent of Activity Monitor, so let's just grab the icon from there:

$ cd ~/Applications/Top.app/Contents/Resources
$ cp icon.icns{,.orig}     # backup original icons file
$ AM=/Applications/Utilities/Activity\ Monitor.app
$ cp "$AM/Contents/Resources/ActivityMonitor.icns" icon.icns

Note that the new icon won't show up until you restart Finder. In most tutorials, this is where you logoff and back on. Fuck that:

$ killall Finder

TIP: If you have Apple's Developer Tools installed, you can find more icns files in the wilderness that is /Developer/Applications.

Top Icon

Customization

Now comes the sickest part of the hack.

We're presented with a bit of a dilemma. We want our Top.app window to be fully customized and we also need a specific command to be executed (/usr/bin/top). That's not the problem - Terminal.app stores these types of customization in .term files, which are easily created by opening a terminal, customizing it using the inspector (Command+I), and then saving it (File -> Save As or Command+Shift+S). When you save the .term file there's an option labeled, Open this file when Terminal starts up and this is the problem: the setting applies to all Terminal applications (even copies) because the default .term file is set as a user global preference in ~/Library/Preferences/com.apple.Terminal.plist. So, if we open Top.app as is, customize it, save it and check that box, we'll end up loading the Top.term file when any terminal is launched.

But it's okay because Mac applications turn out to be extremely hackable. On most systems, you'd be getting out your hex editor and patching binaries. But not here - changing the preference property list used by an application turns out to be trivial:

$ cd ~/Applications/Top.app/Contents/
$ grep -A1 CFBundleId Info.plist 
      <key>CFBundleIdentifier</key>
      <string>com.apple.Terminal</string>

See that? Rock! All we need to do is change com.apple.Terminal to something else, like com.rtomayko.Top. You can open the file in the nice little Property List Editor.app and change the CFBundleIdentifier property or you can start using your machine as God intended:

$ perl -pi.orig -e 's/com\.apple\.Terminal/com.rtomayko.Top/'
$ grep -A1 CFBundleId Info.plist 
      <key>CFBundleIdentifier</key>
      <string>com.rtomayko.Top</string>

It's smooth sailing from here on out. Open Top.app and you should get a little white terminal window. Run top -s1 -ocpu at the prompt so we can get a feel for how the window should be dimensioned. Bump the font size up about 5 times using Command + and then resize the window so that the top output fits just right.

Next, hit Command+I to get into the inspector, and customize it as you see fit. Here are the settings I used for each of the customization areas:

  • Shell - When the shell exits, close the window.
  • Processes - Prompt before closing window: never.
  • Buffer - Buffer Size: Disabled (there's no need for a scrollback buffer in cursors apps).
  • Display - Text: Bitstream Vera Sans Mono, Anti-aliasing, Disable Blinking Text.
  • Color - Green on Black, Transparency.
  • Window - Title: "Top", disable everything else.

The Finished Product

Now, hit Command+S to save the .term file. You get a normal Save As dialog with a few extra options. Enter "Top" in the Save As box, you want to save the Main Window, you want to open this file when Terminal starts up, and you want to execute the following command:

exec /usr/bin/top -s1 -ocpu

You also want to check the Execute command in a shell box (it's the only way I've found that let's you pass arguments).

Save It

Save it and Quit. If everything went right, you should be right back where you left the next time you open Top.app.

Lather, Rinse, Repeat

To sum up, the basic steps are as follows:

  1. Copy Terminal.app to Foo.app.
  2. Edit Locale.lproj/InfoPlist.strings for the menu and Info.plist for the preference.
  3. Overwrite icon.icns with a distinct .icns file.
  4. Open Foo.app, customize, save .term file as Default.

Issues / Ideas

This approach has drawbacks and limitations:

  • It's not correct. Terminal.app should provide a mechanism for achieving these results without all the hackery.

  • Updates to Terminal.app will not automatically be picked up. It may be necessary to go through the process after an update.

  • I have not been able to figure out if it's possible to open files or pass arguments to these applications otherwise.

I'm considering throwing together a quick shell script that's capable of performing the nitty gritty aspects given a directory containing a few files: the .icns file and maybe some kind of simple config file that contains the values needed for the Info.plist.