Blob


1 /* Copyright (c) 1994-1996 David Hogan, see README for licence details */
2 #include <stdio.h>
3 #include <signal.h>
4 #include <errno.h>
5 #include <stdlib.h>
6 #include <unistd.h>
7 #include <X11/X.h>
8 #include <X11/Xos.h>
9 #include <X11/Xlib.h>
10 #include <X11/Xutil.h>
11 #include <X11/Xatom.h>
12 #ifdef SHAPE
13 #include <X11/extensions/shape.h>
14 #endif
15 #include "dat.h"
16 #include "fns.h"
17 #include "patchlevel.h"
19 char *version[] =
20 {
21 "rio version 1.0, Copyright (c) 1994-1996 David Hogan, (c) 2004 Russ Cox", 0
22 };
24 Display *dpy;
25 ScreenInfo *screens;
26 int initting;
27 XFontStruct *font;
28 int nostalgia;
29 char **myargv;
30 char *termprog;
31 char *shell;
32 Bool shape;
33 int _border = 4;
34 int _corner = 25;
35 int _inset = 1;
36 int curtime;
37 int debug;
38 int signalled;
39 int scrolling;
40 int num_screens;
41 int solidsweep = 0;
42 int numvirtuals = 0;
43 int ffm = 0;
45 Atom exit_rio;
46 Atom restart_rio;
47 Atom wm_state;
48 Atom wm_change_state;
49 Atom wm_protocols;
50 Atom wm_delete;
51 Atom wm_take_focus;
52 Atom wm_lose_focus;
53 Atom wm_colormaps;
54 Atom _rio_running;
55 Atom _rio_hold_mode;
56 Atom wm_state_fullscreen;
57 Atom wm_state;
59 char *fontlist[] = {
60 "lucm.latin1.9",
61 "blit",
62 "*-lucidatypewriter-bold-*-14-*-75-*",
63 "*-lucidatypewriter-medium-*-12-*-75-*",
64 "9x15bold",
65 "fixed",
66 "*",
67 0
68 };
70 void
71 usage(void)
72 {
73 fprintf(stderr, "usage: rio [-grey] [-font fname] [-s] [-term prog] [-version] [-virtuals num] [exit|restart]\n");
74 exit(1);
75 }
77 int
78 main(int argc, char *argv[])
79 {
80 int i, background, do_exit, do_restart;
81 char *fname;
82 int shape_event;
83 #ifdef SHAPE
84 int dummy;
85 #endif
87 shape_event = 0;
88 myargv = argv; /* for restart */
90 do_exit = do_restart = 0;
91 background = 0;
92 font = 0;
93 fname = 0;
94 for(i = 1; i < argc; i++)
95 if(strcmp(argv[i], "-nostalgia") == 0)
96 nostalgia++;
97 else if(strcmp(argv[i], "-grey") == 0)
98 background = 1;
99 else if(strcmp(argv[i], "-debug") == 0)
100 debug++;
101 /*
102 else if(strcmp(argv[i], "-ffm") == 0)
103 ffm++;
104 */
105 else if(strcmp(argv[i], "-font") == 0 && i+1<argc){
106 i++;
107 fname = argv[i];
109 else if(strcmp(argv[i], "-term") == 0 && i+1<argc)
110 termprog = argv[++i];
111 else if(strcmp(argv[i], "-virtuals") == 0 && i+1<argc){
112 numvirtuals = atoi(argv[++i]);
113 if(numvirtuals < 0 || numvirtuals > 12){
114 fprintf(stderr, "rio: wrong number of virtual displays, defaulting to 4\n");
115 numvirtuals = 4;
117 } else if(strcmp(argv[i], "-version") == 0){
118 fprintf(stderr, "%s", version[0]);
119 if(PATCHLEVEL > 0)
120 fprintf(stderr, "; patch level %d", PATCHLEVEL);
121 fprintf(stderr, "\n");
122 exit(0);
124 else if(strcmp(argv[i], "-s") == 0){
125 scrolling = 1;
127 else if(argv[i][0] == '-')
128 usage();
129 else
130 break;
131 for(; i < argc; i++)
132 if(strcmp(argv[i], "exit") == 0)
133 do_exit++;
134 else if(strcmp(argv[i], "restart") == 0)
135 do_restart++;
136 else
137 usage();
139 if(do_exit && do_restart)
140 usage();
142 shell = (char *)getenv("SHELL");
143 if(shell == NULL)
144 shell = DEFSHELL;
146 dpy = XOpenDisplay("");
147 if(dpy == 0)
148 fatal("can't open display");
150 initting = 1;
151 XSetErrorHandler(handler);
152 if(signal(SIGTERM, sighandler) == SIG_IGN)
153 signal(SIGTERM, SIG_IGN);
154 if(signal(SIGINT, sighandler) == SIG_IGN)
155 signal(SIGINT, SIG_IGN);
156 if(signal(SIGHUP, sighandler) == SIG_IGN)
157 signal(SIGHUP, SIG_IGN);
159 exit_rio = XInternAtom(dpy, "9WM_EXIT", False);
160 restart_rio = XInternAtom(dpy, "9WM_RESTART", False);
162 curtime = -1; /* don't care */
163 if(do_exit){
164 sendcmessage(DefaultRootWindow(dpy), exit_rio, 0L, 1, 1);
165 XSync(dpy, False);
166 exit(0);
168 if(do_restart){
169 sendcmessage(DefaultRootWindow(dpy), restart_rio, 0L, 1, 1);
170 XSync(dpy, False);
171 exit(0);
174 if(0) XSynchronize(dpy, True);
176 wm_state = XInternAtom(dpy, "WM_STATE", False);
177 wm_change_state = XInternAtom(dpy, "WM_CHANGE_STATE", False);
178 wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
179 wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
180 wm_take_focus = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
181 wm_lose_focus = XInternAtom(dpy, "_9WM_LOSE_FOCUS", False);
182 wm_colormaps = XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False);
183 _rio_running = XInternAtom(dpy, "_9WM_RUNNING", False);
184 _rio_hold_mode = XInternAtom(dpy, "_9WM_HOLD_MODE", False);
185 wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
186 wm_state_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
188 if(fname != 0)
189 if((font = XLoadQueryFont(dpy, fname)) == 0)
190 fprintf(stderr, "rio: warning: can't load font %s\n", fname);
192 if(font == 0){
193 i = 0;
194 for(;;){
195 fname = fontlist[i++];
196 if(fname == 0){
197 fprintf(stderr, "rio: warning: can't find a font\n");
198 break;
200 font = XLoadQueryFont(dpy, fname);
201 if(font != 0)
202 break;
205 if(nostalgia){
206 _border--;
207 _inset--;
210 #ifdef SHAPE
211 shape = XShapeQueryExtension(dpy, &shape_event, &dummy);
212 #endif
214 num_screens = ScreenCount(dpy);
215 screens = (ScreenInfo *)malloc(sizeof(ScreenInfo) * num_screens);
217 for(i = 0; i < num_screens; i++)
218 initscreen(&screens[i], i, background);
220 initb2menu(numvirtuals);
222 /* set selection so that 9term knows we're running */
223 curtime = CurrentTime;
224 XSetSelectionOwner(dpy, _rio_running, screens[0].menuwin, timestamp());
226 XSync(dpy, False);
227 initting = 0;
229 nofocus();
231 for(i = 0; i < num_screens; i++)
232 scanwins(&screens[i]);
234 keysetup();
235 mainloop(shape_event);
236 return 0;
239 void
240 initscreen(ScreenInfo *s, int i, int background)
242 char *ds, *colon, *dot1;
243 unsigned long mask;
244 unsigned long gmask;
245 XGCValues gv;
246 XSetWindowAttributes attr;
247 XVisualInfo xvi;
248 XSetWindowAttributes attrs;
250 s->num = i;
251 s->root = RootWindow(dpy, i);
252 s->def_cmap = DefaultColormap(dpy, i);
253 s->min_cmaps = MinCmapsOfScreen(ScreenOfDisplay(dpy, i));
254 s->depth = DefaultDepth(dpy, i);
256 /*
257 * Figure out underlying screen format.
258 */
259 if(XMatchVisualInfo(dpy, i, 16, TrueColor, &xvi)
260 || XMatchVisualInfo(dpy, i, 16, DirectColor, &xvi)){
261 s->vis = xvi.visual;
262 s->depth = 16;
264 else
265 if(XMatchVisualInfo(dpy, i, 15, TrueColor, &xvi)
266 || XMatchVisualInfo(dpy, i, 15, DirectColor, &xvi)){
267 s->vis = xvi.visual;
268 s->depth = 15;
270 else
271 if(XMatchVisualInfo(dpy, i, 24, TrueColor, &xvi)
272 || XMatchVisualInfo(dpy, i, 24, DirectColor, &xvi)){
273 s->vis = xvi.visual;
274 s->depth = 24;
276 else
277 if(XMatchVisualInfo(dpy, i, 8, PseudoColor, &xvi)
278 || XMatchVisualInfo(dpy, i, 8, StaticColor, &xvi)){
279 s->vis = xvi.visual;
280 s->depth = 8;
282 else{
283 s->depth = DefaultDepth(dpy, i);
284 if(s->depth != 8){
285 fprintf(stderr, "can't understand depth %d screen", s->depth);
286 exit(1);
288 s->vis = DefaultVisual(dpy, i);
290 if(DefaultDepth(dpy, i) != s->depth){
291 s->def_cmap = XCreateColormap(dpy, s->root, s->vis, AllocNone);
294 ds = DisplayString(dpy);
295 colon = rindex(ds, ':');
296 if(colon && num_screens > 1){
297 strcpy(s->display, "DISPLAY=");
298 strcat(s->display, ds);
299 colon = s->display + 8 + (colon - ds); /* use version in buf */
300 dot1 = index(colon, '.'); /* first period after colon */
301 if(!dot1)
302 dot1 = colon + strlen(colon); /* if not there, append */
303 sprintf(dot1, ".%d", i);
305 else
306 s->display[0] = '\0';
308 s->black = BlackPixel(dpy, i);
309 s->white = WhitePixel(dpy, i);
310 s->activeholdborder = colorpixel(dpy, s, s->depth, 0x000099, s->white);
311 s->inactiveholdborder = colorpixel(dpy, s, s->depth, 0x005DBB, s->black);
312 s->activeborder = colorpixel(dpy, s, s->depth, 0x55AAAA, s->black);
313 s->inactiveborder = colorpixel(dpy, s, s->depth, 0x9EEEEE, s->white);
314 s->red = colorpixel(dpy, s, s->depth, 0xDD0000, s->white);
315 s->width = WidthOfScreen(ScreenOfDisplay(dpy, i));
316 s->height = HeightOfScreen(ScreenOfDisplay(dpy, i));
317 s->bkup[0] = XCreatePixmap(dpy, s->root, 2*s->width, BORDER, DefaultDepth(dpy, i));
318 s->bkup[1] = XCreatePixmap(dpy, s->root, BORDER, 2*s->height, DefaultDepth(dpy, i));
320 gv.foreground = s->black^s->white;
321 gv.background = s->white;
322 gv.function = GXxor;
323 gv.line_width = 0;
324 gv.subwindow_mode = IncludeInferiors;
325 gmask = GCForeground | GCBackground | GCFunction | GCLineWidth
326 | GCSubwindowMode;
327 if(font != 0){
328 gv.font = font->fid;
329 gmask |= GCFont;
331 s->gc = XCreateGC(dpy, s->root, gmask, &gv);
333 gv.function = GXcopy;
334 s->gccopy = XCreateGC(dpy, s->root, gmask, &gv);
336 gv.foreground = s->red;
337 s->gcred = XCreateGC(dpy, s->root, gmask, &gv);
339 gv.foreground = colorpixel(dpy, s, s->depth, 0xEEEEEE, s->black);
340 s->gcsweep = XCreateGC(dpy, s->root, gmask, &gv);
342 initcurs(s);
344 attr.cursor = s->arrow;
345 attr.event_mask = SubstructureRedirectMask
346 | SubstructureNotifyMask | ColormapChangeMask
347 | ButtonPressMask | ButtonReleaseMask | PropertyChangeMask
348 | KeyPressMask | EnterWindowMask;
349 mask = CWCursor|CWEventMask;
350 XChangeWindowAttributes(dpy, s->root, mask, &attr);
351 XSync(dpy, False);
353 if(background){
354 XSetWindowBackgroundPixmap(dpy, s->root, s->root_pixmap);
355 XClearWindow(dpy, s->root);
356 } else
357 system("xsetroot -solid grey30");
359 attrs.border_pixel = colorpixel(dpy, s, s->depth, 0x88CC88, s->black);
360 attrs.background_pixel = colorpixel(dpy, s, s->depth, 0xE9FFE9, s->white);
361 attrs.colormap = s->def_cmap;
363 s->menuwin = XCreateWindow(dpy, s->root, 0, 0, 1, 1, 2,
364 s->depth,
365 CopyFromParent,
366 s->vis,
367 CWBackPixel | CWBorderPixel | CWColormap,
368 &attrs
369 );
372 gv.foreground = colorpixel(dpy, s, s->depth, 0xE9FFE9, s->white);
373 s->gcmenubg = XCreateGC(dpy, s->menuwin, gmask, &gv);
375 gv.foreground = colorpixel(dpy, s, s->depth, 0x448844, s->black);
376 s->gcmenubgs = XCreateGC(dpy, s->menuwin, gmask, &gv);
378 gv.foreground = s->black;
379 gv.background = colorpixel(dpy, s, s->depth, 0xE9FFE9, s->white);
380 s->gcmenufg = XCreateGC(dpy, s->menuwin, gmask, &gv);
382 gv.foreground = colorpixel(dpy, s, s->depth, 0xE9FFE9, s->white);
383 gv.background = colorpixel(dpy, s, s->depth, 0x448844, s->black);
384 s->gcmenufgs = XCreateGC(dpy, s->menuwin, gmask, &gv);
386 attrs.border_pixel = s->red;
387 attrs.background_pixel = colorpixel(dpy, s, s->depth, 0xEEEEEE, s->black);
388 attrs.colormap = s->def_cmap;
389 s->sweepwin = XCreateWindow(dpy, s->root, 0, 0, 1, 1, 4,
390 s->depth,
391 CopyFromParent,
392 s->vis,
393 CWBackPixel | CWBorderPixel | CWColormap,
394 &attrs
395 );
398 ScreenInfo*
399 getscreen(Window w)
401 int i;
403 for(i = 0; i < num_screens; i++)
404 if(screens[i].root == w)
405 return &screens[i];
407 return 0;
410 Time
411 timestamp(void)
413 XEvent ev;
415 if(curtime == CurrentTime){
416 XChangeProperty(dpy, screens[0].root, _rio_running, _rio_running, 8,
417 PropModeAppend, (unsigned char *)"", 0);
418 XMaskEvent(dpy, PropertyChangeMask, &ev);
419 curtime = ev.xproperty.time;
421 return curtime;
424 void
425 sendcmessage(Window w, Atom a, long x, int isroot, int usemask)
427 XEvent ev;
428 int status;
429 long mask;
431 memset(&ev, 0, sizeof(ev));
432 ev.xclient.type = ClientMessage;
433 ev.xclient.window = w;
434 ev.xclient.message_type = a;
435 ev.xclient.format = 32;
436 ev.xclient.data.l[0] = x;
437 ev.xclient.data.l[1] = timestamp();
438 mask = 0;
439 if(usemask){
440 mask |= KeyPressMask; /* seems to be necessary */
441 if(isroot)
442 mask |= SubstructureRedirectMask; /* magic! */
443 else
444 mask |= ExposureMask; /* not really correct but so be it */
446 status = XSendEvent(dpy, w, False, mask, &ev);
447 if(status == 0)
448 fprintf(stderr, "rio: sendcmessage failed\n");
451 void
452 sendconfig(Client *c)
454 XConfigureEvent ce;
456 ce.type = ConfigureNotify;
457 ce.event = c->window;
458 ce.window = c->window;
459 ce.x = c->x;
460 ce.y = c->y;
461 ce.width = c->dx;
462 ce.height = c->dy;
463 ce.border_width = c->border;
464 ce.above = None;
465 ce.override_redirect = 0;
466 XSendEvent(dpy, c->window, False, StructureNotifyMask, (XEvent*)&ce);
469 void
470 sighandler(void)
472 signalled = 1;
475 void
476 getevent(XEvent *e)
478 int fd;
479 fd_set rfds;
480 struct timeval t;
482 if(!signalled){
483 if(QLength(dpy) > 0){
484 XNextEvent(dpy, e);
485 return;
487 fd = ConnectionNumber(dpy);
488 FD_ZERO(&rfds);
489 FD_SET(fd, &rfds);
490 t.tv_sec = t.tv_usec = 0;
491 if(select(fd+1, &rfds, NULL, NULL, &t) == 1){
492 XNextEvent(dpy, e);
493 return;
495 XFlush(dpy);
496 FD_SET(fd, &rfds);
497 if(select(fd+1, &rfds, NULL, NULL, NULL) == 1){
498 XNextEvent(dpy, e);
499 return;
501 if(errno != EINTR || !signalled){
502 perror("rio: select failed");
503 exit(1);
506 fprintf(stderr, "rio: exiting on signal\n");
507 cleanup();
508 exit(1);
511 void
512 cleanup(void)
514 Client *c, *cc[2], *next;
515 XWindowChanges wc;
516 int i;
518 /* order of un-reparenting determines final stacking order... */
519 cc[0] = cc[1] = 0;
520 for(c = clients; c; c = next){
521 next = c->next;
522 i = normal(c);
523 c->next = cc[i];
524 cc[i] = c;
527 for(i = 0; i < 2; i++){
528 for(c = cc[i]; c; c = c->next){
529 if(!withdrawn(c)){
530 XReparentWindow(dpy, c->window, c->screen->root,
531 c->x, c->y);
533 wc.border_width = c->border;
534 XConfigureWindow(dpy, c->window, CWBorderWidth, &wc);
538 XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, timestamp());
539 for(i = 0; i < num_screens; i++)
540 cmapnofocus(&screens[i]);
541 XCloseDisplay(dpy);