]> Pileus Git - wmpus/blob - sys-x11.c
Formatting and debugging updates
[wmpus] / sys-x11.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <search.h>
4
5 #include <X11/Xlib.h>
6 #include <X11/Xproto.h>
7 #include <X11/Xatom.h>
8 #include <X11/keysym.h>
9
10 #include "util.h"
11 #include "sys.h"
12 #include "wm.h"
13
14 #define BORDER 2
15
16 /* Internal structures */
17 struct win_sys {
18         Window   xid;
19         Display *dpy;
20         struct {
21                 int left, right, top, bottom;
22         } strut;
23 };
24
25 typedef struct {
26         Key_t key;
27         int   sym;
28 } keymap_t;
29
30 typedef enum {
31         wm_proto, wm_focus, net_strut, natoms
32 } atom_t;
33
34 typedef enum {
35         clr_focus, clr_unfocus, clr_urgent, ncolors
36 } color_t;
37
38 /* Global data */
39 static void *win_cache;
40 static Atom atoms[natoms];
41 static int (*xerrorxlib)(Display *, XErrorEvent *);
42 static unsigned long colors[ncolors];
43
44 /* Conversion functions */
45 static keymap_t key2sym[] = {
46         {key_left    , XK_Left },
47         {key_right   , XK_Right},
48         {key_up      , XK_Up   },
49         {key_down    , XK_Down },
50         {key_home    , XK_Home },
51         {key_end     , XK_End  },
52         {key_pageup  , XK_Prior},
53         {key_pagedown, XK_Next },
54         {key_f1      , XK_F1   },
55         {key_f2      , XK_F2   },
56         {key_f3      , XK_F3   },
57         {key_f4      , XK_F4   },
58         {key_f5      , XK_F5   },
59         {key_f6      , XK_F6   },
60         {key_f7      , XK_F7   },
61         {key_f8      , XK_F8   },
62         {key_f9      , XK_F9   },
63         {key_f10     , XK_F10  },
64         {key_f11     , XK_F11  },
65         {key_f12     , XK_F12  },
66 };
67
68 /* - Modifiers */
69 static mod_t x2mod(unsigned int state, int up)
70 {
71         return (mod_t){
72                .alt   = !!(state & Mod1Mask   ),
73                .ctrl  = !!(state & ControlMask),
74                .shift = !!(state & ShiftMask  ),
75                .win   = !!(state & Mod4Mask   ),
76                .up    = up,
77         };
78 }
79
80 static unsigned int mod2x(mod_t mod)
81 {
82         return (mod.alt   ? Mod1Mask    : 0)
83              | (mod.ctrl  ? ControlMask : 0)
84              | (mod.shift ? ShiftMask   : 0)
85              | (mod.win   ? Mod4Mask    : 0);
86 }
87
88 /* - Keycodes */
89 static Key_t x2key(KeySym sym)
90 {
91         keymap_t *km = map_getr(key2sym,sym);
92         return km ? km->key : sym;
93 }
94
95 static KeySym key2x(Key_t key)
96 {
97         keymap_t *km = map_get(key2sym,key);
98         return km ? km->sym : key;
99 }
100
101 static Key_t x2btn(int btn)
102 {
103         return btn + key_mouse0;
104 }
105
106 static int btn2x(Key_t key)
107 {
108         return key - key_mouse0;
109 }
110
111 /* - Pointers */
112 static ptr_t x2ptr(XEvent *_ev)
113 {
114         XKeyEvent *ev = &_ev->xkey;
115         return (ptr_t){ev->x, ev->y, ev->x_root, ev->y_root};
116 }
117
118 static Window getfocus(win_t *root, XEvent *event)
119 {
120         int revert;
121         Window focus = PointerRoot;
122         if (event->type == KeyPress || event->type == KeyRelease)
123                 XGetInputFocus(root->sys->dpy, &focus, &revert);
124         if (focus == PointerRoot)
125                 focus = event->xkey.subwindow;
126         if (focus == None)
127                 focus = event->xkey.window;
128         return focus;
129 }
130
131 /* Helpers */
132 static int add_strut(win_t *root, win_t *win)
133 {
134         /* Get X11 strut data */
135         Atom ret_type;
136         int ret_size;
137         unsigned long ret_items, bytes_left;
138         unsigned char *xdata;
139         int status = XGetWindowProperty(win->sys->dpy, win->sys->xid,
140                         atoms[net_strut], 0L, 4L, False, XA_CARDINAL,
141                         &ret_type, &ret_size, &ret_items, &bytes_left, &xdata);
142         if (status != Success || ret_size != 32 || ret_items != 4)
143                 return 0;
144
145         int left   = ((int*)xdata)[0];
146         int right  = ((int*)xdata)[1];
147         int top    = ((int*)xdata)[2];
148         int bottom = ((int*)xdata)[3];
149         if (left == 0 && right == 0 && top == 0 && bottom == 0)
150                 return 0;
151
152         win->sys->strut.left   = left;
153         win->sys->strut.right  = right;
154         win->sys->strut.top    = top;
155         win->sys->strut.bottom = bottom;
156         root->x += left;
157         root->y += top;
158         root->w -= left+right;
159         root->h -= top+bottom;
160         return 1;
161 }
162
163 static int del_strut(win_t *root, win_t *win)
164 {
165         int left   = win->sys->strut.left;
166         int right  = win->sys->strut.right;
167         int top    = win->sys->strut.top;
168         int bottom = win->sys->strut.bottom;
169         if (left == 0 && right == 0 && top == 0 && bottom == 0)
170                 return 0;
171
172         root->x -= left;
173         root->y -= top;
174         root->w += left+right;
175         root->h += top+bottom;
176         return 1;
177 }
178
179 /* Window functions */
180 static win_t *win_new(Display *dpy, Window xid)
181 {
182         XWindowAttributes attr;
183         if (XGetWindowAttributes(dpy, xid, &attr))
184                 if (attr.override_redirect)
185                         return NULL;
186         win_t *win    = new0(win_t);
187         win->x        = attr.x;
188         win->y        = attr.y;
189         win->w        = attr.width;
190         win->h        = attr.height;
191         win->sys      = new0(win_sys_t);
192         win->sys->dpy = dpy;
193         win->sys->xid = xid;
194         printf("win_new: %p = %p, %d (%d,%d %dx%d)\n",
195                         win, dpy, (int)xid,
196                         win->x, win->y, win->w, win->h);
197         return win;
198 }
199
200 static int win_cmp(const void *_a, const void *_b)
201 {
202         const win_t *a = _a, *b = _b;
203         if (a->sys->dpy < b->sys->dpy) return -1;
204         if (a->sys->dpy > b->sys->dpy) return  1;
205         if (a->sys->xid < b->sys->xid) return -1;
206         if (a->sys->xid > b->sys->xid) return  1;
207         return 0;
208 }
209
210 static win_t *win_find(Display *dpy, Window xid, int create)
211 {
212         if (!dpy || !xid)
213                 return NULL;
214         //printf("win_find: %p, %d\n", dpy, (int)xid);
215         win_sys_t sys = {.dpy=dpy, .xid=xid};
216         win_t     tmp = {.sys=&sys};
217         win_t **old = NULL, *new = NULL;
218         if ((old = tfind(&tmp, &win_cache, win_cmp)))
219                 return *old;
220         if (create && (new = win_new(dpy,xid)))
221                 tsearch(new, &win_cache, win_cmp);
222         return new;
223 }
224
225 static void win_remove(win_t *win)
226 {
227         tdelete(win, &win_cache, win_cmp);
228         free(win->sys);
229         free(win);
230 }
231
232 static int win_viewable(win_t *win)
233 {
234         XWindowAttributes attr;
235         if (XGetWindowAttributes(win->sys->dpy, win->sys->xid, &attr))
236                 return attr.map_state == IsViewable;
237         else
238                 return True;
239 }
240
241 /* Drawing functions */
242 unsigned long get_color(Display *dpy, const char *name)
243 {
244         XColor color;
245         int screen = DefaultScreen(dpy);
246         Colormap cmap = DefaultColormap(dpy, screen);
247         XAllocNamedColor(dpy, cmap, name, &color, &color);
248         return color.pixel;
249 }
250
251 /* Callbacks */
252 static void process_event(int type, XEvent *ev, win_t *root)
253 {
254         Display  *dpy = root->sys->dpy;
255         win_t *win = NULL;
256         printf("event: %d\n", type);
257
258         /* Common data for all these events ... */
259         ptr_t ptr; mod_t mod;
260         if (type == KeyPress    || type == KeyRelease    ||
261             type == ButtonPress || type == ButtonRelease ||
262             type == MotionNotify) {
263                 Window xid = getfocus(root, ev);
264                 if (!(win = win_find(dpy,xid,0)))
265                         return;
266                 ptr = x2ptr(ev);
267                 mod = x2mod(ev->xkey.state, type==KeyRelease||type==ButtonRelease);
268         }
269
270         /* Split based on event */
271         if (type == KeyPress) {
272                 while (XCheckTypedEvent(dpy, KeyPress, ev));
273                 KeySym sym = XKeycodeToKeysym(dpy, ev->xkey.keycode, 0);
274                 wm_handle_key(win, x2key(sym), mod, ptr);
275         }
276         else if (type == KeyRelease) {
277                 //printf("release: %d\n", type);
278         }
279         else if (type == ButtonPress) {
280                 if (wm_handle_key(win, x2btn(ev->xbutton.button), mod, ptr))
281                         XGrabPointer(dpy, ev->xbutton.root, True, PointerMotionMask|ButtonReleaseMask,
282                                         GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
283                 else {
284                         printf("resending event\n");
285                         XSendEvent(win->sys->dpy, ev->xbutton.window,    True,  NoEventMask, ev);
286                         XSendEvent(win->sys->dpy, ev->xbutton.window,    False, NoEventMask, ev);
287                         XSendEvent(win->sys->dpy, ev->xbutton.root,      True,  NoEventMask, ev);
288                         XSendEvent(win->sys->dpy, ev->xbutton.root,      False, NoEventMask, ev);
289                         XSendEvent(win->sys->dpy, ev->xbutton.subwindow, True,  NoEventMask, ev);
290                         XSendEvent(win->sys->dpy, ev->xbutton.subwindow, False, NoEventMask, ev);
291                 }
292         }
293         else if (type == ButtonRelease) {
294                 XUngrabPointer(dpy, CurrentTime);
295                 wm_handle_key(win, x2btn(ev->xbutton.button), mod, ptr);
296         }
297         else if (type == MotionNotify) {
298                 while (XCheckTypedEvent(dpy, MotionNotify, ev));
299                 wm_handle_ptr(win, ptr);
300         }
301         else if (type == EnterNotify || type == LeaveNotify) {
302                 printf("enter: %d\n", type);
303                 key_t key = EnterNotify ? key_enter : key_leave;
304                 if ((win = win_find(dpy,ev->xcrossing.window,0)))
305                         wm_handle_key(win, key, MOD(), PTR());
306         }
307         else if (type == FocusIn || type == FocusOut) {
308                 printf("focus: %d\n", type);
309                 key_t key = FocusIn ? key_focus : key_unfocus;
310                 if ((win = win_find(dpy,ev->xfocus.window,0)))
311                         wm_handle_key(win, key, MOD(), PTR());
312         }
313         else if (type == ConfigureNotify) {
314                 printf("configure: %d\n", type);
315         }
316         else if (type == MapNotify) {
317                 printf("map: %d\n", type);
318         }
319         else if (type == UnmapNotify) {
320                 //printf("unmap: %d\n", type);
321                 if ((win = win_find(dpy,ev->xunmap.window,0))) {
322                         if (!del_strut(root, win))
323                                 wm_remove(win);
324                         else
325                                 wm_update();
326                         win_remove(win);
327                 }
328         }
329         else if (type == ConfigureRequest) {
330                 XConfigureRequestEvent *cre = &ev->xconfigurerequest;
331                 printf("configure_req: %d - %x, (0x%lx) %dx%d @ %d,%d\n",
332                                 type, (int)cre->window, cre->value_mask,
333                                 cre->height, cre->width, cre->x, cre->y);
334                 XConfigureWindow(dpy, cre->window, cre->value_mask, &(XWindowChanges){
335                         .x      = cre->x,
336                         .y      = cre->y,
337                         .width  = cre->width,
338                         .height = cre->height,
339                 });
340
341                 /* This seems necessasairy for, but causes flicker
342                  * there could be a better way to do this */
343                 if ((win = win_find(dpy,ev->xmaprequest.window,0)))
344                         sys_move(win, win->x, win->y, win->w, win->h);
345         }
346         else if (type == MapRequest) {
347                 printf("map_req: %d\n", type);
348                 if ((win = win_find(dpy,ev->xmaprequest.window,1))) {
349                         if (!add_strut(root, win))
350                                 wm_insert(win);
351                         else
352                                 wm_update();
353                 }
354                 XMapWindow(dpy,ev->xmaprequest.window);
355         }
356         else {
357                 printf("unknown event: %d\n", type);
358         }
359 }
360
361 static int xerror(Display *dpy, XErrorEvent *err)
362 {
363         if (err->error_code == BadWindow ||
364             (err->request_code == X_SetInputFocus     && err->error_code == BadMatch   ) ||
365             (err->request_code == X_PolyText8         && err->error_code == BadDrawable) ||
366             (err->request_code == X_PolyFillRectangle && err->error_code == BadDrawable) ||
367             (err->request_code == X_PolySegment       && err->error_code == BadDrawable) ||
368             (err->request_code == X_ConfigureWindow   && err->error_code == BadMatch   ) ||
369             (err->request_code == X_GrabButton        && err->error_code == BadAccess  ) ||
370             (err->request_code == X_GrabKey           && err->error_code == BadAccess  ) ||
371             (err->request_code == X_CopyArea          && err->error_code == BadDrawable))
372                 return 0;
373         return xerrorxlib(dpy, err);
374 }
375
376 /*****************
377  * Sys functions *
378  *****************/
379 void sys_move(win_t *win, int x, int y, int w, int h)
380 {
381         //printf("sys_move: %p - %d,%d  %dx%d\n", win, x, y, w, h);
382         int b = 2*BORDER;
383         win->x = MAX(x,0);   win->y = MAX(y,0);
384         win->w = MAX(w,1+b); win->h = MAX(h,1+b);
385         w      = MAX(w-b,1); h      = MAX(h-b,1);
386         XMoveResizeWindow(win->sys->dpy, win->sys->xid, x, y, w, h);
387
388         /* Flush events, so moving window doesn't cuase re-focus
389          * There's probably a better way to do this */
390         XEvent ev;
391         XSync(win->sys->dpy, False);
392         while (XCheckMaskEvent(win->sys->dpy, EnterWindowMask|LeaveWindowMask, &ev))
393                 printf("Skipping enter/leave event\n");
394 }
395
396 void sys_raise(win_t *win)
397 {
398         //printf("sys_raise: %p\n", win);
399         XRaiseWindow(win->sys->dpy, win->sys->xid);
400 }
401
402 void sys_focus(win_t *win)
403 {
404         printf("sys_focus: %p\n", win);
405
406         /* Set border on focused window */
407         static win_t *last = NULL;
408         if (last)
409                 XSetWindowBorder(last->sys->dpy, last->sys->xid, colors[clr_unfocus]);
410         XSetWindowBorder(win->sys->dpy, win->sys->xid, colors[clr_focus]);
411         XSetWindowBorderWidth(win->sys->dpy, win->sys->xid, BORDER);
412         last = win;
413
414         /* Set actual focus */
415         XSetInputFocus(win->sys->dpy, win->sys->xid,
416                         RevertToPointerRoot, CurrentTime);
417         XSendEvent(win->sys->dpy, win->sys->xid, False, NoEventMask, &(XEvent){
418                 .type                 = ClientMessage,
419                 .xclient.window       = win->sys->xid,
420                 .xclient.message_type = atoms[wm_proto],
421                 .xclient.format       = 32,
422                 .xclient.data.l[0]    = atoms[wm_focus],
423                 .xclient.data.l[1]    = CurrentTime,
424         });
425 }
426
427 void sys_watch(win_t *win, Key_t key, mod_t mod)
428 {
429         //printf("sys_watch: %p - %x %hhx\n", win, key, mod);
430         XWindowAttributes attr;
431         XGetWindowAttributes(win->sys->dpy, win->sys->xid, &attr);
432         long mask = attr.your_event_mask;
433         if (key_mouse0 <= key && key <= key_mouse7)
434                 XGrabButton(win->sys->dpy, btn2x(key), mod2x(mod), win->sys->xid, False,
435                                 mod.up ? ButtonReleaseMask : ButtonPressMask,
436                                 GrabModeAsync, GrabModeAsync, None, None);
437         else if (key == key_enter)
438                 XSelectInput(win->sys->dpy, win->sys->xid, EnterWindowMask|mask);
439         else if (key == key_leave)
440                 XSelectInput(win->sys->dpy, win->sys->xid, LeaveWindowMask|mask);
441         else if (key == key_focus || key == key_unfocus)
442                 XSelectInput(win->sys->dpy, win->sys->xid, FocusChangeMask|mask);
443         else
444                 XGrabKey(win->sys->dpy, XKeysymToKeycode(win->sys->dpy, key2x(key)),
445                                 mod2x(mod), win->sys->xid, True, GrabModeAsync, GrabModeAsync);
446 }
447
448 void sys_unwatch(win_t *win, Key_t key, mod_t mod)
449 {
450         if (key_mouse0 <= key && key <= key_mouse7)
451                 XUngrabButton(win->sys->dpy, btn2x(key), mod2x(mod), win->sys->xid);
452 }
453
454 win_t *sys_init(void)
455 {
456         Display *dpy;
457         Window   xid;
458         if (!(dpy = XOpenDisplay(NULL)))
459                 error("Unable to get display");
460         if (!(xid = DefaultRootWindow(dpy)))
461                 error("Unable to get root window");
462         atoms[wm_proto]  = XInternAtom(dpy, "WM_PROTOCOLS",  False);
463         atoms[wm_focus]  = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
464         atoms[net_strut] = XInternAtom(dpy, "_NET_WM_STRUT", False);
465         colors[clr_focus]   = get_color(dpy, "#a0a0ff");
466         colors[clr_unfocus] = get_color(dpy, "#101066");
467         colors[clr_urgent]  = get_color(dpy, "#ff0000");
468         printf("colors = #%06lx #%06lx #%06lx\n", colors[0], colors[1], colors[2]);
469         XSelectInput(dpy, xid, SubstructureRedirectMask|SubstructureNotifyMask);
470         XSetInputFocus(dpy, None, RevertToNone, CurrentTime);
471         xerrorxlib = XSetErrorHandler(xerror);
472         return win_find(dpy, xid, 1);
473 }
474
475 void sys_run(win_t *root)
476 {
477         /* Add each initial window */
478         unsigned int nkids;
479         Window par, xid, *kids = NULL;
480         if (XQueryTree(root->sys->dpy, root->sys->xid,
481                                 &par, &xid, &kids, &nkids))
482                 for(int i = 0; i < nkids; i++) {
483                         win_t *win = win_find(root->sys->dpy, kids[i], 1);
484                         if (win && win_viewable(win) && !add_strut(root,win))
485                                 wm_insert(win);
486                 }
487         wm_update(); // For struts
488
489         /* Main loop */
490         for(;;)
491         {
492                 XEvent ev;
493                 XNextEvent(root->sys->dpy, &ev);
494                 process_event(ev.type, &ev, root);
495         }
496 }