diff options
author | MC <mc@hack.org> | 2010-06-29 14:38:42 +0200 |
---|---|---|
committer | MC <mc@brain.hack.org> | 2010-06-29 14:38:42 +0200 |
commit | 9eba663926d93cddfc4bd7d4b1ca3d073e922df9 (patch) | |
tree | f539cca8f7c47c11b983224c1575a3129867439a | |
parent | 4e7bb8c6b19077fbce47094e1b2a5bcc25ace8ba (diff) | |
download | mcwm-9eba663926d93cddfc4bd7d4b1ca3d073e922df9.zip |
Added focus change from keyboard, Mod2-TAB. For now, it's a simple
window ring.
This also means focuswin is now a pointer to a struct client and that
we have a linked list of all mapped windows not in override redirect
mode.
-rw-r--r-- | Makefile | 15 | ||||
-rw-r--r-- | list.c | 188 | ||||
-rw-r--r-- | list.h | 11 | ||||
-rw-r--r-- | mcwm.c | 361 |
4 files changed, 493 insertions, 82 deletions
@@ -1,20 +1,21 @@ -VERSION=201000624-3 +VERSION=201000629 DIST=mcwm-$(VERSION) DISTFILES=LICENSE Makefile NEWS README TODO WISHLIST config.h mcwm.c \ - events.h + events.h mcwm.man CC=gcc -CFLAGS=-g -std=c99 -Wall -I/usr/local/include -L/usr/local/lib -lxcb \ - -lxcb-keysyms -lxcb-icccm -lxcb-atom #-DDEBUG +CFLAGS=-g -std=c99 -Wall -I/usr/local/include #-DDEBUG +LDFLAGS=-L/usr/local/lib -lxcb -lxcb-keysyms -lxcb-icccm -lxcb-atom RM=/bin/rm PREFIX=/usr/local -TARGETS=mcwm +TARGETS=mcwm list.o all: $(TARGETS) -mcwm: mcwm.c config.h events.h Makefile +mcwm: mcwm.o list.o config.h events.h Makefile + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ mcwm.c list.o mcwm-static: mcwm.c config.h $(CC) -o mcwm-static mcwm.c -static -g -std=c99 -Wall \ @@ -38,7 +39,7 @@ $(DIST).tar.bz2: dist: $(DIST).tar.bz2 clean: - $(RM) -f $(TARGETS) + $(RM) -f $(TARGETS) *.o distclean: clean $(RM) -f $(DIST).tar.bz2 @@ -0,0 +1,188 @@ +#include <stdlib.h> +#include <stdio.h> +#include "list.h" + +#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 + +void movetohead(struct item **mainlist, struct item *item) +{ + if (*mainlist == item) + { + /* Already at head. Do nothing. */ + return; + } + + /* Braid together the list where we are now. */ + if (NULL != item->prev) + { + item->prev->next = item->next; + } + + if (NULL != item->next) + { + item->next->prev = item->prev; + } + + /* Now at head. */ + item->prev = NULL; + item->next = *mainlist; +} + +/* + */ +struct item *additem(struct item **mainlist) +{ + struct item *item; + + if (NULL == (item = (struct item *) malloc(sizeof (struct item)))) + { + return NULL; + } + + if (NULL == *mainlist) + { + /* First in the list. */ + + PDEBUG("First in list.\n"); + item->prev = NULL; + item->next = NULL; + } + else + { + /* Add to beginning of list. */ + + item->next = *mainlist; + item->next->prev = item; + item->prev = NULL; + } + + *mainlist = item; + + return item; +} + +void delitem(struct item **mainlist, struct item *item) +{ + struct item *ml = *mainlist; + + if (NULL == mainlist) + { + return; + } + + if (item == *mainlist) + { + /* First entry was removed. Remember the next one instead. */ + *mainlist = ml->next; + } + else + { + item->prev->next = item->next; + + if (NULL != item->next) + { + /* This is not the last item in the list. */ + item->next->prev = item->prev; + } + } + + free(item); +} + +void listitems(struct item *mainlist) +{ + struct item *item; + int i; + + for (item = mainlist, i = 1; item != NULL; item = item->next, i ++) + { + printf("item #%d (stored at %p).\n", i, item); + } +} + +#if 0 +int main(void) +{ + struct item *mainlist = NULL; + struct item *item1; + struct item *item2; + struct item *item3; + char *foo1 = "1"; + char *foo2 = "2"; + char *foo3 = "3"; + + item1 = addhead(&mainlist); + if (NULL == item1) + { + printf("Couldn't allocate.\n"); + exit(1); + } + item1->data = foo1; + listitems(mainlist); + + item2 = addhead(&mainlist); + if (NULL == item2) + { + printf("Couldn't allocate.\n"); + exit(1); + } + item2->data = foo2; + listitems(mainlist); + + item3 = addhead(&mainlist); + if (NULL == item3) + { + printf("Couldn't allocate.\n"); + exit(1); + } + item3->data = foo3; + listitems(mainlist); + + printf("DELETING.\n"); + +/* + delclient: entry removed. item = 0x28201040, *mainlist = 0x28201050 + in listitems: mainlist = 0x28201050 + item #1 (stored at 0x28201050): 3 + item #2 (stored at 0x28201030): 1 + + Removing last item... + + next == NULL + prev == *mainlist + + delclient: entry removed. item = 0x28201030, *mainlist = 0x28201050 + in listitems: mainlist = 0x28201050 + item #1 (stored at 0x28201050): 3 + item #2 (stored at 0x28201030): 1 + + delclient: first entry removed. + in listitems: mainlist = 0x28201030 + item #1 (stored at 0x28201030): 1 +*/ + + delitem(&mainlist, item2); + listitems(mainlist); + + puts(""); + + delitem(&mainlist, item1); + listitems(mainlist); + + puts(""); + + delitem(&mainlist, item3); + listitems(mainlist); + + puts(""); + + exit(0); +} +#endif @@ -0,0 +1,11 @@ +struct item +{ + void *data; + struct item *prev; + struct item *next; +}; + +void movetohead(struct item **mainlist, struct item *item); +struct item *additem(struct item **mainlist); +void delitem(struct item **mainlist, struct item *item); +void listitems(struct item *mainlist); @@ -44,6 +44,8 @@ #include "events.h" #endif +#include "list.h" + /* Check here for user configurable parts: */ #include "config.h" @@ -80,12 +82,28 @@ typedef enum { KEY_TAB, KEY_MAX } key_enum_t; + +struct client +{ + xcb_drawable_t id; + int16_t x; + int16_t y; + uint16_t width; + uint16_t height; + int32_t min_width, min_height; + int32_t max_width, max_height; + int32_t width_inc, height_inc; + int32_t base_width, base_height; + int vscreen; /* Virtual screen. */ + struct item *winitem; /* Pointer to our place in list of all windows. */ +}; /* Globals */ xcb_connection_t *conn; /* Connection to X server. */ xcb_screen_t *screen; /* Our current screen. */ -xcb_drawable_t focuswin; /* Current focus window. */ +struct client *focuswin; /* Current focus window. */ +struct item *winlist = NULL; struct keys { @@ -115,16 +133,20 @@ struct conf /* Functions declerations. */ uint32_t getcolor(const char *colstr); +void forgetwin(xcb_window_t win); void newwin(xcb_window_t win); -void setupwin(xcb_window_t win); +struct client *setupwin(xcb_window_t win, + xcb_get_window_attributes_reply_t *attr); 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); +struct client *findclient(xcb_drawable_t win); +void focusnext(void); void setunfocus(xcb_drawable_t win); -void setfocus(xcb_drawable_t win); +void setfocus(struct client *client); int start_terminal(void); void resize(xcb_drawable_t win, uint32_t width, uint32_t height); void resizestep(xcb_drawable_t win, char direction); @@ -163,6 +185,29 @@ uint32_t getcolor(const char *colstr) return col_reply->pixel; } +void forgetwin(xcb_window_t win) +{ + struct item *item; + struct client *client; + + for (item = winlist; item != NULL; item = item->next) + { + client = item->data; + + PDEBUG("Win %d == client ID %d\n", win, client->id); + if (win == client->id) + { + /* Found it. */ + PDEBUG("Found it. Forgetting...\n"); + + free(item->data); + + delitem(&winlist, item); + return; + } + } +} + /* * Set position, geometry and attributes of a new window and show it * on the screen. @@ -170,12 +215,13 @@ uint32_t getcolor(const char *colstr) void newwin(xcb_window_t win) { xcb_query_pointer_reply_t *pointer; - xcb_get_geometry_reply_t *geom; int x; int y; int32_t width; int32_t height; - + xcb_get_window_attributes_reply_t *attr; + struct client *client; + /* 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); @@ -194,19 +240,27 @@ void newwin(xcb_window_t win) } /* - * Get window geometry so we can check if it fits on the screen - * where the cursor is. + * Set up stuff, like borders, add the window to the client list, + * et cetera. */ - geom = xcb_get_geometry_reply(conn, - xcb_get_geometry(conn, win), - NULL); - if (NULL == geom) + attr = xcb_get_window_attributes_reply( + conn, xcb_get_window_attributes(conn, win), NULL); + + if (!attr) { + fprintf(stderr, "Couldn't get attributes for window %d.", win); return; } - - width = geom->width; - height = geom->height; + client = setupwin(win, attr); + + if (NULL == client) + { + fprintf(stderr, "mcwm: Couldn't set up window. Out of memory.\n"); + return; + } + + width = client->width; + height = client->height; /* FIXME: XCB_SIZE_HINT_BASE_SIZE */ @@ -238,9 +292,8 @@ void newwin(xcb_window_t win) /* Move the window to cursor position. */ movewindow(win, x, y); - - /* Set up stuff, like borders. */ - setupwin(win); + client->x = x; + client->y = y; /* Show window on screen. */ xcb_map_window(conn, win); @@ -250,19 +303,21 @@ void newwin(xcb_window_t win) * pointer to another window. */ xcb_warp_pointer(conn, XCB_NONE, win, 0, 0, 0, 0, - geom->width / 2, geom->height / 2); - - free(geom); + client->width / 2, client->height / 2); xcb_flush(conn); } /* set border colour, width and event mask for window. */ -void setupwin(xcb_window_t win) +struct client *setupwin(xcb_window_t win, + xcb_get_window_attributes_reply_t *attr) { uint32_t mask = 0; uint32_t values[2]; - + xcb_get_geometry_reply_t *geom; + struct item *item; + struct client *client; + if (conf.borders) { /* Set border color. */ @@ -278,10 +333,51 @@ void setupwin(xcb_window_t win) mask = XCB_CW_EVENT_MASK; values[0] = XCB_EVENT_MASK_ENTER_WINDOW; xcb_change_window_attributes_checked(conn, win, mask, values); - - /* FIXME: set properties. */ xcb_flush(conn); + + /* Remember window and store a few things about it. */ + geom = xcb_get_geometry_reply(conn, + xcb_get_geometry(conn, win), + NULL); + if (NULL == geom) + { + goto bad; + } + + item = additem(&winlist); + + if (NULL == item) + { + PDEBUG("newwin: Out of memory.\n"); + goto bad; + } + + client = malloc(sizeof (struct client)); + if (NULL == client) + { + PDEBUG("newwin: Out of memory.\n"); + goto bad; + } + + item->data = client; + + client->id = win; + PDEBUG("Adding window %d\n", client->id); + client->x = geom->x; + client->y = geom->y; + client->width = geom->width; + client->height = geom->height; + client->vscreen = 0; + client->winitem = item; + + free(geom); + + return client; + +bad: + free(geom); + return NULL; } xcb_keycode_t keysymtokeycode(xcb_keysym_t keysym, xcb_key_symbols_t *keysyms) @@ -341,7 +437,7 @@ int setupscreen(void) int len; xcb_window_t *children; xcb_get_window_attributes_reply_t *attr; - + /* Get all children. */ reply = xcb_query_tree_reply(conn, xcb_query_tree(conn, screen->root), 0); @@ -361,33 +457,33 @@ int setupscreen(void) if (!attr) { - PDEBUG("Couldn't get attributes."); + fprintf(stderr, "Couldn't get attributes for window %d.", + children[i]); + continue; } - else + + /* + * Don't set up or even bother windows in override redirect + * mode. + * + * This mode means they wouldn't have been reported to us + * with a MapRequest if we had been running, so in the + * normal case we wouldn't have seen them. + * + * Usually, this mode is used for menu windows and the + * like. + * + * We don't care for any unmapped windows either. If they get + * unmapped later, we handle them when we get a MapRequest. + */ + if (!attr->override_redirect && + XCB_MAP_STATE_UNMAPPED != attr->map_state) { - /* - * Don't set up windows in override redirect mode. - * - * This mode means they wouldn't have been reported to us - * with a MapRequest if we had been running, so in the - * normal case we wouldn't have seen them. - * - * Usually, this mode is used for menu windows and the - * like. - */ - if (!attr->override_redirect) - { - setupwin(children[i]); - } -#if #DEBUG - else - { - PDEBUG("win %d has override_redirect.\n", children[i]); - } -#endif - free(attr); + setupwin(children[i], attr); } - } + + free(attr); + } /* for */ /* * Get pointer position so we can set focus on any window which @@ -398,11 +494,11 @@ int setupscreen(void) if (NULL == pointer) { - focuswin = screen->root; + focuswin = NULL; } else { - setfocus(pointer->child); + setfocus(findclient(pointer->child)); free(pointer); } @@ -463,11 +559,71 @@ void movewindow(xcb_drawable_t win, uint16_t x, uint16_t y) } +void focusnext(void) +{ + struct client *client; + bool found = false; + +#if DEBUG + if (NULL != focuswin) + { + PDEBUG("Focus now in win %d\n", focuswin->id); + } +#endif + + /* If we currently focus the root, focus first in list. */ + if (NULL == focuswin) + { + if (NULL == winlist) + { + PDEBUG("No windows to focus on.\n"); + return; + } + + client = winlist->data; + found = true; + } + else + { + if (NULL != focuswin->winitem->next) + { + client = focuswin->winitem->next->data; + found = true; + } + else + { + /* + * We were at the end of list. Focusing on first window in + * list instead. + */ + client = winlist->data; + found = true; + } + } + + if (found) + { + raisewindow(client->id); + xcb_warp_pointer(conn, XCB_NONE, client->id, 0, 0, 0, 0, + client->width / 2, client->height / 2); + setfocus(client); + } + else + { + PDEBUG("Couldn't find any new window to focus on.\n"); + } +} + void setunfocus(xcb_drawable_t win) { uint32_t values[1]; + + if (NULL == focuswin) + { + return; + } - if (focuswin == screen->root || !conf.borders) + if (focuswin->id == screen->root || !conf.borders) { return; } @@ -479,15 +635,45 @@ void setunfocus(xcb_drawable_t win) xcb_flush(conn); } -void setfocus(xcb_drawable_t win) +struct client *findclient(xcb_drawable_t win) +{ + struct item *item; + struct client *client; + + for (item = winlist; item != NULL; item = item->next) + { + client = item->data; + if (win == client->id) + { + PDEBUG("findclient: Found it. Win: %d\n", client->id); + return client; + } + } + + return NULL; +} + +void setfocus(struct client *client) { uint32_t values[1]; + /* if client is NULL, we focus on the root. */ + if (NULL == client) + { + focuswin = NULL; + + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, screen->root, + XCB_CURRENT_TIME); + xcb_flush(conn); + + return; + } + /* * Don't bother focusing on the root window or on the same window * that already has focus. */ - if (win == screen->root || win == focuswin) + if (client->id == screen->root || client == focuswin) { return; } @@ -496,18 +682,24 @@ void setfocus(xcb_drawable_t win) { /* Set new border colour. */ values[0] = conf.focuscol; - xcb_change_window_attributes(conn, win, XCB_CW_BORDER_PIXEL, values); + xcb_change_window_attributes(conn, client->id, XCB_CW_BORDER_PIXEL, + values); /* Unset last focus. */ - setunfocus(focuswin); + if (NULL != focuswin) + { + setunfocus(focuswin->id); + } } /* Set new input focus. */ - focuswin = win; - xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, win, + + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, client->id, XCB_CURRENT_TIME); xcb_flush(conn); + + focuswin = client; } int start_terminal(void) @@ -1045,19 +1237,19 @@ void handle_keypress(xcb_drawable_t win, xcb_key_press_event_t *ev) switch (key) { case KEY_H: /* h */ - resizestep(focuswin, 'h'); + resizestep(focuswin->id, 'h'); break; case KEY_J: /* j */ - resizestep(focuswin, 'j'); + resizestep(focuswin->id, 'j'); break; case KEY_K: /* k */ - resizestep(focuswin, 'k'); + resizestep(focuswin->id, 'k'); break; case KEY_L: /* l */ - resizestep(focuswin, 'l'); + resizestep(focuswin->id, 'l'); break; default: @@ -1074,34 +1266,35 @@ void handle_keypress(xcb_drawable_t win, xcb_key_press_event_t *ev) break; case KEY_H: /* h */ - movestep(focuswin, 'h'); + movestep(focuswin->id, 'h'); break; case KEY_J: /* j */ - movestep(focuswin, 'j'); + movestep(focuswin->id, 'j'); break; case KEY_K: /* k */ - movestep(focuswin, 'k'); + movestep(focuswin->id, 'k'); break; case KEY_L: /* l */ - movestep(focuswin, 'l'); + movestep(focuswin->id, 'l'); break; case KEY_TAB: /* tab */ + focusnext(); break; case KEY_M: /* m */ - maxvert(focuswin); + maxvert(focuswin->id); break; case KEY_R: /* r*/ - raiseorlower(focuswin); + raiseorlower(focuswin->id); break; case KEY_X: /* x */ - maximize(focuswin); + maximize(focuswin->id); break; default: @@ -1156,10 +1349,26 @@ void events(void) { xcb_destroy_notify_event_t *e; - PDEBUG("event: Destroy notify.\n"); e = (xcb_destroy_notify_event_t *) ev; - /* FIXME: Find the window in list of clients. */ + /* + * If we had focus in this window, forget about the focus. + * We will get an EnterNotify if there's another window + * under the pointer so we can set the focus proper later. + */ + if (focuswin->id == e->window) + { + focuswin = NULL; + } + + /* + * Find this window in list of clients and forget about + * it. + */ + forgetwin(e->window); +#if DEBUG + listitems(winlist); +#endif } break; @@ -1316,7 +1525,7 @@ void events(void) mode = 0; free(geom); - setfocus(e->event); + setfocus(findclient(e->event)); PDEBUG("mode now = %d\n", mode); @@ -1336,10 +1545,11 @@ void events(void) handle_keypress(win, e); } break; - + case XCB_ENTER_NOTIFY: { xcb_enter_notify_event_t *e = (xcb_enter_notify_event_t *)ev; + struct client *client; PDEBUG("event: Enter notify eventwin %d child %d.\n", e->event, @@ -1361,12 +1571,13 @@ void events(void) * If we're entering the same window we focus now, * then don't bother focusing. */ - if (e->event != focuswin) + if (NULL == focuswin || e->event != focuswin->id) { /* * Otherwise, set focus to the window we just entered. */ - setfocus(e->event); + client = findclient(e->event); + setfocus(client); } } } |