summaryrefslogtreecommitdiff
path: root/mcwm.c
diff options
context:
space:
mode:
authorMC <mc@hack.org>2010-06-18 15:56:56 +0200
committerMC <mc@hack.org>2010-06-18 15:56:56 +0200
commit1aa60fdcbc0c00728a74690fd8d207064edfd7c1 (patch)
tree19af58b2f0cbc9b3000fb594baa67962430d503f /mcwm.c
downloadmcwm-1aa60fdcbc0c00728a74690fd8d207064edfd7c1.zip
Version 20100618.20100618
Diffstat (limited to 'mcwm.c')
-rw-r--r--mcwm.c1075
1 files changed, 1075 insertions, 0 deletions
diff --git a/mcwm.c b/mcwm.c
new file mode 100644
index 0000000..b6a9495
--- /dev/null
+++ b/mcwm.c
@@ -0,0 +1,1075 @@
+/*
+ * mcwm, a small window manager for the X Window System using the X
+ * protocol C Binding libraries.
+ *
+ * For 'user' configurable stuff, see config.h.
+ *
+ * MC, mc at the domain hack.org
+ * http://hack.org/mc/
+ *
+ * Copyright (c) 2010 Michael Cardell Widerkrantz, mc at the domain hack.org.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <xcb/xcb.h>
+#include <xcb/xcb_keysyms.h>
+#include <X11/keysym.h>
+
+/* Check here for user configurable parts: */
+#include "config.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+#ifdef DEBUG
+#define PDEBUG(Args...) \
+ do { fprintf(stderr, "mcwm: "); fprintf(stderr, ##Args); } while(0)
+#define D(x) x
+#else
+#define PDEBUG(Args...)
+#define D(x)
+#endif
+
+
+/* Internal Constants. */
+#define MCWM_MOVE 2
+#define MCWM_RESIZE 3
+
+
+/* Globals */
+xcb_connection_t *conn; /* Connection to X server. */
+xcb_screen_t *screen; /* Our current screen. */
+char *terminal = TERMINAL; /* Terminal to start. */
+xcb_drawable_t focuswin; /* Current focus window. */
+
+
+/* Types. */
+typedef enum {
+ KEY_H,
+ KEY_J,
+ KEY_K,
+ KEY_L,
+ KEY_M,
+ KEY_R,
+ KEY_RET,
+ KEY_X,
+ KEY_TAB,
+ KEY_MAX
+} key_enum_t;
+
+struct keys
+{
+ xcb_keysym_t keysym;
+ xcb_keycode_t keycode;
+} keys[KEY_MAX] =
+{
+ { USERKEY_MOVE_LEFT, 0 },
+ { USERKEY_MOVE_DOWN, 0 },
+ { USERKEY_MOVE_UP, 0 },
+ { USERKEY_MOVE_RIGHT, 0 },
+ { USERKEY_MAXVERT, 0 },
+ { USERKEY_RAISE, 0 },
+ { USERKEY_TERMINAL, 0 },
+ { USERKEY_MAX, 0 },
+ { USERKEY_CHANGE, 0 }
+};
+
+
+/* Functions declerations. */
+
+void setupwin(xcb_window_t win);
+xcb_keycode_t keysymtokeycode(xcb_keysym_t keysym, xcb_key_symbols_t *keysyms);
+int setupkeys(void);
+int setupscreen(void);
+void raisewindow(xcb_drawable_t win);
+void movewindow(xcb_drawable_t win, uint16_t x, uint16_t y);
+void setunfocus(xcb_drawable_t win);
+void setfocus(xcb_drawable_t win);
+int start_terminal(void);
+void resize(xcb_drawable_t win, uint32_t width, uint32_t height);
+void resizestep(xcb_drawable_t win, char direction);
+void mousemove(xcb_drawable_t win, int rel_x, int rel_y);
+void mouseresize(xcb_drawable_t win, int rel_x, int rel_y);
+void movestep(xcb_drawable_t win, char direction);
+void maximize(xcb_drawable_t win);
+void maxvert(xcb_drawable_t win);
+void handle_keypress(xcb_drawable_t win, xcb_key_press_event_t *ev);
+
+
+/* Function bodies. */
+
+/* set border colour, width and event mask for window. */
+void setupwin(xcb_window_t win)
+{
+ uint32_t mask = 0;
+ uint32_t values[2];
+
+ PDEBUG("Setting up window %d\n", win);
+
+ /* Set border color. */
+ values[0] = UNFOCUSCOL;
+ xcb_change_window_attributes(conn, win, XCB_CW_BORDER_PIXEL, values);
+
+ /* Set border width. */
+ values[0] = BORDERWIDTH;
+ mask = XCB_CONFIG_WINDOW_BORDER_WIDTH;
+ xcb_configure_window(conn, win, mask, values);
+
+ mask = XCB_CW_EVENT_MASK;
+ values[0] = XCB_EVENT_MASK_ENTER_WINDOW;
+ xcb_change_window_attributes_checked(conn, win, mask, values);
+
+ /* Show window on screen. */
+ xcb_map_window(conn, win);
+
+ /* FIXME: set properties. */
+
+ xcb_flush(conn);
+}
+
+xcb_keycode_t keysymtokeycode(xcb_keysym_t keysym, xcb_key_symbols_t *keysyms)
+{
+ xcb_keycode_t *keyp;
+ xcb_keycode_t key;
+
+ /* We only use the first keysymbol, even if there are more. */
+ keyp = xcb_key_symbols_get_keycode(keysyms, keysym);
+ if (NULL == keyp)
+ {
+ fprintf(stderr, "mcwm: Couldn't look up key. Exiting.\n");
+ exit(1);
+ return 0;
+ }
+
+ key = *keyp;
+ free(keyp);
+
+ return key;
+}
+
+int setupkeys(void)
+{
+ xcb_key_symbols_t *keysyms;
+ int i;
+
+ /* Get all the keysymbols. */
+ keysyms = xcb_key_symbols_alloc(conn);
+
+ for (i = KEY_H; i < KEY_MAX; i ++)
+ {
+ keys[i].keycode = keysymtokeycode(keys[i].keysym, keysyms);
+ if (0 == keys[i].keycode)
+ {
+ /* Couldn't set up keys! */
+
+ /* Get rid of key symbols. */
+ free(keysyms);
+
+ return -1;
+ }
+ }
+
+ /* Get rid of the key symbols table. */
+ free(keysyms);
+
+ return 0;
+}
+
+/* Walk through all existing windows and set them up. */
+int setupscreen(void)
+{
+ xcb_query_tree_reply_t *reply;
+ int i;
+ int len;
+ xcb_window_t *children;
+
+ /* Get all children. */
+ reply = xcb_query_tree_reply(conn,
+ xcb_query_tree(conn, screen->root), 0);
+ if (NULL == reply)
+ {
+ return -1;
+ }
+
+ len = xcb_query_tree_children_length(reply);
+ children = xcb_query_tree_children(reply);
+
+ /* Set up all windows. */
+ for (i = 0; i < len; i ++)
+ {
+ setupwin(children[i]);
+ }
+
+ free(reply);
+
+ return 0;
+}
+
+void raisewindow(xcb_drawable_t win)
+{
+ uint32_t values[] = { XCB_STACK_MODE_ABOVE };
+
+ if (screen->root == win || 0 == win)
+ {
+ return;
+ }
+
+ xcb_configure_window(conn, win,
+ XCB_CONFIG_WINDOW_STACK_MODE,
+ values);
+ xcb_flush(conn);
+}
+
+void movewindow(xcb_drawable_t win, uint16_t x, uint16_t y)
+{
+ uint32_t values[2];
+
+ if (screen->root == win || 0 == win)
+ {
+ /* Can't move root. */
+ return;
+ }
+
+ raisewindow(win);
+
+ values[0] = x;
+ values[1] = y;
+
+ xcb_configure_window(conn, win, XCB_CONFIG_WINDOW_X
+ | XCB_CONFIG_WINDOW_Y, values);
+
+ xcb_flush(conn);
+
+}
+
+void setunfocus(xcb_drawable_t win)
+{
+ uint32_t values[1];
+
+ if (focuswin == screen->root)
+ {
+ return;
+ }
+
+ /* Set new border colour. */
+ values[0] = UNFOCUSCOL;
+ xcb_change_window_attributes(conn, win, XCB_CW_BORDER_PIXEL, values);
+
+ xcb_flush(conn);
+}
+
+void setfocus(xcb_drawable_t win)
+{
+ uint32_t values[1];
+
+ /*
+ * Don't bother focusing on the root window or on the same window
+ * that already has focus.
+ */
+ if (win == screen->root || win == focuswin)
+ {
+ return;
+ }
+
+ /* Set new border colour. */
+ values[0] = FOCUSCOL;
+ xcb_change_window_attributes(conn, win, XCB_CW_BORDER_PIXEL, values);
+
+ /* Unset last focus. */
+ setunfocus(focuswin);
+
+ /* Set new input focus. */
+ focuswin = win;
+ xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, win,
+ XCB_CURRENT_TIME);
+
+ xcb_flush(conn);
+}
+
+int start_terminal(void)
+{
+ pid_t pid;
+
+ pid = fork();
+ if (-1 == pid)
+ {
+ perror("fork");
+ return -1;
+ }
+ else if (0 == pid)
+ {
+ char *argv[2];
+
+ /* In the child. */
+
+ argv[0] = terminal;
+ argv[1] = NULL;
+
+ /*
+ * Create new process leader, otherwise the terminal will die
+ * when wm dies.
+ */
+ if (-1 == setsid())
+ {
+ perror("setsid");
+ exit(1);
+ }
+
+ if (-1 == execvp(terminal, argv))
+ {
+ perror("execve");
+ exit(1);
+ }
+ }
+
+ return 0;
+}
+
+void resize(xcb_drawable_t win, uint32_t width, uint32_t height)
+{
+ uint32_t values[2];
+
+ if (screen->root == win || 0 == win)
+ {
+ /* Can't resize root. */
+ return;
+ }
+
+ values[0] = width;
+ values[1] = height;
+
+ xcb_configure_window(conn, win,
+ XCB_CONFIG_WINDOW_WIDTH
+ | XCB_CONFIG_WINDOW_HEIGHT, values);
+ xcb_flush(conn);
+}
+
+void resizestep(xcb_drawable_t win, char direction)
+{
+ xcb_get_geometry_reply_t *geom;
+ int width;
+ int height;
+
+ if (0 == win)
+ {
+ /* Can't resize root. */
+ return;
+ }
+
+ /* Get window geometry. */
+ geom = xcb_get_geometry_reply(conn,
+ xcb_get_geometry(conn, win),
+ NULL);
+ if (NULL == geom)
+ {
+ return;
+ }
+
+ switch (direction)
+ {
+ case 'h':
+ width = geom->width - MOVE_STEP;
+ height = geom->height;
+ if (width < 0)
+ {
+ width = 0;
+ }
+ break;
+
+ case 'j':
+ width = geom->width;
+ height = geom->height + MOVE_STEP;
+ if (height + geom->y > screen->height_in_pixels)
+ {
+ goto bad;
+ }
+ break;
+
+ case 'k':
+ width = geom->width;
+ height = geom->height - MOVE_STEP;
+ if (height < 0)
+ {
+ goto bad;
+ }
+ break;
+
+ case 'l':
+ width = geom->width + MOVE_STEP;
+ height = geom->height;
+ if (width + geom->x > screen->width_in_pixels)
+ {
+ goto bad;
+ }
+ break;
+
+ default:
+ PDEBUG("resizestep in unknown direction.\n");
+ break;
+ } /* switch direction */
+
+ resize(win, width, height);
+
+ /*
+ * Move cursor into the middle of the window so we don't lose the
+ * pointer.
+ */
+ xcb_warp_pointer(conn, XCB_NONE, win, 0, 0, 0, 0,
+ width / 2, height / 2);
+
+ xcb_flush(conn);
+
+bad:
+ free(geom);
+}
+
+void mousemove(xcb_drawable_t win, int rel_x, int rel_y)
+{
+ xcb_get_geometry_reply_t *geom;
+ int x;
+ int y;
+
+ raisewindow(win);
+
+ /* Get window geometry. */
+ geom = xcb_get_geometry_reply(conn,
+ xcb_get_geometry(conn, win),
+ NULL);
+ if (NULL == geom)
+ {
+ return;
+ }
+
+ x = rel_x;
+ y = rel_y;
+
+ if (x < BORDERWIDTH)
+ {
+ x = BORDERWIDTH;
+ }
+ if (y < BORDERWIDTH)
+ {
+ y = BORDERWIDTH;
+ }
+ if (y + geom->height + BORDERWIDTH * 2 > screen->height_in_pixels)
+ {
+ y = screen->height_in_pixels - (geom->height + BORDERWIDTH * 2);
+ }
+ if (x + geom->width + BORDERWIDTH * 2 > screen->width_in_pixels)
+ {
+ x = screen->width_in_pixels - (geom->width + BORDERWIDTH * 2);
+ }
+
+ movewindow(win, x, y);
+
+ free(geom);
+}
+
+void mouseresize(xcb_drawable_t win, int rel_x, int rel_y)
+{
+ xcb_get_geometry_reply_t *geom;
+ uint32_t width;
+ uint32_t height;
+
+ raisewindow(win);
+
+ /* Get window geometry. */
+ geom = xcb_get_geometry_reply(conn,
+ xcb_get_geometry(conn, win),
+ NULL);
+ if (NULL == geom)
+ {
+ return;
+ }
+
+ if (rel_x - geom->x <= 1)
+ {
+ return;
+ }
+
+ if (rel_y - geom->y <= 1)
+ {
+ return;
+ }
+
+ width = rel_x - geom->x;
+ height = rel_y - geom->y;
+
+ if (width > screen->width_in_pixels)
+ {
+ width = screen->width_in_pixels - geom->x;
+ }
+
+ if (height > screen->height_in_pixels)
+ {
+ height = screen->height_in_pixels - geom->y;
+ }
+
+ PDEBUG("mousresize: Resizing to %d x %d\n\n", width, height);
+
+ resize(win, width, height);
+
+ free(geom);
+}
+
+
+void movestep(xcb_drawable_t win, char direction)
+{
+ xcb_get_geometry_reply_t *geom;
+ int x;
+ int y;
+ int width;
+ int height;
+
+ if (0 == win)
+ {
+ /* Can't move root. */
+ return;
+ }
+
+ /* Get window geometry. */
+ geom = xcb_get_geometry_reply(conn,
+ xcb_get_geometry(conn, win),
+ NULL);
+
+ width = geom->width + BORDERWIDTH * 2;
+ height = geom->height + BORDERWIDTH * 2;
+
+ if (NULL == geom)
+ {
+ return;
+ }
+
+ switch (direction)
+ {
+ case 'h':
+ x = geom->x - MOVE_STEP;
+ if (x < 0)
+ {
+ x = 0;
+ }
+
+ movewindow(win, x, geom->y);
+ break;
+
+ case 'j':
+ y = geom->y + MOVE_STEP;
+ if (y + height > screen->height_in_pixels)
+ {
+ y = screen->height_in_pixels - height;
+ }
+ movewindow(win, geom->x, y);
+ break;
+
+ case 'k':
+ y = geom->y - MOVE_STEP;
+ if (y < 0)
+ {
+ y = 0;
+ }
+
+ movewindow(win, geom->x, y);
+ break;
+
+ case 'l':
+ x = geom->x + MOVE_STEP;
+ if (x + width > screen->width_in_pixels)
+ {
+ x = screen->width_in_pixels - width;
+ }
+
+ movewindow(win, x, geom->y);
+ break;
+
+ default:
+ PDEBUG("movestep: Moving in unknown direction.\n");
+ break;
+ } /* switch direction */
+
+ /* Move cursor into the middle of the window after moving. */
+ xcb_warp_pointer(conn, XCB_NONE, win, 0, 0, 0, 0,
+ width / 2, height / 2);
+
+ xcb_flush(conn);
+
+ free(geom);
+}
+
+void maximize(xcb_drawable_t win)
+{
+ xcb_get_geometry_reply_t *geom;
+ uint32_t values[2];
+ uint32_t mask = 0;
+
+ if (screen->root == win || 0 == win)
+ {
+ return;
+ }
+
+ /* FIXME: Check if maximized already. If so, revert to stored geometry. */
+
+ /* Get window geometry. */
+ geom = xcb_get_geometry_reply(conn,
+ xcb_get_geometry(conn, win),
+ NULL);
+ if (NULL == geom)
+ {
+ return;
+ }
+
+ /* Raise first. Pretty silly to maximize below something else. */
+ raisewindow(win);
+
+ /* FIXME: Store original geom in property. */
+
+ /* Remove borders. */
+ values[0] = 0;
+ mask = XCB_CONFIG_WINDOW_BORDER_WIDTH;
+ xcb_configure_window(conn, win, mask, values);
+
+ /* Move to top left. */
+ values[0] = 0;
+ values[1] = 0;
+
+ xcb_configure_window(conn, win, XCB_CONFIG_WINDOW_X
+ | XCB_CONFIG_WINDOW_Y, values);
+
+ /* Then resize. */
+ resize(win, screen->width_in_pixels,
+ screen->height_in_pixels);
+
+ free(geom);
+}
+
+void maxvert(xcb_drawable_t win)
+{
+ xcb_get_geometry_reply_t *geom;
+ uint32_t values[2];
+
+ if (screen->root == win || 0 == win)
+ {
+ return;
+ }
+
+ /* FIXME: Check if maximized already. If so, revert to stored geometry. */
+
+ /* Get window geometry. */
+ geom = xcb_get_geometry_reply(conn,
+ xcb_get_geometry(conn, win),
+ NULL);
+ if (NULL == geom)
+ {
+ return;
+ }
+
+ /* FIXME: Store original geom in property. */
+
+ /* Move to top of screen. */
+ values[0] = geom->x;
+ values[1] = 0;
+
+ xcb_configure_window(conn, win, XCB_CONFIG_WINDOW_X
+ | XCB_CONFIG_WINDOW_Y, values);
+
+ /* Then resize. */
+ resize(win, geom->width, screen->height_in_pixels - BORDERWIDTH * 2);
+
+ free(geom);
+}
+
+void handle_keypress(xcb_drawable_t win, xcb_key_press_event_t *ev)
+{
+ int i;
+ key_enum_t key;
+
+ for (key = KEY_MAX, i = KEY_H; i < KEY_MAX; i ++)
+ {
+ if (ev->detail == keys[i].keycode)
+ {
+ key = i;
+ }
+ }
+ if (key == KEY_MAX)
+ {
+ PDEBUG("Unknown key pressed.\n");
+ return;
+ }
+
+ /* Is it shifted? */
+ if (ev->state & XCB_MOD_MASK_SHIFT)
+ {
+ switch (key)
+ {
+ case KEY_H: /* h */
+ resizestep(win, 'h');
+ break;
+
+ case KEY_J: /* j */
+ resizestep(win, 'j');
+ break;
+
+ case KEY_K: /* k */
+ resizestep(win, 'k');
+ break;
+
+ case KEY_L: /* l */
+ resizestep(win, 'l');
+ break;
+ }
+ }
+ else
+ {
+ switch (key)
+ {
+ case KEY_RET: /* return */
+ start_terminal();
+ break;
+
+ case KEY_H: /* h */
+ movestep(win, 'h');
+ break;
+
+ case KEY_J: /* j */
+ movestep(win, 'j');
+ break;
+
+ case KEY_K: /* k */
+ movestep(win, 'k');
+ break;
+
+ case KEY_L: /* l */
+ movestep(win, 'l');
+ break;
+
+ case KEY_TAB: /* tab */
+ break;
+
+ case KEY_M: /* m */
+ maxvert(win);
+ break;
+
+ case KEY_R: /* r*/
+ raisewindow(win);
+ break;
+
+ case KEY_X: /* x */
+ maximize(win);
+ break;
+
+ } /* switch unshifted */
+ }
+} /* handle_keypress() */
+
+int main(void)
+{
+ xcb_window_t root;
+ xcb_generic_event_t *ev;
+ uint32_t mask = 0;
+ uint32_t values[2];
+ xcb_drawable_t win;
+ xcb_get_geometry_reply_t *geom;
+ int mode = 0;
+ uint16_t mode_x;
+ uint16_t mode_y;
+
+ /*
+ int xcb_parse_display(const char *name, char **host, int *display, int *screen);
+ */
+/*
+ xcb_parse_display(dispname, &host, &disp, NULL)
+*/
+
+ conn = xcb_connect(NULL, NULL);
+ if (xcb_connection_has_error(conn))
+ {
+ perror("xcb_connect");
+ exit(1);
+ }
+
+ /* Get the first screen */
+ screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
+
+ /* screen->root is our root. */
+ root = screen->root;
+
+ /* Initial focus is root */
+ /* FIXME: Ask the X server what window has focus. */
+ focuswin = root;
+
+ PDEBUG("Screen size: %dx%d\nRoot window: %d\n", screen->width_in_pixels,
+ screen->height_in_pixels, screen->root);
+
+ /* FIXME: Get some colours. */
+
+ /* Loop over all clients and set up stuff. */
+ if (0 != setupscreen())
+ {
+ fprintf(stderr, "Failed to initialize windows. Exiting.\n");
+ exit(1);
+ }
+
+ /* Set up key bindings. */
+ if (0 != setupkeys())
+ {
+ fprintf(stderr, "mcwm: Couldn't set up keycodes. Exiting.");
+ exit(1);
+ }
+
+ /* Grab some keys and mouse buttons. */
+
+ xcb_grab_key(conn, 1, root, MODKEY, XCB_NO_SYMBOL,
+ XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
+
+ xcb_grab_button(conn, 0, root, XCB_EVENT_MASK_BUTTON_PRESS
+ | XCB_EVENT_MASK_BUTTON_RELEASE,
+ XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
+ 1 /* left mouse button */,
+ MOUSEMODKEY);
+
+ xcb_grab_button(conn, 0, root, XCB_EVENT_MASK_BUTTON_PRESS
+ | XCB_EVENT_MASK_BUTTON_RELEASE,
+ XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
+ 2 /* middle mouse button */,
+ MOUSEMODKEY);
+
+ xcb_grab_button(conn, 0, root, XCB_EVENT_MASK_BUTTON_PRESS
+ | XCB_EVENT_MASK_BUTTON_RELEASE,
+ XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
+ 3 /* right mouse button */,
+ MOUSEMODKEY);
+
+#if 0
+
+ /*
+ * If we really want to be a wm, ask for these events as well:
+ */
+ mask = XCB_CW_EVENT_MASK;
+ values[0] =
+ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT /* only one client can use this. */
+ | XCB_EVENT_MASK_STRUCTURE_NOTIFY; /* don't know about this. New screens? */
+
+ xcb_void_cookie_t cookie;
+
+ cookie = xcb_change_window_attributes_checked(conn, root, mask, values);
+
+#endif
+
+ mask = XCB_CW_EVENT_MASK;
+ values[0] = XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY;
+ xcb_change_window_attributes_checked(conn, root, mask, values);
+
+ xcb_flush(conn);
+
+ /* Loop over events. */
+ for (;;)
+ {
+ ev = xcb_wait_for_event(conn);
+ if (NULL == ev)
+ {
+ perror("xcb_wait_for_event");
+ break;
+ }
+
+ PDEBUG("Event: %d\n", ev->response_type);
+
+ switch (ev->response_type & ~0x80)
+ {
+ case XCB_CREATE_NOTIFY:
+ {
+ xcb_create_notify_event_t *e;
+
+ PDEBUG("A new window!\n");
+ e = ( xcb_create_notify_event_t *) ev;
+ setupwin(e->window);
+ }
+ break;
+
+ case XCB_BUTTON_PRESS:
+ {
+ xcb_button_press_event_t *e;
+
+ e = ( xcb_button_press_event_t *) ev;
+ PDEBUG ("Button %d pressed in window %ld, subwindow %d "
+ "coordinates (%d,%d)\n",
+ e->detail, e->event, e->child, e->event_x, e->event_y);
+
+ if (e->child != 0)
+ {
+ win = e->child;
+
+ /* If middle button was pressed, raise window. */
+
+ /*
+ * FIXME: If already raised, lower
+ * instead. XCB_STACK_MODE_BELOW
+ */
+
+ if (2 == e->detail)
+ {
+ raisewindow(win);
+ }
+ else
+ {
+ /* We're moving or resizing. */
+
+ /* Save the pointer coordinates when starting. */
+ mode_x = e->event_x;
+ mode_y = e->event_y;
+
+ /* Get window geometry. */
+ geom = xcb_get_geometry_reply(conn, xcb_get_geometry(conn,
+ win),
+ NULL);
+ if (NULL == geom)
+ {
+ break;
+ }
+
+ if (1 == e->detail)
+ {
+ mode = MCWM_MOVE;
+
+ xcb_warp_pointer(conn, XCB_NONE, win, 0, 0, 0, 0,
+ 1, 1);
+ }
+ else
+ {
+ mode = MCWM_RESIZE;
+
+ /* Warp pointer to lower right. */
+ xcb_warp_pointer(conn, XCB_NONE, win, 0, 0, 0, 0,
+ geom->width, geom->height);
+ }
+
+ /*
+ * Take control of the pointer. Wait for the
+ * button to be released or for the pointer to
+ * move.
+ */
+
+ xcb_grab_pointer(conn,
+ 0, /* get all from mask below */
+ root, /* grab here */
+ XCB_EVENT_MASK_BUTTON_RELEASE
+ | XCB_EVENT_MASK_POINTER_MOTION,
+ XCB_GRAB_MODE_ASYNC, /* get more pointer events */
+ XCB_GRAB_MODE_ASYNC,
+ root, /* stay here */
+ XCB_NONE, /* no other cursor. */
+ XCB_CURRENT_TIME);
+ xcb_flush(conn);
+
+ PDEBUG("mode now : %d\n", mode);
+ }
+ } /* subwindow */
+ else
+ {
+ /*
+ * Do something on the root when mouse buttons are
+ * pressed?
+ */
+ }
+ }
+ break;
+
+ case XCB_MOTION_NOTIFY:
+ {
+ xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t *)ev;
+
+ /*
+ * Our pointer is moving and since we even get this event
+ * we're resizing or moving a window.
+ */
+ if (mode == MCWM_MOVE)
+ {
+ mousemove(win, e->event_x, e->event_y);
+ }
+ else if (mode == MCWM_RESIZE)
+ {
+ /* Resize. */
+ mouseresize(win, e->event_x, e->event_y);
+ }
+ else
+ {
+ PDEBUG("Motion event when we're not moving our resizing!\n");
+ }
+ }
+ break;
+
+ case XCB_BUTTON_RELEASE:
+ PDEBUG("Mouse button released! mode = %d\n", mode);
+
+ if (0 != mode)
+ {
+ xcb_button_release_event_t *e =
+ (xcb_button_release_event_t *)ev;
+
+ /* We're finished moving or resizing. */
+ xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
+ xcb_flush(conn); /* Important! */
+
+ mode = 0;
+ free(geom);
+
+ setfocus(e->event);
+
+ PDEBUG("mode now = %d\n", mode);
+
+ }
+ break;
+
+ case XCB_KEY_PRESS:
+ {
+ xcb_key_press_event_t *e = (xcb_key_press_event_t *)ev;
+
+ win = e->child;
+
+ PDEBUG("Key %d pressed in window %ld\n",
+ e->detail,
+ win);
+
+ handle_keypress(win, e);
+ }
+ break;
+
+ case XCB_ENTER_NOTIFY:
+ if (0 == mode)
+ {
+ xcb_enter_notify_event_t *e = (xcb_enter_notify_event_t *)ev;
+
+ /*
+ * If we're entering the same window we focus now,
+ * then don't bother focusing.
+ */
+ if (e->event != focuswin)
+ {
+ setfocus(e->event);
+ }
+ }
+ break;
+
+ } /* switch */
+
+ free(ev);
+ }
+
+ xcb_disconnect(conn);
+
+ exit(0);
+}