]> Pileus Git - wmpus/blobdiff - sys-xcb.c
Add map and unmap handlers
[wmpus] / sys-xcb.c
index 1dcb102be4b81233936d3603911d9aa081ea121e..af650cd2c1d648fefd60cc357f8788ca6149f463 100644 (file)
--- a/sys-xcb.c
+++ b/sys-xcb.c
@@ -39,8 +39,7 @@ static int no_capture = 0;
 struct win_sys {
        xcb_window_t     xcb;    // xcb window id
        xcb_event_mask_t events; // currently watch events
-       int override;            // normal vs override redirect
-       int mapped;              // window is currently mapped
+       int managed;             // window is managed by wm
 };
 
 typedef enum {
@@ -55,16 +54,27 @@ static xcb_connection_t  *conn;
 static xcb_key_symbols_t *keysyms;
 static xcb_colormap_t     colormap;
 static xcb_window_t       root;
-static xcb_window_t       last;
 static xcb_event_mask_t   events;
 static list_t            *screens;
 static void              *cache;
 static xcb_pixmap_t       colors[NCOLORS];
+static unsigned int       grabbed;
 
 /************************
  * Conversion functions *
  ************************/
 
+/* State names */
+static char *state_map[] = {
+       [ST_HIDE ] "hide ",
+       [ST_SHOW ] "show ",
+       [ST_FULL ] "full ",
+       [ST_MAX  ] "max  ",
+       [ST_SHADE] "shade",
+       [ST_ICON ] "icon ",
+       [ST_CLOSE] "close",
+};
+
 /* Key presses */
 static struct {
        event_t      ev;
@@ -92,6 +102,10 @@ static struct {
        { EV_F12,      0xFFC9 },
 };
 
+/************************
+ * Conversion functions *
+ ************************/
+
 static xcb_keycode_t *event_to_keycodes(event_t ev)
 {
        xcb_keycode_t *codes = NULL;
@@ -151,10 +165,12 @@ static mod_t mask_to_mod(xcb_mod_mask_t mask, int up)
 static ptr_t list_to_ptr(int16_t *list)
 {
        ptr_t ptr = {};
-       ptr.rx = list[0]; // root_x
-       ptr.ry = list[1]; // root_y
-       ptr.x  = list[2]; // event_x
-       ptr.y  = list[3]; // event_y
+       if (list) {
+               ptr.rx = list[0]; // root_x
+               ptr.ry = list[1]; // root_y
+               ptr.x  = list[2]; // event_x
+               ptr.y  = list[3]; // event_y
+       }
        return ptr;
 }
 
@@ -289,6 +305,21 @@ static int do_query_screens(xcb_xinerama_screen_info_t **info)
        return ninfo;
 }
 
+static int do_get_input_focus(void)
+{
+       xcb_get_input_focus_cookie_t cookie =
+               xcb_get_input_focus(conn);
+       if (!cookie.sequence)
+               return warn("do_get_input_focus: bad cookie");
+
+       xcb_get_input_focus_reply_t *reply =
+               xcb_get_input_focus_reply(conn, cookie, NULL);
+       if (!reply)
+               return warn("do_get_input_focus: no reply");
+
+       return reply->focus;
+}
+
 static xcb_pixmap_t do_alloc_color(uint32_t rgb)
 {
        uint16_t r = (rgb & 0xFF0000) >> 8;
@@ -308,6 +339,114 @@ static xcb_pixmap_t do_alloc_color(uint32_t rgb)
        return reply->pixel;
 }
 
+static void do_grab_pointer(xcb_event_mask_t mask)
+{
+       if (!grabbed)
+               xcb_grab_pointer(conn, 0, root, mask,
+                               XCB_GRAB_MODE_ASYNC,
+                               XCB_GRAB_MODE_ASYNC,
+                               0, 0, XCB_CURRENT_TIME);
+       grabbed++;
+}
+
+static void do_ungrab_pointer(void)
+{
+       grabbed--;
+       if (!grabbed)
+               xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
+}
+
+static void do_configure_window(xcb_window_t win,
+               int x, int y, int w, int h,
+               int b, int s, int r)
+{
+       int table[][2] = {
+               { x, XCB_CONFIG_WINDOW_X            },
+               { y, XCB_CONFIG_WINDOW_Y            },
+               { w, XCB_CONFIG_WINDOW_WIDTH        },
+               { h, XCB_CONFIG_WINDOW_HEIGHT       },
+               { b, XCB_CONFIG_WINDOW_BORDER_WIDTH },
+               { s, XCB_CONFIG_WINDOW_SIBLING      },
+               { r, XCB_CONFIG_WINDOW_STACK_MODE   },
+       };
+
+       uint16_t mask    = 0;
+       uint32_t list[7] = {};
+       for (int i = 0; i < 7; i++) {
+               if (table[i][0] >= 0) {
+                       list[i] = table[i][0];
+                       mask   |= table[i][1];
+               }
+       }
+
+       xcb_configure_window(conn, win, mask, list);
+}
+
+/**************************
+ * Window Manager Helpers *
+ **************************/
+
+/* Send event info */
+static int send_event(event_t ev, xcb_window_t ewin)
+{
+       win_t *win = win_get(ewin);
+       do_grab_pointer(0);
+       int status = wm_handle_event(win, ev, MOD(), PTR());
+       do_ungrab_pointer();
+       return status;
+}
+
+/* Send event info */
+static int send_event_info(event_t ev, xcb_mod_mask_t mask, int up, int16_t *pos,
+               xcb_window_t rwin, xcb_window_t ewin, xcb_window_t cwin)
+{
+       xcb_window_t xcb = ewin == rwin ? cwin : ewin;
+       win_t *win = win_get(xcb);
+       mod_t  mod = mask_to_mod(mask, up);
+       ptr_t  ptr = list_to_ptr(pos);
+       do_grab_pointer(0);
+       int status = wm_handle_event(win, ev, mod, ptr);
+       do_ungrab_pointer();
+       return status;
+}
+
+/* Send pointer motion info */
+static int send_pointer(int16_t *pos,
+               xcb_window_t rwin, xcb_window_t ewin, xcb_window_t cwin)
+{
+       xcb_window_t xcb = ewin == rwin ? cwin : ewin;
+       win_t *win = win_get(xcb);
+       ptr_t  ptr = list_to_ptr(pos);
+       do_grab_pointer(0);
+       int status = wm_handle_ptr(win, ptr);
+       do_ungrab_pointer();
+       return status;
+}
+
+/* Send window state info */
+static void send_manage(win_t *win, int managed)
+{
+       if (win->sys->managed == managed)
+               return;
+       if (managed)
+               wm_insert(win);
+       else
+               wm_remove(win);
+       win->sys->managed = managed;
+}
+
+/* Send window state info */
+static void send_state(win_t *win, state_t next)
+{
+       if (!win->sys->managed)
+               return;
+       if (win->state == next)
+               return;
+       state_t prev = win->state;
+       win->state = next;
+       wm_handle_state(win, prev, next);
+}
+
 /**********************
  * X11 Event Handlers *
  **********************/
@@ -315,49 +454,76 @@ static xcb_pixmap_t do_alloc_color(uint32_t rgb)
 /* Specific events */
 static void on_key_event(xcb_key_press_event_t *event, int up)
 {
-       xcb_window_t xcb = event->event == root ?
-               event->child : event->event;
-       win_t  *win = win_get(xcb);
-       event_t ev  = keycode_to_event(event->detail);
-       mod_t   mod = mask_to_mod(event->state, up);
-       ptr_t   ptr = list_to_ptr(&event->root_x);
-       printf("on_key_event:         xcb=%u -> win=%p\n", xcb, win);
-       wm_handle_event(win, ev, mod, ptr);
+       printf("on_key_event:         xcb=%-8u\n", event->event);
+       xcb_window_t focus = do_get_input_focus();
+       event_t ev = keycode_to_event(event->detail);
+       send_event_info(ev, event->state, up, &event->root_x,
+               event->root, focus, event->child);
 }
 
 static void on_button_event(xcb_button_press_event_t *event, int up)
 {
-       xcb_window_t xcb = event->event == root ?
-               event->child : event->event;
-       win_t  *win = win_get(xcb);
-       event_t ev  = button_to_event(event->detail);
-       mod_t   mod = mask_to_mod(event->state, up);
-       ptr_t   ptr = list_to_ptr(&event->root_x);
-       printf("on_button_event:      xcb=%u -> win=%p\n", xcb, win);
-
-       if (!wm_handle_event(win, ev, mod, ptr))
+       printf("on_button_event:      xcb=%-8u\n", event->event);
+       event_t ev = button_to_event(event->detail);
+       if (!send_event_info(ev, event->state, up, &event->root_x,
+                               event->root, event->event, event->child))
                xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
        else if (!up)
-               xcb_grab_pointer(conn, 1, xcb,
-                               XCB_EVENT_MASK_POINTER_MOTION |
-                               XCB_EVENT_MASK_BUTTON_RELEASE,
-                               XCB_GRAB_MODE_ASYNC,
-                               XCB_GRAB_MODE_ASYNC,
-                               0, 0, XCB_CURRENT_TIME);
+               do_grab_pointer(XCB_EVENT_MASK_POINTER_MOTION |
+                               XCB_EVENT_MASK_BUTTON_RELEASE);
        else
-               xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
+               do_ungrab_pointer();
 
 }
 
 static void on_motion_notify(xcb_motion_notify_event_t *event)
 {
-       xcb_window_t xcb = event->event == root ?
-               event->child : event->event;
-       win_t *win = win_get(xcb);
-       ptr_t  ptr = list_to_ptr(&event->root_x);
-       printf("on_motion_notify:     xcb=%u -> win=%p - %d,%d / %d.%d\n", xcb, win,
-                       ptr.x, ptr.y, ptr.rx, ptr.ry);
-       wm_handle_ptr(win, ptr);
+       printf("on_motion_notify:     xcb=%-8u - %d,%d / %d.%d\n", event->event,
+                       event->event_x, event->event_y,
+                       event->root_x,  event->root_y);
+       send_pointer(&event->root_x, event->root, event->event, event->child);
+}
+
+static void on_enter_notify(xcb_enter_notify_event_t *event)
+{
+       if (event->mode != XCB_NOTIFY_MODE_NORMAL)
+               return;
+       printf("on_enter_notify:      xcb=%-8u\n", event->event);
+       send_event_info(EV_ENTER, event->state, 0, &event->root_x,
+               event->root, event->event, event->child);
+}
+
+static void on_leave_notify(xcb_leave_notify_event_t *event)
+{
+       if (event->mode != XCB_NOTIFY_MODE_NORMAL)
+               return;
+       printf("on_leave_notify:      xcb=%-8u\n", event->event);
+       send_event_info(EV_LEAVE, event->state, 0, &event->root_x,
+               event->root, event->event, event->child);
+}
+
+static void on_focus_in(xcb_focus_in_event_t *event)
+{
+       if (event->mode != XCB_NOTIFY_MODE_NORMAL &&
+           event->mode != XCB_NOTIFY_MODE_WHILE_GRABBED)
+               return;
+       printf("on_focus_in:          xcb=%-8u mode=%d\n", event->event, event->mode);
+       xcb_change_window_attributes(conn, event->event,
+                       XCB_CW_BORDER_PIXEL, &colors[CLR_FOCUS]);
+       if (event->mode == XCB_NOTIFY_MODE_NORMAL)
+               send_event(EV_FOCUS, event->event);
+}
+
+static void on_focus_out(xcb_focus_out_event_t *event)
+{
+       if (event->mode != XCB_NOTIFY_MODE_NORMAL &&
+           event->mode != XCB_NOTIFY_MODE_WHILE_GRABBED)
+               return;
+       printf("on_focus_out:         xcb=%-8u mode=%d\n", event->event, event->mode);
+       xcb_change_window_attributes(conn, event->event,
+                       XCB_CW_BORDER_PIXEL, &colors[CLR_UNFOCUS]);
+       if (event->mode == XCB_NOTIFY_MODE_NORMAL)
+               send_event(EV_UNFOCUS, event->event);
 }
 
 static void on_create_notify(xcb_create_notify_event_t *event)
@@ -365,7 +531,7 @@ static void on_create_notify(xcb_create_notify_event_t *event)
        win_t     *win = new0(win_t);
        win_sys_t *sys = new0(win_sys_t);
 
-       printf("on_create_notify:     xcb=%u -> win=%p\n",
+       printf("on_create_notify:     xcb=%-8u -> win=%p\n",
                        event->window, win);
 
        win->x        = event->x;
@@ -375,44 +541,68 @@ static void on_create_notify(xcb_create_notify_event_t *event)
        win->sys      = sys;
 
        sys->xcb      = event->window;
-       sys->override = event->override_redirect;
+
+       if (!event->override_redirect)
+               send_manage(win, 1);
 
        tsearch(win, &cache, win_cmp);
 }
 
-static void on_destroy_notify(win_t *win, xcb_destroy_notify_event_t *event)
+static void on_destroy_notify(xcb_destroy_notify_event_t *event)
 {
-       printf("on_destroy_notify:    xcb=%u -> win=%p\n",
+       win_t *win = win_get(event->window);
+       printf("on_destroy_notify:    xcb=%-8u -> win=%p\n",
                        event->window, win);
+       if (!win) return;
 
-       tdelete(win, &cache, win_cmp);
+       send_manage(win, 0);
 
-       if (win->sys->xcb == last)
-               last = 0;
+       tdelete(win, &cache, win_cmp);
 
        free(win->sys);
        free(win);
 }
 
-static void on_map_request(win_t *win, xcb_map_request_event_t *event)
+static void on_unmap_notify(xcb_unmap_notify_event_t *event)
 {
-       printf("on_map_request:       xcb=%u -> win=%p\n",
+       win_t *win = win_get(event->window);
+       printf("on_unmap_notify:      xcb=%-8u -> win=%p\n",
                        event->window, win);
+       if (!win) return;
 
-       if (!win->sys->override && !win->sys->mapped)
-               wm_insert(win);
-       win->sys->mapped = 1;
+       send_state(win, ST_HIDE);
+}
+
+static void on_map_notify(xcb_map_notify_event_t *event)
+{
+       win_t *win = win_get(event->window);
+       printf("on_map_notify:        xcb=%-8u -> win=%p\n",
+                       event->window, win);
+       if (!win) return;
+
+       send_state(win, ST_SHOW);
+}
+
+static void on_map_request(xcb_map_request_event_t *event)
+{
+       win_t *win = win_get(event->window);
+       printf("on_map_request:       xcb=%-8u -> win=%p\n",
+                       event->window, win);
+       if (!win) return;
 
+       send_state(win, ST_SHOW);
        xcb_map_window(conn, win->sys->xcb);
        sys_move(win, win->x, win->y, win->w, win->h);
 }
 
-static void on_configure_request(win_t *win, xcb_configure_request_event_t *event)
+static void on_configure_request(xcb_configure_request_event_t *event)
 {
-       printf("on_configure_request: xcb=%u -> win=%p -- %dx%d @ %d,%d\n",
+       win_t *win = win_get(event->window);
+       printf("on_configure_request: xcb=%-8u -> win=%p -- %dx%d @ %d,%d\n",
                        event->window, win,
                        event->width, event->height,
                        event->x, event->y);
+       if (!win) return;
 
        win->x = event->x;
        win->y = event->y;
@@ -438,11 +628,7 @@ static void on_configure_request(win_t *win, xcb_configure_request_event_t *even
 /* Generic Event */
 static void on_event(xcb_generic_event_t *event)
 {
-       win_t   *win = NULL;
-
        int type = XCB_EVENT_RESPONSE_TYPE(event);
-       int sent = XCB_EVENT_SENT(event);
-       const char *name = NULL;
 
        switch (type) {
                /* Input handling */
@@ -461,29 +647,45 @@ static void on_event(xcb_generic_event_t *event)
                case XCB_MOTION_NOTIFY:
                        on_motion_notify((xcb_motion_notify_event_t *)event);
                        break;
+               case XCB_ENTER_NOTIFY:
+                       on_enter_notify((xcb_enter_notify_event_t *)event);
+                       break;
+               case XCB_LEAVE_NOTIFY:
+                       on_leave_notify((xcb_leave_notify_event_t *)event);
+                       break;
+               case XCB_FOCUS_IN:
+                       on_focus_in((xcb_focus_in_event_t *)event);
+                       break;
+               case XCB_FOCUS_OUT:
+                       on_focus_out((xcb_focus_out_event_t *)event);
+                       break;
 
                /* Window management */
                case XCB_CREATE_NOTIFY:
                        on_create_notify((xcb_create_notify_event_t *)event);
                        break;
                case XCB_DESTROY_NOTIFY:
-                       if ((win = win_get(((xcb_destroy_notify_event_t *)event)->window)))
-                               on_destroy_notify(win, (xcb_destroy_notify_event_t *)event);
+                       on_destroy_notify((xcb_destroy_notify_event_t *)event);
+                       break;
+               case XCB_UNMAP_NOTIFY:
+                       on_unmap_notify((xcb_unmap_notify_event_t *)event);
+                       break;
+               case XCB_MAP_NOTIFY:
+                       on_map_notify((xcb_map_notify_event_t *)event);
                        break;
                case XCB_MAP_REQUEST:
-                       if ((win = win_get(((xcb_map_request_event_t *)event)->window)))
-                               on_map_request(win, (xcb_map_request_event_t *)event);
+                       on_map_request((xcb_map_request_event_t *)event);
                        break;
                case XCB_CONFIGURE_REQUEST:
-                       if ((win = win_get(((xcb_configure_request_event_t *)event)->window)))
-                               on_configure_request(win, (xcb_configure_request_event_t *)event);
+                       on_configure_request((xcb_configure_request_event_t *)event);
                        break;
 
                /* Unknown events */
                default:
-                       name = xcb_event_get_label(type);
                        printf("on_event: %d:%02X -> %s\n",
-                               !!sent, type, name?:"unknown_event");
+                               XCB_EVENT_SENT(event) != 0,
+                               XCB_EVENT_RESPONSE_TYPE(event),
+                               xcb_event_get_label(type) ?: "unknown_event");
                        break;
        }
 }
@@ -506,20 +708,17 @@ void sys_move(win_t *win, int x, int y, int w, int h)
        w      = MAX(w-b,1);
        h      = MAX(h-b,1);
 
-       uint16_t mask   = XCB_CONFIG_WINDOW_X
-                       | XCB_CONFIG_WINDOW_Y
-                       | XCB_CONFIG_WINDOW_WIDTH
-                       | XCB_CONFIG_WINDOW_HEIGHT
-                       | XCB_CONFIG_WINDOW_BORDER_WIDTH;
-       uint32_t list[] = {x, y, w, h, border};
-
-       xcb_configure_window(conn, win->sys->xcb, mask, list);
+       do_configure_window(win->sys->xcb, x, y, w, h, -1, -1, -1);
 }
 
 void sys_raise(win_t *win)
 {
        printf("sys_raise: %p\n", win);
-       xcb_circulate_window(conn, XCB_CIRCULATE_RAISE_LOWEST, win->sys->xcb);
+
+       uint16_t mask = XCB_CONFIG_WINDOW_STACK_MODE;
+       uint32_t list = XCB_STACK_MODE_ABOVE;
+
+       xcb_configure_window(conn, win->sys->xcb, mask, &list);
 }
 
 void sys_focus(win_t *win)
@@ -529,16 +728,73 @@ void sys_focus(win_t *win)
 
        xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT,
                        xcb, XCB_CURRENT_TIME);
-
-       if (last)
-               xcb_change_window_attributes(conn, last, XCB_CW_BORDER_PIXEL, &colors[CLR_UNFOCUS]);
-       xcb_change_window_attributes(conn, xcb, XCB_CW_BORDER_PIXEL, &colors[CLR_FOCUS]);
-       last = xcb;
 }
 
 void sys_show(win_t *win, state_t state)
 {
-       printf("sys_show:  %p - %d\n", win, state);
+       printf("sys_show:  %p - %s -> %s\n", win,
+                       state_map[win->state], state_map[state]);
+       xcb_window_t xcb = win ? win->sys->xcb : root;
+
+       /* 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;
+               }
+       }
+
+       /* Change window state */
+       switch (state) {
+               case ST_HIDE:
+                       xcb_unmap_window(conn, xcb);
+                       break;
+
+               case ST_SHOW:
+                       xcb_map_window(conn, xcb);
+                       do_configure_window(xcb, win->x, win->y,
+                                       MAX(win->w - 2*border, 1),
+                                       MAX(win->h - 2*border, 1),
+                                       border, -1, -1);
+                       break;
+
+               case ST_FULL:
+                       xcb_map_window(conn, xcb);
+                       do_configure_window(xcb, screen->x, screen->y, screen->w, screen->h,
+                                       0, -1, XCB_STACK_MODE_ABOVE);
+                       xcb_circulate_window(conn, XCB_CIRCULATE_RAISE_LOWEST, xcb);
+                       break;
+
+               case ST_MAX:
+                       xcb_map_window(conn, xcb);
+                       do_configure_window(xcb, screen->x, screen->y,
+                                       MAX(screen->w - 2*border, 1),
+                                       MAX(screen->h - 2*border, 1),
+                                       border, -1, XCB_STACK_MODE_ABOVE);
+                       break;
+
+               case ST_SHADE:
+                       xcb_map_window(conn, xcb);
+                       do_configure_window(xcb, -1, -1, -1, stack,
+                                       border, -1, -1);
+                       break;
+
+               case ST_ICON:
+                       xcb_map_window(conn, xcb);
+                       do_configure_window(xcb, -1, -1, 100, 100,
+                                       border, -1, -1);
+                       break;
+
+               case ST_CLOSE:
+                       xcb_kill_client(conn, xcb);
+                       break;
+       }
+
+       /* Update state */
+       win->state = state;
 }
 
 void sys_watch(win_t *win, event_t ev, mod_t mod)
@@ -659,6 +915,9 @@ void sys_init(void)
        root     = iter.data->root;
        colormap = iter.data->default_colormap;
 
+       /* Setup for for ST_CLOSE */
+       xcb_set_close_down_mode(conn, XCB_CLOSE_DOWN_DESTROY_ALL);
+
        /* Allocate key symbols */
        if (!(keysyms = xcb_key_symbols_alloc(conn)))
                error("cannot allocate key symbols");
@@ -688,15 +947,17 @@ void sys_run(void)
                xcb_window_t *kids = NULL;
                int nkids = do_query_tree(root, &kids);
                for(int i = 0; i < nkids; i++) {
+                       int override=0, mapped=0;
                        win_t *win = new0(win_t);
                        win->sys = new0(win_sys_t);
                        win->sys->xcb = kids[i];
-                       do_get_geometry(kids[i], &win->x, &win->y, &win->w, &win->h);
-                       do_get_window_attributes(kids[i],
-                               &win->sys->override, &win->sys->mapped);
                        tsearch(win, &cache, win_cmp);
-                       if (!win->sys->override && win->sys->mapped)
-                               wm_insert(win);
+                       do_get_geometry(kids[i], &win->x, &win->y, &win->w, &win->h);
+                       do_get_window_attributes(kids[i], &override, &mapped);
+                       if (!override)
+                               send_manage(win, 1);
+                       if (mapped)
+                               send_state(win, ST_SHOW);
                }
                xcb_flush(conn);
        }