/*- * Copyright (c) 2001 Jordan DeLong * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "wm.h" /* number of steps to use for the sliding of windows */ #define SLIDESTEPS 5 /* XXX: make an option? */ /* add a client to a workspace */ void workspace_add_client(workspace_t *workspace, client_t *client) { client->workspace = workspace; if (!client->flags.nofocus) focus_list_add(client); } /* remove a client from the workspace it is on */ void workspace_rm_client(client_t *client) { if (!client->flags.nofocus) { focus_unfocus(client); focus_list_rm(client); } client->workspace = NULL; } /* * add a client to the appropriate workspace based on it's position. * we handle any removal from a workspace that the client is on here: to * allow us to not do anything if it's on the workspace that this would move * it to. this function makes calls to desktop_add_client and * desktop_rm_cilent when a client's old workspace is on a different desktop * than the new one. */ void workspace_add_bypos(desktop_t *desktop, client_t *client) { workspace_t *newspace; dgroup_t *dgroup; int newdesk = 0; int vx, vy; /* * if the client is even partially visible on this workspace * it must be in this workspace; otherwise it's nonimportant which * workspace it is in if it crosses over lines: we favor up and left; * if it becomes visible on a current_space it isn't part of * workspace_viewport_move will take care of switching it as neccessary. */ dgroup = client->dgroup; if (client->x > -FULLWIDTH(client) && client->y > -FULLHEIGHT(client) && client->x < client->screen->width && client->y < client->screen->height) { newspace = desktop->current_space; } else { /* get the offsets in the workspace grid */ vx = (client->x + desktop->viewx * client->screen->width) / client->screen->width; vy = (client->y + desktop->viewy * client->screen->height) / client->screen->height; /* clip them to the desktop's size */ if (vx < 0) vx = 0; else if (vx > desktop->width) vx = desktop->width - 1; if (vy < 0) vy = 0; else if (vy > desktop->height) vy = desktop->height - 1; /* get new workspace */ newspace = desktop->workspaces[vx + (vy * desktop->width)]; } /* make sure the client isn't already on this space */ if (client->workspace == newspace) return; /* * remove from the old, add to the new. if the client * used to be on an old workspace it might have also been * on a differnt desktop, so we handle that too. */ if (client->workspace) { if (client->workspace->desktop != desktop) { desktop_rm_client(client); newdesk = 1; } workspace_rm_client(client); } else newdesk = 1; workspace_add_client(newspace, client); if (newdesk) desktop_add_client(client); } /* * modify the effective viewport for a large workspace, xmove and ymove * are relative (i.e., 1 or -1 or 0, etc) and in terms of screens. * * we do virtual desktops by moving windows in the opposite direction of * the viewport. to make stuff make more sense to the user we maintain * focus lists for each viewport; which means that here we need to handle * clients that'll end up still visible (i.e. are half between viewports) * by putting them into the new focuslist. */ int workspace_viewport_move(screen_t *screen, desktop_t *desktop, int xmove, int ymove) { XEvent ev; client_t *focusthis = NULL; client_t *client; workspace_t *workspace; int tmpx, tmpy; int i, modx, mody; int moving; /* get new offsets */ tmpx = desktop->viewx + xmove; if (tmpx >= desktop->width || tmpx < 0) return 0; tmpy = desktop->viewy + ymove; if (tmpy >= desktop->height || tmpy < 0) return 0; /* don't move to the current workspace */ if (tmpx == desktop->viewx && tmpy == desktop->viewy) return 0; /* get the new workspace */ workspace = desktop->workspaces[tmpx + (tmpy * desktop->width)]; /* slide the windows to their new position */ if (options.workspace_slide && desktop == screen->desktop) { moving = 0; modx = (xmove * screen->width) / SLIDESTEPS; mody = (ymove * screen->height) / SLIDESTEPS; for (i = 0; i < SLIDESTEPS; i++) { LIST_FOREACH(client, &client_list, c_list) { if (client->state == NormalState && !client->flags.internal && client->workspace->desktop == desktop && !client->flags.sticky) { XMoveWindow(display, client->frame, client->x - (modx * i), client->y - (mody * i)); moving = 1; } } /* if nothing is moving we just move on */ if (!moving) break; /* * delay for this frame of the sliding. it also * necessary to handle any exposures we may be * getting to keep our visuals looking good. */ XSync(display, 0); while (XCheckMaskEvent(display, ExposureMask, &ev)) event_handle(&ev); usleep(5); } } /* keep a client by putting it into the new workspace's focuslist */ #define KEEPCLIENT(c) do { \ if (desktop->current_space->focused == (c)) { \ if (focusthis == NULL) \ focusthis = (c); \ } \ workspace_rm_client((c)); \ workspace_add_client(workspace, (c)); \ } while (0) /* now switch to the new workspace */ LIST_FOREACH(client, &client_list, c_list) if (client->state == NormalState && !client->flags.internal && client->workspace->desktop == desktop) { if (!client->flags.sticky) { client->x -= (xmove * screen->width); client->y -= (ymove * screen->height); client_sizeframe(client); /* * clients recieve a synthetic config, because they have * moved, even though the illusion is that the 'viewport' * is what moved. */ action_send_config(client); /* * if the client is visible but thinks it's on a diff * wspace it must be kept. */ if (client->workspace != workspace && client->x > -FULLWIDTH(client) && client->y > -FULLHEIGHT(client) && client->x < screen->width && client->y < screen->height) KEEPCLIENT(client); } else KEEPCLIENT(client); } #undef KEEPCLIENT /* set the new offsets */ if (tmpx == desktop->viewx && tmpy == desktop->viewy) return 0; desktop->viewx = tmpx; desktop->viewy = tmpy; /* set the new current_space */ desktop->current_space = workspace; /* set the focus */ if (focusthis) focus_client(focusthis); else if (workspace->focused) focus_client(workspace->focused); else focus_none(screen); /* let plugins know */ plugin_workspace_change(screen, desktop); return 1; }