/*-
* 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;
}
syntax highlighted by Code2HTML, v. 0.9.1