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;
44 Atom exit_rio;
45 Atom restart_rio;
46 Atom wm_state;
47 Atom wm_change_state;
48 Atom wm_protocols;
49 Atom wm_delete;
50 Atom wm_take_focus;
51 Atom wm_lose_focus;
52 Atom wm_colormaps;
53 Atom _rio_running;
54 Atom _rio_hold_mode;
56 char *fontlist[] = {
57 "lucm.latin1.9",
58 "blit",
59 "*-lucidatypewriter-bold-*-14-*-75-*",
60 "*-lucidatypewriter-medium-*-12-*-75-*",
61 "9x15bold",
62 "fixed",
63 "*",
64 0,
65 };
67 void
68 usage(void)
69 {
70 fprintf(stderr, "usage: rio [-grey] [-font fname] [-s] [-term prog] [-version] [-virtuals num] [exit|restart]\n");
71 exit(1);
72 }
74 int
75 main(int argc, char *argv[])
76 {
77 int i, background, do_exit, do_restart;
78 char *fname;
79 int shape_event;
80 #ifdef SHAPE
81 int dummy;
82 #endif
84 shape_event = 0;
85 myargv = argv; /* for restart */
87 do_exit = do_restart = 0;
88 background = 0;
89 font = 0;
90 fname = 0;
91 for (i = 1; i < argc; i++)
92 if (strcmp(argv[i], "-nostalgia") == 0)
93 nostalgia++;
94 else if (strcmp(argv[i], "-grey") == 0)
95 background = 1;
96 else if (strcmp(argv[i], "-debug") == 0)
97 debug++;
98 else if (strcmp(argv[i], "-font") == 0 && i+1<argc) {
99 i++;
100 fname = argv[i];
102 else if (strcmp(argv[i], "-term") == 0 && i+1<argc)
103 termprog = argv[++i];
104 else if (strcmp(argv[i], "-virtuals") == 0 && i+1<argc) {
105 numvirtuals = atoi(argv[++i]);
106 if(numvirtuals < 0 || numvirtuals > 12) {
107 fprintf(stderr, "rio: wrong number of virtual displays, defaulting to 4\n");
108 numvirtuals = 4;
110 } else if (strcmp(argv[i], "-version") == 0) {
111 fprintf(stderr, "%s", version[0]);
112 if (PATCHLEVEL > 0)
113 fprintf(stderr, "; patch level %d", PATCHLEVEL);
114 fprintf(stderr, "\n");
115 exit(0);
117 else if (strcmp(argv[i], "-s") == 0) {
118 scrolling = 1;
120 else if (argv[i][0] == '-')
121 usage();
122 else
123 break;
124 for (; i < argc; i++)
125 if (strcmp(argv[i], "exit") == 0)
126 do_exit++;
127 else if (strcmp(argv[i], "restart") == 0)
128 do_restart++;
129 else
130 usage();
132 if (do_exit && do_restart)
133 usage();
135 shell = (char *)getenv("SHELL");
136 if (shell == NULL)
137 shell = DEFSHELL;
139 dpy = XOpenDisplay("");
140 if (dpy == 0)
141 fatal("can't open display");
143 initting = 1;
144 XSetErrorHandler(handler);
145 if (signal(SIGTERM, sighandler) == SIG_IGN)
146 signal(SIGTERM, SIG_IGN);
147 if (signal(SIGINT, sighandler) == SIG_IGN)
148 signal(SIGINT, SIG_IGN);
149 if (signal(SIGHUP, sighandler) == SIG_IGN)
150 signal(SIGHUP, SIG_IGN);
152 exit_rio = XInternAtom(dpy, "9WM_EXIT", False);
153 restart_rio = XInternAtom(dpy, "9WM_RESTART", False);
155 curtime = -1; /* don't care */
156 if (do_exit) {
157 sendcmessage(DefaultRootWindow(dpy), exit_rio, 0L, 1, 1);
158 XSync(dpy, False);
159 exit(0);
161 if (do_restart) {
162 sendcmessage(DefaultRootWindow(dpy), restart_rio, 0L, 1, 1);
163 XSync(dpy, False);
164 exit(0);
167 if (0) XSynchronize(dpy, True);
169 wm_state = XInternAtom(dpy, "WM_STATE", False);
170 wm_change_state = XInternAtom(dpy, "WM_CHANGE_STATE", False);
171 wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
172 wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
173 wm_take_focus = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
174 wm_lose_focus = XInternAtom(dpy, "_9WM_LOSE_FOCUS", False);
175 wm_colormaps = XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False);
176 _rio_running = XInternAtom(dpy, "_9WM_RUNNING", False);
177 _rio_hold_mode = XInternAtom(dpy, "_9WM_HOLD_MODE", False);
179 if (fname != 0)
180 if ((font = XLoadQueryFont(dpy, fname)) == 0)
181 fprintf(stderr, "rio: warning: can't load font %s\n", fname);
183 if (font == 0) {
184 i = 0;
185 for (;;) {
186 fname = fontlist[i++];
187 if (fname == 0) {
188 fprintf(stderr, "rio: warning: can't find a font\n");
189 break;
191 font = XLoadQueryFont(dpy, fname);
192 if (font != 0)
193 break;
196 if (nostalgia) {
197 _border--;
198 _inset--;
201 #ifdef SHAPE
202 shape = XShapeQueryExtension(dpy, &shape_event, &dummy);
203 #endif
205 num_screens = ScreenCount(dpy);
206 screens = (ScreenInfo *)malloc(sizeof(ScreenInfo) * num_screens);
208 for (i = 0; i < num_screens; i++)
209 initscreen(&screens[i], i, background);
211 initb2menu(numvirtuals);
213 /* set selection so that 9term knows we're running */
214 curtime = CurrentTime;
215 XSetSelectionOwner(dpy, _rio_running, screens[0].menuwin, timestamp());
217 XSync(dpy, False);
218 initting = 0;
220 nofocus();
222 for (i = 0; i < num_screens; i++)
223 scanwins(&screens[i]);
225 mainloop(shape_event);
226 return 0;
229 void
230 initscreen(ScreenInfo *s, int i, int background)
232 char *ds, *colon, *dot1;
233 unsigned long mask;
234 unsigned long gmask;
235 XGCValues gv;
236 XSetWindowAttributes attr;
237 XVisualInfo xvi;
238 XSetWindowAttributes attrs;
240 s->num = i;
241 s->root = RootWindow(dpy, i);
242 s->def_cmap = DefaultColormap(dpy, i);
243 s->min_cmaps = MinCmapsOfScreen(ScreenOfDisplay(dpy, i));
244 s->depth = DefaultDepth(dpy, i);
246 /*
247 * Figure out underlying screen format.
248 */
249 if(XMatchVisualInfo(dpy, i, 16, TrueColor, &xvi)
250 || XMatchVisualInfo(dpy, i, 16, DirectColor, &xvi)){
251 s->vis = xvi.visual;
252 s->depth = 16;
254 else
255 if(XMatchVisualInfo(dpy, i, 15, TrueColor, &xvi)
256 || XMatchVisualInfo(dpy, i, 15, DirectColor, &xvi)){
257 s->vis = xvi.visual;
258 s->depth = 15;
260 else
261 if(XMatchVisualInfo(dpy, i, 24, TrueColor, &xvi)
262 || XMatchVisualInfo(dpy, i, 24, DirectColor, &xvi)){
263 s->vis = xvi.visual;
264 s->depth = 24;
266 else
267 if(XMatchVisualInfo(dpy, i, 8, PseudoColor, &xvi)
268 || XMatchVisualInfo(dpy, i, 8, StaticColor, &xvi)){
269 s->vis = xvi.visual;
270 s->depth = 8;
272 else{
273 s->depth = DefaultDepth(dpy, i);
274 if(s->depth != 8){
275 fprintf(stderr, "can't understand depth %d screen", s->depth);
276 exit(1);
278 s->vis = DefaultVisual(dpy, i);
280 if(DefaultDepth(dpy, i) != s->depth) {
281 s->def_cmap = XCreateColormap(dpy, s->root, s->vis, AllocNone);
284 ds = DisplayString(dpy);
285 colon = rindex(ds, ':');
286 if (colon && num_screens > 1) {
287 strcpy(s->display, "DISPLAY=");
288 strcat(s->display, ds);
289 colon = s->display + 8 + (colon - ds); /* use version in buf */
290 dot1 = index(colon, '.'); /* first period after colon */
291 if (!dot1)
292 dot1 = colon + strlen(colon); /* if not there, append */
293 sprintf(dot1, ".%d", i);
295 else
296 s->display[0] = '\0';
298 s->black = BlackPixel(dpy, i);
299 s->white = WhitePixel(dpy, i);
300 s->activeholdborder = colorpixel(dpy, s, s->depth, 0x000099, s->white);
301 s->inactiveholdborder = colorpixel(dpy, s, s->depth, 0x005DBB, s->black);
302 s->activeborder = colorpixel(dpy, s, s->depth, 0x55AAAA, s->black);
303 s->inactiveborder = colorpixel(dpy, s, s->depth, 0x9EEEEE, s->white);
304 s->red = colorpixel(dpy, s, s->depth, 0xDD0000, s->white);
305 s->width = WidthOfScreen(ScreenOfDisplay(dpy, i));
306 s->height = HeightOfScreen(ScreenOfDisplay(dpy, i));
307 s->bkup[0] = XCreatePixmap(dpy, s->root, 2*s->width, BORDER, DefaultDepth(dpy, i));
308 s->bkup[1] = XCreatePixmap(dpy, s->root, BORDER, 2*s->height, DefaultDepth(dpy, i));
310 gv.foreground = s->black^s->white;
311 gv.background = s->white;
312 gv.function = GXxor;
313 gv.line_width = 0;
314 gv.subwindow_mode = IncludeInferiors;
315 gmask = GCForeground | GCBackground | GCFunction | GCLineWidth
316 | GCSubwindowMode;
317 if (font != 0) {
318 gv.font = font->fid;
319 gmask |= GCFont;
321 s->gc = XCreateGC(dpy, s->root, gmask, &gv);
323 gv.function = GXcopy;
324 s->gccopy = XCreateGC(dpy, s->root, gmask, &gv);
326 gv.foreground = s->red;
327 s->gcred = XCreateGC(dpy, s->root, gmask, &gv);
329 gv.foreground = colorpixel(dpy, s, s->depth, 0xEEEEEE, s->black);
330 s->gcsweep = XCreateGC(dpy, s->root, gmask, &gv);
332 initcurs(s);
334 attr.cursor = s->arrow;
335 attr.event_mask = SubstructureRedirectMask
336 | SubstructureNotifyMask | ColormapChangeMask
337 | ButtonPressMask | ButtonReleaseMask | PropertyChangeMask;
338 mask = CWCursor|CWEventMask;
339 XChangeWindowAttributes(dpy, s->root, mask, &attr);
340 XSync(dpy, False);
342 if (background) {
343 XSetWindowBackgroundPixmap(dpy, s->root, s->root_pixmap);
344 XClearWindow(dpy, s->root);
345 } else
346 system("xsetroot -solid grey30");
348 attrs.border_pixel = colorpixel(dpy, s, s->depth, 0x88CC88, s->black);
349 attrs.background_pixel = colorpixel(dpy, s, s->depth, 0xE9FFE9, s->white);
350 attrs.colormap = s->def_cmap;
352 s->menuwin = XCreateWindow(dpy, s->root, 0, 0, 1, 1, 2,
353 s->depth,
354 CopyFromParent,
355 s->vis,
356 CWBackPixel | CWBorderPixel | CWColormap,
357 &attrs
358 );
361 gv.foreground = colorpixel(dpy, s, s->depth, 0xE9FFE9, s->white);
362 s->gcmenubg = XCreateGC(dpy, s->menuwin, gmask, &gv);
364 gv.foreground = colorpixel(dpy, s, s->depth, 0x448844, s->black);
365 s->gcmenubgs = XCreateGC(dpy, s->menuwin, gmask, &gv);
367 gv.foreground = s->black;
368 gv.background = colorpixel(dpy, s, s->depth, 0xE9FFE9, s->white);
369 s->gcmenufg = XCreateGC(dpy, s->menuwin, gmask, &gv);
371 gv.foreground = colorpixel(dpy, s, s->depth, 0xE9FFE9, s->white);
372 gv.background = colorpixel(dpy, s, s->depth, 0x448844, s->black);
373 s->gcmenufgs = XCreateGC(dpy, s->menuwin, gmask, &gv);
375 attrs.border_pixel = s->red;
376 attrs.background_pixel = colorpixel(dpy, s, s->depth, 0xEEEEEE, s->black);
377 attrs.colormap = s->def_cmap;
378 s->sweepwin = XCreateWindow(dpy, s->root, 0, 0, 1, 1, 4,
379 s->depth,
380 CopyFromParent,
381 s->vis,
382 CWBackPixel | CWBorderPixel | CWColormap,
383 &attrs
384 );
387 ScreenInfo*
388 getscreen(Window w)
390 int i;
392 for (i = 0; i < num_screens; i++)
393 if (screens[i].root == w)
394 return &screens[i];
396 return 0;
399 Time
400 timestamp(void)
402 XEvent ev;
404 if (curtime == CurrentTime) {
405 XChangeProperty(dpy, screens[0].root, _rio_running, _rio_running, 8,
406 PropModeAppend, (unsigned char *)"", 0);
407 XMaskEvent(dpy, PropertyChangeMask, &ev);
408 curtime = ev.xproperty.time;
410 return curtime;
413 void
414 sendcmessage(Window w, Atom a, long x, int isroot, int usemask)
416 XEvent ev;
417 int status;
418 long mask;
420 memset(&ev, 0, sizeof(ev));
421 ev.xclient.type = ClientMessage;
422 ev.xclient.window = w;
423 ev.xclient.message_type = a;
424 ev.xclient.format = 32;
425 ev.xclient.data.l[0] = x;
426 ev.xclient.data.l[1] = timestamp();
427 mask = 0;
428 if(usemask){
429 mask |= KeyPressMask; /* seems to be necessary */
430 if (isroot)
431 mask |= SubstructureRedirectMask; /* magic! */
432 else
433 mask |= ExposureMask; /* not really correct but so be it */
435 status = XSendEvent(dpy, w, False, mask, &ev);
436 if (status == 0)
437 fprintf(stderr, "rio: sendcmessage failed\n");
440 void
441 sendconfig(Client *c)
443 XConfigureEvent ce;
445 ce.type = ConfigureNotify;
446 ce.event = c->window;
447 ce.window = c->window;
448 ce.x = c->x;
449 ce.y = c->y;
450 ce.width = c->dx;
451 ce.height = c->dy;
452 ce.border_width = c->border;
453 ce.above = None;
454 ce.override_redirect = 0;
455 XSendEvent(dpy, c->window, False, StructureNotifyMask, (XEvent*)&ce);
458 void
459 sighandler(void)
461 signalled = 1;
464 void
465 getevent(XEvent *e)
467 int fd;
468 fd_set rfds;
469 struct timeval t;
471 if (!signalled) {
472 if (QLength(dpy) > 0) {
473 XNextEvent(dpy, e);
474 return;
476 fd = ConnectionNumber(dpy);
477 FD_ZERO(&rfds);
478 FD_SET(fd, &rfds);
479 t.tv_sec = t.tv_usec = 0;
480 if (select(fd+1, &rfds, NULL, NULL, &t) == 1) {
481 XNextEvent(dpy, e);
482 return;
484 XFlush(dpy);
485 FD_SET(fd, &rfds);
486 if (select(fd+1, &rfds, NULL, NULL, NULL) == 1) {
487 XNextEvent(dpy, e);
488 return;
490 if (errno != EINTR || !signalled) {
491 perror("rio: select failed");
492 exit(1);
495 fprintf(stderr, "rio: exiting on signal\n");
496 cleanup();
497 exit(1);
500 void
501 cleanup(void)
503 Client *c, *cc[2], *next;
504 XWindowChanges wc;
505 int i;
507 /* order of un-reparenting determines final stacking order... */
508 cc[0] = cc[1] = 0;
509 for (c = clients; c; c = next) {
510 next = c->next;
511 i = normal(c);
512 c->next = cc[i];
513 cc[i] = c;
516 for (i = 0; i < 2; i++) {
517 for (c = cc[i]; c; c = c->next) {
518 if (!withdrawn(c)) {
519 gravitate(c, 1);
520 XReparentWindow(dpy, c->window, c->screen->root,
521 c->x, c->y);
523 wc.border_width = c->border;
524 XConfigureWindow(dpy, c->window, CWBorderWidth, &wc);
528 XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, timestamp());
529 for (i = 0; i < num_screens; i++)
530 cmapnofocus(&screens[i]);
531 XCloseDisplay(dpy);