From bb59bfd71ec1731587467f64625c7c6818c973dc Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Sat, 2 Jan 2021 23:27:41 -0800 Subject: *: replace the "shelf" with "contexts" This is unfortunately a bit of a large commit, but it's at least pretty much all on-topic for the generalized "contexts" feature. Rather than waste time trying to split this further into smaller commits, I'm just landing it as-is, now that I've lived with the interaction model long enough to not completely hate it. I fully expect to revisit this in the future. One TODO item in particular I'd like to note is "sending" windows to contexts always creates a new virtual desktop for the sent window in the destination context. What should really happen is the destination context should be checked for an empty desktop, and a new desktop created only when there isn't an empty one to be reused for receiving the sent window. Note this only affects non-migrate sends, as migrates (modified by Shift) explicitly use the existing focused desktop at the destination context. See the README for more information on how contexts work and what's different about the interaction model. It's fairly minimal, most of what you already know how to do should keep working as-is. The only oddity would be Mos1-s no longer "shelves" windows, it's now a modifier to turn "migrates" into "sends", and by itself is a noop now. Colors used for contexts haven't been refined and are enumerated in src/context_colors.def. --- src/colors.def | 3 - src/context.c | 222 +++++++++++++++++++++++++++++++++++------------------ src/context.h | 23 ++++-- src/desktop.c | 137 ++++++++++++++++++++++----------- src/desktop.h | 4 +- src/key.c | 235 +++++++++++++++++++++++++++++++++++++++------------------ src/screen.c | 11 +-- src/vwm.c | 12 ++- src/vwm.h | 19 ++++- src/window.c | 185 +++++++++++++++++++++++---------------------- src/window.h | 2 +- src/xwindow.c | 20 +---- 12 files changed, 550 insertions(+), 323 deletions(-) (limited to 'src') diff --git a/src/colors.def b/src/colors.def index 97c0dc9..7c71da0 100644 --- a/src/colors.def +++ b/src/colors.def @@ -1,6 +1,3 @@ -color(focused_window_border, "Green") -color(shelved_window_border, "purple") color(unfocused_window_border, "DarkGray") -color(shelved_console_border, "red") color(rubberband, "Orange") color(logo, "LimeGreen") diff --git a/src/context.c b/src/context.c index 32d7b3f..255480c 100644 --- a/src/context.c +++ b/src/context.c @@ -15,89 +15,163 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - /* desktop/shelf context handling */ + /* contexts (this is derived from desktops) */ +#include #include +#include +#include +#include "list.h" #include "context.h" #include "desktop.h" #include "vwm.h" #include "xwindow.h" -#include "window.h" -/* switch to the desired context if it isn't already the focused one, inform caller if anything happened */ -int vwm_context_focus(vwm_t *vwm, vwm_context_t desired_context) +/* make the specified context the most recently used one */ +vwm_context_t * vwm_context_mru(vwm_t *vwm, vwm_context_t *context) { - vwm_context_t entry_context = vwm->focused_context; - - switch (vwm->focused_context) { - vwm_xwindow_t *xwin; - vwm_window_t *vwin; - - case VWM_CONTEXT_SHELF: - if (desired_context == VWM_CONTEXT_SHELF) - break; - - /* desired == DESKTOP && focused == SHELF */ - - VWM_TRACE("unmapping shelf window \"%s\"", vwm->focused_shelf->xwindow->name); - vwm_win_unmap(vwm, vwm->focused_shelf); - XFlush(VWM_XDISPLAY(vwm)); /* for a more responsive feel */ - - /* map the focused desktop, from the top of the stack down */ - list_for_each_entry_prev(xwin, &vwm->xwindows, xwindows) { - if (!(vwin = xwin->managed)) - continue; - - if (vwin->desktop == vwm->focused_desktop && !vwin->shelved) { - VWM_TRACE("Mapping desktop window \"%s\"", xwin->name); - vwm_win_map(vwm, vwin); - } - } - - if (vwm->focused_desktop->focused_window) { - VWM_TRACE("Focusing \"%s\"", vwm->focused_desktop->focused_window->xwindow->name); - XSetInputFocus(VWM_XDISPLAY(vwm), vwm->focused_desktop->focused_window->xwindow->id, RevertToPointerRoot, CurrentTime); - } - - vwm->focused_context = VWM_CONTEXT_DESKTOP; - break; - - case VWM_CONTEXT_DESKTOP: - /* unmap everything, map the shelf */ - if (desired_context == VWM_CONTEXT_DESKTOP) - break; - - /* desired == SHELF && focused == DESKTOP */ - - /* there should be a focused shelf if the shelf contains any windows, we NOOP the switch if the shelf is empty. */ - if (vwm->focused_shelf) { - /* unmap everything on the current desktop */ - list_for_each_entry(xwin, &vwm->xwindows, xwindows) { - if (!(vwin = xwin->managed)) - continue; - - if (vwin->desktop == vwm->focused_desktop) { - VWM_TRACE("Unmapping desktop window \"%s\"", xwin->name); - vwm_win_unmap(vwm, vwin); - } - } - - XFlush(VWM_XDISPLAY(vwm)); /* for a more responsive feel */ - - VWM_TRACE("Mapping shelf window \"%s\"", vwm->focused_shelf->xwindow->name); - vwm_win_map(vwm, vwm->focused_shelf); - vwm_win_focus(vwm, vwm->focused_shelf); - - vwm->focused_context = VWM_CONTEXT_SHELF; - } - break; - - default: - VWM_BUG("unexpected focused context %x", vwm->focused_context); - break; + VWM_TRACE("MRU context: %p", context); + list_move(&context->contexts_mru, &vwm->contexts_mru); + + return context; +} + + +/* return next MRU context relative to the supplied context */ +vwm_context_t * vwm_context_next_mru(vwm_t *vwm, vwm_context_t *context, vwm_direction_t direction) +{ + list_head_t *next; + + switch (direction) { + case VWM_DIRECTION_FORWARD: + if (context->contexts_mru.next == &vwm->contexts_mru) { + next = context->contexts_mru.next->next; + } else { + next = context->contexts_mru.next; + } + break; + + case VWM_DIRECTION_REVERSE: + if (context->contexts_mru.prev == &vwm->contexts_mru) { + next = context->contexts_mru.prev->prev; + } else { + next = context->contexts_mru.prev; + } + break; + + default: + assert(0); + } + + return list_entry(next, vwm_context_t, contexts_mru); +} + + +/* return next context spatially relative to the supplied context, no wrap-around */ +vwm_context_t * vwm_context_next(vwm_t *vwm, vwm_context_t *context, vwm_direction_t direction) +{ + switch (direction) { + case VWM_DIRECTION_FORWARD: + if (context->contexts.next != &vwm->contexts) + context = list_entry(context->contexts.next, vwm_context_t, contexts); + break; + + case VWM_DIRECTION_REVERSE: + if (context->contexts.prev != &vwm->contexts) + context = list_entry(context->contexts.prev, vwm_context_t, contexts); + break; + } + + return context; +} + + +/* helper for automatically assigning context colors */ +static int next_context_color_idx(vwm_t *vwm) +{ + int counts[VWM_CONTEXT_COLOR_MAX] = {}; + vwm_context_t *context; + int color = 0; + + /* TODO: contexts should probably keep a window count, + * so this could skip empty contexts, then those + * would be automatically recycled. + */ + list_for_each_entry(context, &vwm->contexts, contexts) + counts[context->color]++; + + for (int i = 0; i < NELEMS(counts); i++) { + if (counts[i] < counts[color]) + color = i; + } + + return color; +} + + +/* create a context */ +/* if color = -1 one is automatically assigned, + * otherwise the supplied color is used. + */ +vwm_context_t * vwm_context_create(vwm_t *vwm, int color, vwm_desktop_t *desktop) +{ + vwm_context_t *context; + + context = calloc(1, sizeof(vwm_context_t)); + if (context == NULL) { + VWM_PERROR("Failed to allocate context"); + goto _fail; + } + + if (color < 0) + color = next_context_color_idx(vwm); + + assert(color < NELEMS(vwm->context_colors)); + + context->color = color; + + list_add_tail(&context->contexts, &vwm->contexts); + list_add_tail(&context->contexts_mru, &vwm->contexts_mru); + + if (!desktop) + desktop = vwm_desktop_create(vwm, context); + + context->focused_desktop = desktop; + + return context; + +_fail: + return NULL; +} + + +/* destroy a context */ +void vwm_context_destroy(vwm_t *vwm, vwm_context_t *context) +{ + /* silently refuse to destroy a context having windows (for now) */ + /* there's _always_ a focused window on a context having mapped windows */ + if (context->focused_desktop && context->focused_desktop->focused_window) + return; + + /* also silently refuse to destroy the last context (for now) */ + if (context->contexts.next == context->contexts.prev) + return; + + list_del(&context->contexts); + list_del(&context->contexts_mru); +} + + +/* find a context by color, creating one if needed */ +vwm_context_t * vwm_context_by_color(vwm_t *vwm, unsigned color) +{ + vwm_context_t *context; + + list_for_each_entry(context, &vwm->contexts, contexts) { + if (context->color == color) + return context; } - /* return if the context has been changed, the caller may need to branch differently if nothing happened */ - return (vwm->focused_context != entry_context); + return vwm_context_create(vwm, color, NULL); } diff --git a/src/context.h b/src/context.h index 1604bd3..5eebc43 100644 --- a/src/context.h +++ b/src/context.h @@ -1,14 +1,27 @@ #ifndef _CONTEXT_H #define _CONTEXT_H +#include "direction.h" +#include "list.h" + typedef struct _vwm_t vwm_t; +typedef struct _vwm_desktop_t vwm_desktop_t; -typedef enum _vwm_context_t { - VWM_CONTEXT_DESKTOP = 0, /* focus the desktop context */ - VWM_CONTEXT_SHELF, /* focus the shelf context */ - VWM_CONTEXT_OTHER /* focus the other context relative to the current one */ +/* contexts and desktops are *very* similar, they should likely share code, + * simply duplicating for now. + */ +typedef struct _vwm_context_t { + list_head_t contexts; /* global list of contexts in spatial created-in order */ + list_head_t contexts_mru; /* global list of contexts in MRU order */ + vwm_desktop_t *focused_desktop; /* the focused desktop on this context */ + unsigned color; /* color used for focused border on this context */ } vwm_context_t; -int vwm_context_focus(vwm_t *vwm, vwm_context_t desired_context); +vwm_context_t * vwm_context_mru(vwm_t *vwm, vwm_context_t *context); +vwm_context_t * vwm_context_create(vwm_t *vwm, int color, vwm_desktop_t *desktop); +void vwm_context_destroy(vwm_t *vwm, vwm_context_t *context); +vwm_context_t * vwm_context_next_mru(vwm_t *vwm, vwm_context_t *context, vwm_direction_t direction); +vwm_context_t * vwm_context_next(vwm_t *vwm, vwm_context_t *context, vwm_direction_t direction); +vwm_context_t * vwm_context_by_color(vwm_t *vwm, unsigned color); #endif diff --git a/src/desktop.c b/src/desktop.c index 8abc4d6..d88fa93 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -33,26 +33,27 @@ vwm_desktop_t * vwm_desktop_mru(vwm_t *vwm, vwm_desktop_t *desktop) { VWM_TRACE("MRU desktop: %p", desktop); list_move(&desktop->desktops_mru, &vwm->desktops_mru); + vwm_context_mru(vwm, desktop->context); return desktop; } /* focus a virtual desktop */ -/* this switches to the desktop context if necessary, maps and unmaps windows accordingly if necessary */ +/* this updates the focused context if necessary, maps and unmaps windows accordingly if necessary */ int vwm_desktop_focus(vwm_t *vwm, vwm_desktop_t *desktop) { XGrabServer(VWM_XDISPLAY(vwm)); XSync(VWM_XDISPLAY(vwm), False); - /* if the context switched and the focused desktop is the desired desktop there's nothing else to do */ - if ((vwm_context_focus(vwm, VWM_CONTEXT_DESKTOP) && vwm->focused_desktop != desktop) || vwm->focused_desktop != desktop) { + if (vwm->focused_desktop != desktop) { vwm_xwindow_t *xwin; vwm_window_t *vwin; /* unmap the windows on the currently focused desktop, map those on the newly focused one */ list_for_each_entry(xwin, &vwm->xwindows, xwindows) { - if (!(vwin = xwin->managed) || vwin->shelved) + vwin = xwin->managed; + if (!vwin) continue; if (vwin->desktop == vwm->focused_desktop) @@ -62,7 +63,8 @@ int vwm_desktop_focus(vwm_t *vwm, vwm_desktop_t *desktop) XFlush(VWM_XDISPLAY(vwm)); list_for_each_entry_prev(xwin, &vwm->xwindows, xwindows) { - if (!(vwin = xwin->managed) || vwin->shelved) + vwin = xwin->managed; + if (!vwin) continue; if (vwin->desktop == desktop) @@ -70,6 +72,7 @@ int vwm_desktop_focus(vwm_t *vwm, vwm_desktop_t *desktop) } vwm->focused_desktop = desktop; + desktop->context->focused_desktop = desktop; } /* directly focus the desktop's focused window if there is one, we don't use vwm_win_focus() intentionally XXX */ @@ -84,53 +87,67 @@ int vwm_desktop_focus(vwm_t *vwm, vwm_desktop_t *desktop) } -/* return next MRU desktop relative to the supplied desktop, wraps-around */ +/* return next MRU desktop within the same context relative to the supplied desktop, wraps-around */ vwm_desktop_t * vwm_desktop_next_mru(vwm_t *vwm, vwm_desktop_t *desktop, vwm_direction_t direction) { vwm_desktop_t *next = desktop; - /* this dance is necessary because the list head @ vwm->desktops_mru has no vwm_desktop_t container, - * and we're exploiting the circular nature of the doubly linked lists, so we need to take care to skip - * past the container-less head. - */ - switch (direction) { - case VWM_DIRECTION_FORWARD: - if (next->desktops_mru.next == &vwm->desktops_mru) { - next = list_entry(next->desktops_mru.next->next, vwm_desktop_t, desktops_mru); - } else { - next = list_entry(next->desktops_mru.next, vwm_desktop_t, desktops_mru); - } - break; + do { + /* this dance is necessary because the list head @ vwm->desktops_mru has no vwm_desktop_t container, + * and we're exploiting the circular nature of the doubly linked lists, so we need to take care to skip + * past the container-less head. + */ + switch (direction) { + case VWM_DIRECTION_FORWARD: + if (next->desktops_mru.next == &vwm->desktops_mru) { + next = list_entry(next->desktops_mru.next->next, vwm_desktop_t, desktops_mru); + } else { + next = list_entry(next->desktops_mru.next, vwm_desktop_t, desktops_mru); + } + break; - case VWM_DIRECTION_REVERSE: - if (next->desktops_mru.prev == &vwm->desktops_mru) { - next = list_entry(next->desktops_mru.prev->prev, vwm_desktop_t, desktops_mru); - } else { - next = list_entry(next->desktops_mru.prev, vwm_desktop_t, desktops_mru); - } - break; + case VWM_DIRECTION_REVERSE: + if (next->desktops_mru.prev == &vwm->desktops_mru) { + next = list_entry(next->desktops_mru.prev->prev, vwm_desktop_t, desktops_mru); + } else { + next = list_entry(next->desktops_mru.prev, vwm_desktop_t, desktops_mru); + } + break; - default: - assert(0); - } + default: + assert(0); + } + } while (next->context != desktop->context); return next; } -/* return next desktop spatially relative to the supplied desktop, no wrap-around */ +/* return next in-context desktop spatially relative to the supplied desktop, no wrap-around */ vwm_desktop_t * vwm_desktop_next(vwm_t *vwm, vwm_desktop_t *desktop, vwm_direction_t direction) { switch (direction) { - case VWM_DIRECTION_FORWARD: - if (desktop->desktops.next != &vwm->desktops) - return list_entry(desktop->desktops.next, vwm_desktop_t, desktops); + case VWM_DIRECTION_FORWARD: { + vwm_desktop_t *next = desktop; + + while (next->desktops.next != &vwm->desktops) { + next = list_entry(next->desktops.next, vwm_desktop_t, desktops); + if (next->context == desktop->context) + return next; + } break; + } - case VWM_DIRECTION_REVERSE: - if (desktop->desktops.prev != &vwm->desktops) - return list_entry(desktop->desktops.prev, vwm_desktop_t, desktops); + case VWM_DIRECTION_REVERSE: { + vwm_desktop_t *next = desktop; + + while (next->desktops.prev != &vwm->desktops) { + next = list_entry(next->desktops.prev, vwm_desktop_t, desktops); + if (next->context == desktop->context) + return next; + } break; + } default: assert(0); @@ -139,24 +156,39 @@ vwm_desktop_t * vwm_desktop_next(vwm_t *vwm, vwm_desktop_t *desktop, vwm_directi return desktop; } -/* create a virtual desktop */ -vwm_desktop_t * vwm_desktop_create(vwm_t *vwm) + +/* TODO: when "sending" windows to contexts, I currently always create a + * new desktop with this function. There should really be a "create if needed" + * variant which just returns an empty desktop in the target context, creating + * only if no empty one already exists, for "sending" purposes. The current + * approach tends to litter empty desktops unnecessarily. + */ +/* create a virtual desktop on the supplied context, + * if context is NULL a new one is created + * the desktop becomes the context's focused desktop if there isn't already one + */ +vwm_desktop_t * vwm_desktop_create(vwm_t *vwm, vwm_context_t *context) { vwm_desktop_t *desktop; desktop = calloc(1, sizeof(vwm_desktop_t)); if (desktop == NULL) { VWM_PERROR("Failed to allocate desktop"); - goto _fail; + return NULL; } + if (!context) + context = vwm_context_create(vwm, -1, desktop); + + if (!context->focused_desktop) + context->focused_desktop = desktop; + + desktop->context = context; + list_add_tail(&desktop->desktops, &vwm->desktops); list_add_tail(&desktop->desktops_mru, &vwm->desktops_mru); return desktop; - -_fail: - return NULL; } @@ -169,16 +201,31 @@ void vwm_desktop_destroy(vwm_t *vwm, vwm_desktop_t *desktop) if (desktop->focused_window || (desktop->desktops.next == desktop->desktops.prev)) return; - /* focus the MRU desktop that isn't this one if we're the focused desktop */ - if (desktop == vwm->focused_desktop) { + /* focus the desktop context's MRU desktop that isn't this one, + * if desktop is the context's focused desktop */ + if (desktop == desktop->context->focused_desktop) { vwm_desktop_t *next_desktop; list_for_each_entry(next_desktop, &vwm->desktops_mru, desktops_mru) { - if (next_desktop != desktop) { - vwm_desktop_focus(vwm, next_desktop); + if (next_desktop != desktop && next_desktop->context == desktop->context) { + desktop->context->focused_desktop = next_desktop; break; } } + + /* if *still* the context's focused desktop, the context is finished. + * find a desktop from the next MRU context to focus if this desktop + * was the vwm focused one before destroying the context + */ + if (desktop == desktop->context->focused_desktop) { + if (desktop == vwm->focused_desktop) + next_desktop = vwm_context_next_mru(vwm, desktop->context, VWM_DIRECTION_FORWARD)->focused_desktop; + + vwm_context_destroy(vwm, desktop->context); + } + + if (desktop == vwm->focused_desktop) + vwm_desktop_focus(vwm, next_desktop); } list_del(&desktop->desktops); diff --git a/src/desktop.h b/src/desktop.h index 1503407..597adb8 100644 --- a/src/desktop.h +++ b/src/desktop.h @@ -7,16 +7,18 @@ typedef struct _vwm_t vwm_t; typedef struct _vwm_window_t vwm_window_t; +typedef struct _vwm_context_t vwm_context_t; typedef struct _vwm_desktop_t { list_head_t desktops; /* global list of (virtual) desktops */ list_head_t desktops_mru; /* global list of (virtual) desktops in MRU order */ + vwm_context_t *context; /* context this desktop belongs to */ vwm_window_t *focused_window; /* the focused window on this virtual desktop */ } vwm_desktop_t; vwm_desktop_t * vwm_desktop_mru(vwm_t *vwm, vwm_desktop_t *desktop); int vwm_desktop_focus(vwm_t *vwm, vwm_desktop_t *desktop); -vwm_desktop_t * vwm_desktop_create(vwm_t *vwm); +vwm_desktop_t * vwm_desktop_create(vwm_t *vwm, vwm_context_t *context); void vwm_desktop_destroy(vwm_t *vwm, vwm_desktop_t *desktop); vwm_desktop_t * vwm_desktop_next_mru(vwm_t *vwm, vwm_desktop_t *desktop, vwm_direction_t direction); vwm_desktop_t * vwm_desktop_next(vwm_t *vwm, vwm_desktop_t *desktop, vwm_direction_t direction); diff --git a/src/key.c b/src/key.c index e1eee5d..da1a22b 100644 --- a/src/key.c +++ b/src/key.c @@ -31,6 +31,7 @@ static int key_is_grabbed; /* flag for tracking keyboard grab state */ static vwm_direction_t direction = VWM_DIRECTION_FORWARD; /* flag for reversing directional actions */ +static int send_it; /* flag for "sending" a migration operation without following it */ /* Poll the keyboard state to see if _any_ keys are pressed */ static int keys_pressed(vwm_t *vwm) @@ -75,8 +76,7 @@ void vwm_key_released(vwm_t *vwm, Window win, XKeyReleasedEvent *keyrelease) vwm_win_mru(vwm, vwin); /* make the focused desktop the most recently used */ - if (vwm->focused_context == VWM_CONTEXT_DESKTOP && vwm->focused_desktop) - vwm_desktop_mru(vwm, vwm->focused_desktop); + vwm_desktop_mru(vwm, vwm->focused_desktop); break; @@ -85,6 +85,11 @@ void vwm_key_released(vwm_t *vwm, Window win, XKeyReleasedEvent *keyrelease) direction = VWM_DIRECTION_FORWARD; break; + case XK_s: + VWM_TRACE("XK_s released with send_it=%i", send_it); + send_it = 0; + break; + default: VWM_TRACE("Unhandled keycode: %x", (unsigned int)sym); break; @@ -144,9 +149,32 @@ void vwm_key_pressed(vwm_t *vwm, Window win, XKeyPressedEvent *keypress) direction = VWM_DIRECTION_REVERSE; break; - case XK_grave: /* toggle shelf visibility */ - vwm_context_focus(vwm, VWM_CONTEXT_OTHER); + case XK_s: /* "send" migrational actions */ + VWM_TRACE("XK_s pressed with send_it=%i", send_it); + send_it = 1; + break; + + case XK_grave: { /* cycle focused desktop by context */ + vwm_context_t *next_context; + + do_grab = 1; /* update MRU desktop on commit (Mod1 release) */ + next_context = vwm_context_next_mru(vwm, vwm->focused_desktop->context, direction); + + if (send_it && (keypress->state & ShiftMask)) { /* "send" the focused window to the MRU context's MRU desktop */ + if (vwin) + vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, next_context->focused_desktop)); + } else if (send_it) { /* "send" the focused window to a new desktop created on the MRU context */ + if (vwin) + vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, vwm_desktop_create(vwm, next_context))); + } else if (keypress->state & ShiftMask) { + /* migrate the focused window with the desktop focus to the MRU context's MRU desktop */ + if (vwin) + vwm_win_migrate(vwm, vwin, next_context->focused_desktop); + } else { + vwm_desktop_focus(vwm, next_context->focused_desktop); + } break; + } case XK_Tab: /* cycle focused window */ do_grab = 1; /* update MRU window on commit (Mod1 release) */ @@ -154,22 +182,53 @@ void vwm_key_pressed(vwm_t *vwm, Window win, XKeyPressedEvent *keypress) /* focus the next window, note this doesn't affect MRU yet, that happens on Mod1 release */ if (vwin) { if (keypress->state & ShiftMask) { - vwm_win_focus_next(vwm, vwin, direction, VWM_FENCE_MASKED_VIOLATE); + /* TODO: in keeping with the Shift==migrate behavior, perhaps + * for Tab it should really do a in-desktop migration of sorts + * where the focused window swaps places with the next window? + */ + VWM_TRACE("in-desktop migrate not implemented yet"); } else { vwm_win_focus_next(vwm, vwin, direction, VWM_FENCE_RESPECT); } } break; + case XK_backslash: + do_grab = 1; + + if (vwin) { + if (keypress->state & ShiftMask) { + /* TODO: migrate window to another screen within this desktop, + * like VWM_FENCE_MASKED_VIOLATE would focus the next window on + * the next screen, but instead of focusing the next window on + * the next display, move the focused one to that next desktop. + * + * since screens are handled within vwm_win_focus_next() via + * the fence abstraction, but fences aren't exposed outside of + * their, it's non-trivial to implement here. I may want to + * break that out into a more public interface to make things + * more composable at the screen level. + */ + VWM_TRACE("migrate window to screen not implemented yet"); + } else { + vwm_win_focus_next(vwm, vwin, direction, VWM_FENCE_MASKED_VIOLATE); + } + } + break; + case XK_space: { /* cycle focused desktop utilizing MRU */ vwm_desktop_t *next_desktop; - next_desktop = vwm_desktop_next_mru(vwm, vwm->focused_desktop, direction); - do_grab = 1; /* update MRU desktop on commit (Mod1 release) */ + next_desktop = vwm_desktop_next_mru(vwm, vwm->focused_desktop, direction); - if (keypress->state & ShiftMask) { - /* migrate the focused window with the desktop focus to the most recently used desktop */ + if (send_it && (keypress->state & ShiftMask)) { /* "send" the focused window to the MRU desktop */ + if (vwin) + vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, next_desktop)); + } else if (send_it) { /* "send" the focused window to a new desktop in the current context, kind of an alias of send_it+XK_v */ + if (vwin) + vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, vwm_desktop_create(vwm, vwin->desktop->context))); + } else if (keypress->state & ShiftMask) { /* migrate the focused window with the desktop focus to the most recently used desktop */ if (vwin) vwm_win_migrate(vwm, vwin, next_desktop); } else { @@ -185,7 +244,7 @@ void vwm_key_pressed(vwm_t *vwm, Window win, XKeyPressedEvent *keypress) } else { /* kindly destroy the focused window */ vwm_xwin_message(vwm, vwin->xwindow, vwm->wm_protocols_atom, vwm->wm_delete_atom); } - } else if (vwm->focused_context == VWM_CONTEXT_DESKTOP) { + } else { /* destroy the focused desktop when destroy occurs without any windows */ vwm_desktop_destroy(vwm, vwm->focused_desktop); } @@ -201,80 +260,111 @@ void vwm_key_pressed(vwm_t *vwm, Window win, XKeyPressedEvent *keypress) case XK_v: /* instantiate (and focus) a new (potentially empty, unless migrating) virtual desktop */ do_grab = 1; /* update MRU desktop on commit (Mod1 release) */ - if (keypress->state & ShiftMask) { - if (vwin) { - /* migrate the focused window to a newly created virtual desktop, focusing the new desktop simultaneously */ - vwm_win_migrate(vwm, vwin, vwm_desktop_create(vwm)); - } + if (send_it) { /* "send" the focused window to a newly created virtual desktop, */ + if (vwin) + vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, vwm_desktop_create(vwm, vwin->desktop->context))); + } else if (keypress->state & ShiftMask) { /* migrate the focused window to a newly created virtual desktop, focusing the new desktop simultaneously */ + if (vwin) + vwm_win_migrate(vwm, vwin, vwm_desktop_create(vwm, vwin->desktop->context)); } else { - vwm_desktop_focus(vwm, vwm_desktop_create(vwm)); - vwm_desktop_mru(vwm, vwm->focused_desktop); + vwm_desktop_focus(vwm, vwm_desktop_create(vwm, vwm->focused_desktop->context)); } break; - case XK_h: /* previous virtual desktop, if we're in the shelf context this will simply switch to desktop context */ + case XK_c: /* instantiate (and focus) a new (potentialy empty, unless migrating) virtual desktop in a new context */ do_grab = 1; /* update MRU desktop on commit (Mod1 release) */ - if (keypress->state & ShiftMask) { - if (vwin) { - /* migrate the focused window with the desktop focus to the previous desktop */ - vwm_win_migrate(vwm, vwin, vwm_desktop_next(vwm, vwin->desktop, VWM_DIRECTION_REVERSE)); - } + if (send_it) { /* "send" the focused window to a newly created virtual desktop in a new context */ + if (vwin) + vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, vwm_desktop_create(vwm, NULL))); + } else if (keypress->state & ShiftMask) { /* migrate the focused window to a newly created virtual desktop in a new context, focusing the new desktop simultaneously */ + + if (vwin) + vwm_win_migrate(vwm, vwin, vwm_desktop_create(vwm, NULL)); } else { - if (vwm->focused_context == VWM_CONTEXT_SHELF) { - /* focus the focused desktop instead of the shelf */ - vwm_context_focus(vwm, VWM_CONTEXT_DESKTOP); - } else { - /* focus the previous desktop */ - vwm_desktop_focus(vwm, vwm_desktop_next(vwm, vwm->focused_desktop, VWM_DIRECTION_REVERSE)); - } + vwm_desktop_focus(vwm, vwm_desktop_create(vwm, NULL)); } break; - case XK_l: /* next virtual desktop, if we're in the shelf context this will simply switch to desktop context */ + case XK_0: + case XK_1: + case XK_2: + case XK_3: + case XK_4: + case XK_5: + case XK_6: + case XK_7: + case XK_8: + case XK_9: do_grab = 1; /* update MRU desktop on commit (Mod1 release) */ - if (keypress->state & ShiftMask) { - if (vwin) { - /* migrate the focused window with the desktop focus to the next desktop */ - vwm_win_migrate(vwm, vwin, vwm_desktop_next(vwm, vwin->desktop, VWM_DIRECTION_FORWARD)); - } + if (send_it && (keypress->state & ShiftMask)) { /* "send" the focused window to the specified context */ + if (vwin) + vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, vwm_context_by_color(vwm, sym - XK_0)->focused_desktop)); + } else if (send_it) { /* "send" the focused window to a new desktop created on the specified context */ + if (vwin) + vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, vwm_desktop_create(vwm, vwm_context_by_color(vwm, sym - XK_0)))); + } else if (keypress->state & ShiftMask) { /* migrate the focused window to the specified context */ + if (vwin) + vwm_win_migrate(vwm, vwin, vwm_context_by_color(vwm, sym - XK_0)->focused_desktop); } else { - if (vwm->focused_context == VWM_CONTEXT_SHELF) { - /* focus the focused desktop instead of the shelf */ - vwm_context_focus(vwm, VWM_CONTEXT_DESKTOP); - } else { - /* focus the next desktop */ - vwm_desktop_focus(vwm, vwm_desktop_next(vwm, vwm->focused_desktop, VWM_DIRECTION_FORWARD)); - } + vwm_desktop_focus(vwm, vwm_context_by_color(vwm, sym - XK_0)->focused_desktop); + } + break; + + case XK_h: /* previous virtual desktop spatially */ + do_grab = 1; /* update MRU desktop on commit (Mod1 release) */ + + if (send_it) { /* "send" the focused window to the previous desktop */ + if (vwin) + vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, vwm_desktop_next(vwm, vwin->desktop, VWM_DIRECTION_REVERSE))); + } else if (keypress->state & ShiftMask) { /* migrate the focused window with the desktop focus to the previous desktop */ + if (vwin) + vwm_win_migrate(vwm, vwin, vwm_desktop_next(vwm, vwin->desktop, VWM_DIRECTION_REVERSE)); + } else { /* focus the previous desktop */ + vwm_desktop_focus(vwm, vwm_desktop_next(vwm, vwm->focused_desktop, VWM_DIRECTION_REVERSE)); + } + break; + + case XK_l: /* next virtual desktop spatially */ + do_grab = 1; /* update MRU desktop on commit (Mod1 release) */ + + if (send_it) { /* "send" the focused window to the next desktop */ + if (vwin) + vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, vwm_desktop_next(vwm, vwin->desktop, VWM_DIRECTION_FORWARD))); + } else if (keypress->state & ShiftMask) { /* migrate the focused window with the desktop focus to the next desktop */ + if (vwin) + vwm_win_migrate(vwm, vwin, vwm_desktop_next(vwm, vwin->desktop, VWM_DIRECTION_FORWARD)); + } else { /* focus the next desktop */ + vwm_desktop_focus(vwm, vwm_desktop_next(vwm, vwm->focused_desktop, VWM_DIRECTION_FORWARD)); } break; - case XK_k: /* raise or shelve the focused window */ + case XK_k: /* raise or context-migrate the focused window up */ if (vwin) { - if (keypress->state & ShiftMask) { /* shelf the window and focus the shelf */ - if (vwm->focused_context != VWM_CONTEXT_SHELF) { - /* shelve the focused window while focusing the shelf */ - vwm_win_shelve(vwm, vwin); - vwm_context_focus(vwm, VWM_CONTEXT_SHELF); - } - } else { - do_grab = 1; + do_grab = 1; + + /* TODO: maybe bare send_it should create a new desktop in the next context, + * with Shift+send_it being the migrate-like send */ + if (send_it) { /* "send" the focused window to the next context */ + vwm_context_t *next_context = vwm_context_next(vwm, vwin->desktop->context, VWM_DIRECTION_FORWARD); + + vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, next_context->focused_desktop)); + } else if (keypress->state & ShiftMask) { /* migrate the window and focus the new context */ + vwm_context_t *next_context = vwm_context_next(vwm, vwin->desktop->context, VWM_DIRECTION_FORWARD); + vwm_win_migrate(vwm, vwin, next_context->focused_desktop); + } else { XRaiseWindow(VWM_XDISPLAY(vwm), vwin->xwindow->id); - if (repeat_cnt == 1) { - /* double: reraise & fullscreen */ + if (repeat_cnt == 1) { /* double: reraise & fullscreen */ vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_FULL); - } else if (repeat_cnt == 2) { - /* triple: reraise & fullscreen w/borders obscured by screen perimiter */ + } else if (repeat_cnt == 2) { /* triple: reraise & fullscreen w/borders obscured by screen perimiter */ vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_ALL); } else if (vwm->xinerama_screens_cnt > 1) { - if (repeat_cnt == 3) { - /* triple: reraise & fullscreen across all screens */ + if (repeat_cnt == 3) { /* triple: reraise & fullscreen across all screens */ vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_TOTAL, VWM_WIN_AUTOCONF_FULL); - } else if (repeat_cnt == 4) { - /* quadruple: reraise & fullscreen w/borders obscured by screen perimiter */ + } else if (repeat_cnt == 4) { /* quadruple: reraise & fullscreen w/borders obscured by screen perimiter */ vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_TOTAL, VWM_WIN_AUTOCONF_ALL); } } @@ -283,13 +373,20 @@ void vwm_key_pressed(vwm_t *vwm, Window win, XKeyPressedEvent *keypress) } break; - case XK_j: /* lower or unshelve the focused window */ + case XK_j: /* lower or context-migrate the focused window down */ if (vwin) { - if (keypress->state & ShiftMask) { /* unshelf the window to the focused desktop, and focus the desktop */ - if (vwm->focused_context == VWM_CONTEXT_SHELF) { - /* unshelve the focused window, focus the desktop it went to */ - vwm_win_migrate(vwm, vwin, vwm->focused_desktop); - } + do_grab = 1; + + /* TODO: maybe bare send_it should create a new desktop in the previous context, + * with Shift+send_it being the migrate-like send */ + if (send_it) { /* "send" the focused window to the previous context */ + vwm_context_t *prev_context = vwm_context_next(vwm, vwin->desktop->context, VWM_DIRECTION_REVERSE); + + vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, prev_context->focused_desktop)); + } else if (keypress->state & ShiftMask) { /* migrate the window and focus the new context */ + vwm_context_t *prev_context = vwm_context_next(vwm, vwin->desktop->context, VWM_DIRECTION_REVERSE); + + vwm_win_migrate(vwm, vwin, prev_context->focused_desktop); } else { if (vwin->autoconfigured == VWM_WIN_AUTOCONF_ALL) { vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_FULL); @@ -311,12 +408,6 @@ void vwm_key_pressed(vwm_t *vwm, Window win, XKeyPressedEvent *keypress) } break; - case XK_s: /* shelve focused window */ - if (vwin && !vwin->shelved) - vwm_win_shelve(vwm, vwin); - - break; - case XK_bracketleft: /* reconfigure the focused window to occupy the left or top half of the screen or left quarters on repeat */ if (vwin) { do_grab = 1; diff --git a/src/screen.c b/src/screen.c index 32a0fc7..4449fb5 100644 --- a/src/screen.c +++ b/src/screen.c @@ -169,23 +169,20 @@ _out: int vwm_screen_is_empty(vwm_t *vwm, const vwm_screen_t *scr, vwm_xwindow_t *ignore_xwin) { vwm_xwindow_t *xwin; - int is_empty = 1; list_for_each_entry(xwin, &vwm->xwindows, xwindows) { if (xwin == ignore_xwin || !xwin->client_mapped) continue; - if (!xwin->managed || (xwin->managed->desktop == vwm->focused_desktop && !xwin->managed->shelved)) { + if (!xwin->managed || xwin->managed->desktop == vwm->focused_desktop) { /* XXX: it may make more sense to see what %age of the screen is overlapped by windows, and consider it empty if < some % */ /* This is just seeing if any window is predominantly within the specified screen, the rationale being if you had a focusable * window on the screen you would have used the keyboard to make windows go there; this function is only used in determining * wether a new window should go where the pointer is or not. */ - if (vwm_screen_overlaps_xwin(vwm, scr, xwin) >= 0.05) { - is_empty = 0; - break; - } + if (vwm_screen_overlaps_xwin(vwm, scr, xwin) >= 0.05) + return 0; } } - return is_empty; + return 1; } diff --git a/src/vwm.c b/src/vwm.c index 0e47fb0..9bc60de 100644 --- a/src/vwm.c +++ b/src/vwm.c @@ -70,6 +70,8 @@ static vwm_t * vwm_startup(void) goto _err; } + INIT_LIST_HEAD(&vwm->contexts); + INIT_LIST_HEAD(&vwm->contexts_mru); INIT_LIST_HEAD(&vwm->desktops); INIT_LIST_HEAD(&vwm->desktops_mru); INIT_LIST_HEAD(&vwm->windows_mru); @@ -123,6 +125,11 @@ static vwm_t * vwm_startup(void) #define color(_sym, _str) \ XAllocNamedColor(VWM_XDISPLAY(vwm), VWM_XCMAP(vwm), _str, &vwm->colors._sym ## _color, &vwm->colors._sym ## _color); #include "colors.def" +#undef color + +#define color(_num, _str) \ + XAllocNamedColor(VWM_XDISPLAY(vwm), VWM_XCMAP(vwm), _str, &vwm->context_colors[_num], &vwm->context_colors[_num]); +#include "context_colors.def" #undef color XSelectInput(VWM_XDISPLAY(vwm), VWM_XROOT(vwm), @@ -133,8 +140,9 @@ static vwm_t * vwm_startup(void) XSetInputFocus(VWM_XDISPLAY(vwm), VWM_XROOT(vwm), RevertToPointerRoot, CurrentTime); - /* create initial virtual desktop */ - vwm_desktop_focus(vwm, vwm_desktop_create(vwm)); + /* create initial contexts and desktop */ + vwm_desktop_create(vwm, NULL); /* shelf */ + vwm_desktop_focus(vwm, vwm_desktop_create(vwm, NULL)); /* general */ vwm_desktop_mru(vwm, vwm->focused_desktop); /* manage all preexisting windows */ diff --git a/src/vwm.h b/src/vwm.h index a25e2b6..fec6a58 100644 --- a/src/vwm.h +++ b/src/vwm.h @@ -32,6 +32,15 @@ typedef struct _vwm_window_t vwm_window_t; typedef struct _vwm_desktop_t vwm_desktop_t; +/* this contortion is currently just to get VWM_CONTEXT_COLOR_MAX defined */ +typedef enum _vwm_context_color_t { +#define color(_num, _str) \ + VWM_CONTEXT_COLOR_ ## _num, +#include "context_colors.def" +#undef color + VWM_CONTEXT_COLOR_MAX +} vwm_context_color_t; + typedef struct _vwm_t { vwm_xserver_t *xserver; /* global xserver instance */ vwm_charts_t *charts; /* golbal charts instance */ @@ -45,24 +54,26 @@ typedef struct _vwm_t { int xinerama_screens_cnt; int done; /* global flag to cause vwm to quit */ + list_head_t contexts; /* global list of all contexts in spatial created-in order */ + list_head_t contexts_mru; /* global list of all contexts kept in MRU order */ list_head_t desktops; /* global list of all (virtual) desktops in spatial created-in order */ list_head_t desktops_mru; /* global list of all (virtual) desktops in MRU order */ list_head_t windows_mru; /* global list of all managed windows kept in MRU order */ list_head_t xwindows; /* global list of all xwindows kept in the X server stacking order */ + vwm_window_t *console; /* the console window */ vwm_window_t *focused_origin; /* the originating window in a grabbed operation/transaction */ - vwm_desktop_t *focused_desktop; /* currently focused (virtual) desktop */ - vwm_window_t *focused_shelf; /* currently focused shelved window */ - vwm_context_t focused_context; /* currently focused context */ + vwm_desktop_t *focused_desktop; /* currently focused desktop */ int priority; /* scheduling priority of the vwm process, launcher nices relative to this */ unsigned long fence_mask; /* global mask state for vwm_win_focus_next(... VWM_FENCE_MASKED_VIOLATE), - * if you use vwm on enough screens to overflow this, pics or it didn't happen. */ + * if you use vwm on enough screens to overflow this, pics or it didn't happen. */ struct colors { #define color(_sym, _str) \ XColor _sym ## _color; #include "colors.def" #undef color } colors; + XColor context_colors[VWM_CONTEXT_COLOR_MAX]; } vwm_t; #endif diff --git a/src/window.c b/src/window.c index d1441f3..5410ba6 100644 --- a/src/window.c +++ b/src/window.c @@ -80,20 +80,10 @@ vwm_window_t * vwm_win_lookup(vwm_t *vwm, Window win) } -/* return the currently focused window (considers current context...), may return NULL */ +/* return the currently focused window, may return NULL */ vwm_window_t * vwm_win_get_focused(vwm_t *vwm) { - switch (vwm->focused_context) { - case VWM_CONTEXT_SHELF: - return vwm->focused_shelf; - - case VWM_CONTEXT_DESKTOP: - return vwm->focused_desktop->focused_window; - - default: - VWM_BUG("Unsupported context"); - assert(0); - } + return vwm->focused_desktop->focused_window; } @@ -103,22 +93,15 @@ vwm_window_t * vwm_win_get_focused(vwm_t *vwm) void vwm_win_set_focused(vwm_t *vwm, vwm_window_t *vwin) { /* update the border color accordingly */ - if (vwin->shelved) { - /* set the border of the newly focused window to the shelved color */ - XSetWindowBorder(VWM_XDISPLAY(vwm), vwin->xwindow->id, vwin == vwm->console ? vwm->colors.shelved_console_border_color.pixel : vwm->colors.shelved_window_border_color.pixel); - /* fullscreen windows in the shelf when focused, since we don't intend to overlap there */ - vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_POINTER, VWM_WIN_AUTOCONF_FULL); /* XXX TODO: for now the shelf follows the pointer, it's simple. */ - } else { - if (vwin->desktop->focused_window) - /* set the border of the previously focused window on the same desktop to the unfocused color */ - XSetWindowBorder(VWM_XDISPLAY(vwm), vwin->desktop->focused_window->xwindow->id, vwm->colors.unfocused_window_border_color.pixel); + if (vwin->desktop->focused_window) + /* set the border of the previously focused window on the same desktop to the unfocused color */ + XSetWindowBorder(VWM_XDISPLAY(vwm), vwin->desktop->focused_window->xwindow->id, vwm->colors.unfocused_window_border_color.pixel); - /* set the border of the newly focused window to the focused color */ - XSetWindowBorder(VWM_XDISPLAY(vwm), vwin->xwindow->id, vwm->colors.focused_window_border_color.pixel); + /* set the border of the newly focused window to the focused color */ + XSetWindowBorder(VWM_XDISPLAY(vwm), vwin->xwindow->id, vwm->context_colors[vwin->desktop->context->color].pixel); - /* persist this on a per-desktop basis so it can be restored on desktop switches */ - vwin->desktop->focused_window = vwin; - } + /* persist this on a per-desktop basis so it can be restored on desktop switches */ + vwin->desktop->focused_window = vwin; } @@ -131,7 +114,7 @@ void vwm_win_autoconf_magic(vwm_t *vwm, vwm_window_t *vwin, const vwm_screen_t * if (!scr) scr = vwm_screen_find(vwm, VWM_SCREEN_REL_RECT, x, y, width, height); - if (!vwin->shelved && scr && + if (scr && width == scr->width && height == scr->height) { VWM_TRACE_WIN(vwin->xwindow->id, "auto-allscreened window"); @@ -329,21 +312,9 @@ _retry: VWM_TRACE("VWM_FENCE_MASKED_VIOLATE fence_mask now: 0x%lx\n", vwm->fence_mask); } - if (vwin->shelved) { - if (next != vwm->focused_shelf) { - if (vwm->focused_context == VWM_CONTEXT_SHELF) { - vwm_win_unmap(vwm, vwm->focused_shelf); - XFlush(VWM_XDISPLAY(vwm)); - vwm_win_map(vwm, next); - } - vwm->focused_shelf = next; - vwm_win_focus(vwm, next); - } - } else { - if (next != next->desktop->focused_window) { - vwm_win_focus(vwm, next); - XRaiseWindow(VWM_XDISPLAY(vwm), next->xwindow->id); - } + if (next != next->desktop->focused_window) { + vwm_win_focus(vwm, next); + XRaiseWindow(VWM_XDISPLAY(vwm), next->xwindow->id); } VWM_TRACE("vwin=%p xwin=%p name=\"%s\"", next, next->xwindow, next->xwindow->name); @@ -352,48 +323,62 @@ _retry: } -/* shelves a window, if the window is focused we focus the next one (if possible) */ +/* "shelves" a window, if the window is focused we focus the next one (if exists) */ +/* originally there was a special shelf context having different semantics of a fullscreen + * window at a time, this evolved into generic contexts containing virtual + * desktops, and now shelving is just the process of sending a window to the bottom/first + * context into a newly created desktop there, in an unattended fashion (like an unattended migrate, + * to an assumed bottom destination context created for this purpose at startup, and into its own + * desktop there). + */ void vwm_win_shelve(vwm_t *vwm, vwm_window_t *vwin) { - /* already shelved, NOOP */ - if (vwin->shelved) + vwm_context_t *shelf = list_entry(vwm->contexts.next, vwm_context_t, contexts); + vwm_desktop_t *desktop; + + /* already in the first, AKA "shelf" context, NOOP */ + if (&vwin->desktop->context->contexts == vwm->contexts.next) return; /* shelving focused window, focus the next window */ if (vwin == vwin->desktop->focused_window) vwm_win_mru(vwm, vwm_win_focus_next(vwm, vwin, VWM_DIRECTION_FORWARD, VWM_FENCE_RESPECT)); + /* vwin appears to be alone */ if (vwin == vwin->desktop->focused_window) - /* TODO: we can probably put this into vwm_win_focus_next() and have it always handled there... */ vwin->desktop->focused_window = NULL; - vwin->shelved = 1; - vwm_win_mru(vwm, vwin); + /* TODO: ^^^ there should probably be a helper for withdrawing a window + * from a desktop which handles the above focus next -> lone window + * nonsense, and hands back an orphan window to do whatever with. + */ + + /* shelved windows always get an empty desktop in the shelf context, + * look for an empty one and only create a new one if there is none + * to use. + */ + vwin->desktop = NULL; + list_for_each_entry(desktop, &vwm->desktops_mru, desktops_mru) { + if (desktop->context == shelf && !desktop->focused_window) { + vwin->desktop = desktop; + break; + } + } - /* newly shelved windows always become the focused shelf */ - vwm->focused_shelf = vwin; + if (!vwin->desktop) + vwin->desktop = vwm_desktop_create(vwm, shelf); + /* always leave the newly shelved window's desktop focused */ + vwin->desktop->context->focused_desktop = vwin->desktop; + vwm_win_set_focused(vwm, vwin); + vwm_win_mru(vwm, vwin); vwm_win_unmap(vwm, vwin); } -/* helper for (idempotently) unfocusing a window, deals with context switching etc... */ +/* helper for (idempotently) unfocusing a window */ void vwm_win_unfocus(vwm_t *vwm, vwm_window_t *vwin) { - /* if we're the focused shelved window, cycle the focus to the next shelved window if possible, if there's no more shelf, switch to the desktop */ - /* TODO: there's probably some icky behaviors for focused windows unmapping/destroying in unfocused contexts, we probably jump contexts suddenly. */ - if (vwin == vwm->focused_shelf) { - VWM_TRACE("unfocusing focused shelf"); - vwm_win_focus_next(vwm, vwin, VWM_DIRECTION_FORWARD, VWM_FENCE_IGNORE); - - if (vwin == vwm->focused_shelf) { - VWM_TRACE("shelf empty, leaving"); - /* no other shelved windows, exit the shelf context */ - vwm_context_focus(vwm, VWM_CONTEXT_DESKTOP); - vwm->focused_shelf = NULL; - } - } - /* if we're the focused window cycle the focus to the next window on the desktop if possible */ if (vwin->desktop->focused_window == vwin) { VWM_TRACE("unfocusing focused window"); @@ -401,7 +386,7 @@ void vwm_win_unfocus(vwm_t *vwm, vwm_window_t *vwin) } if (vwin->desktop->focused_window == vwin) { - VWM_TRACE("desktop empty"); + VWM_TRACE("unfocused last window on desktop"); vwin->desktop->focused_window = NULL; } } @@ -468,26 +453,23 @@ static void vwm_win_assimilate(vwm_t *vwm, vwm_window_t *vwin) /* TODO: this is a good place to hook in a window placement algo */ /* on client-requested mapping we place the window */ - if (!vwin->shelved) { - /* we place the window on the screen containing the the pointer only if that screen is empty, - * otherwise we place windows on the screen containing the currently focused window */ - scr = vwm_screen_find(vwm, VWM_SCREEN_REL_POINTER); - if (vwm_screen_is_empty(vwm, scr, vwin->xwindow)) { - /* focus the new window if it isn't already focused when it's going to an empty screen */ - VWM_TRACE("window \"%s\" is alone on screen \"%i\", focusing", vwin->xwindow->name, scr->screen_number); - vwm_win_focus(vwm, vwin); - } else { - scr = vwm_screen_find(vwm, VWM_SCREEN_REL_XWIN, vwm->focused_desktop->focused_window->xwindow); - } - - changes.x = scr->x_org; - changes.y = scr->y_org; - } else if (vwm->focused_context == VWM_CONTEXT_SHELF) { - scr = vwm_screen_find(vwm, VWM_SCREEN_REL_XWIN, vwm->focused_shelf->xwindow); - changes.x = scr->x_org; - changes.y = scr->y_org; + /* we place the window on the screen containing the the pointer only if that screen is empty, + * otherwise we place windows on the screen containing the currently focused window */ + scr = vwm_screen_find(vwm, VWM_SCREEN_REL_POINTER); + if (vwm_screen_is_empty(vwm, scr, vwin->xwindow)) { + /* focus the new window if it isn't already focused when it's going to an empty screen */ + VWM_TRACE("window \"%s\" is alone on screen \"%i\", focusing", vwin->xwindow->name, scr->screen_number); + vwm_win_focus(vwm, vwin); + } else { + /* FIXME TODO: there's some situation where we get here but focused_desktop->focused_window == NULL, + * which shouldn't be possible; for there to be a non-empty screen, the focused_desktop must have a focused_window. + */ + scr = vwm_screen_find(vwm, VWM_SCREEN_REL_XWIN, vwm->focused_desktop->focused_window->xwindow); } + changes.x = scr->x_org; + changes.y = scr->y_org; + /* XXX TODO: does this belong here? */ XGetWMNormalHints(VWM_XDISPLAY(vwm), xwin->id, vwin->hints, &vwin->hints_supplied); XGetWindowAttributes(VWM_XDISPLAY(vwm), xwin->id, &attrs); @@ -541,7 +523,6 @@ vwm_window_t * vwm_win_manage_xwin(vwm_t *vwm, vwm_xwindow_t *xwin) vwin->desktop = vwm->focused_desktop; vwin->autoconfigured = VWM_WIN_AUTOCONF_NONE; - vwin->shelved = (vwm->focused_context == VWM_CONTEXT_SHELF); /* if we're in the shelf when the window is created, the window is shelved */ vwin->client = xwin->attrs; /* remember whatever the current attributes are */ VWM_TRACE("hints: flags=%lx x=%i y=%i w=%i h=%i minw=%i minh=%i maxw=%i maxh=%i winc=%i hinc=%i basew=%i baseh=%i grav=%x", @@ -572,9 +553,9 @@ vwm_window_t * vwm_win_manage_xwin(vwm_t *vwm, vwm_xwindow_t *xwin) /* always raise newly managed windows so we know about them. */ XRaiseWindow(VWM_XDISPLAY(vwm), xwin->id); - /* if the desktop has no focused window yet, automatically focus the newly managed one, provided we're on the desktop context */ - if (!vwm->focused_desktop->focused_window && vwm->focused_context == VWM_CONTEXT_DESKTOP) { - VWM_TRACE("Mapped new window \"%s\" is alone on desktop, focusing", xwin->name); + /* if the desktop has no focused window yet, automatically focus the newly managed one */ + if (!vwm->focused_desktop->focused_window) { + VWM_TRACE("Mapped new window \"%s\" is alone on desktop \"%s\", focusing", xwin->name, vwm->focused_desktop->name); vwm_win_focus(vwm, vwin); } @@ -595,12 +576,34 @@ _fail: void vwm_win_migrate(vwm_t *vwm, vwm_window_t *vwin, vwm_desktop_t *desktop) { vwm_win_unfocus(vwm, vwin); /* go through the motions of unfocusing the window if it is focused */ - vwin->shelved = 0; /* ensure not shelved */ - vwin->desktop = desktop; /* assign the new desktop */ + vwin->desktop = desktop; /* assign the new desktop */ vwm_desktop_focus(vwm, desktop); /* currently we always focus the new desktop in a migrate */ vwm_win_focus(vwm, vwin); /* focus the window so borders get updated */ vwm_win_mru(vwm, vwin); /* TODO: is this right? shouldn't the Mod1 release be what's responsible for this? I migrate so infrequently it probably doesn't matter */ - XRaiseWindow(VWM_XDISPLAY(vwm), vwin->xwindow->id); /* ensure the window is raised */ + XRaiseWindow(VWM_XDISPLAY(vwm), vwin->xwindow->id); /* ensure the window is @ top of stack */ +} + + +/* "send" a window to another desktop, no desktop/context switching occurs. */ +void vwm_win_send(vwm_t *vwm, vwm_window_t *vwin, vwm_desktop_t *desktop) +{ + if (desktop == vwin->desktop) + return; + + vwm_win_unfocus(vwm, vwin); + vwm_win_unmap(vwm, vwin); + vwin->desktop = desktop; + + /* XXX: only focus the destination desktop when not the focused context, as + * it creates an awkward disconnect for the focused context's focused desktop + * to become updated to something else while looking at it without actually + * realizing that focus change like a migrate does. + */ + if (vwm->focused_desktop->context != desktop->context) + desktop->context->focused_desktop = desktop; + + vwm_win_set_focused(vwm, vwin); + XRaiseWindow(VWM_XDISPLAY(vwm), vwin->xwindow->id); /* ensure the window is @ top of stack */ } diff --git a/src/window.h b/src/window.h index eef639e..daddf14 100644 --- a/src/window.h +++ b/src/window.h @@ -35,7 +35,6 @@ typedef struct _vwm_window_t { unsigned int autoconfigured:3; /* autoconfigured window states (none/quarter/half/full/all) */ unsigned int mapping:1; /* is the window being mapped? (by vwm) */ unsigned int unmapping:1; /* is the window being unmapped? (by vwm) */ - unsigned int shelved:1; /* is the window shelved? */ } vwm_window_t; @@ -78,6 +77,7 @@ void vwm_win_unfocus(vwm_t *vwm, vwm_window_t *vwin); vwm_xwindow_t * vwm_win_unmanage(vwm_t *vwm, vwm_window_t *vwin); vwm_window_t * vwm_win_manage_xwin(vwm_t *vwm, vwm_xwindow_t *xwin); void vwm_win_migrate(vwm_t *vwm, vwm_window_t *vwin, vwm_desktop_t *desktop); +void vwm_win_send(vwm_t *vwm, vwm_window_t *vwin, vwm_desktop_t *desktop); #endif diff --git a/src/xwindow.c b/src/xwindow.c index 86e8e9d..1deaf33 100644 --- a/src/xwindow.c +++ b/src/xwindow.c @@ -62,31 +62,15 @@ vwm_xwindow_t * vwm_xwin_lookup(vwm_t *vwm, Window win) } -/* determine if a window is mapped (vwm-mapped) according to the current context */ +/* determine if a window is mapped (vwm-mapped) according to the focused context */ int vwm_xwin_is_mapped(vwm_t *vwm, vwm_xwindow_t *xwin) { vwm_window_t *vwin = xwin->managed; - int ret = 0; if (!xwin->client_mapped || !vwin) return xwin->client_mapped; - switch (vwm->focused_context) { - case VWM_CONTEXT_SHELF: - if (vwm->focused_shelf == vwin) - ret = 1; - break; - - case VWM_CONTEXT_DESKTOP: - if (vwm->focused_desktop == vwin->desktop && !vwin->shelved) - ret = 1; - break; - - default: - VWM_BUG("Unsupported context"); - } - - return ret; + return (vwm->focused_desktop == vwin->desktop); } -- cgit v1.2.3