/* WindowLab - an X11 window manager * Copyright (c) 2001-2005 Nick Gravgaard * me at nickgravgaard.com * http://nickgravgaard.com/windowlab/ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "windowlab.h" static void limit_size(Client *, Rect *); static int get_incsize(Client *, unsigned int *, unsigned int *, Rect *, int); void raise_lower(Client *c) { if (c != NULL) { if (c == topmost_client) { lower_win(c); topmost_client = NULL; // lazy but amiwm does similar } else { raise_win(c); topmost_client = c; } } } /* Increment ignore_unmap here and decrement it in handle_unmap_event in * events.c */ void hide(Client *c) { if (c != NULL) { if (!c->hidden) { c->ignore_unmap++; c->hidden = 1; if (c == topmost_client) { topmost_client = NULL; } XUnmapWindow(dsply, c->frame); XUnmapWindow(dsply, c->window); set_wm_state(c, IconicState); check_focus(get_prev_focused()); } } } void unhide(Client *c) { if (c != NULL) { if (c->hidden) { c->hidden = 0; topmost_client = c; XMapWindow(dsply, c->window); XMapRaised(dsply, c->frame); set_wm_state(c, NormalState); } } } void toggle_fullscreen(Client *c) { int xoffset, yoffset, maxwinwidth, maxwinheight; if (c != NULL && !c->trans) { if (c == fullscreen_client) // reset to original size { c->x = fs_prevdims.x; c->y = fs_prevdims.y; c->width = fs_prevdims.width; c->height = fs_prevdims.height; XMoveResizeWindow(dsply, c->frame, c->x, c->y - BARHEIGHT(), c->width, c->height + BARHEIGHT()); XMoveResizeWindow(dsply, c->window, 0, BARHEIGHT(), c->width, c->height); send_config(c); fullscreen_client = NULL; showing_taskbar = 1; } else // make fullscreen { xoffset = yoffset = 0; maxwinwidth = DisplayWidth(dsply, screen); maxwinheight = DisplayHeight(dsply, screen) - BARHEIGHT(); if (fullscreen_client != NULL) // reset existing fullscreen window to original size { fullscreen_client->x = fs_prevdims.x; fullscreen_client->y = fs_prevdims.y; fullscreen_client->width = fs_prevdims.width; fullscreen_client->height = fs_prevdims.height; XMoveResizeWindow(dsply, fullscreen_client->frame, fullscreen_client->x, fullscreen_client->y - BARHEIGHT(), fullscreen_client->width, fullscreen_client->height + BARHEIGHT()); XMoveResizeWindow(dsply, fullscreen_client->window, 0, BARHEIGHT(), fullscreen_client->width, fullscreen_client->height); send_config(fullscreen_client); } fs_prevdims.x = c->x; fs_prevdims.y = c->y; fs_prevdims.width = c->width; fs_prevdims.height = c->height; c->x = 0 - BORDERWIDTH(c); c->y = BARHEIGHT() - BORDERWIDTH(c); c->width = maxwinwidth; c->height = maxwinheight; if (c->size->flags & PMaxSize || c->size->flags & PResizeInc) { if (c->size->flags & PResizeInc) { Rect maxwinsize; maxwinsize.x = xoffset; maxwinsize.width = maxwinwidth; maxwinsize.y = yoffset; maxwinsize.height = maxwinheight; get_incsize(c, (unsigned int *)&c->size->max_width, (unsigned int *)&c->size->max_height, &maxwinsize, PIXELS); } if (c->size->max_width < maxwinwidth) { c->width = c->size->max_width; xoffset = (maxwinwidth - c->width) / 2; } if (c->size->max_height < maxwinheight) { c->height = c->size->max_height; yoffset = (maxwinheight - c->height) / 2; } } XMoveResizeWindow(dsply, c->frame, c->x, c->y, maxwinwidth, maxwinheight); XMoveResizeWindow(dsply, c->window, xoffset, yoffset, c->width, c->height); send_config(c); fullscreen_client = c; showing_taskbar = in_taskbar; } redraw_taskbar(); } } /* The name of this function is a bit misleading: if the client * doesn't listen to WM_DELETE then we just terminate it with extreme * prejudice. */ void send_wm_delete(Client *c) { int i, n, found = 0; Atom *protocols; if (XGetWMProtocols(dsply, c->window, &protocols, &n)) { for (i = 0; i < n; i++) { if (protocols[i] == wm_delete) { found++; } } XFree(protocols); } if (found) { send_xmessage(c->window, wm_protos, wm_delete); } else { XKillClient(dsply, c->window); } } void move(Client *c) { XEvent ev; int old_cx = c->x; int old_cy = c->y; int mousex, mousey, dw, dh; Client *exposed_c; Rect bounddims; Window constraint_win; XSetWindowAttributes pattr; dw = DisplayWidth(dsply, screen); dh = DisplayHeight(dsply, screen); get_mouse_position(&mousex, &mousey); bounddims.x = (mousex - c->x) - BORDERWIDTH(c); bounddims.width = (dw - bounddims.x - (c->width - bounddims.x)) + 1; bounddims.y = mousey - c->y; bounddims.height = (dh - bounddims.y - (c->height - bounddims.y)) + 1; bounddims.y += (BARHEIGHT() * 2) - BORDERWIDTH(c); bounddims.height += c->height - ((BARHEIGHT() * 2) - DEF_BORDERWIDTH); constraint_win = XCreateWindow(dsply, root, bounddims.x, bounddims.y, bounddims.width, bounddims.height, 0, CopyFromParent, InputOnly, CopyFromParent, 0, &pattr); #ifdef DEBUG fprintf(stderr, "move() : constraint_win is (%d, %d)-(%d, %d)\n", bounddims.x, bounddims.y, bounddims.x + bounddims.width, bounddims.y + bounddims.height); #endif XMapWindow(dsply, constraint_win); if (!(XGrabPointer(dsply, root, False, MouseMask, GrabModeAsync, GrabModeAsync, constraint_win, None, CurrentTime) == GrabSuccess)) { XDestroyWindow(dsply, constraint_win); return; } do { XMaskEvent(dsply, ExposureMask|MouseMask, &ev); switch (ev.type) { case Expose: exposed_c = find_client(ev.xexpose.window, FRAME); if (exposed_c != NULL) { redraw(exposed_c); } break; case MotionNotify: c->x = old_cx + (ev.xmotion.x - mousex); c->y = old_cy + (ev.xmotion.y - mousey); XMoveWindow(dsply, c->frame, c->x, c->y - BARHEIGHT()); send_config(c); break; } } while (ev.type != ButtonRelease); ungrab(); XDestroyWindow(dsply, constraint_win); } void resize(Client *c, int x, int y) { XEvent ev; Client *exposed_c; Rect newdims, recalceddims, bounddims; unsigned int dragging_outwards, dw, dh; Window constraint_win, resize_win, resizebar_win; XSetWindowAttributes pattr, resize_pattr, resizebar_pattr; if (x > c->x + BORDERWIDTH(c) && x < (c->x + c->width) - BORDERWIDTH(c) && y > (c->y - BARHEIGHT()) + BORDERWIDTH(c) && y < (c->y + c->height) - BORDERWIDTH(c)) { // inside the window, dragging outwards dragging_outwards = 1; } else { // outside the window, dragging inwards dragging_outwards = 0; } dw = DisplayWidth(dsply, screen); dh = DisplayHeight(dsply, screen); bounddims.x = 0; bounddims.width = dw; bounddims.y = 0; bounddims.height = dh; constraint_win = XCreateWindow(dsply, root, bounddims.x, bounddims.y, bounddims.width, bounddims.height, 0, CopyFromParent, InputOnly, CopyFromParent, 0, &pattr); XMapWindow(dsply, constraint_win); if (!(XGrabPointer(dsply, root, False, MouseMask, GrabModeAsync, GrabModeAsync, constraint_win, resize_curs, CurrentTime) == GrabSuccess)) { XDestroyWindow(dsply, constraint_win); return; } newdims.x = c->x; newdims.y = c->y - BARHEIGHT(); newdims.width = c->width; newdims.height = c->height + BARHEIGHT(); copy_dims(&newdims, &recalceddims); // create and map resize window resize_pattr.override_redirect = True; resize_pattr.background_pixel = menu_col.pixel; resize_pattr.border_pixel = border_col.pixel; resize_pattr.event_mask = ChildMask|ButtonPressMask|ExposureMask|EnterWindowMask; resize_win = XCreateWindow(dsply, root, newdims.x, newdims.y, newdims.width, newdims.height, DEF_BORDERWIDTH, DefaultDepth(dsply, screen), CopyFromParent, DefaultVisual(dsply, screen), CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWEventMask, &resize_pattr); XMapRaised(dsply, resize_win); resizebar_pattr.override_redirect = True; resizebar_pattr.background_pixel = active_col.pixel; resizebar_pattr.border_pixel = border_col.pixel; resizebar_pattr.event_mask = ChildMask|ButtonPressMask|ExposureMask|EnterWindowMask; resizebar_win = XCreateWindow(dsply, resize_win, -DEF_BORDERWIDTH, -DEF_BORDERWIDTH, newdims.width, BARHEIGHT() - DEF_BORDERWIDTH, DEF_BORDERWIDTH, DefaultDepth(dsply, screen), CopyFromParent, DefaultVisual(dsply, screen), CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWEventMask, &resizebar_pattr); XMapRaised(dsply, resizebar_win); #ifdef XFT // temporarily swap drawables in order to draw on the resize windows XFT context XftDrawChange(c->xftdraw, (Drawable) resizebar_win); #endif // hide real windows frame XUnmapWindow(dsply, c->frame); do { XMaskEvent(dsply, ExposureMask|MouseMask, &ev); switch (ev.type) { case Expose: if (ev.xexpose.window == resizebar_win) { write_titletext(c, resizebar_win); } else { exposed_c = find_client(ev.xexpose.window, FRAME); if (exposed_c) { redraw(exposed_c); } } break; case MotionNotify: { unsigned int in_taskbar = 1, leftedge_changed = 0, rightedge_changed = 0, topedge_changed = 0, bottomedge_changed = 0; int newwidth, newheight; // warping the pointer is wrong - wait until it leaves the taskbar if (ev.xmotion.y < BARHEIGHT()) { in_taskbar = 1; } else { if (in_taskbar == 1) // first time outside taskbar { in_taskbar = 0; bounddims.x = 0; bounddims.width = dw; bounddims.y = BARHEIGHT(); bounddims.height = dh - BARHEIGHT(); XMoveResizeWindow(dsply, constraint_win, bounddims.x, bounddims.y, bounddims.width, bounddims.height); in_taskbar = 0; } // inside the window, dragging outwards if (dragging_outwards) { if (ev.xmotion.x < newdims.x + BORDERWIDTH(c)) { newdims.width += newdims.x + BORDERWIDTH(c) - ev.xmotion.x; newdims.x = ev.xmotion.x - BORDERWIDTH(c); leftedge_changed = 1; } else if (ev.xmotion.x > newdims.x + newdims.width + BORDERWIDTH(c)) { newdims.width = (ev.xmotion.x - newdims.x - BORDERWIDTH(c)) + 1; // add 1 to allow window to be flush with edge of screen rightedge_changed = 1; } if (ev.xmotion.y < newdims.y + BORDERWIDTH(c)) { newdims.height += newdims.y + BORDERWIDTH(c) - ev.xmotion.y; newdims.y = ev.xmotion.y - BORDERWIDTH(c); topedge_changed = 1; } else if (ev.xmotion.y > newdims.y + newdims.height + BORDERWIDTH(c)) { newdims.height = (ev.xmotion.y - newdims.y - BORDERWIDTH(c)) + 1; // add 1 to allow window to be flush with edge of screen bottomedge_changed = 1; } } // outside the window, dragging inwards else { unsigned int above_win, below_win, leftof_win, rightof_win; unsigned int in_win; above_win = (ev.xmotion.y < newdims.y + BORDERWIDTH(c)); below_win = (ev.xmotion.y > newdims.y + newdims.height + BORDERWIDTH(c)); leftof_win = (ev.xmotion.x < newdims.x + BORDERWIDTH(c)); rightof_win = (ev.xmotion.x > newdims.x + newdims.width + BORDERWIDTH(c)); in_win = ((!above_win) && (!below_win) && (!leftof_win) && (!rightof_win)); if (in_win) { unsigned int from_left, from_right, from_top, from_bottom; from_left = ev.xmotion.x - newdims.x - BORDERWIDTH(c); from_right = newdims.x + newdims.width + BORDERWIDTH(c) - ev.xmotion.x; from_top = ev.xmotion.y - newdims.y - BORDERWIDTH(c); from_bottom = newdims.y + newdims.height + BORDERWIDTH(c) - ev.xmotion.y; if (from_left < from_right && from_left < from_top && from_left < from_bottom) { newdims.width -= ev.xmotion.x - newdims.x - BORDERWIDTH(c); newdims.x = ev.xmotion.x - BORDERWIDTH(c); leftedge_changed = 1; } else if (from_right < from_top && from_right < from_bottom) { newdims.width = ev.xmotion.x - newdims.x - BORDERWIDTH(c); rightedge_changed = 1; } else if (from_top < from_bottom) { newdims.height -= ev.xmotion.y - newdims.y - BORDERWIDTH(c); newdims.y = ev.xmotion.y - BORDERWIDTH(c); topedge_changed = 1; } else { newdims.height = ev.xmotion.y - newdims.y - BORDERWIDTH(c); bottomedge_changed = 1; } } } // coords have changed if (leftedge_changed || rightedge_changed || topedge_changed || bottomedge_changed) { copy_dims(&newdims, &recalceddims); recalceddims.height -= BARHEIGHT(); if (get_incsize(c, (unsigned int *)&newwidth, (unsigned int *)&newheight, &recalceddims, PIXELS)) { if (leftedge_changed) { recalceddims.x = (recalceddims.x + recalceddims.width) - newwidth; recalceddims.width = newwidth; } else if (rightedge_changed) { recalceddims.width = newwidth; } if (topedge_changed) { recalceddims.y = (recalceddims.y + recalceddims.height) - newheight; recalceddims.height = newheight; } else if (bottomedge_changed) { recalceddims.height = newheight; } } recalceddims.height += BARHEIGHT(); limit_size(c, &recalceddims); XMoveResizeWindow(dsply, resize_win, recalceddims.x, recalceddims.y, recalceddims.width, recalceddims.height); XResizeWindow(dsply, resizebar_win, recalceddims.width, BARHEIGHT() - DEF_BORDERWIDTH); } } } break; } } while (ev.type != ButtonRelease); XUngrabServer(dsply); ungrab(); c->x = recalceddims.x; c->y = recalceddims.y + BARHEIGHT(); c->width = recalceddims.width; c->height = recalceddims.height - BARHEIGHT(); XMoveResizeWindow(dsply, c->frame, c->x, c->y - BARHEIGHT(), c->width, c->height + BARHEIGHT()); XResizeWindow(dsply, c->window, c->width, c->height); // unhide real windows frame XMapWindow(dsply, c->frame); XSetInputFocus(dsply, c->window, RevertToNone, CurrentTime); send_config(c); XDestroyWindow(dsply, constraint_win); #ifdef XFT // reset the drawable XftDrawChange(c->xftdraw, (Drawable) c->frame); #endif XDestroyWindow(dsply, resizebar_win); XDestroyWindow(dsply, resize_win); } static void limit_size(Client *c, Rect *newdims) { int dw, dh; dw = DisplayWidth(dsply, screen); dh = DisplayHeight(dsply, screen); if (c->size->flags & PMinSize) { if (newdims->width < c->size->min_width) { newdims->width = c->size->min_width; } if (newdims->height < c->size->min_height) { newdims->height = c->size->min_height; } } if (c->size->flags & PMaxSize) { if (newdims->width > c->size->max_width) { newdims->width = c->size->max_width; } if (newdims->height > c->size->max_height) { newdims->height = c->size->max_height; } } if (newdims->width < MINWINWIDTH) { newdims->width = MINWINWIDTH; } if (newdims->height < MINWINHEIGHT) { newdims->height = MINWINHEIGHT; } if (newdims->width > dw) { newdims->width = dw; } if (newdims->height > (dh - BARHEIGHT())) { newdims->height = (dh - BARHEIGHT()); } } /* If the window in question has a ResizeInc int, then it wants to be * resized in multiples of some (x,y). Here we set x_ret and y_ret to * the number of multiples (if mode == INCREMENTS) or the correct size * in pixels for said multiples (if mode == PIXELS). */ static int get_incsize(Client *c, unsigned int *x_ret, unsigned int *y_ret, Rect *newdims, int mode) { int basex, basey; if (c->size->flags & PResizeInc) { basex = (c->size->flags & PBaseSize) ? c->size->base_width : (c->size->flags & PMinSize) ? c->size->min_width : 0; basey = (c->size->flags & PBaseSize) ? c->size->base_height : (c->size->flags & PMinSize) ? c->size->min_height : 0; // work around broken apps that set their resize increments to 0 if (mode == PIXELS) { if (c->size->width_inc != 0) { *x_ret = newdims->width - ((newdims->width - basex) % c->size->width_inc); } if (c->size->height_inc != 0) { *y_ret = newdims->height - ((newdims->height - basey) % c->size->height_inc); } } else // INCREMENTS { if (c->size->width_inc != 0) { *x_ret = (newdims->width - basex) / c->size->width_inc; } if (c->size->height_inc != 0) { *y_ret = (newdims->height - basey) / c->size->height_inc; } } return 1; } return 0; } void write_titletext(Client *c, Window bar_win) { #ifdef MWM_HINTS if (!c->has_title) { return; } #endif if (!c->trans && c->name != NULL) { #ifdef XFT XftDrawString8(c->xftdraw, &xft_detail, xftfont, SPACE, SPACE + xftfont->ascent, (unsigned char *)c->name, strlen(c->name)); #else XDrawString(dsply, bar_win, text_gc, SPACE, SPACE + font->ascent, c->name, strlen(c->name)); #endif } }