/* Copyright (c) 1994-1996 David Hogan, see README for licence details */ #include #include #include #include #include #include #include #include #include #include #ifdef SHAPE #include #endif #include "config.h" #include "dat.h" #include "fns.h" #include "patchlevel.h" char *version[] = { "ryudo version 0.1, Copyright (c) 1994-1996 David Hogan, (c) 2004 Russ Cox, (c) 2019 Derek Stevens", 0 }; Display *dpy; ScreenInfo *screens; int initting; XFontStruct *font; int nostalgia; char **myargv; char *termprog; char *shell; Bool shape; int _border = 4; int _corner = 25; int _inset = 1; int curtime; int debug; int signalled; int scrolling; int num_screens; int solidsweep = 0; int numvirtuals = 0; int ffm = 0; int kbLaunch = 0; Atom exit_rio; Atom restart_rio; Atom wm_state; Atom wm_change_state; Atom wm_protocols; Atom wm_delete; Atom wm_take_focus; Atom wm_lose_focus; Atom wm_colormaps; Atom _rio_running; Atom _rio_hold_mode; Atom wm_state_fullscreen; Atom wm_state; char *fontlist[] = { "*-lucidatypewriter-medium-*-12-*-75-*", "lucm.latin1.9", "blit", "*-lucidatypewriter-bold-*-14-*-75-*", "9x15bold", "fixed", "*", 0 }; void usage(void) { fprintf(stderr, "usage: ryudo [-font fname] [-s] [-term prog] [-version] [-virtuals num] [exit|restart]\n"); exit(1); } int main(int argc, char *argv[]) { int i, do_exit, do_restart; char *fname; int shape_event; #ifdef SHAPE int dummy; #endif shape_event = 0; myargv = argv; /* for restart */ do_exit = do_restart = 0; font = 0; fname = 0; for(i = 1; i < argc; i++) if(strcmp(argv[i], "-nostalgia") == 0) nostalgia++; else if(strcmp(argv[i], "-debug") == 0) debug++; /* else if(strcmp(argv[i], "-ffm") == 0) ffm++; */ else if(strcmp(argv[i], "-font") == 0 && i+1 12){ fprintf(stderr, "ryudo: wrong number of virtual displays, defaulting to 4\n"); numvirtuals = 4; } } else if(strcmp(argv[i], "-version") == 0){ fprintf(stderr, "%s", version[0]); if(PATCHLEVEL > 0) fprintf(stderr, "; patch level %d", PATCHLEVEL); fprintf(stderr, "\n"); exit(0); } else if(strcmp(argv[i], "-s") == 0){ scrolling = 1; } else if(argv[i][0] == '-') usage(); else break; for(; i < argc; i++) if(strcmp(argv[i], "exit") == 0) do_exit++; else if(strcmp(argv[i], "restart") == 0) do_restart++; else usage(); if(do_exit && do_restart) usage(); shell = (char *)getenv("SHELL"); if(shell == NULL) shell = DEFSHELL; dpy = XOpenDisplay(""); if(dpy == 0) fatal("can't open display"); initting = 1; XSetErrorHandler(handler); if(signal(SIGTERM, sighandler) == SIG_IGN) signal(SIGTERM, SIG_IGN); if(signal(SIGINT, sighandler) == SIG_IGN) signal(SIGINT, SIG_IGN); if(signal(SIGHUP, sighandler) == SIG_IGN) signal(SIGHUP, SIG_IGN); exit_rio = XInternAtom(dpy, "9WM_EXIT", False); restart_rio = XInternAtom(dpy, "9WM_RESTART", False); curtime = -1; /* don't care */ if(do_exit){ sendcmessage(DefaultRootWindow(dpy), exit_rio, 0L, 1, 1); XSync(dpy, False); exit(0); } if(do_restart){ sendcmessage(DefaultRootWindow(dpy), restart_rio, 0L, 1, 1); XSync(dpy, False); exit(0); } if(0) XSynchronize(dpy, True); wm_state = XInternAtom(dpy, "WM_STATE", False); wm_change_state = XInternAtom(dpy, "WM_CHANGE_STATE", False); wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False); wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", False); wm_take_focus = XInternAtom(dpy, "WM_TAKE_FOCUS", False); wm_lose_focus = XInternAtom(dpy, "_9WM_LOSE_FOCUS", False); wm_colormaps = XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False); _rio_running = XInternAtom(dpy, "_9WM_RUNNING", False); _rio_hold_mode = XInternAtom(dpy, "_9WM_HOLD_MODE", False); wm_state = XInternAtom(dpy, "_NET_WM_STATE", False); wm_state_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); if(fname != 0) if((font = XLoadQueryFont(dpy, fname)) == 0) fprintf(stderr, "ryudo: warning: can't load font %s\n", fname); if(font == 0){ i = 0; for(;;){ fname = fontlist[i++]; if(fname == 0){ fprintf(stderr, "ryudo: warning: can't find a font\n"); break; } font = XLoadQueryFont(dpy, fname); if(font != 0) break; } } if(nostalgia){ _border--; _inset--; } #ifdef SHAPE shape = XShapeQueryExtension(dpy, &shape_event, &dummy); #endif num_screens = ScreenCount(dpy); screens = (ScreenInfo *)malloc(sizeof(ScreenInfo) * num_screens); for(i = 0; i < num_screens; i++) initscreen(&screens[i], i, 0); initb2menu(numvirtuals); /* set selection so that 9term knows we're running */ curtime = CurrentTime; XSetSelectionOwner(dpy, _rio_running, screens[0].menuwin, timestamp()); XSync(dpy, False); initting = 0; nofocus(); for(i = 0; i < num_screens; i++) scanwins(&screens[i]); keysetup(); mainloop(shape_event); return 0; } void initscreen(ScreenInfo *s, int i, int background) { char *ds, *colon, *dot1; unsigned long mask; unsigned long gmask; XGCValues gv; XSetWindowAttributes attr; XVisualInfo xvi; XSetWindowAttributes attrs; s->num = i; s->root = RootWindow(dpy, i); s->def_cmap = DefaultColormap(dpy, i); s->min_cmaps = MinCmapsOfScreen(ScreenOfDisplay(dpy, i)); s->depth = DefaultDepth(dpy, i); /* * Figure out underlying screen format. */ if(XMatchVisualInfo(dpy, i, 16, TrueColor, &xvi) || XMatchVisualInfo(dpy, i, 16, DirectColor, &xvi)){ s->vis = xvi.visual; s->depth = 16; } else if(XMatchVisualInfo(dpy, i, 15, TrueColor, &xvi) || XMatchVisualInfo(dpy, i, 15, DirectColor, &xvi)){ s->vis = xvi.visual; s->depth = 15; } else if(XMatchVisualInfo(dpy, i, 24, TrueColor, &xvi) || XMatchVisualInfo(dpy, i, 24, DirectColor, &xvi)){ s->vis = xvi.visual; s->depth = 24; } else if(XMatchVisualInfo(dpy, i, 8, PseudoColor, &xvi) || XMatchVisualInfo(dpy, i, 8, StaticColor, &xvi)){ s->vis = xvi.visual; s->depth = 8; } else{ s->depth = DefaultDepth(dpy, i); if(s->depth != 8){ fprintf(stderr, "can't understand depth %d screen", s->depth); exit(1); } s->vis = DefaultVisual(dpy, i); } if(DefaultDepth(dpy, i) != s->depth){ s->def_cmap = XCreateColormap(dpy, s->root, s->vis, AllocNone); } ds = DisplayString(dpy); colon = rindex(ds, ':'); if(colon && num_screens > 1){ strcpy(s->display, "DISPLAY="); strcat(s->display, ds); colon = s->display + 8 + (colon - ds); /* use version in buf */ dot1 = index(colon, '.'); /* first period after colon */ if(!dot1) dot1 = colon + strlen(colon); /* if not there, append */ sprintf(dot1, ".%d", i); } else s->display[0] = '\0'; s->black = BlackPixel(dpy, i); s->white = WhitePixel(dpy, i); s->activeholdborder = colorpixel(dpy, s, s->depth, SHOLDCOL, s->white); s->inactiveholdborder = colorpixel(dpy, s, s->depth, HOLDCOL, s->black); s->activeborder = colorpixel(dpy, s, s->depth, SBORDERCOL, s->black); s->inactiveborder = colorpixel(dpy, s, s->depth, BORDERCOL, s->white); s->red = colorpixel(dpy, s, s->depth, GHOSTCOL, s->white); s->width = WidthOfScreen(ScreenOfDisplay(dpy, i)); s->height = HeightOfScreen(ScreenOfDisplay(dpy, i)); s->bkup[0] = XCreatePixmap(dpy, s->root, 2*s->width, BORDER, DefaultDepth(dpy, i)); s->bkup[1] = XCreatePixmap(dpy, s->root, BORDER, 2*s->height, DefaultDepth(dpy, i)); gv.foreground = s->black^s->white; gv.background = s->white; gv.function = GXxor; gv.line_width = 0; gv.subwindow_mode = IncludeInferiors; gmask = GCForeground | GCBackground | GCFunction | GCLineWidth | GCSubwindowMode; if(font != 0){ gv.font = font->fid; gmask |= GCFont; } s->gc = XCreateGC(dpy, s->root, gmask, &gv); gv.function = GXcopy; s->gccopy = XCreateGC(dpy, s->root, gmask, &gv); gv.foreground = s->red; s->gcred = XCreateGC(dpy, s->root, gmask, &gv); gv.foreground = colorpixel(dpy, s, s->depth, 0xEEEEEE, s->black); s->gcsweep = XCreateGC(dpy, s->root, gmask, &gv); initcurs(s); attr.cursor = s->arrow; attr.event_mask = SubstructureRedirectMask | SubstructureNotifyMask | ColormapChangeMask | ButtonPressMask | ButtonReleaseMask | PropertyChangeMask | KeyPressMask | EnterWindowMask; mask = CWCursor|CWEventMask; XChangeWindowAttributes(dpy, s->root, mask, &attr); XSync(dpy, False); attrs.border_pixel = colorpixel(dpy, s, s->depth, MBORDERCOL, s->black); attrs.background_pixel = colorpixel(dpy, s, s->depth, MENUBGCOL, s->white); attrs.colormap = s->def_cmap; s->menuwin = XCreateWindow(dpy, s->root, 0, 0, 1, 1, MENUBORDER, s->depth, CopyFromParent, s->vis, CWBackPixel | CWBorderPixel | CWColormap, &attrs ); gv.foreground = colorpixel(dpy, s, s->depth, MENUBGCOL, s->black); s->gcmenubg = XCreateGC(dpy, s->menuwin, gmask, &gv); gv.foreground = colorpixel(dpy, s, s->depth, SMENUBGCOL, s->white); s->gcmenubgs = XCreateGC(dpy, s->menuwin, gmask, &gv); gv.foreground = colorpixel(dpy, s, s->depth, MENUFGCOL, s->white); gv.background = colorpixel(dpy, s, s->depth, MENUBGCOL, s->black); s->gcmenufg = XCreateGC(dpy, s->menuwin, gmask, &gv); gv.foreground = colorpixel(dpy, s, s->depth, SMENUFGCOL, s->white); gv.background = colorpixel(dpy, s, s->depth, SMENUBGCOL, s->black); s->gcmenufgs = XCreateGC(dpy, s->menuwin, gmask, &gv); attrs.border_pixel = s->red; attrs.background_pixel = colorpixel(dpy, s, s->depth, 0xEEEEEE, s->black); attrs.colormap = s->def_cmap; s->sweepwin = XCreateWindow(dpy, s->root, 0, 0, 1, 1, 4, s->depth, CopyFromParent, s->vis, CWBackPixel | CWBorderPixel | CWColormap, &attrs ); } ScreenInfo* getscreen(Window w) { int i; for(i = 0; i < num_screens; i++) if(screens[i].root == w) return &screens[i]; return 0; } Time timestamp(void) { XEvent ev; if(curtime == CurrentTime){ XChangeProperty(dpy, screens[0].root, _rio_running, _rio_running, 8, PropModeAppend, (unsigned char *)"", 0); XMaskEvent(dpy, PropertyChangeMask, &ev); curtime = ev.xproperty.time; } return curtime; } void sendcmessage(Window w, Atom a, long x, int isroot, int usemask) { XEvent ev; int status; long mask; memset(&ev, 0, sizeof(ev)); ev.xclient.type = ClientMessage; ev.xclient.window = w; ev.xclient.message_type = a; ev.xclient.format = 32; ev.xclient.data.l[0] = x; ev.xclient.data.l[1] = timestamp(); mask = 0; if(usemask){ mask |= KeyPressMask; /* seems to be necessary */ if(isroot) mask |= SubstructureRedirectMask; /* magic! */ else mask |= ExposureMask; /* not really correct but so be it */ } status = XSendEvent(dpy, w, False, mask, &ev); if(status == 0) fprintf(stderr, "ryudo: sendcmessage failed\n"); } void sendconfig(Client *c) { XConfigureEvent ce; ce.type = ConfigureNotify; ce.event = c->window; ce.window = c->window; ce.x = c->x; ce.y = c->y; ce.width = c->dx; ce.height = c->dy; ce.border_width = c->border; ce.above = None; ce.override_redirect = 0; XSendEvent(dpy, c->window, False, StructureNotifyMask, (XEvent*)&ce); } void sighandler(void) { signalled = 1; } void getevent(XEvent *e) { int fd; fd_set rfds; struct timeval t; if(!signalled){ if(QLength(dpy) > 0){ XNextEvent(dpy, e); return; } fd = ConnectionNumber(dpy); FD_ZERO(&rfds); FD_SET(fd, &rfds); t.tv_sec = t.tv_usec = 0; if(select(fd+1, &rfds, NULL, NULL, &t) == 1){ XNextEvent(dpy, e); return; } XFlush(dpy); FD_SET(fd, &rfds); if(select(fd+1, &rfds, NULL, NULL, NULL) == 1){ XNextEvent(dpy, e); return; } if(errno != EINTR || !signalled){ perror("ryudo: select failed"); exit(1); } } fprintf(stderr, "ryudo: exiting on signal\n"); cleanup(); exit(1); } void cleanup(void) { Client *c, *cc[2], *next; XWindowChanges wc; int i; /* order of un-reparenting determines final stacking order... */ cc[0] = cc[1] = 0; for(c = clients; c; c = next){ next = c->next; i = normal(c); c->next = cc[i]; cc[i] = c; } for(i = 0; i < 2; i++){ for(c = cc[i]; c; c = c->next){ if(!withdrawn(c)){ XReparentWindow(dpy, c->window, c->screen->root, c->x, c->y); } wc.border_width = c->border; XConfigureWindow(dpy, c->window, CWBorderWidth, &wc); } } XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, timestamp()); for(i = 0; i < num_screens; i++) cmapnofocus(&screens[i]); XCloseDisplay(dpy); }