summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--NEWS7
-rw-r--r--TODO66
-rw-r--r--config.h1
-rw-r--r--list.c76
-rw-r--r--list.h17
-rw-r--r--mcwm.c211
7 files changed, 318 insertions, 62 deletions
diff --git a/Makefile b/Makefile
index 498fc62..e0aeee4 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-VERSION=20101118-2
+VERSION=20110308
DIST=mcwm-$(VERSION)
SRC=mcwm.c list.c config.h events.h list.h
DISTFILES=LICENSE Makefile NEWS README TODO WISHLIST mcwm.man $(SRC)
diff --git a/NEWS b/NEWS
index ae6068e..b10914c 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,13 @@
User visible changes
+2011-02-23
+
+ * If you change focus window mcwm remembers where we last had
+ focus. If you start using MODKEY+Tab to move around the window
+ list the first Tab will always bring you to where we last had
+ focus.
+
2011-02-19
* Resets per window state of max or vertical max when physical
diff --git a/TODO b/TODO
index c368367..41ad987 100644
--- a/TODO
+++ b/TODO
@@ -28,32 +28,64 @@
Save the subwindows of the root window and focus each when pressing
modkey + user_key_change. First option always last focused window.
- Partly done. I have added a linked list of all windows and can walk
- around them in a simple window ring.
-
I would like it to work like this: modkey-Tab-Tab-Tab... Release
modkey to focus on a window *and* to remember the window where we
- started. The window we remember would be moved to the next position
- in the window list.
+ started.
+
+ This will also need to work if the change focus with the mouse.
+
+ A single modkey-Tab would then get the focus to end up in the window
+ where we started tabbing or where we last had our focus if changing
+ with the mouse.
+
+ This is now implemented like this:
+
+ Always keep the current window as the first element in the current
+ workspace window list. Always keep the window we focused before it
+ as the second element.
+
+ When we focus on the next window from the current one, the first
+ Tab always goes to the window we had focus in before.
- A single modkey-Tab would then get the focus to end up in the window where
- we started.
+ Currently, we stop tabbing when the MODKEY is released. We might
+ want to stop tabbing and focus on the new window if we get another
+ command as well...
- It's possible to get a Release Event from the Alt key. Consider what
- xev(1) says about the key.
+ I have bound Alt_L explicitly and looking for key release events.
+ This is a bit ugly. It is possible to ask for what keys give
+ XCB_MOD_MASK_4 or whatever MODKEY is.
- I don't know if it's possible to ask the X server what keycode currently
- gives you XCB_MOD_MASK_4 or whatever, which would be the nicest way.
+ GetModifierMapping
- I guess, but I haven't yet tried, that I will have to do a specific grab
- key for the XK_Alt_L or whatever instead.
+ This request returns the keycodes of the keys being used as
+ modifiers. The number of keycodes in the list is
+ 8*keycodes-per-modifier. The keycodes are divided into eight sets,
+ with each set containing keycodes-per-modifier elements. The sets
+ are assigned to the modifiers Shift, Lock, Control, Mod1, Mod2,
+ Mod3, Mod4, and Mod5, in order. The keycodes-per-modifier value is
+ chosen arbitrarily by the server; zeroes are used to fill in
+ unused elements within each set. If only zero values are given in
+ a set, the use of the corresponding modifier has been disabled.
+ The order of keycodes within each set is chosen arbitrarily by the
+ server.
- This also means we have to add a new state, just like MCWM_MOVE et
- cetera, to know that we're currently jumping around the window list
- trying to find our next focused window.
+ xcb_get_modifier_mapping()
+
+ xcb_get_modifier_mapping_unchecked()
+
+ xcb_keycode_t * xcb_get_modifier_mapping_keycodes()
+
+ xcb_keycode_t *
+ xcb_get_modifier_mapping_keycodes (const xcb_get_modifier_mapping_reply_t *R /**< */);
+
+ xcb_get_modifier_mapping_keycodes_length (const xcb_get_modifier_mapping_reply_t *R /**< */);
How do we re-establish stack order after moving around windows to
- focus on?
+ focus on? Do we want to?
+
+ There is a bug when we change from one ws to another and focuswin
+ doesn't get reset. That is, focuswin isn't NULL but the
+ focuswin->wsitem[curws] is empty.
* Virtual screens/workspaces
diff --git a/config.h b/config.h
index db44bb3..0e6b949 100644
--- a/config.h
+++ b/config.h
@@ -76,3 +76,4 @@
#define USERKEY_BOTLEFT XK_B
#define USERKEY_BOTRIGHT XK_N
#define USERKEY_DELETE XK_End
+#define USERKEY_MOD XK_Alt_L
diff --git a/list.c b/list.c
index ab278a1..d6ecb1a 100644
--- a/list.c
+++ b/list.c
@@ -11,6 +11,9 @@
#define D(x)
#endif
+/*
+ * Move element in item to the head of list mainlist.
+ */
void movetohead(struct item **mainlist, struct item *item)
{
if (NULL == item || NULL == mainlist || NULL == *mainlist)
@@ -20,7 +23,7 @@ void movetohead(struct item **mainlist, struct item *item)
if (*mainlist == item)
{
- /* Already at head. Do nothing. */
+ /* item is NULL or we're already at head. Do nothing. */
return;
}
@@ -35,15 +38,23 @@ void movetohead(struct item **mainlist, struct item *item)
item->next->prev = item->prev;
}
- /* Now at head. */
+ /* Now we'at head, so no one before us. */
item->prev = NULL;
+
+ /* Old head is our next. */
item->next = *mainlist;
+ /* Old head needs to know about us. */
+ item->next->prev = item;
+
/* Remember the new head. */
*mainlist = item;
}
/*
+ * Create space for a new item and add it to the head of mainlist.
+ *
+ * Returns item or NULL if out of memory.
*/
struct item *additem(struct item **mainlist)
{
@@ -120,10 +131,14 @@ void listall(struct item *mainlist)
{
struct item *item;
int i;
+
+ printf("Listing all:\n");
for (item = mainlist, i = 1; item != NULL; item = item->next, i ++)
{
printf("%d at %p: %s.\n", i, (void *)item, (char *)item->data);
+ printf(" prev: %p\n", item->prev);
+ printf(" next: %p\n", item->next);
}
}
@@ -133,7 +148,10 @@ int main(void)
struct item *item1;
struct item *item2;
struct item *item3;
- struct item *item4;
+ struct item *item4;
+ struct item *item;
+ struct item *nextitem;
+ int i;
char *foo1 = "1";
char *foo2 = "2";
char *foo3 = "3";
@@ -179,12 +197,58 @@ int main(void)
printf("Current elements:\n");
listall(mainlist);
- movetohead(&mainlist, item2);
+ printf("----------------------------------------------------------------------\n");
+
+ printf("Moving item3 to be after item2\n");
+ movetonext(&mainlist, item2, item3);
+ printf("Current elements:\n");
+ listall(mainlist);
+
+ printf("----------------------------------------------------------------------\n");
+
+ printf("Moving head! item4 to be after item2\n");
+ movetonext(&mainlist, item2, item4);
printf("Current elements:\n");
listall(mainlist);
+
+ printf("----------------------------------------------------------------------\n");
+ printf("Moving tail! item1 to be after item2\n");
+ movetonext(&mainlist, item2, item1);
+ printf("Current elements:\n");
+ listall(mainlist);
+
printf("----------------------------------------------------------------------\n");
+ printf("Moving head to be after tail.\n");
+ movetonext(&mainlist, item3, item2);
+ printf("Current elements:\n");
+ listall(mainlist);
+
+ printf("Moving all the items after each other.\n");
+ /* item3 is tail. work backwards. */
+ for (item = mainlist, i = 1;
+ item != NULL;
+ item = item->next, i ++)
+ {
+ for (nextitem = item2; nextitem != NULL; nextitem = nextitem->prev)
+ {
+ movetonext(&mainlist, nextitem, item);
+ printf("Current elements:\n");
+ listall(mainlist);
+ }
+ }
+
+ printf("----------------------------------------------------------------------\n");
+
+#if 0
+ movetohead(&mainlist, item2);
+ printf("Current elements:\n");
+ listall(mainlist);
+
+ printf("----------------------------------------------------------------------\n");
+#endif
+
printf("Deleting item stored at %p\n", item3);
delitem(&mainlist, item3);
printf("Current elements:\n");
@@ -203,7 +267,9 @@ int main(void)
listall(mainlist);
puts("");
-
+
+ printf("----------------------------------------------------------------------\n");
+
exit(0);
}
#endif
diff --git a/list.h b/list.h
index ffa323d..ecd2bfb 100644
--- a/list.h
+++ b/list.h
@@ -5,7 +5,24 @@ struct item
struct item *next;
};
+/*
+ * Move element in item to the head of list mainlist.
+ */
void movetohead(struct item **mainlist, struct item *item);
+
+/*
+ * Create space for a new item and add it to the head of mainlist.
+ *
+ * Returns item or NULL if out of memory.
+ */
struct item *additem(struct item **mainlist);
+
+/*
+ *
+ */
void delitem(struct item **mainlist, struct item *item);
+
+/*
+ *
+ */
void listitems(struct item *mainlist);
diff --git a/mcwm.c b/mcwm.c
index fc2d5df..f5c03c0 100644
--- a/mcwm.c
+++ b/mcwm.c
@@ -30,6 +30,7 @@
#include <getopt.h>
#include <string.h>
#include <signal.h>
+#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>
@@ -73,6 +74,12 @@
/* We're currently resizing a window with the mouse. */
#define MCWM_RESIZE 3
+/*
+ * We're currently tabbing around the window list, looking for a new
+ * window to focus on.
+ */
+#define MCWM_TABBING 4
+
/* Our highest workspace. */
#define WORKSPACE_MAX 9
@@ -112,6 +119,7 @@ typedef enum {
KEY_B,
KEY_N,
KEY_END,
+ KEY_ALT,
KEY_MAX
} key_enum_t;
@@ -145,7 +153,12 @@ xcb_connection_t *conn; /* Connection to X server. */
xcb_screen_t *screen; /* Our current screen. */
uint32_t curws = 0; /* Current workspace. */
struct client *focuswin; /* Current focus window. */
+struct client *lastfocuswin; /* Last focused window. NOTE! Only
+ * used to communicate between
+ * start and end of tabbing
+ * mode. */
struct item *winlist = NULL; /* Global list of all client windows. */
+int mode = 0; /* Internal mode, such as move or resize */
/*
* Workspace list: Every workspace has a list of all visible
@@ -172,7 +185,7 @@ struct keys
xcb_keycode_t keycode;
} keys[KEY_MAX] =
{
- { USERKEY_FIX, 0 },
+ { USERKEY_FIX, 0 },
{ USERKEY_MOVE_LEFT, 0 },
{ USERKEY_MOVE_DOWN, 0 },
{ USERKEY_MOVE_UP, 0 },
@@ -180,8 +193,8 @@ struct keys
{ USERKEY_MAXVERT, 0 },
{ USERKEY_RAISE, 0 },
{ USERKEY_TERMINAL, 0 },
- { USERKEY_MAX, 0 },
- { USERKEY_CHANGE, 0, },
+ { USERKEY_MAX, 0 },
+ { USERKEY_CHANGE, 0 },
{ USERKEY_WS1, 0 },
{ USERKEY_WS2, 0 },
{ USERKEY_WS3, 0 },
@@ -196,7 +209,8 @@ struct keys
{ USERKEY_TOPRIGHT, 0 },
{ USERKEY_BOTLEFT, 0 },
{ USERKEY_BOTRIGHT, 0 },
- { USERKEY_DELETE, 0 }
+ { USERKEY_DELETE, 0 },
+ { USERKEY_MOD, 0 },
};
/* Global configuration. */
@@ -506,13 +520,13 @@ void changeworkspace(uint32_t ws)
PDEBUG("Changing from workspace #%d to #%d\n", curws, ws);
/*
- * We lose our focus temporarily if the window we focus isn't
- * fixed. An EnterNotify event will get our focus back later.
+ * We lose our focus if the window we focus isn't fixed. An
+ * EnterNotify event will set focus later.
*/
if (NULL != focuswin && !focuswin->fixed)
{
setunfocus(focuswin->id);
- focuswin = NULL;
+ focuswin = NULL;
}
/* Go through list of current ws. Unmap everything that isn't fixed. */
@@ -985,7 +999,7 @@ int setupkeys(void)
/* Get all the keysymbols. */
keysyms = xcb_key_symbols_alloc(conn);
-
+
for (i = KEY_F; i < KEY_MAX; i ++)
{
keys[i].keycode = keysymtokeycode(keys[i].keysym, keysyms);
@@ -999,17 +1013,38 @@ int setupkeys(void)
return -1;
}
- /* Grab this key. */
- xcb_grab_key(conn, 1, screen->root, MODKEY, keys[i].keycode,
- XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
+ if (i == KEY_ALT)
+ {
+ /*
+ * Grab Alt with all modifiers.
+ *
+ * FIXME: We can ask the X server for the keycode that
+ * gives us the MODKEY mask with the GetModifierMapping
+ * request.
+ */
+ xcb_grab_key(conn, 1, screen->root, XCB_MOD_MASK_ANY,
+ keys[KEY_ALT].keycode,
+ XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
+ }
+ else
+ {
+ /* Grab other keys with a modifier mask. */
+ xcb_grab_key(conn, 1, screen->root, MODKEY, keys[i].keycode,
+ XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
- /*
- * XXX Also grab it's shifted counterpart. A bit ugly here
- * because we grab all of them not just the ones we want.
- */
- xcb_grab_key(conn, 1, screen->root, MODKEY | SHIFTMOD, keys[i].keycode,
- XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
+ /*
+ * XXX Also grab it's shifted counterpart. A bit ugly here
+ * because we grab all of them not just the ones we want.
+ */
+ xcb_grab_key(conn, 1, screen->root, MODKEY | SHIFTMOD,
+ keys[i].keycode,
+ XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
+ }
} /* for */
+
+
+ /* Need this to take effect NOW! */
+ xcb_flush(conn);
/* Get rid of the key symbols table. */
free(keysyms);
@@ -1196,7 +1231,7 @@ void movewindow(xcb_drawable_t win, uint16_t x, uint16_t y)
/* Change focus to next in window ring. */
void focusnext(void)
{
- struct client *client;
+ struct client *client = NULL;
#if DEBUG
if (NULL != focuswin)
@@ -1205,16 +1240,36 @@ void focusnext(void)
}
#endif
- /* If we currently have no focus, focus first in list. */
- if (NULL == focuswin)
+ if (NULL == wslist[curws])
+ {
+ PDEBUG("No windows to focus on in this workspace.\n");
+ return;
+ }
+
+ if (MCWM_TABBING != mode)
{
- if (NULL == wslist[curws])
+ /*
+ * Remember what we last focused on. We need this when the
+ * MODKEY is released and we move the last focused window in
+ * the tabbing order list.
+ */
+ lastfocuswin = focuswin;
+ mode = MCWM_TABBING;
+
+ PDEBUG("Began tabbing.\n");
+ }
+
+ /* If we currently have no focus focus first in list. */
+ if (NULL == focuswin || NULL == focuswin->wsitem[curws])
+ {
+ PDEBUG("Focusing first in list: %p\n", wslist[curws]);
+ client = wslist[curws]->data;
+
+ if (NULL != focuswin && NULL == focuswin->wsitem[curws])
{
- PDEBUG("No windows to focus on.\n");
- return;
+ PDEBUG("XXX Our focused window %d isn't on this workspace!\n",
+ focuswin->id);
}
-
- client = wslist[curws]->data;
}
else
{
@@ -1222,27 +1277,37 @@ void focusnext(void)
{
/*
* We were at the end of list. Focusing on first window in
- * list.
+ * list unless we were already there.
*/
- client = wslist[curws]->data;
+ if (focuswin->wsitem[curws] != wslist[curws]->data)
+ {
+ PDEBUG("End of list. Focusing first in list: %p\n",
+ wslist[curws]);
+ client = wslist[curws]->data;
+ }
}
else
{
/* Otherwise, focus the next in list. */
+ PDEBUG("Tabbing. Focusing next: %p.\n",
+ focuswin->wsitem[curws]->next);
client = focuswin->wsitem[curws]->next->data;
}
} /* if NULL focuswin */
-
- /*
- * Raise window if it's occluded, then warp pointer into it and
- * set keyboard focus to it.
- */
- uint32_t values[] = { XCB_STACK_MODE_TOP_IF };
- xcb_configure_window(conn, client->id, XCB_CONFIG_WINDOW_STACK_MODE,
- values);
- xcb_warp_pointer(conn, XCB_NONE, client->id, 0, 0, 0, 0, 0, 0);
- setfocus(client);
+ if (NULL != client)
+ {
+ /*
+ * Raise window if it's occluded, then warp pointer into it and
+ * set keyboard focus to it.
+ */
+ uint32_t values[] = { XCB_STACK_MODE_TOP_IF };
+
+ xcb_configure_window(conn, client->id, XCB_CONFIG_WINDOW_STACK_MODE,
+ values);
+ xcb_warp_pointer(conn, XCB_NONE, client->id, 0, 0, 0, 0, 0, 0);
+ setfocus(client);
+ }
}
/* Mark window win as unfocused. */
@@ -2251,6 +2316,12 @@ void handle_keypress(xcb_key_press_event_t *ev)
return;
}
+ if (MCWM_TABBING == mode && key != KEY_TAB)
+ {
+ /* We don't allow any other key while in this mode. */
+ return;
+ }
+
/* Is it shifted? */
if (ev->state & SHIFTMOD)
{
@@ -2391,7 +2462,7 @@ void handle_keypress(xcb_key_press_event_t *ev)
void events(void)
{
xcb_generic_event_t *ev;
- int mode = 0; /* Internal mode, such as move or resize */
+
int16_t mode_x = 0; /* X coord when in special mode */
int16_t mode_y = 0; /* Y coord when in special mode */
int fd; /* Our X file descriptor */
@@ -2472,7 +2543,9 @@ void events(void)
e = (xcb_destroy_notify_event_t *) ev;
/*
- * If we had focus in this window, forget about the focus.
+ * If we had focus or our last 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.
*/
@@ -2483,6 +2556,13 @@ void events(void)
focuswin = NULL;
}
}
+ if (NULL != lastfocuswin)
+ {
+ if (lastfocuswin->id == e->window)
+ {
+ lastfocuswin = NULL;
+ }
+ }
/*
* Find this window in list of clients and forget about
@@ -2741,6 +2821,38 @@ void events(void)
}
break;
+ case XCB_KEY_RELEASE:
+ {
+ xcb_key_release_event_t *e = (xcb_key_release_event_t *)ev;
+
+ PDEBUG("Key %d released.\n", e->detail);
+
+ if (e->detail == keys[KEY_ALT].keycode && MCWM_TABBING == mode)
+ {
+ /* MODKEY was released after tabbing around the
+ * workspace window ring. This means this mode is
+ * finished and we have found a new focus window.
+ *
+ * We need to move first the window we used to focus
+ * on to the head of the window list and then move the
+ * new focus to the head of the list as well. The list
+ * should always start with the window we're focusing
+ * on.
+ */
+
+ mode = 0;
+
+ if (NULL != lastfocuswin)
+ {
+ movetohead(&wslist[curws], lastfocuswin->wsitem[curws]);
+ lastfocuswin = NULL;
+ }
+
+ movetohead(&wslist[curws], focuswin->wsitem[curws]);
+ } /* if KEY_ALT */
+ }
+ break;
+
case XCB_ENTER_NOTIFY:
{
xcb_enter_notify_event_t *e = (xcb_enter_notify_event_t *)ev;
@@ -2783,6 +2895,27 @@ void events(void)
client = findclient(e->event);
if (NULL != client)
{
+ if (MCWM_TABBING != mode)
+ {
+ /*
+ * We are focusing on a new window. Since
+ * we're not currently tabbing around the
+ * window ring, we need to update the
+ * current workspace window list: Move
+ * first the old focus to the head of the
+ * list and then the new focus to the head
+ * of the list.
+ */
+ if (NULL != focuswin)
+ {
+ movetohead(&wslist[curws],
+ focuswin->wsitem[curws]);
+ lastfocuswin = NULL;
+ }
+
+ movetohead(&wslist[curws], client->wsitem[curws]);
+ } /* if not tabbing */
+
setfocus(client);
}
}