Newbie Emacs tip: Last April I described several ways to customize colors in Emacs. In particular, I mentioned that a blanket setting for a color can be annoying if you use Emacs both in a windowing system and in a color terminal, because the terminal will attempt to approximate the colors you chose carefully from 16 million options from its 16 (period) options. My subtle dark green background (#003344) becomes an unusable bright green in a terminal.
My original solution was to test whether Emacs was running in a window system using an (if ...) block and the (window-system) variable, which is nil (false) when Emacs is running in a terminal. This works great if you run a fresh instance of Emacs in your window system or in your terminal: .emacs loads, does the check, and sets the right things for the environment.
This does not work so well when you use the same instance of Emacs in both the window system and the terminal.
To explain that last sentence, let's consider another Emacs feature briefly. Emacs Client lets you invoke an already running instance of Emacs from another program for the purposes of visiting a file. This is especially useful for programs that try to open an editor to let you edit configuration or enter a paragraph of data, such as crontab -e or svn. Instead of opening a fresh instance of Emacs and waiting for all of your .emacs to load, it just tells the already running Emacs to visit the given file. Close the session, and control goes back to the calling application.
In Emacs, type M-x server-start to start the server. (To start the server every time Emacs starts, add the line (server-start) to your .emacs file.) With the server running, open a shell in a terminal window, find a file to edit, then type the following command:
emacsclient filename
Emacs visits the file, and the terminal reports it is waiting for Emacs. Switch to Emacs, edit and save the file if you like, then type C-x # (control-x shift-3). The buffer goes away, and control returns to the terminal.
There are a handful of standard things to say about Emacs Client, such as how to get it to open Emacs (or something else) when Emacs isn't running, how to tweak its behavior to open a new frame for the new file instead of reusing an existing one, and so forth. I'm going to skip those for now, so see the Emacs wiki article on Emacs Client for more. (That page needs some updates, so I might do a newbie tip on this later.)
When Emacs is running in a window system, Emacs Client uses windowed frames (existing ones by default). When Emacs is running in a terminal, Emacs Client uses that terminal "frame." So how do you use one instance of Emacs to open both a windowed frame and a terminal frame?
Why would you want to? Well, say you leave Emacs running all the time on your favorite machine, with all your favorite libraries loaded and all your project files open in dozens of buffers. If you're not where your favorite machine is (at work, at home), you might access your machine remotely with a terminal (hopefully via ssh). Normally, doing so leaves you stranded from your Emacs instance. By itself, Emacs Client doesn't help you here: If you're running a windowed Emacs, all emacsclient can do is visit files in windowed buffers, inaccessible from your terminal.
Enter MultiTTY, a new feature destined for Emacs 23. MultiTTY adds a flag to emacsclient that tells it to create a terminal frame for the file you're visiting, even if Emacs is running in a window system. It's the same Emacs instance, and you can access all of the open buffers from the terminal frame. Best of all, it opens instantaneously, because it doesn't have to load libraries and configuration.
Running the latest experimental version of Emacs is pretty straightforward. It's not a "newbie" level task, but I recommend it as a next step once you're comfortable with managing files and command paths for your shell. In addition to letting you play with the latest features, it gives you more control over how Emacs is set up, regardless of whether or not you have administrator access to the machine you're using.
I mentioned how to build the latest experimental Emacs for Mac OS X back in 2006 because at the time Emacs 22 had not yet been released, but its spiffy Mac OS X support was already implemented. I won't go into any more detail on how to check out and build Emacs, but see that article for the commands. Non-Mac users, remove the --enable-carbon-app flag. If you don't have administrator access to the machine you're on, use the --prefix option to change the installation directory (e.g. --prefix=~/apps/emacs), then adjust your shell's command execution paths accordingly once it is installed.
Why did I start this tip with a discussion of color settings? Now that we're using a windowed Emacs for everything including our terminal-based sessions, our (window-system) test no longer does the right thing. Emacs starts up windowed, sets the default colors for new frames, then keeps on running. A MultiTTY frame is just a new frame, and so it uses the defaults—including a nice bright green background.
What we really want is code that executes for each new frame, checks whether the frame is windowed or terminal'd, then sets the colors. Naturally, there's a hook for that: 'after-make-frame-functions The following code defines a function that sets colors for a frame depending on whether or not the frame is windowed, then adds that function to this hook:
(defun my-set-display-for-windowed-frames (frame)
"Set display parameters for the current frame the way I like them."
(select-frame frame)
(if (window-system frame)
(progn
(set-background-color "#003344")
(set-foreground-color "white")
(set-cursor-color "red"))))
(add-hook 'after-make-frame-functions 'my-set-display-for-windowed-frames)
We need to do one more thing to complete the picture: This code is sufficient to get all frames created after the add-hook to get configured by the function. However, the first frame Emacs opens when it first starts up is opened before .emacs is evaluated, so it doesn't get the colors. Thankfully, the frame settings are wrapped in a function, so we merely need to call it on the already-opened frame:
(my-set-display-for-windowed-frames (selected-frame))
* * *
One more thing about MultiTTY and Emacs Client: If you already have Emacs Client set up and you have configured it to open a new frame when it starts and delete the opened frame when it stops, you might want to reconsider: emacsclient -t ... handles the opening and closing of the terminal frame automatically. If you have a server-done-hook that calls (delete-frame) without verifying that the frame it's deleting is the one you want deleted, emacsclient -t ... will delete one of your other frames on its way out the door. Emacs won't let it delete the last frame, but it can still cause headaches. Similar behavior that accounts for emacsclient -t ... shouldn't be too difficult to implement, but I don't yet have something to share.
Ryan McGuire has neat scripts for running the main Emacs instance hidden inside a screen session, then using emacsclient -t ... for everything. Of course, run in this way, Emacs starts up without a window system, and to my knowledge there is no way to open windowed frames from a non-windowed Emacs instance, so this is only useful for running Emacs only in terminals. But windows are overrated.