diff options
Diffstat (limited to 'src/context.c')
-rw-r--r-- | src/context.c | 222 |
1 files changed, 148 insertions, 74 deletions
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); } |