summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile7
-rw-r--r--mcwm.c287
2 files changed, 218 insertions, 76 deletions
diff --git a/Makefile b/Makefile
index 45577dc..d755aec 100644
--- a/Makefile
+++ b/Makefile
@@ -4,9 +4,7 @@ DISTFILES=LICENSE Makefile NEWS 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.
+ -lxcb-keysyms -lxcb-icccm -lxcb-atom -DDEBUG
RM=/bin/rm
@@ -17,7 +15,8 @@ all: $(TARGETS)
mcwm-static: mcwm.c config.h
$(CC) -o mcwm-static mcwm.c -static -g -std=c99 -Wall \
-I/usr/local/include/ -L/usr/local/lib \
- -lxcb -lxcb-keysyms -lXau -lXdmcp
+ -lxcb -lxcb-keysyms -lxcb-icccm -lxcb-atom -lxcb-property \
+ -lxcb-event -lXau -lXdmcp
$(DIST).tar.bz2:
mkdir $(DIST)
diff --git a/mcwm.c b/mcwm.c
index c79b1ed..d253357 100644
--- a/mcwm.c
+++ b/mcwm.c
@@ -30,6 +30,8 @@
#include <xcb/xcb.h>
#include <xcb/xcb_keysyms.h>
+#include <xcb/xcb_atom.h>
+#include <xcb/xcb_icccm.h>
#include <X11/keysym.h>
/* Check here for user configurable parts: */
@@ -67,12 +69,12 @@ typedef enum {
KEY_TAB,
KEY_MAX
} key_enum_t;
-
+
/* Globals */
xcb_connection_t *conn; /* Connection to X server. */
xcb_screen_t *screen; /* Our current screen. */
-char *terminal = TERMINAL; /* Terminal to start. */
+char *terminal = TERMINAL; /* Path to terminal to start. */
xcb_drawable_t focuswin; /* Current focus window. */
struct keys
@@ -105,6 +107,7 @@ 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 raiseorlower(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);
@@ -121,14 +124,18 @@ void handle_keypress(xcb_drawable_t win, xcb_key_press_event_t *ev);
/* Function bodies. */
-/* Set position, geometry and attributes of a new window. */
+/*
+ * Set position, geometry and attributes of a new window and show it
+ * on the screen.
+ */
void newwin(xcb_window_t win)
{
xcb_query_pointer_reply_t *pointer;
+ xcb_get_geometry_reply_t *geom;
int x;
int y;
-
- /* Get pointer position. */
+
+ /* Get pointer position so we can move the window to the cursor. */
pointer = xcb_query_pointer_reply(
conn, xcb_query_pointer(conn, screen->root), 0);
@@ -141,11 +148,44 @@ void newwin(xcb_window_t win)
{
x = pointer->root_x;
y = pointer->root_y;
+
+ free(pointer);
+ }
+
+ /*
+ * Get window geometry so we can check if it fits on the screen
+ * where the cursor is.
+ */
+ geom = xcb_get_geometry_reply(conn,
+ xcb_get_geometry(conn, win),
+ NULL);
+ if (NULL == geom)
+ {
+ return;
+ }
+
+ /* If the window is larger than our screen, just place it in the corner. */
+ if (geom->width > screen->width_in_pixels)
+ {
+ x = 1;
+ }
+ else if (x + geom->width + BORDERWIDTH * 2 > screen->width_in_pixels)
+ {
+ x = screen->width_in_pixels - (geom->width + BORDERWIDTH * 2);
+ }
+
+ if (geom->height > screen->width_in_pixels)
+ {
+ y = 1;
+ }
+ else if (y + geom->height + BORDERWIDTH * 2 > screen->height_in_pixels)
+ {
+ y = screen->height_in_pixels - (geom->height + BORDERWIDTH * 2);
}
/* Move the window to cursor position. */
movewindow(win, x, y);
-
+
/* Set up stuff and raise the window. */
setupwin(win);
raisewindow(win);
@@ -153,6 +193,15 @@ void newwin(xcb_window_t win)
/* Show window on screen. */
xcb_map_window(conn, win);
+ /*
+ * Move cursor into the middle of the window so we don't lose the
+ * pointer to another window.
+ */
+ xcb_warp_pointer(conn, XCB_NONE, win, 0, 0, 0, 0,
+ geom->width / 2, geom->height / 2);
+
+ free(geom);
+
xcb_flush(conn);
}
@@ -179,7 +228,7 @@ void setupwin(xcb_window_t win)
xcb_change_window_attributes_checked(conn, win, mask, values);
/* FIXME: set properties. */
-
+
xcb_flush(conn);
}
@@ -235,10 +284,11 @@ int setupkeys(void)
int setupscreen(void)
{
xcb_query_tree_reply_t *reply;
+ xcb_query_pointer_reply_t *pointer;
int i;
int len;
xcb_window_t *children;
-
+
/* Get all children. */
reply = xcb_query_tree_reply(conn,
xcb_query_tree(conn, screen->root), 0);
@@ -256,6 +306,23 @@ int setupscreen(void)
setupwin(children[i]);
}
+ /*
+ * Get pointer position so we can set focus on any window which
+ * might be under it.
+ */
+ pointer = xcb_query_pointer_reply(
+ conn, xcb_query_pointer(conn, screen->root), 0);
+
+ if (NULL == pointer)
+ {
+ focuswin = screen->root;
+ }
+ else
+ {
+ setfocus(pointer->child);
+ free(pointer);
+ }
+
xcb_flush(conn);
free(reply);
@@ -278,6 +345,21 @@ void raisewindow(xcb_drawable_t win)
xcb_flush(conn);
}
+void raiseorlower(xcb_drawable_t win)
+{
+ uint32_t values[] = { XCB_STACK_MODE_OPPOSITE };
+
+ 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];
@@ -408,7 +490,10 @@ void resizestep(xcb_drawable_t win, char direction)
xcb_get_geometry_reply_t *geom;
int width;
int height;
-
+ xcb_size_hints_t hints;
+ int step_x = MOVE_STEP;
+ int step_y = MOVE_STEP;
+
if (0 == win)
{
/* Can't resize root. */
@@ -416,7 +501,7 @@ void resizestep(xcb_drawable_t win, char direction)
}
raisewindow(win);
-
+
/* Get window geometry. */
geom = xcb_get_geometry_reply(conn,
xcb_get_geometry(conn, win),
@@ -426,10 +511,36 @@ void resizestep(xcb_drawable_t win, char direction)
return;
}
+ /*
+ * Get the window's incremental size step, if any, and use that
+ * when resizing.
+ */
+ if (!xcb_get_wm_normal_hints_reply(
+ conn, xcb_get_wm_normal_hints_unchecked(
+ conn, win), &hints, NULL))
+ {
+ PDEBUG("Couldn't get size hints.");
+ }
+
+ if (hints.flags & XCB_SIZE_HINT_P_RESIZE_INC)
+ {
+ if (0 == hints.width_inc || 0 == hints.height_inc)
+ {
+ PDEBUG("Client lied. No size inc hints.\n");
+ step_x = 1;
+ step_y = 1;
+ }
+ else
+ {
+ step_x = hints.width_inc;
+ step_y = hints.height_inc;
+ }
+ }
+
switch (direction)
{
case 'h':
- width = geom->width - MOVE_STEP;
+ width = geom->width - step_x;
height = geom->height;
if (width < 0)
{
@@ -439,7 +550,7 @@ void resizestep(xcb_drawable_t win, char direction)
case 'j':
width = geom->width;
- height = geom->height + MOVE_STEP;
+ height = geom->height + step_y;
if (height + geom->y > screen->height_in_pixels)
{
goto bad;
@@ -448,7 +559,7 @@ void resizestep(xcb_drawable_t win, char direction)
case 'k':
width = geom->width;
- height = geom->height - MOVE_STEP;
+ height = geom->height - step_y;
if (height < 0)
{
goto bad;
@@ -456,7 +567,7 @@ void resizestep(xcb_drawable_t win, char direction)
break;
case 'l':
- width = geom->width + MOVE_STEP;
+ width = geom->width + step_x;
height = geom->height;
if (width + geom->x > screen->width_in_pixels)
{
@@ -468,12 +579,12 @@ void resizestep(xcb_drawable_t win, char direction)
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.
+ * pointer to another window.
*/
xcb_warp_pointer(conn, XCB_NONE, win, 0, 0, 0, 0,
width / 2, height / 2);
@@ -486,11 +597,12 @@ bad:
void mousemove(xcb_drawable_t win, int rel_x, int rel_y)
{
- xcb_get_geometry_reply_t *geom;
+ xcb_get_geometry_reply_t *geom;
int x;
int y;
-
+
/* Get window geometry. */
+
geom = xcb_get_geometry_reply(conn,
xcb_get_geometry(conn, win),
NULL);
@@ -520,15 +632,18 @@ void mousemove(xcb_drawable_t win, int rel_x, int rel_y)
}
movewindow(win, x, y);
-
+
free(geom);
}
void mouseresize(xcb_drawable_t win, int rel_x, int rel_y)
{
xcb_get_geometry_reply_t *geom;
+ xcb_size_hints_t hints;
uint32_t width;
uint32_t height;
+ uint32_t width_inc = 1;
+ uint32_t height_inc = 1;
raisewindow(win);
@@ -554,23 +669,60 @@ void mouseresize(xcb_drawable_t win, int rel_x, int rel_y)
width = rel_x - geom->x;
height = rel_y - geom->y;
+ /*
+ * Get the window's incremental size step, if any, and use that
+ * when resizing.
+ */
+ if (!xcb_get_wm_normal_hints_reply(
+ conn, xcb_get_wm_normal_hints_unchecked(conn, win), &hints, NULL))
+ {
+ PDEBUG("Couldn't get size hints.");
+
+ }
+ if (hints.flags & XCB_SIZE_HINT_P_RESIZE_INC)
+ {
+ if (0 == hints.width_inc || 0 == hints.height_inc)
+ {
+ PDEBUG("The client lied. There is no resize inc here.\n");
+
+ hints.width_inc = 1;
+ hints.height_inc = 1;
+ }
+ else
+ {
+ width_inc = hints.width_inc;
+ height_inc = hints.height_inc;
+
+ if (0 != width % width_inc)
+ {
+ width -= width % width_inc;
+ }
+
+ if (0 != height % height_inc)
+ {
+ height -= height % height_inc;
+ }
+ }
+ }
+
if (width > screen->width_in_pixels)
{
- width = screen->width_in_pixels - geom->x;
+ width = (screen->width_in_pixels - geom->x) / width_inc;
}
if (height > screen->height_in_pixels)
{
- height = screen->height_in_pixels - geom->y;
+ height = (screen->height_in_pixels - geom->y) / height_inc;
}
- PDEBUG("mousresize: Resizing to %d x %d\n\n", width, height);
+ PDEBUG("Resizing to %dx%d (%dx%d)\n", width, height,
+ width / width_inc,
+ height / height_inc);
resize(win, width, height);
free(geom);
}
-
void movestep(xcb_drawable_t win, char direction)
{
@@ -650,7 +802,7 @@ void movestep(xcb_drawable_t win, char 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);
@@ -762,19 +914,19 @@ void handle_keypress(xcb_drawable_t win, xcb_key_press_event_t *ev)
switch (key)
{
case KEY_H: /* h */
- resizestep(win, 'h');
+ resizestep(focuswin, 'h');
break;
case KEY_J: /* j */
- resizestep(win, 'j');
+ resizestep(focuswin, 'j');
break;
case KEY_K: /* k */
- resizestep(win, 'k');
+ resizestep(focuswin, 'k');
break;
case KEY_L: /* l */
- resizestep(win, 'l');
+ resizestep(focuswin, 'l');
break;
}
}
@@ -787,34 +939,34 @@ void handle_keypress(xcb_drawable_t win, xcb_key_press_event_t *ev)
break;
case KEY_H: /* h */
- movestep(win, 'h');
+ movestep(focuswin, 'h');
break;
case KEY_J: /* j */
- movestep(win, 'j');
+ movestep(focuswin, 'j');
break;
case KEY_K: /* k */
- movestep(win, 'k');
+ movestep(focuswin, 'k');
break;
case KEY_L: /* l */
- movestep(win, 'l');
+ movestep(focuswin, 'l');
break;
case KEY_TAB: /* tab */
break;
case KEY_M: /* m */
- maxvert(win);
+ maxvert(focuswin);
break;
case KEY_R: /* r*/
- raisewindow(win);
+ raiseorlower(focuswin);
break;
case KEY_X: /* x */
- maximize(win);
+ maximize(focuswin);
break;
} /* switch unshifted */
@@ -830,14 +982,14 @@ void events(void)
int mode = 0;
uint16_t mode_x;
uint16_t mode_y;
-
+
for (;;)
{
ev = xcb_wait_for_event(conn);
if (NULL == ev)
{
- perror("xcb_wait_for_event");
- break;
+ fprintf(stderr, "mcwm: Couldn't get event. Exiting...\n");
+ exit(1);
}
PDEBUG("Event: %d\n", ev->response_type);
@@ -850,22 +1002,23 @@ void events(void)
{
xcb_create_notify_event_t *e;
- PDEBUG("Map request\n");
+ PDEBUG("event: Map request.\n");
e = ( xcb_create_notify_event_t *) ev;
newwin(e->window);
}
break;
-
- /* If we're not the only window manager, we receive these instead. */
- case XCB_CREATE_NOTIFY:
+
+ case XCB_DESTROY_NOTIFY:
{
xcb_create_notify_event_t *e;
- PDEBUG("Create notify event\n");
+ PDEBUG("event: Destroy notify.\n");
e = ( xcb_create_notify_event_t *) ev;
- setupwin(e->window);
+
+ /* FIXME: Find the window in list of clients. */
}
-
+ break;
+
case XCB_BUTTON_PRESS:
{
xcb_button_press_event_t *e;
@@ -879,16 +1032,13 @@ void events(void)
{
win = e->child;
- /* If middle button was pressed, raise window. */
-
/*
- * FIXME: If already raised, lower
- * instead. XCB_STACK_MODE_BELOW
+ * If middle button was pressed, raise window or lower
+ * it if it was already on top.
*/
-
if (2 == e->detail)
{
- raisewindow(win);
+ raiseorlower(win);
}
else
{
@@ -931,13 +1081,12 @@ void events(void)
* button to be released or for the pointer to
* move.
*/
-
xcb_grab_pointer(conn,
- 0, /* get all from mask below */
- screen->root, /* grab here */
+ 0,
+ screen->root, /* grab in here */
XCB_EVENT_MASK_BUTTON_RELEASE
| XCB_EVENT_MASK_POINTER_MOTION,
- XCB_GRAB_MODE_ASYNC, /* get more pointer events */
+ XCB_GRAB_MODE_ASYNC,
XCB_GRAB_MODE_ASYNC,
screen->root, /* stay here */
XCB_NONE, /* no other cursor. */
@@ -1018,6 +1167,7 @@ void events(void)
break;
case XCB_ENTER_NOTIFY:
+ PDEBUG("event: Enter notify.\n");
if (0 == mode)
{
xcb_enter_notify_event_t *e = (xcb_enter_notify_event_t *)ev;
@@ -1128,34 +1278,27 @@ int main(int argc, char **argv)
/* Subscribe to events. */
mask = XCB_CW_EVENT_MASK;
- values[0] = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT;
+
+ values[0] = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
+ | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY;
cookie =
xcb_change_window_attributes_checked(conn, root, mask, values);
error = xcb_request_check(conn, cookie);
+
+ xcb_flush(conn);
+
if (NULL != error)
{
fprintf(stderr, "mcwm: Can't subscribe to SUBSTRUCTURE REDIRECT event. "
"Error code: %d\n"
- "Another window manager running?\n"
- "Falling back to co-running mode.\n",
+ "Another window manager running?\n",
error->error_code);
- values[0] = XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY;
- cookie =
- xcb_change_window_attributes_checked(conn, root, mask, values);
- error = xcb_request_check(conn, cookie);
- if (NULL != error)
- {
- fprintf(stderr, "Co-running didn't work either. Error code: %d\n"
- "Exiting\n",
- error->error_code);
- xcb_disconnect(conn);
- exit(1);
- }
+ xcb_disconnect(conn);
+
+ exit(1);
}
-
- xcb_flush(conn);
/* Loop over events. */
events();