ryudo/main.c

526 lines
12 KiB
C
Executable file

/* Copyright (c) 1994-1996 David Hogan, see README for licence details */
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <X11/X.h>
#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#ifdef SHAPE
#include <X11/extensions/shape.h>
#endif
#include "config.h"
#include "dat.h"
#include "fns.h"
#include "patchlevel.h"
char *version[] =
{
"ryudo version 0.3\nCopyright (c) 1994-1996 David Hogan,\n(c) 2004 Russ Cox,\n(c) 2019-2021 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[] = FONTLIST;
void
usage(void)
{
fprintf(stderr, "usage: ryudo [-ffm] [-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<argc){
i++;
fname = argv[i];
}
else if(strcmp(argv[i], "-term") == 0 && i+1<argc)
termprog = argv[++i];
else if(strcmp(argv[i], "-virtuals") == 0 && i+1<argc){
numvirtuals = atoi(argv[++i]);
if(numvirtuals < 0 || numvirtuals > 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);
}