+ int n, found = 0;
+ Atom *protos;
+ if (!XGetWMProtocols(win->sys->dpy, win->sys->xid, &protos, &n))
+ return 0;
+
+ while (!found && n--)
+ found = protos[n] == atoms[msg];
+ XFree(protos);
+ if (!found)
+ return 0;
+
+ XSendEvent(win->sys->dpy, win->sys->xid, False, NoEventMask, &(XEvent){
+ .xclient.type = ClientMessage,
+ .xclient.window = win->sys->xid,
+ .xclient.message_type = atoms[WM_PROTO],
+ .xclient.format = 32,
+ .xclient.data.l[0] = atoms[msg],
+ .xclient.data.l[1] = CurrentTime,
+ });
+ return 1;
+}
+
+static Atom win_prop(win_t *win, atom_t prop)
+{
+ int format;
+ unsigned long nitems, bytes;
+ unsigned char *buf = NULL;
+ Atom atom, type = XA_ATOM;
+ if (XGetWindowProperty(win->sys->dpy, win->sys->xid, atoms[prop],
+ 0L, sizeof(Atom), False, type, &type, &format, &nitems, &bytes, &buf) || !buf)
+ return 0;
+ atom = *(Atom *)buf;
+ XFree(buf);
+ return atom;
+}
+
+/* Drawing functions */
+static unsigned long get_color(Display *dpy, const char *name)
+{
+ XColor color;
+ int screen = DefaultScreen(dpy);
+ Colormap cmap = DefaultColormap(dpy, screen);
+ XAllocNamedColor(dpy, cmap, name, &color, &color);
+ return color.pixel;
+}
+
+/* Callbacks */
+static void process_event(int type, XEvent *xe, win_t *root)
+{
+ Display *dpy = root->sys->dpy;
+ win_t *win = NULL;
+ //printf("event: %d\n", type);
+
+ /* Common data for all these events ... */
+ ptr_t ptr = {}; mod_t mod = {};
+ if (type == KeyPress || type == KeyRelease ||
+ type == ButtonPress || type == ButtonRelease ||
+ type == MotionNotify) {
+ Window xid = getfocus(root, xe);
+ if (!(win = win_find(dpy,xid,0)))
+ return;
+ //printf("button-press %p\n", win);
+ ptr = x2ptr(xe);
+ mod = x2mod(xe->xkey.state, type==KeyRelease||type==ButtonRelease);
+ }
+
+ /* Split based on event */
+ if (type == KeyPress) {
+ while (XCheckTypedEvent(dpy, KeyPress, xe));
+ KeySym sym = XLookupKeysym(&xe->xkey, 0);
+ //printf("got xe %c %hhx\n", xk2ev(sym), mod2int(mod));
+ wm_handle_event(win, xk2ev(sym), mod, ptr);
+ }
+ else if (type == KeyRelease) {
+ //printf("release: %lx\n", xe->xkey.window);
+ }
+ else if (type == ButtonPress) {
+ if (wm_handle_event(win, xb2ev(xe->xbutton.button), mod, ptr)) {
+ //printf("grab pointer\n");
+ XGrabPointer(dpy, xe->xbutton.root, True, PointerMotionMask|ButtonReleaseMask,
+ GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
+ } else {
+ //printf("allow events\n");
+ XAllowEvents(win->sys->dpy, ReplayPointer, xe->xbutton.time);
+ }
+ }
+ else if (type == ButtonRelease) {
+ XUngrabPointer(dpy, CurrentTime);
+ wm_handle_event(win, xb2ev(xe->xbutton.button), mod, ptr);
+ }
+ else if (type == MotionNotify) {
+ while (XCheckTypedEvent(dpy, MotionNotify, xe));
+ wm_handle_ptr(win, ptr);
+ }
+ else if (type == EnterNotify || type == LeaveNotify) {
+ printf("%s: %lx\n", type==EnterNotify?"enter":"leave",
+ xe->xcrossing.window);
+ event_t ev = type == EnterNotify ? EV_ENTER : EV_LEAVE;
+ if ((win = win_find(dpy,xe->xcrossing.window,0)))
+ wm_handle_event(win, ev, MOD(), PTR());
+ }
+ else if (type == FocusIn || type == FocusOut) {
+ //printf("focus: %lx\n", xe->xfocus.window);
+ event_t ev = type == FocusIn ? EV_FOCUS : EV_UNFOCUS;
+ if ((win = win_find(dpy,xe->xfocus.window,0)))
+ wm_handle_event(win, ev, MOD(), PTR());
+ }
+ else if (type == ConfigureNotify) {
+ //printf("configure: %lx\n", xe->xconfigure.window);
+ }
+ else if (type == MapNotify) {
+ printf("map: %lx\n", xe->xmap.window);
+ }
+ else if (type == UnmapNotify) {
+ if ((win = win_find(dpy,xe->xunmap.window,0)) &&
+ win->state != ST_HIDE) {
+ printf("unmap: %lx\n", xe->xunmap.window);
+ wm_handle_state(win, win->state, ST_HIDE);
+ win->state = ST_HIDE;
+ }
+ }
+ else if (type == DestroyNotify) {
+ printf("destroy: %lx\n", xe->xdestroywindow.window);
+ if ((win = win_find(dpy,xe->xdestroywindow.window,0)))
+ win_remove(win);
+ }
+ else if (type == ConfigureRequest) {
+ XConfigureRequestEvent *cre = &xe->xconfigurerequest;
+ printf("configure_req: %lx - (0x%lx) %dx%d @ %d,%d\n",
+ cre->window, cre->value_mask,
+ cre->height, cre->width, cre->x, cre->y);
+ if ((win = win_find(dpy,cre->window,1))) {
+ XSendEvent(dpy, cre->window, False, StructureNotifyMask, &(XEvent){
+ .xconfigure.type = ConfigureNotify,
+ .xconfigure.display = win->sys->dpy,
+ .xconfigure.event = win->sys->xid,
+ .xconfigure.window = win->sys->xid,
+ .xconfigure.x = win->x,
+ .xconfigure.y = win->y,
+ .xconfigure.width = win->w,
+ .xconfigure.height = win->h,
+ .xconfigure.border_width = border,
+ });
+ XSync(win->sys->dpy, False);
+ }
+ }
+ else if (type == MapRequest) {
+ printf("map_req: %lx\n", xe->xmaprequest.window);
+ win = win_find(dpy,xe->xmaprequest.window,1);
+ // fixme, for hide -> max, etc
+ if (win->state == ST_HIDE) {
+ wm_handle_state(win, win->state, ST_SHOW);
+ win->state = ST_SHOW;
+ }
+ sys_show(win, win->state);
+ }
+ else if (type == ClientMessage) {
+ XClientMessageEvent *cme = &xe->xclient;
+ printf("client_msg: %lx - %ld %ld,%ld,%ld,%ld,%ld\n",
+ cme->window, cme->message_type,
+ cme->data.l[0], cme->data.l[1], cme->data.l[2],
+ cme->data.l[3], cme->data.l[4]);
+ if ((win = win_find(dpy,cme->window,0)) &&
+ (cme->message_type == atoms[NET_STATE]) &&
+ (cme->data.l[1] == atoms[NET_FULL] ||
+ cme->data.l[2] == atoms[NET_FULL])) {
+ state_t next = (cme->data.l[0] == 1 || /* _NET_WM_STATE_ADD */
+ (cme->data.l[0] == 2 && /* _NET_WM_STATE_TOGGLE */
+ win->state != ST_FULL)) ? ST_FULL : ST_SHOW;
+ wm_handle_state(win, win->state, next);
+ sys_show(win, next);
+ }
+ }
+ else if (type == PropertyNotify) {
+ printf("prop: %lx - %d\n", xe->xproperty.window, xe->xproperty.state);
+ }
+ else {
+ printf("unknown event: %d\n", type);
+ }
+}
+
+static int xerror(Display *dpy, XErrorEvent *err)
+{
+ if (err->error_code == BadWindow ||
+ (err->request_code == X_SetInputFocus && err->error_code == BadMatch ) ||
+ (err->request_code == X_PolyText8 && err->error_code == BadDrawable) ||
+ (err->request_code == X_PolyFillRectangle && err->error_code == BadDrawable) ||
+ (err->request_code == X_PolySegment && err->error_code == BadDrawable) ||
+ (err->request_code == X_ConfigureWindow && err->error_code == BadMatch ) ||
+ (err->request_code == X_GrabButton && err->error_code == BadAccess ) ||
+ (err->request_code == X_GrabKey && err->error_code == BadAccess ) ||
+ (err->request_code == X_CopyArea && err->error_code == BadDrawable))
+ return 0;
+ if (err->request_code == X_ChangeWindowAttributes && err->error_code == BadAccess)
+ error("Another window manager is already running");
+ return xerrorxlib(dpy, err);
+}
+
+static int xnoerror(Display *dpy, XErrorEvent *err)
+{
+ return 0;
+}
+
+
+/********************
+ * System functions *
+ ********************/
+void sys_move(win_t *win, int x, int y, int w, int h)
+{
+ //printf("sys_move: %p - %d,%d %dx%d\n", win, x, y, w, h);
+ int b = 2*border;
+ win->x = x; win->y = y;
+ win->w = MAX(w,1+b); win->h = MAX(h,1+b);
+ w = MAX(w-b,1); h = MAX(h-b,1);
+ XConfigureWindow(win->sys->dpy, win->sys->xid, CWX|CWY|CWWidth|CWHeight,
+ &(XWindowChanges) { .x=x, .y=y, .width=w, .height=h });
+ XMoveResizeWindow(win->sys->dpy, win->sys->xid, x, y, w, h);
+
+ /* Flush events, so moving window doesn't cause re-focus
+ * There's probably a better way to do this */
+ XEvent xe;
+ XSync(win->sys->dpy, False);
+ while (XCheckMaskEvent(win->sys->dpy, EnterWindowMask|LeaveWindowMask, &xe))
+ printf("Skipping enter/leave event\n");
+}
+
+void sys_raise(win_t *win)
+{
+ //printf("sys_raise: %p\n", win);
+ XRaiseWindow(win->sys->dpy, win->sys->xid);
+ for (list_t *cur = struts; cur; cur = cur->next)
+ XRaiseWindow(((win_t*)cur->data)->sys->dpy,
+ ((win_t*)cur->data)->sys->xid);
+}
+
+void sys_focus(win_t *win)
+{
+ //printf("sys_focus: %p\n", win);
+
+ /* Set actual focus */
+ XSetInputFocus(win->sys->dpy, win->sys->xid,
+ RevertToPointerRoot, CurrentTime);
+ //win_msg(win, WM_FOCUS);
+
+ /* Set border on focused window */
+ if (last)
+ XSetWindowBorder(last->sys->dpy, last->sys->xid, colors[CLR_UNFOCUS]);
+ XSync(win->sys->dpy, False);
+ XSetWindowBorder(win->sys->dpy, win->sys->xid, colors[CLR_FOCUS]);
+ last = win;
+}
+
+void sys_show(win_t *win, state_t state)
+{
+ //if (win->state == state)
+ // return;
+
+ /* Debug */
+ printf("sys_show: %p: %s -> %s\n", win,
+ state_map[win->state], state_map[state]);
+
+ /* Find screen */
+ win_t *screen = NULL;
+ if (state == ST_FULL || state == ST_MAX) {
+ for (list_t *cur = screens; cur; cur = cur->next) {
+ screen = cur->data;
+ if (win->x >= screen->x && win->x <= screen->x+screen->w &&
+ win->y >= screen->y && win->y <= screen->y+screen->h)
+ break;