/* * BadWM - minimalistic window manager for the X Window System * Copyright (C) Robert Annessi * * 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 (at your option) 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 #include #include #include "include/BadWM.h" #include "include/config.h" #include void draw_outline(Client *c) { XDrawRectangle(dpy, c->screen->root, c->screen->invert_gc, c->x - c->border, c->y - c->border, c->width + opt_bw, c->height + opt_bw); } void get_mouse_position(int *x, int *y, Window root) { Window dw1, dw2; int t1, t2; unsigned int t3; XQueryPointer(dpy, root, &dw1, &dw2, x, y, &t1, &t2, &t3); } void recalculate_sweep(Client *c, int x1, int y1, int x2, int y2, int westwards, int northwards ) { int basex, basey; int xsnap=0, ysnap=0; c->width = abs(x1 - x2); c->height = abs(y1 - y2); 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; xsnap = (c->width - basex) % c->size->width_inc; ysnap = (c->height - basey) % c->size->height_inc; c->width -= xsnap; c->height -= ysnap; } if (c->size->flags & PMinSize) { if (c->width < c->size->min_width) c->width = c->size->min_width; if (c->height < c->size->min_height) c->height = c->size->min_height; } if (c->size->flags & PMaxSize) { if (c->width > c->size->max_width) c->width = c->size->max_width; if (c->height > c->size->max_height) c->height = c->size->max_height; } c->x = (x1 <= x2) ? x1 : x1 - c->width; c->y = (y1 <= y2) ? y1 : y1 - c->height; if (x1>x2) xsnap = -xsnap; if (y1>y2) ysnap = -ysnap; if (westwards) c->x += xsnap; if (northwards) c->y += ysnap; } void sweep(Client *c) { XEvent ev; int win_px,win_py; double win_px_n,win_py_n; int root_px,root_py; Window dumw; int dumi; ResizeDir dir; int x1 = c->x; int y1 = c->y; int x2 = c->x+c->width; int y2 = c->y+c->height; XQueryPointer(dpy,c->window,&dumw,&dumw,&root_px,&root_py,&win_px,&win_py,&dumi); win_px_n = (double)win_px/(double)c->width; win_py_n = (double)win_py/(double)c->height; if (win_py_n<0.3) { if (win_px_n<0.3) dir = NorthWest; else if (win_px_n>0.7) dir = NorthEast; else dir = North; } else if (win_py_n>0.7) { if (win_px_n<0.3) dir = SouthWest; else if (win_px_n>0.7) dir = SouthEast; else dir = South; } else { if (win_px_n<=0.5) dir = West; else dir = East; } if (!grab_pointer(c->screen->root, MouseMask, resize_cursors[dir])) return; if ( config.global->solidwindows == 0 ) { XGrabServer(dpy); draw_outline(c); } for (;;) { int dx; int dy; XMaskEvent(dpy, MouseMask, &ev); switch (ev.type) { case MotionNotify: draw_outline(c); /* clear */ XUngrabServer(dpy); dx = ev.xmotion.x-root_px; dy = ev.xmotion.y-root_py; switch (dir) { case NorthWest: recalculate_sweep(c,x1+dx,y1+dy,x2,y2,1,1); break; case North: recalculate_sweep(c,x1,y1+dy,x2,y2,0,1); break; case NorthEast: recalculate_sweep(c,x1,y1+dy,x2+dx,y2,0,1); break; case East: recalculate_sweep(c,x1,y1,x2+dx,y2,0,0); break; case SouthEast: recalculate_sweep(c,x1,y1,x2+dx,y2+dy,0,0); break; case South: recalculate_sweep(c,x1,y1,x2,y2+dy,0,0); break; case SouthWest: recalculate_sweep(c,x1+dx,y1,x2,y2+dy,1,0); break; case West: recalculate_sweep(c,x1+dx,y1,x2,y2,1,0); break; } if (config.global->solidwindows == 0) { XSync(dpy, False); XGrabServer(dpy); draw_outline(c); } else { XMoveResizeWindow(dpy, c->parent, c->x - c->border, c->y - c->border, c->width + (c->border*2), c->height + (c->border*2)); XMoveResizeWindow(dpy, c->window, c->border, c->border, c->width, c->height); send_config(c); } break; case ButtonRelease: if (config.global->solidwindows == 0) { draw_outline(c); /* clear */ XUngrabServer(dpy); } XUngrabPointer(dpy, CurrentTime); return; } } } static int absmin(int a, int b) { if (abs(a) < abs(b)) return a; return b; } static void snap_client(Client *c) { int dx, dy; Client *ci; /* snap to screen border */ if (abs(c->x - c->border) < config.global->snap_distance) c->x = c->border; if (abs(c->y - c->border) < config.global->snap_distance) c->y = c->border; if (abs(c->x + c->width + c->border - DisplayWidth(dpy, c->screen->screen)) < config.global->snap_distance) c->x = DisplayWidth(dpy, c->screen->screen) - c->width - c->border; if (abs(c->y + c->height + c->border - DisplayHeight(dpy, c->screen->screen)) < config.global->snap_distance) c->y = DisplayHeight(dpy, c->screen->screen) - c->height - c->border; /* snap to other windows */ dx = dy = config.global->snap_distance; for (ci = head_client; ci; ci = ci->next) { if (ci != c && (ci->vdesk == vdesk || ci->vdesk == STICKY)) { if (ci->y - ci->border - c->border - c->height - c->y <= config.global->snap_distance && c->y - c->border - ci->border - ci->height - ci->y <= config.global->snap_distance) { dx = absmin(dx, ci->x + ci->width - c->x + c->border + ci->border); dx = absmin(dx, ci->x + ci->width - c->x - c->width); dx = absmin(dx, ci->x - c->x - c->width - c->border - ci->border); dx = absmin(dx, ci->x - c->x); } if (ci->x - ci->border - c->border - c->width - c->x <= config.global->snap_distance && c->x - c->border - ci->border - ci->width - ci->x <= config.global->snap_distance) { dy = absmin(dy, ci->y + ci->height - c->y + c->border + ci->border); dy = absmin(dy, ci->y + ci->height - c->y - c->height); dy = absmin(dy, ci->y - c->y - c->height - c->border - ci->border); dy = absmin(dy, ci->y - c->y); } } } if (abs(dx) < config.global->snap_distance) c->x += dx; if (abs(dy) < config.global->snap_distance) c->y += dy; } void drag(Client *c) { int x1, y1; int old_cx = c->x; int old_cy = c->y; XEvent ev; if (!grab_pointer(c->screen->root, MouseMask, move_curs)) return; get_mouse_position(&x1, &y1, c->screen->root); if ( config.global->solidwindows == 0 ) { XGrabServer(dpy); draw_outline(c); } for (;;) { XMaskEvent(dpy, MouseMask, &ev); switch (ev.type) { case MotionNotify: if ( config.global->solidwindows == 0 ) { draw_outline(c); XUngrabServer(dpy); } /* changing vdesk when pointer is at the edge of screen */ if ( ev.xmotion.x == 0) { ev.xmotion.x = DisplayWidth(dpy, c->screen->screen) - 2; setmouse(c->screen->root, ev.xmotion.x, ev.xmotion.y); switch_vdesk_down(); if ( config.global->solidwindows == 1 ) { /* we have to move the window to if solid */ c->vdesk = vdesk; unhide(c, RAISE); } } else if ( ev.xmotion.x == DisplayWidth(dpy, c->screen->screen) - 1) { ev.xmotion.x = 2; setmouse(c->screen->root, ev.xmotion.x, ev.xmotion.y); switch_vdesk_up(); if ( config.global->solidwindows == 1 ) { /* we have to move the window to if solid */ c->vdesk = vdesk; unhide(c, RAISE); } } c->x = old_cx + (ev.xmotion.x - x1); c->y = old_cy + (ev.xmotion.y - y1); snap_client(c); if ( config.global->solidwindows == 0 ) { XSync(dpy, False); XGrabServer(dpy); draw_outline(c); } else { XMoveWindow(dpy, c->parent, c->x - c->border, c->y - c->border); send_config(c); } break; case ButtonRelease: /* redrawing (unhiding) the window if vdesk changed */ if ( c->vdesk != vdesk && c->vdesk != STICKY ) { c->vdesk = vdesk; client_update_current(current, NULL); unhide(c, RAISE); } else if ( config.global->solidwindows == 0 ) draw_outline(c); XUngrabServer(dpy); XUngrabPointer(dpy, CurrentTime); return; default: break; } } } void move(Client *c, int set) { if (c) { XRaiseWindow(dpy, c->parent); if (!set) drag(c); XMoveWindow(dpy, c->parent, c->x - c->border, c->y - c->border); send_config(c); } } void resize(Client *c, int set) { if (c) { XRaiseWindow(dpy, c->parent); if (!set) sweep(c); XMoveResizeWindow(dpy, c->parent, c->x - c->border, c->y - c->border, c->width + (c->border*2), c->height + (c->border*2)); XMoveResizeWindow(dpy, c->window, c->border, c->border, c->width, c->height); send_config(c); } } void maximise_horiz(Client *c) { #ifdef DEBUG bad_debug(1,"SCREEN: maximise_horiz()\n"); #endif if (c->oldw) { c->x = c->oldx; c->width = c->oldw; c->oldw = 0; } else { c->oldx = c->x; c->oldw = c->width; recalculate_sweep(c, 0, c->y, DisplayWidth(dpy, c->screen->screen), c->y + c->height,0,0); } } void maximise_vert(Client *c) { #ifdef DEBUG bad_debug(1,"SCREEN: maximise_vert()\n"); #endif if (c->oldh) { c->y = c->oldy; c->height = c->oldh; c->oldh = 0; } else { c->oldy = c->y; c->oldh = c->height; recalculate_sweep(c, c->x, 0, c->x + c->width, DisplayHeight(dpy, c->screen->screen),0,0); } } void hide(Client *c) { if (c) { c->ignore_unmap += 2; #ifdef DEBUG bad_debug(1,"screen:XUnmapWindow(parent)\n"); #endif XUnmapWindow(dpy, c->parent); #ifdef DEBUG bad_debug(1,"screen:XUnmapWindow(window)\n"); #endif XUnmapWindow(dpy, c->window); set_wm_state(c, IconicState); } } void unhide(Client *c, int raise) { XMapWindow(dpy, c->window); raise ? XMapRaised(dpy, c->parent) : XMapWindow(dpy, c->parent); set_wm_state(c, NormalState); } void next(void) { Client *newc = current; Client *stop; #ifdef DEBUG bad_debug(1,"\n"); #endif if (current) { stop = current; newc = current->fix_next; } else { stop = newc = head_client; } /* selecting window */ assert(newc != NULL); for (;;) { if ( newc->vdesk == vdesk ) break; assert(newc->fix_next != NULL); newc = newc->fix_next; if ( newc == stop ) { newc = NULL; break; } } if ( newc ) { /* focusing new window */ unhide(newc,RAISE); focus_client(newc); #ifdef DEBUG } else { bad_debug(1,"NEXT: hmm, no next window\n"); #endif } } void switch_vdesk(int v) { Client *c; int wdesk; if (v == vdesk) return; if (current && current->vdesk != STICKY) { client_update_current(current, NULL); } #ifdef DEBUG bad_debug(1,"Switching to desk %d\n", v); #endif for (c = head_client; c; c = c->next) { wdesk = c->vdesk; if (wdesk == vdesk) { hide(c); } else if (wdesk == v) { unhide(c, NO_RAISE); } } vdesk = v; } ScreenInfo *find_screen(Window root) { int i; for (i = 0; i < num_screens; i++) { if (screens[i].root == root) return &screens[i]; } return NULL; } void switch_vdesk_up(void) { if (config.global->max_vdesks == 10 && vdesk == 9) switch_vdesk(0); else if (vdesk < config.global->max_vdesks) switch_vdesk(vdesk+1); else switch_vdesk(1); } void switch_vdesk_down(void) { if (vdesk > config.global->min_vdesk) switch_vdesk(vdesk-1); else if (config.global->max_vdesks == 10 && vdesk == 0) switch_vdesk(9); else switch_vdesk(config.global->max_vdesks); }