diff options
-rw-r--r-- | LICENSE | 14 | ||||
-rw-r--r-- | Makefile | 34 | ||||
-rw-r--r-- | README | 55 | ||||
-rw-r--r-- | TODO | 56 | ||||
-rw-r--r-- | WISHLIST | 39 | ||||
-rw-r--r-- | config.h | 52 | ||||
-rw-r--r-- | mcwm.c | 1075 |
7 files changed, 1325 insertions, 0 deletions
@@ -0,0 +1,14 @@ +Copyright (c) 2010 Michael Cardell Widerkrantz <mc@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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fa2d1bc --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +VERSION=201000618 +DIST=mcwm-$(VERSION) +DISTFILES=LICENSE Makefile README TODO WISHLIST config.h mcwm.c + +CC=gcc +CFLAGS=-g -std=c99 -Wall -I/usr/local/include -L/usr/local/lib -lxcb \ + -lxcb-keysyms + +# Define -DDEBUG for lots of debug information. + +#CFLAGS=-g -std=c99 -Wall -I/usr/local/include -L/usr/local/lib -lxcb -lxcb-keysyms \ +# -DDMALLOC -DMALLOC_FUNC_CHECK -ldmalloc + + +RM=/bin/rm + +TARGETS=mcwm + +all: $(TARGETS) + +$(DIST).tar.bz2: + mkdir $(DIST) + cp $(DISTFILES) $(DIST)/ + tar cf $(DIST).tar --exclude .git $(DIST) + bzip2 -9 $(DIST).tar + $(RM) -rf $(DIST) + +dist: $(DIST).tar.bz2 + +clean: + $(RM) -f $(TARGETS) + +distclean: clean + $(RM) -f $(DIST).tar.bz2 @@ -0,0 +1,55 @@ +-*- text -*- + +This is a small window manager for the X Window System written +entirely with XCB, the straight X protocol binding to the C language. +It doesn't use libX11, like most older window managers do. + +It's a traditional 'floating' window manager. It doesn't really do +anything new to the user experience. It is pretty stupid, but can +probably be useful for some people. + +Unlike most window managers, however, it is not a reparenting window +manager. This means it might be able to run simultaneously with +another window manager if you just want to get at the key bindings to +manipulate windows. If this will work or not depends pretty much on +what the other window manager thinks about stuff happening to windows +without it looking. + +== Usage == + +With the the default configuration, use it like this: + + Mod1 key + mouse buttons: + + 1 move + 2 raise + 3 resize + + Mod2 key + key: + + r raise + x maximize + m maximize vertically + h move left + j move down + k move up + l move right + H resize left + J resize down + K resize up + L resize right + Return start terminal + +If you don't like the default key bindings, border width, et cetera, +look in the config.h file, change and recompile. + +== More information & Contact == + +Please see: + + http://hack.org/mc/hacks/mcwm/ + +To contact me, write to: + + Michael Cardell Widerkrantz <mc@hack.org> + @@ -0,0 +1,56 @@ +-*- text -*- + +In order of importance: + +* Run under some memory checker. DMALLOC? + +* Changing focus from keyboard + + Save the subwindows of the root window and focus each when pressing + modkey + user_key_change. First option always last focused window. + +* "R" should toggle stack order. + +* "M" and "X" should toggle. + + Store original geom in X properties for the window. + +* Move to corners: YU BN. + + Quickly move a window to the corners of the screen, a la evilwm. + +* Use window resizing hints. + +* Obey no-input hints. + + Don't focus on these windows at all. Does unclutter use this? + +* Flag to disable dontmoveoff. + +* Flag for borders off or on. + +* Handle colours for real. Don't just assume raw pixel values will work. + +* Special treatment when someone resizes a maximed window... Should it + be possible at all? Set new border width. + + Set and read window hint about maximized state. + +* Virtual workspaces + + Store workspace data in an X property for the window. + + When changing workspace, look for the windows with the right + property. + + possibly store internally as well, so we don't generate so much + traffic? + +* RandR/Xinerama + + Get physical screen characteristics. Maximize and move to corners + should consider the screen it's on. + +* Key to move to another physical screen. + +* Use event handlers? diff --git a/WISHLIST b/WISHLIST new file mode 100644 index 0000000..aade3ce --- /dev/null +++ b/WISHLIST @@ -0,0 +1,39 @@ +-*- text -*- + +A wishlist for a window manager: + +- Small. + +- Few external dependencies, both for running and building. + +- Fast. + +- Minimal decoration. + +- Sloppy focus. + +- Changing focus from keyboard which rememebers last focused window. + Keep on the same physical screen. + +- Focus change from keyboard should restore stacking order after focus + switch. + +- Key bindings for all window functions. + +- Configurable key bindings. + +- Vertical and maximum maximizing. + +- Don't move off physical screen. Configurable? + +- Know about physical screens dynamically (RandR). + +- Virtual screens. Separate sets for each physical screen. + +- Remember where pointer was on virtual screen. Or at least remember + what window had focus. + +- Quickly move windows to corners. + +- Window placement on virtual screens should be remembered even if wm + killed and restarted. diff --git a/config.h b/config.h new file mode 100644 index 0000000..a97ea33 --- /dev/null +++ b/config.h @@ -0,0 +1,52 @@ +/* User configurable stuff. */ + +/* Move this many pixels when moving or resizing with keyboard. */ +#define MOVE_STEP 32 + +/* + * Use this modifier combined with other keys to control wm from + * keyboard. Default is Mod2, which on my keyboard is the Alt/Windows + * key. + */ +#define MODKEY XCB_MOD_MASK_2 + +/* Extra modifier for resizing. Default is Shift. */ +#define SHIFTMOD XCB_MOD_MASK_SHIFT + +/* + * Modifier key to use with mouse buttons. Default Mod1, Meta on my + * keyboard. + */ +#define MOUSEMODKEY XCB_MOD_MASK_1 + +/* Start this program when pressing MODKEY + USERKEY_TERMINAL. */ +#define TERMINAL "/usr/local/bin/urxvt" + +/* for VNC when running another wm simultaneously: XCB_BUTTON_MASK_ANY */ + +/* Colour on border for focused windows. */ + +/* FIXME: We blatantly ignore displays that doesn't handle direct pixel values. */ + +#define FOCUSCOL 0xe5e5e5 +/* amber #define FOCUSCOL 0xff7f24 */ + +/* Ditto for unfocused. */ +#define UNFOCUSCOL 0x666666 + +/* Width of border window, in pixels. */ +#define BORDERWIDTH 1 + +/* + * Keysym codes for window operations. Look in X11/keysymdefs.h for + * actual symbols. + */ +#define USERKEY_MOVE_LEFT XK_H +#define USERKEY_MOVE_DOWN XK_J +#define USERKEY_MOVE_UP XK_K +#define USERKEY_MOVE_RIGHT XK_L +#define USERKEY_MAXVERT XK_M +#define USERKEY_RAISE XK_R +#define USERKEY_TERMINAL XK_Return +#define USERKEY_MAX XK_X +#define USERKEY_CHANGE XK_Tab @@ -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); +} |