First, I moved a bunch of useless files into the new "dreg" directory: beos.c directfb.c directfb_cursors.h finger.c framebuffer.c pmshell.c smb.c svgalib.c win32.c x.c I also copied os_dep.c, os_dep.h and os_depx.h into dreg, then removed all non-Plan 9 stuff from them. Coping with variety by using #ifdefs is a bad idea and slows down the compiler by making it pointlessly read too-large header files. Altogether, that eliminated about 10,000 lines of useless code that we no longer have to compile. I edit mkfile to match. I have tried to resist the strong temptation to improve the code, except where I had to understand it. I have worked over links.h. I added the abbreviations uchar, ushort and ulong and typedefs for some structs. I cleaned out a lot of the GNU #include stupidity: if a header is required by ANSI, I just include it without #ifdefing it. People who don't have ANSI C compilers yet are just out of luck. I deleted the incorrect default definition of FD_SETSIZE and added a correct, portable definition to select.c, the only file that uses it. I added the Chunk and Block types for future use; the code obviously needs them. I can't believe links.h is 4,000 lines! I fixed a misplaced #endif in cfg.h. I sorted config.h so I could find things faster, but I don't think you need to use my config.h I have rationalised connect.c somewhat, especially its SSL code. In addition, it no longer uses select to detect output streams ready for output, since select only guarantees that a single byte can be written without blocking. Instead, I just write the stuff and take the risk of blocking, which shouldn't happen given that Plan 9 has generous networking buffering and that we aren't writing very much to the network at one time. I had hoped to use just the Plan 9 event machinery, which is only good for input streams, but realised it's just too hard: with http connections coming and going, we'd have to have a way to undo the effect of estart(), say an estop() routine. It might be feasible to fork or rfork a process just to create an http connection, write a request, read the result, and exit, which would permit the use of ordinary blocking I/O and eliminate the need for select. It might even help to manage memory more effectively. It looks like my changes to default.c are cosmetic, so you don't have to pick them up. I did get rid of a pile of (unsigned char *) casts in the tables, though. I cleaned up dither.c in preparation for making it run faster, but ultimately didn't, so you probably don't need it either. I added a little tracing to http.c, but I don't think you need it either. I fixed many font errors in links.1 and generally made it less ugly. main.c is mostly cosmetic changes, but I added the "shootme" machinery for trying to kill the new event-watching process in plan9.c. It doesn't always work; I don't know why. I unbuffered stderr so that error messages appear immediately instead of being buffered (mostly useful during debugging). You'll want my mkfile. os_dep* are greatly slimmed-down. I worked over plan9.c a bit too: used structure assignment to copy rectangles, cleaned up key-code translation, redid event handling and select implementation, changed when to call flushimage and redraw. I call flushimage at the end of the two bitmap routines and also after each select(); it's probably overkill but it seems to help. I also call redraw once every 5 seconds to force out delayed updates. Calling redraw more often does make the system noticeably more busy (keep `stats -slIc' running while you run links). It looks like my changes to sched.c are just cosmetic. select.c has been mauled. I got rid of the fd_sets for writes entirely. I refactored some of the code out of the main select_loop. Some of the stupider had has been removed with if (Braindamage) stupid-code; where Braindamage is defined as 0. There are bits of shootme machinery in here too. There's new debugging code (normally compiled out). There are now strict upper and lower bounds on how long select may sleep. The upper bound is to make sure flushimage and redraw get called regularly. Debugging showed sleeps as short as 1 microsecond and a lot of them were just 101 microseconds; no wonder we were eating up the CPU! select is not cheap on Unix, it's even more expensive on APE. The code under `if (errno == EBADF) {' is no longer getting called; it was needed at one time, but no more; it could be deleted. Similarly, the "sleep(1)" in the `errno != EINTR' if statement could probably be deleted now; I was paranoid about CPU-bound loops chewing up the CPU. The frequent, yet random, calls to check_timers, check_signals and CHK_BH seem arbitrary and appear to be symptoms of a program whose author doesn't understand it. There's new code for plan9.c's benefit at the end, I think it's pretty obvious. url.c removes dumb shit like "finger" and "smb" protocols; we don't need them, don't want them and nobody uses them in web pages. Good riddance. Here and elsewhere I have deleted code, including entire functions, that was commented out. If it's not needed, get rid of the trash, I figure. More (unsigned char *) casts in tables are gone. My approach to events should be pretty obvious from the code, but it's essentially this: create a (bidirectional) pipe, call einit, and rfork(RFPROC|RFMEM) a trivial process to wait for keyboard and mouse events, which are then store in (shared) memory. When one arrives, send a byte up the pipe to the parent process, which is selecting on that end of the pipe, along with any other fds that the rest of the program is watching. When activated after select returns, the parent, to synchronise, reads the byte from the pipe, processes any queued mouse and keyboard events (which should be quick), and writes a different byte back down the pipe. The child process has been reading from its end of the pipe for synchronisation, and upon seeing the byte from the parent, resumes waiting for events. The event package will queue any events that arrive while the child is reading and writing the pipe, so none should be lost. The main reason for having the parent process the events is so that it can do them synchronously with the main select() call (from select_loop), thus eliminating a possible source of races. Obviously it would be better to radically rewrite the program to eliminate all use of select, but I'll leave that for another day. This has been working well for me, except that links is a major memory hog, and it did crash my terminal once during debugging. I'm attaching the code, omitting the files that I didn't touch.