summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE14
-rw-r--r--Makefile34
-rw-r--r--README55
-rw-r--r--TODO56
-rw-r--r--WISHLIST39
-rw-r--r--config.h52
-rw-r--r--mcwm.c1075
7 files changed, 1325 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..6390fca
--- /dev/null
+++ b/LICENSE
@@ -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
diff --git a/README b/README
new file mode 100644
index 0000000..a42752e
--- /dev/null
+++ b/README
@@ -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>
+
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..9403f4d
--- /dev/null
+++ b/TODO
@@ -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
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);
+}