697 lines
16 KiB
C
697 lines
16 KiB
C
/*
|
|
* Copyright (c) 2019 Iris Lightshard, 2005 Rus Cox, 1994-1996 David Hogan
|
|
* see README for licence details
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <X11/X.h>
|
|
#include <X11/Xos.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
#include <X11/Xatom.h>
|
|
#include <X11/extensions/shape.h>
|
|
#include <X11/extensions/Xrandr.h>
|
|
#include "config.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "patchlevel.h"
|
|
|
|
void mainloop(int shape_event) {
|
|
static XEvent ev;
|
|
static Client* c;
|
|
static int monitor;
|
|
|
|
for (;;) {
|
|
getevent(&ev);
|
|
#ifdef DEBUG_EV
|
|
if (debug) {
|
|
ShowEvent(&ev);
|
|
printf("\n");
|
|
}
|
|
#endif
|
|
switch (ev.type) {
|
|
default:
|
|
#ifdef SHAPE
|
|
if (shape && ev.type == shape_event)
|
|
shapenotify((XShapeEvent*)&ev);
|
|
else
|
|
#endif
|
|
fprintf(stderr, "ryudo: unknown ev.type %d\n", ev.type);
|
|
break;
|
|
/* XRANDR related events; idk what the define for this is */
|
|
case 89:
|
|
if (XRRUpdateConfiguration(&ev)) {
|
|
fetchmonitorinfo();
|
|
monitor = getmonitorbyclient(c);
|
|
if (monitor < 0 || monitor >= nmonitors) {
|
|
break;
|
|
}
|
|
for (c = clients; c; c = c->next) {
|
|
monitor = getmonitorbyclient(c);
|
|
wrangle(c, monitorinfo[monitor]);
|
|
}
|
|
}
|
|
break;
|
|
case KeyPress:
|
|
keypress(&ev.xkey);
|
|
break;
|
|
case KeyRelease:
|
|
keyrelease(&ev.xkey);
|
|
break;
|
|
case ButtonPress:
|
|
button(&ev.xbutton);
|
|
XAllowEvents(dpy, ReplayPointer, ev.xbutton.time);
|
|
break;
|
|
case ButtonRelease:
|
|
XAllowEvents(dpy, ReplayPointer, ev.xbutton.time);
|
|
break;
|
|
case MapRequest:
|
|
mapreq(&ev.xmaprequest);
|
|
break;
|
|
case ConfigureRequest:
|
|
configurereq(&ev.xconfigurerequest);
|
|
break;
|
|
case CirculateRequest:
|
|
circulatereq(&ev.xcirculaterequest);
|
|
break;
|
|
case UnmapNotify:
|
|
unmap(&ev.xunmap);
|
|
break;
|
|
case CreateNotify:
|
|
newwindow(&ev.xcreatewindow);
|
|
break;
|
|
case DestroyNotify:
|
|
destroy(ev.xdestroywindow.window);
|
|
break;
|
|
case ClientMessage:
|
|
clientmesg(&ev.xclient);
|
|
break;
|
|
case ColormapNotify:
|
|
cmap(&ev.xcolormap);
|
|
break;
|
|
case PropertyNotify:
|
|
property(&ev.xproperty);
|
|
break;
|
|
case SelectionClear:
|
|
fprintf(stderr, "ryudo: SelectionClear (this should not happen)\n");
|
|
break;
|
|
case SelectionNotify:
|
|
fprintf(stderr, "ryudo: SelectionNotify (this should not happen)\n");
|
|
break;
|
|
case SelectionRequest:
|
|
fprintf(stderr, "ryudo: SelectionRequest (this should not happen)\n");
|
|
break;
|
|
case EnterNotify:
|
|
enter(&ev.xcrossing);
|
|
break;
|
|
case LeaveNotify:
|
|
leave(&ev.xcrossing);
|
|
break;
|
|
case ReparentNotify:
|
|
reparent(&ev.xreparent);
|
|
break;
|
|
case FocusIn:
|
|
focusin(&ev.xfocus);
|
|
break;
|
|
case MotionNotify:
|
|
motionnotify(&ev.xmotion);
|
|
break;
|
|
case Expose:
|
|
case NoExpose:
|
|
case FocusOut:
|
|
case ConfigureNotify:
|
|
case MapNotify:
|
|
case MappingNotify:
|
|
case GraphicsExpose:
|
|
/* not interested */
|
|
trace("ignore", 0, &ev);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void configurereq(XConfigureRequestEvent* e) {
|
|
XWindowChanges wc;
|
|
Client* c;
|
|
XRRMonitorInfo monitor;
|
|
int m;
|
|
/* we don't set curtime as nothing here uses it */
|
|
c = getclient(e->window, 0);
|
|
trace("configurereq", c, e);
|
|
|
|
e->value_mask &= ~CWSibling;
|
|
|
|
if (c) {
|
|
m = getmonitorbyclient(c);
|
|
monitor = monitorinfo[m];
|
|
|
|
if (e->value_mask & CWX)
|
|
c->x = e->x;
|
|
if (e->value_mask & CWY)
|
|
c->y = e->y;
|
|
if (e->value_mask & CWWidth)
|
|
c->dx = e->width;
|
|
if (e->value_mask & CWHeight)
|
|
c->dy = e->height;
|
|
if (
|
|
e->value_mask & CWBorderWidth && e->width >= monitor.width &&
|
|
e->height >= monitor.height)
|
|
c->border = 0;
|
|
else
|
|
c->border = BORDER;
|
|
|
|
if (e->value_mask & CWStackMode) {
|
|
if (e->detail == Above)
|
|
top(c);
|
|
else
|
|
e->value_mask &= ~CWStackMode;
|
|
}
|
|
e->value_mask |= CWX | CWY | CWHeight | CWWidth;
|
|
|
|
if (c->parent != c->screen->root && c->window == e->window) {
|
|
wc.x = c->x - c->border;
|
|
wc.y = c->y - c->border;
|
|
wc.width = c->dx + c->border + c->border;
|
|
wc.height = c->dy + c->border + c->border;
|
|
wc.sibling = None;
|
|
wc.border_width = c->border;
|
|
wc.stack_mode = e->detail;
|
|
XConfigureWindow(dpy, c->parent, e->value_mask, &wc);
|
|
|
|
if (e->value_mask & CWStackMode) {
|
|
if (c->virt > 0 && c->virt != virt)
|
|
switch_to(c->virt);
|
|
if (hidden(c)) {
|
|
unhidec(c, 1);
|
|
}
|
|
top(c);
|
|
active(c);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (c && c->parent != c->screen->root) {
|
|
wc.x = c->border;
|
|
wc.y = c->border;
|
|
} else {
|
|
wc.x = c->x;
|
|
wc.y = c->y;
|
|
}
|
|
wc.width = c->dx;
|
|
wc.height = c->dy;
|
|
wc.border_width = 0;
|
|
wc.sibling = None;
|
|
wc.stack_mode = Above;
|
|
e->value_mask &= ~CWStackMode;
|
|
e->value_mask |= CWBorderWidth;
|
|
XConfigureWindow(dpy, c->window, e->value_mask, &wc);
|
|
}
|
|
|
|
void mapreq(XMapRequestEvent* e) {
|
|
Client* c;
|
|
int i, m;
|
|
XRRMonitorInfo monitor;
|
|
|
|
#ifdef MONITORFOLLOWSMOUSE
|
|
m = getmonitorbymouse();
|
|
#else
|
|
m = getmonitorbyclient(current);
|
|
#endif
|
|
|
|
monitor = monitorinfo[m];
|
|
|
|
curtime = CurrentTime;
|
|
c = getclient(e->window, 0);
|
|
trace("mapreq", c, e);
|
|
|
|
if (c == 0 || c->window != e->window) {
|
|
/* workaround for stupid NCDware */
|
|
fprintf(
|
|
stderr,
|
|
"ryudo: bad mapreq c %p w %x, rescanning\n",
|
|
(void*)c,
|
|
(int)e->window);
|
|
for (i = 0; i < num_screens; i++)
|
|
scanwins(&screens[i]);
|
|
c = getclient(e->window, 0);
|
|
if (c == 0 || c->window != e->window) {
|
|
fprintf(stderr, "ryudo: window not found after rescan\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (kbLaunch && CASCADE_DENSITY < 2) {
|
|
// usleep(MICROSLEEP_DELAY);
|
|
#ifdef CENTERVMAX
|
|
centerclient(c, monitor, 1);
|
|
#else
|
|
centerclient(c, monitor, 0);
|
|
#endif
|
|
kbLaunch = 0;
|
|
} else if (CASCADE_DENSITY >= 2 && c->trans == None) {
|
|
getcascadecoords(c->window, m, &(c->x), &(c->y));
|
|
}
|
|
switch (c->state) {
|
|
case WithdrawnState:
|
|
if (c->parent == c->screen->root) {
|
|
if (!manage(c, 0))
|
|
return;
|
|
break;
|
|
}
|
|
XReparentWindow(dpy, c->window, c->parent, BORDER, BORDER);
|
|
XAddToSaveSet(dpy, c->window);
|
|
/* fall through... */
|
|
case NormalState:
|
|
XMapWindow(dpy, c->window);
|
|
XMapRaised(dpy, c->parent);
|
|
top(c);
|
|
setstate(c, NormalState);
|
|
if (c->trans != None && current && c->trans == current->window)
|
|
active(c);
|
|
break;
|
|
case IconicState:
|
|
unhidec(c, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void unmap(XUnmapEvent* e) {
|
|
Client *c, *revertc;
|
|
int m;
|
|
|
|
curtime = CurrentTime;
|
|
c = getclient(e->window, 0);
|
|
|
|
if (c) {
|
|
m = getmonitorbyclient(c);
|
|
switch (c->state) {
|
|
case IconicState:
|
|
if (e->send_event) {
|
|
unhidec(c, 0);
|
|
withdraw(c);
|
|
}
|
|
break;
|
|
case NormalState:
|
|
if (c == current) {
|
|
nofocus();
|
|
if (revertc = getclient(getrevert(c), 0)) {
|
|
top(revertc);
|
|
active(revertc);
|
|
} else {
|
|
shuffleonmonitor(m);
|
|
}
|
|
}
|
|
if (!c->reparenting)
|
|
withdraw(c);
|
|
break;
|
|
}
|
|
c->reparenting = 0;
|
|
}
|
|
}
|
|
|
|
void circulatereq(XCirculateRequestEvent* e) {
|
|
fprintf(stderr, "It must be the warlock Krill!\n"); /* ☺ */
|
|
}
|
|
|
|
void newwindow(XCreateWindowEvent* e) {
|
|
Client* c;
|
|
ScreenInfo* s;
|
|
|
|
/* we don't set curtime as nothing here uses it */
|
|
if (e->override_redirect)
|
|
return;
|
|
c = getclient(e->window, 1);
|
|
if (c && c->window == e->window && (s = getscreen(e->parent))) {
|
|
c->x = e->x;
|
|
c->y = e->y;
|
|
c->dx = e->width;
|
|
c->dy = e->height;
|
|
c->border = e->border_width;
|
|
c->screen = s;
|
|
if (c->parent == None)
|
|
c->parent = c->screen->root;
|
|
}
|
|
#if defined(OPACITY) && defined(TRANSPARENTLIST)
|
|
if (istransparent(c)) {
|
|
/* We need to set the atom on both the window and its parent */
|
|
const Atom alpha_atom = XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", 0);
|
|
unsigned long opacity = (unsigned long)OPACITY * 0x1010101;
|
|
XChangeProperty(
|
|
dpy,
|
|
c->window,
|
|
alpha_atom,
|
|
XA_CARDINAL,
|
|
32,
|
|
PropModeReplace,
|
|
(unsigned char*)&opacity,
|
|
1);
|
|
XChangeProperty(
|
|
dpy,
|
|
c->parent,
|
|
alpha_atom,
|
|
XA_CARDINAL,
|
|
32,
|
|
PropModeReplace,
|
|
(unsigned char*)&opacity,
|
|
1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void destroy(Window w) {
|
|
int i;
|
|
Client *c, *revertc;
|
|
Window revert;
|
|
|
|
curtime = CurrentTime;
|
|
c = getclient(w, 0);
|
|
if (c == 0)
|
|
return;
|
|
|
|
revert = getrevert(c);
|
|
|
|
if (numvirtuals > 1)
|
|
for (i = 0; i < numvirtuals; i++)
|
|
if (currents[i] == c)
|
|
currents[i] = 0;
|
|
|
|
rmclient(c);
|
|
|
|
if (!current && (revertc = getclient(revert, 0))) {
|
|
top(revertc);
|
|
active(revertc);
|
|
}
|
|
|
|
/* flush any errors generated by the window's sudden demise */
|
|
ignore_badwindow = 1;
|
|
XSync(dpy, False);
|
|
ignore_badwindow = 0;
|
|
}
|
|
|
|
void clientmesg(XClientMessageEvent* e) {
|
|
Client* c;
|
|
|
|
curtime = CurrentTime;
|
|
if (e->message_type == exit_rio) {
|
|
cleanup();
|
|
exit(0);
|
|
}
|
|
if (e->message_type == restart_rio) {
|
|
fprintf(stderr, "*** ryudo restarting ***\n");
|
|
cleanup();
|
|
execvp(myargv[0], myargv);
|
|
perror("ryudo: exec failed");
|
|
exit(1);
|
|
}
|
|
|
|
if (e->message_type == wm_protocols) {
|
|
return;
|
|
}
|
|
if (e->message_type == wm_change_state) {
|
|
c = getclient(e->window, 0);
|
|
if (e->format == 32 && e->data.l[0] == IconicState && c != 0) {
|
|
if (normal(c))
|
|
hide(c);
|
|
} else
|
|
fprintf(
|
|
stderr,
|
|
"ryudo: WM_CHANGE_STATE: format %d data %d w 0x%x\n",
|
|
(int)e->format,
|
|
(int)e->data.l[0],
|
|
(int)e->window);
|
|
return;
|
|
}
|
|
if (e->message_type == wm_state) {
|
|
// c = getclient(e->window, 0);
|
|
// if(e->format == 32 && e->data.l[1] == wm_state_fullscreen){
|
|
// }else
|
|
fprintf(
|
|
stderr,
|
|
"ryudo: WM_STATE: format %d data %d %d w 0x%x\n",
|
|
(int)e->format,
|
|
(int)e->data.l[0],
|
|
(int)e->data.l[1],
|
|
(int)e->window);
|
|
return;
|
|
}
|
|
fprintf(
|
|
stderr,
|
|
"ryudo: strange ClientMessage, type 0x%x window 0x%x\n",
|
|
(int)e->message_type,
|
|
(int)e->window);
|
|
}
|
|
|
|
void cmap(XColormapEvent* e) {
|
|
Client* c;
|
|
int i;
|
|
|
|
/* we don't set curtime as nothing here uses it */
|
|
if (e->new) {
|
|
c = getclient(e->window, 0);
|
|
if (c) {
|
|
c->cmap = e->colormap;
|
|
if (c == current)
|
|
cmapfocus(c);
|
|
} else
|
|
for (c = clients; c; c = c->next) {
|
|
for (i = 0; i < c->ncmapwins; i++)
|
|
if (c->cmapwins[i] == e->window) {
|
|
c->wmcmaps[i] = e->colormap;
|
|
if (c == current)
|
|
cmapfocus(c);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void property(XPropertyEvent* e) {
|
|
Atom a;
|
|
int delete;
|
|
Client* c;
|
|
long msize;
|
|
|
|
/* we don't set curtime as nothing here uses it */
|
|
a = e->atom;
|
|
delete = (e->state == PropertyDelete);
|
|
c = getclient(e->window, 0);
|
|
if (c == 0)
|
|
return;
|
|
|
|
switch (a) {
|
|
case XA_WM_ICON_NAME:
|
|
if (c->iconname != 0)
|
|
XFree((char*)c->iconname);
|
|
c->iconname = delete ? 0 : getprop(c->window, a);
|
|
setlabel(c);
|
|
renamec(c, c->label);
|
|
return;
|
|
case XA_WM_NAME:
|
|
if (c->name != 0)
|
|
XFree((char*)c->name);
|
|
c->name = delete ? 0 : getprop(c->window, a);
|
|
setlabel(c);
|
|
renamec(c, c->label);
|
|
return;
|
|
case XA_WM_TRANSIENT_FOR:
|
|
gettrans(c);
|
|
return;
|
|
case XA_WM_HINTS:
|
|
case XA_WM_SIZE_HINTS:
|
|
case XA_WM_ZOOM_HINTS:
|
|
/* placeholders to not forget. ignore for now. -Axel */
|
|
return;
|
|
case XA_WM_NORMAL_HINTS:
|
|
if (
|
|
XGetWMNormalHints(dpy, c->window, &c->size, &msize) == 0 ||
|
|
c->size.flags == 0)
|
|
c->size.flags = PSize; /* not specified - punt */
|
|
return;
|
|
}
|
|
if (a == _rio_hold_mode) {
|
|
c->hold = getiprop(c->window, _rio_hold_mode);
|
|
if (c == current)
|
|
draw_border(c, 1);
|
|
} else if (a == wm_colormaps) {
|
|
getcmaps(c);
|
|
if (c == current)
|
|
cmapfocus(c);
|
|
}
|
|
}
|
|
|
|
void reparent(XReparentEvent* e) {
|
|
Client* c;
|
|
XWindowAttributes attr;
|
|
ScreenInfo* s;
|
|
|
|
/* we don't set curtime as nothing here uses it */
|
|
if (!getscreen(e->event) || e->override_redirect)
|
|
return;
|
|
if ((s = getscreen(e->parent)) != 0) {
|
|
c = getclient(e->window, 1);
|
|
if (c != 0 && (c->dx == 0 || c->dy == 0)) {
|
|
/* flush any errors */
|
|
ignore_badwindow = 1;
|
|
XGetWindowAttributes(dpy, c->window, &attr);
|
|
XSync(dpy, False);
|
|
ignore_badwindow = 0;
|
|
|
|
c->x = attr.x;
|
|
c->y = attr.y;
|
|
c->dx = attr.width;
|
|
c->dy = attr.height;
|
|
c->border = attr.border_width;
|
|
c->screen = s;
|
|
if (c->parent == None)
|
|
c->parent = c->screen->root;
|
|
}
|
|
} else {
|
|
c = getclient(e->window, 0);
|
|
if (c != 0 && (c->parent == c->screen->root || withdrawn(c)))
|
|
rmclient(c);
|
|
}
|
|
}
|
|
|
|
#ifdef SHAPE
|
|
void shapenotify(XShapeEvent* e) {
|
|
Client* c;
|
|
|
|
/* we don't set curtime as nothing here uses it */
|
|
c = getclient(e->window, 0);
|
|
if (c == 0)
|
|
return;
|
|
|
|
setshape(c);
|
|
}
|
|
#endif
|
|
|
|
void enter(XCrossingEvent* e) {
|
|
Client* c;
|
|
|
|
curtime = e->time;
|
|
if (e->mode != NotifyGrab || e->detail != NotifyNonlinearVirtual)
|
|
return;
|
|
c = getclient(e->window, 0);
|
|
if (c != 0 && c != current) {
|
|
/* someone grabbed the pointer; make them current */
|
|
XMapRaised(dpy, c->parent);
|
|
top(c);
|
|
active(c);
|
|
}
|
|
}
|
|
|
|
void leave(XCrossingEvent* e) {
|
|
Client* c;
|
|
|
|
c = getclient(e->window, 0);
|
|
if (c)
|
|
XUndefineCursor(dpy, c->parent);
|
|
/* XDefineCursor(dpy, c->parent, c->screen->arrow); */
|
|
}
|
|
|
|
void focusin(XFocusChangeEvent* e) {
|
|
Client* c;
|
|
|
|
curtime = CurrentTime;
|
|
if (e->detail != NotifyNonlinearVirtual)
|
|
return;
|
|
c = getclient(e->window, 0);
|
|
if (c != 0 && c->window == e->window && c != current) {
|
|
#ifdef AUTOSTICK
|
|
if (!isautostick(c)) {
|
|
#endif
|
|
/* someone grabbed keyboard or seized focus; make them current */
|
|
XMapRaised(dpy, c->parent);
|
|
top(c);
|
|
active(c);
|
|
#ifdef AUTOSTICK
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
BorderOrient borderorient(Client* c, int x, int y) {
|
|
if (x <= BORDER) {
|
|
if (y <= CORNER) {
|
|
if (debug)
|
|
fprintf(stderr, "topleft\n");
|
|
return BorderWNW;
|
|
}
|
|
if (y >= (c->dy + 2 * BORDER) - CORNER) {
|
|
if (debug)
|
|
fprintf(stderr, "botleft\n");
|
|
return BorderWSW;
|
|
}
|
|
if (y > CORNER && y < (c->dy + 2 * BORDER) - CORNER) {
|
|
if (debug)
|
|
fprintf(stderr, "left\n");
|
|
return BorderW;
|
|
}
|
|
} else if (x <= CORNER) {
|
|
if (y <= BORDER) {
|
|
if (debug)
|
|
fprintf(stderr, "topleft\n");
|
|
return BorderNNW;
|
|
}
|
|
if (y >= (c->dy + BORDER)) {
|
|
if (debug)
|
|
fprintf(stderr, "botleft\n");
|
|
return BorderSSW;
|
|
}
|
|
} else if (x >= (c->dx + BORDER)) {
|
|
if (y <= CORNER) {
|
|
if (debug)
|
|
fprintf(stderr, "topright\n");
|
|
return BorderENE;
|
|
}
|
|
if (y >= (c->dy + 2 * BORDER) - CORNER) {
|
|
if (debug)
|
|
fprintf(stderr, "botright\n");
|
|
return BorderESE;
|
|
}
|
|
if (y > CORNER && y < (c->dy + 2 * BORDER) - CORNER) {
|
|
if (debug)
|
|
fprintf(stderr, "right\n");
|
|
return BorderE;
|
|
}
|
|
} else if (x >= (c->dx + 2 * BORDER) - CORNER) {
|
|
if (y <= BORDER) {
|
|
if (debug)
|
|
fprintf(stderr, "topright\n");
|
|
return BorderNNE;
|
|
}
|
|
if (y >= (c->dy + BORDER)) {
|
|
if (debug)
|
|
fprintf(stderr, "botright\n");
|
|
return BorderSSE;
|
|
}
|
|
} else if (x > CORNER && x < (c->dx + 2 * BORDER) - CORNER) {
|
|
if (y <= BORDER) {
|
|
if (debug)
|
|
fprintf(stderr, "top\n");
|
|
return BorderN;
|
|
}
|
|
if (y >= (c->dy + BORDER)) {
|
|
if (debug)
|
|
fprintf(stderr, "bot\n");
|
|
return BorderS;
|
|
}
|
|
}
|
|
return BorderUnknown;
|
|
}
|
|
|
|
void motionnotify(XMotionEvent* e) {
|
|
Client* c;
|
|
BorderOrient bl;
|
|
|
|
c = getclient(e->window, 0);
|
|
if (c) {
|
|
bl = borderorient(c, e->x, e->y);
|
|
if (bl == BorderUnknown)
|
|
XUndefineCursor(dpy, c->parent);
|
|
else
|
|
XDefineCursor(dpy, c->parent, c->screen->bordcurs[bl]);
|
|
}
|
|
}
|