/*- * 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); }