diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/colors.def | 3 | ||||
-rw-r--r-- | src/context.c | 222 | ||||
-rw-r--r-- | src/context.h | 23 | ||||
-rw-r--r-- | src/desktop.c | 137 | ||||
-rw-r--r-- | src/desktop.h | 4 | ||||
-rw-r--r-- | src/key.c | 235 | ||||
-rw-r--r-- | src/screen.c | 11 | ||||
-rw-r--r-- | src/vwm.c | 12 | ||||
-rw-r--r-- | src/vwm.h | 19 | ||||
-rw-r--r-- | src/window.c | 185 | ||||
-rw-r--r-- | src/window.h | 2 | ||||
-rw-r--r-- | src/xwindow.c | 20 |
12 files changed, 550 insertions, 323 deletions
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 <http://www.gnu.org/licenses/>. */ - /* desktop/shelf context handling */ + /* contexts (this is derived from desktops) */ +#include <assert.h> #include <X11/Xlib.h> +#include <stdlib.h> +#include <string.h> +#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); @@ -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; } @@ -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); @@ -125,6 +127,11 @@ static vwm_t * vwm_startup(void) #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), FocusChangeMask | PropertyChangeMask | SubstructureNotifyMask | SubstructureRedirectMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask); XGrabKey(VWM_XDISPLAY(vwm), AnyKey, WM_GRAB_MODIFIER, VWM_XROOT(vwm), False, GrabModeAsync, GrabModeAsync); @@ -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 */ @@ -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); } |