/*-
* 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"
/*
* window placement X event loop, returns 1 if the
* user moved the mouse, and 0 if not.
*/
static __inline int placeloop(client_t *client, long eventmask,
point_t *saveptr, point_t *win, point_t *oldwin) {
struct timeval tv;
fd_set fdset;
XEvent event;
Window dumwin;
int dumint, xfd;
int mousing;
/*
* obtain the pointer position; set the win and oldwin
* points to the top left corner of the client window.
*/
XQueryPointer(display, client->screen->root, &dumwin, &dumwin,
&saveptr->x, &saveptr->y, &dumint, &dumint, &dumint);
oldwin->x = win->x = client->x;
oldwin->y = win->y = client->y;
xfd = ConnectionNumber(display);
mousing = 0;
/*
* move the mouse cursor to the upper left corner of
* the window and draw the first winbox
*/
XWarpPointer(display, None, client->screen->root,
0, 0, 1, 1, win->x, win->y);
draw_winbox(client->screen, client, win->x, win->y,
client->width, client->height);
/* keyboard events can come from our keyboard grab */
eventmask |= KeyPressMask | KeyReleaseMask;
/* window placement event loop */
while (1) {
while (XCheckMaskEvent(display, eventmask, &event))
switch (event.type) {
case MotionNotify:
mousing = 1;
win->x = event.xmotion.x_root;
win->y = event.xmotion.y_root;
draw_winbox(client->screen, client, oldwin->x,
oldwin->y, client->width, client->height);
draw_winbox(client->screen, client, win->x,
win->y, client->width, client->height);
oldwin->x = win->x;
oldwin->y = win->y;
break;
case KeyPress:
event.xkey.window = client->window;
event.xkey.subwindow = client->window;
event.xkey.x = event.xkey.y = 0;
XSendEvent(display, client->window, 0,
KeyPressMask, &event);
return mousing;
case ButtonRelease:
return 1;
}
/*
* wait for X events for a specified amount
* of time; if none are recieved, we're done.
*/
tv.tv_usec = options.interact_timeout;
tv.tv_sec = 0;
FD_ZERO(&fdset);
FD_SET(xfd, &fdset);
if (select(xfd + 1, &fdset, NULL, NULL,
tv.tv_usec ? &tv : NULL) == 0)
return mousing;
}
}
/*
* interact with the user for placement of a window; works similarly
* to the window movement routine with the added provisions that the
* operation can time out, keyboard presses terminate the operation,
* and that it is always a nonopaque move.
*/
static void placement_interact(client_t *client) {
point_t saveptr, win, oldwin;
long eventmask;
int mousing;
/*
* grab the X pointer, keyboard and server for placement
* operation.
*/
eventmask = PointerMotionMask | ButtonMotionMask
| ButtonReleaseMask;
if (XGrabPointer(display, client->screen->root, 0, eventmask,
GrabModeAsync, GrabModeAsync, client->screen->root,
cursor_place, CurrentTime) != GrabSuccess)
return;
if (XGrabKeyboard(display, client->screen->root, 0,GrabModeAsync,
GrabModeAsync, CurrentTime) != GrabSuccess)
goto ungrab;
XGrabServer(display);
/* call the window placement event loop */
mousing = placeloop(client, eventmask, &saveptr, &win, &oldwin);
/*
* clean up window boxes, send a configure event
* if the client window was moved, and move the
* pointer.
*/
draw_winbox(client->screen, client, oldwin.x, oldwin.y,
client->width, client->height);
if (win.x != client->x || win.y != client->y) {
client->x = win.x;
client->y = win.y;
action_send_config(client);
}
if (mousing)
XWarpPointer(display, None, client->screen->root, 0, 0, 1, 1,
client->x + (FULLWIDTH(client) / 2),
client->y + (FULLHEIGHT(client) / 2));
else
XWarpPointer(display, None, client->screen->root,
0, 0, 1, 1, saveptr.x, saveptr.y);
/* release all grabs */
XUngrabServer(display);
XUngrabKeyboard(display, CurrentTime);
ungrab:
XUngrabPointer(display, CurrentTime);
}
/* random placement */
static void placement_random(client_t *client) {
client->x = random() % (client->screen->width - FULLWIDTH(client));
client->y = random() % (client->screen->height - FULLHEIGHT(client));
xinerama_correctloc(client);
}
/* place the client where the mouse cursor is */
static void placement_pointer(client_t *client) {
Window dumwin;
int dumint, mask;
XQueryPointer(display, client->screen->root, &dumwin,
&dumwin, &client->x, &client->y, &dumint, &dumint, &mask);
}
/*
* calculate the total area of of other managed windows that
* client would cover if it were placed at rclient.
*/
static __inline int area_covered(client_t *client, rect_t *rect,
rect_t *rclient) {
rect_t rtest;
client_t *test;
int area = 0;
LIST_FOREACH(test, &client_list, c_list) {
if (test == client)
continue;
if (test->screen != client->screen)
continue;
if (test->state != NormalState)
continue;
if (!test->workspace)
continue;
if (test->workspace->desktop != client->screen->desktop)
continue;
if (test->x + FULLWIDTH(test) < rect->x1)
continue;
if (test->y + FULLHEIGHT(test) < rect->y1)
continue;
if (test->x >= rect->x2 || test->y >= rect->y2)
continue;
/* this test client is valid, add it's intersection */
rtest.x1 = test->x;
rtest.y1 = test->y;
rtest.x2 = test->x + FULLWIDTH(test);
rtest.y2 = test->y + FULLHEIGHT(test);
area += rect_intersection(&rtest, rclient);
}
return area;
}
/*
* smart placement; the basic concept is that the
* best place to put the window is the place
* that obscures the least amount of other windows.
*/
static void placement_smart(client_t *client) {
rect_t rect, rclient;
point_t mid, low;
int width, height;
int sum, lowsum;
int xwrap, ywrap;
int x, y;
int mon;
/* set up some locals */
width = FULLWIDTH(client);
height = FULLHEIGHT(client);
low.x = low.y = 0;
lowsum = INT_MAX;
/*
* try each xinerama screen in turn; if we are compiled
* without xinerama support or if the display doesn't
* have xinerama this just gives us the dimensions of
* the screen.
*/
mon = 0;
while (xinerama_scrdims(client->screen, &mon, &rect)) {
/*
* only can do this for things that are smaller
* than the size of the screen.
*/
if (width >= RECTWIDTH(&rect) || height >= RECTHEIGHT(&rect))
continue;
mid.x = rect.x1 + (RECTWIDTH(&rect) - width) / 2;
mid.y = rect.y1 + (RECTHEIGHT(&rect) - height) / 2;
/*
* calculate the total area of windows being covered
* at the potential positions in this screen; if it's
* lower than the currently lowest value, it becomes
* the new lowest sum.
*/
for (y = mid.y, ywrap = 0; !ywrap || y < mid.y; y += PLACETEST_YINC) {
for (x = mid.x, xwrap = 0; !xwrap || x < mid.x; x += PLACETEST_XINC) {
rclient.x1 = x;
rclient.y1 = y;
rclient.x2 = x + width;
rclient.y2 = y + width;
sum = area_covered(client, &rect, &rclient);
if (sum < lowsum) {
lowsum = sum;
low.x = x;
low.y = y;
}
if (!xwrap && x >= rect.x2 - width - PLACETEST_XINC) {
x = rect.x1 - PLACETEST_XINC;
xwrap = 1;
}
}
if (!ywrap && y >= rect.y2 - height - PLACETEST_YINC) {
y = rect.y1 - PLACETEST_YINC;
ywrap = 1;
}
}
}
/*
* set the client position to the cordinates at which
* we found it would cover the least amount of other
* windows.
*/
client->x = low.x;
client->y = low.y;
}
/* exported client placement function */
void placement_place(client_t *client) {
int interact;
if (!options.place_transients && client->flags.transient) {
if (options.xinerama_correctloc)
xinerama_correctloc(client);
interact = 0;
} else if (!options.place_nonzeros && (client->x || client->y)) {
if (options.xinerama_correctloc)
xinerama_correctloc(client);
interact = 1;
} else {
interact = 1;
switch (options.placement) {
case PLACEMENT_NONE: break;
case PLACEMENT_RANDOM: placement_random(client); break;
case PLACEMENT_POINTER: placement_pointer(client); break;
case PLACEMENT_SMART: placement_smart(client); break;
}
}
/* allow plugins to draw an animation */
plugin_anim_birth(client);
if (interact && options.place_interactive)
placement_interact(client);
}
syntax highlighted by Code2HTML, v. 0.9.1