| 1 | /* evilwm - Minimalist Window Manager for X |
|---|
| 2 | * Copyright (C) 1999-2006 Ciaran Anscomb <evilwm@6809.org.uk> |
|---|
| 3 | * see README for license and other details. */ |
|---|
| 4 | |
|---|
| 5 | #include <stdio.h> |
|---|
| 6 | #include <stdlib.h> |
|---|
| 7 | #include "evilwm.h" |
|---|
| 8 | #include "log.h" |
|---|
| 9 | |
|---|
| 10 | static int send_xmessage(Window w, Atom a, long x); |
|---|
| 11 | |
|---|
| 12 | /* used all over the place. return the client that has specified window as |
|---|
| 13 | * either window or parent */ |
|---|
| 14 | |
|---|
| 15 | Client *find_client(Window w) { |
|---|
| 16 | Client *c; |
|---|
| 17 | |
|---|
| 18 | for (c = head_client; c; c = c->next) |
|---|
| 19 | if (w == c->parent || w == c->window) |
|---|
| 20 | return c; |
|---|
| 21 | return NULL; |
|---|
| 22 | } |
|---|
| 23 | |
|---|
| 24 | void set_wm_state(Client *c, int state) { |
|---|
| 25 | /* Using "long" for the type of "data" looks wrong, but the |
|---|
| 26 | * fine people in the X Consortium defined it this way |
|---|
| 27 | * (even on 64-bit machines). |
|---|
| 28 | */ |
|---|
| 29 | long data[2]; |
|---|
| 30 | data[0] = state; |
|---|
| 31 | data[1] = None; |
|---|
| 32 | XChangeProperty(dpy, c->window, xa_wm_state, xa_wm_state, 32, |
|---|
| 33 | PropModeReplace, (unsigned char *)data, 2); |
|---|
| 34 | } |
|---|
| 35 | |
|---|
| 36 | void send_config(Client *c) { |
|---|
| 37 | XConfigureEvent ce; |
|---|
| 38 | |
|---|
| 39 | ce.type = ConfigureNotify; |
|---|
| 40 | ce.event = c->window; |
|---|
| 41 | ce.window = c->window; |
|---|
| 42 | ce.x = c->x; |
|---|
| 43 | ce.y = c->y; |
|---|
| 44 | ce.width = c->width; |
|---|
| 45 | ce.height = c->height; |
|---|
| 46 | ce.border_width = 0; |
|---|
| 47 | ce.above = None; |
|---|
| 48 | ce.override_redirect = False; |
|---|
| 49 | |
|---|
| 50 | XSendEvent(dpy, c->window, False, StructureNotifyMask, (XEvent *)&ce); |
|---|
| 51 | //puts("config"); |
|---|
| 52 | //todo: config -- hook |
|---|
| 53 | } |
|---|
| 54 | |
|---|
| 55 | /* Support for 'gravitating' clients based on their original |
|---|
| 56 | * border width and configured window manager frame width. */ |
|---|
| 57 | void gravitate_client(Client *c, int sign) { |
|---|
| 58 | int d0 = sign * c->border; |
|---|
| 59 | int d1 = sign * c->old_border; |
|---|
| 60 | int d2 = sign * (2*c->old_border - c->border); |
|---|
| 61 | switch (c->win_gravity) { |
|---|
| 62 | case NorthGravity: |
|---|
| 63 | c->x += d1; |
|---|
| 64 | c->y += d0; |
|---|
| 65 | break; |
|---|
| 66 | case NorthEastGravity: |
|---|
| 67 | c->x += d2; |
|---|
| 68 | c->y += d0; |
|---|
| 69 | break; |
|---|
| 70 | case EastGravity: |
|---|
| 71 | c->x += d2; |
|---|
| 72 | c->y += d1; |
|---|
| 73 | break; |
|---|
| 74 | case SouthEastGravity: |
|---|
| 75 | c->x += d2; |
|---|
| 76 | c->y += d2; |
|---|
| 77 | break; |
|---|
| 78 | case SouthGravity: |
|---|
| 79 | c->x += d1; |
|---|
| 80 | c->y += d2; |
|---|
| 81 | break; |
|---|
| 82 | case SouthWestGravity: |
|---|
| 83 | c->x += d0; |
|---|
| 84 | c->y += d2; |
|---|
| 85 | break; |
|---|
| 86 | case WestGravity: |
|---|
| 87 | c->x += d0; |
|---|
| 88 | c->y += d1; |
|---|
| 89 | break; |
|---|
| 90 | case NorthWestGravity: |
|---|
| 91 | default: |
|---|
| 92 | c->x += d0; |
|---|
| 93 | c->y += d0; |
|---|
| 94 | break; |
|---|
| 95 | } |
|---|
| 96 | } |
|---|
| 97 | |
|---|
| 98 | void select_client(Client *c) { |
|---|
| 99 | if (current) |
|---|
| 100 | XSetWindowBorder(dpy, current->parent, current->screen->bg.pixel); |
|---|
| 101 | if (c) { |
|---|
| 102 | unsigned long bpixel; |
|---|
| 103 | #ifdef VWM |
|---|
| 104 | if (is_sticky(c)) |
|---|
| 105 | bpixel = c->screen->fc.pixel; |
|---|
| 106 | else |
|---|
| 107 | #endif |
|---|
| 108 | bpixel = c->screen->fg.pixel; |
|---|
| 109 | XSetWindowBorder(dpy, c->parent, bpixel); |
|---|
| 110 | #ifdef COLOURMAP |
|---|
| 111 | XInstallColormap(dpy, c->cmap); |
|---|
| 112 | #endif |
|---|
| 113 | XSetInputFocus(dpy, c->window, RevertToPointerRoot, CurrentTime); |
|---|
| 114 | } |
|---|
| 115 | current = c; |
|---|
| 116 | } |
|---|
| 117 | |
|---|
| 118 | #ifdef VWM |
|---|
| 119 | void fix_client(Client *c) { |
|---|
| 120 | toggle_sticky(c); |
|---|
| 121 | select_client(c); |
|---|
| 122 | update_net_wm_state(c); |
|---|
| 123 | } |
|---|
| 124 | #endif |
|---|
| 125 | |
|---|
| 126 | void remove_client(Client *c) { |
|---|
| 127 | Client *p; |
|---|
| 128 | |
|---|
| 129 | LOG_DEBUG("remove_client() : Removing...\n"); |
|---|
| 130 | |
|---|
| 131 | XGrabServer(dpy); |
|---|
| 132 | ignore_xerror = 1; |
|---|
| 133 | |
|---|
| 134 | /* ICCCM 4.1.3.1 |
|---|
| 135 | * "When the window is withdrawn, the window manager will either |
|---|
| 136 | * change the state field's value to WithdrawnState or it will |
|---|
| 137 | * remove the WM_STATE property entirely." |
|---|
| 138 | * EWMH 1.3 |
|---|
| 139 | * "The Window Manager should remove the property whenever a |
|---|
| 140 | * window is withdrawn but it should leave the property in |
|---|
| 141 | * place when it is shutting down." (both _NET_WM_DESKTOP and |
|---|
| 142 | * _NET_WM_STATE) */ |
|---|
| 143 | if (c->remove) { |
|---|
| 144 | LOG_DEBUG("\tremove_client() : setting WithdrawnState\n"); |
|---|
| 145 | set_wm_state(c, WithdrawnState); |
|---|
| 146 | #ifdef VWM |
|---|
| 147 | XDeleteProperty(dpy, c->window, xa_net_wm_desktop); |
|---|
| 148 | XDeleteProperty(dpy, c->window, xa_net_wm_state); |
|---|
| 149 | #endif |
|---|
| 150 | } |
|---|
| 151 | |
|---|
| 152 | ungravitate(c); |
|---|
| 153 | XReparentWindow(dpy, c->window, c->screen->root, c->x, c->y); |
|---|
| 154 | XSetWindowBorderWidth(dpy, c->window, c->old_border); |
|---|
| 155 | XRemoveFromSaveSet(dpy, c->window); |
|---|
| 156 | if (c->parent) |
|---|
| 157 | XDestroyWindow(dpy, c->parent); |
|---|
| 158 | |
|---|
| 159 | if (head_client == c) head_client = c->next; |
|---|
| 160 | else for (p = head_client; p && p->next; p = p->next) |
|---|
| 161 | if (p->next == c) p->next = c->next; |
|---|
| 162 | |
|---|
| 163 | if (current == c) |
|---|
| 164 | current = NULL; /* an enter event should set this up again */ |
|---|
| 165 | Py_DECREF(c->dict); |
|---|
| 166 | free(c); |
|---|
| 167 | #ifdef DEBUG |
|---|
| 168 | { |
|---|
| 169 | Client *pp; |
|---|
| 170 | int i = 0; |
|---|
| 171 | for (pp = head_client; pp; pp = pp->next) |
|---|
| 172 | i++; |
|---|
| 173 | LOG_DEBUG("\tremove_client() : free(), window count now %d\n", i); |
|---|
| 174 | } |
|---|
| 175 | #endif |
|---|
| 176 | |
|---|
| 177 | XUngrabServer(dpy); |
|---|
| 178 | XSync(dpy, False); |
|---|
| 179 | ignore_xerror = 0; |
|---|
| 180 | LOG_DEBUG("remove_client() returning\n"); |
|---|
| 181 | } |
|---|
| 182 | |
|---|
| 183 | void send_wm_delete(Client *c, int kill_client) { |
|---|
| 184 | int i, n, found = 0; |
|---|
| 185 | Atom *protocols; |
|---|
| 186 | |
|---|
| 187 | if (!kill_client && XGetWMProtocols(dpy, c->window, &protocols, &n)) { |
|---|
| 188 | for (i = 0; i < n; i++) |
|---|
| 189 | if (protocols[i] == xa_wm_delete) |
|---|
| 190 | found++; |
|---|
| 191 | XFree(protocols); |
|---|
| 192 | } |
|---|
| 193 | if (found) |
|---|
| 194 | send_xmessage(c->window, xa_wm_protos, xa_wm_delete); |
|---|
| 195 | else |
|---|
| 196 | XKillClient(dpy, c->window); |
|---|
| 197 | } |
|---|
| 198 | |
|---|
| 199 | static int send_xmessage(Window w, Atom a, long x) { |
|---|
| 200 | XEvent ev; |
|---|
| 201 | |
|---|
| 202 | ev.type = ClientMessage; |
|---|
| 203 | ev.xclient.window = w; |
|---|
| 204 | ev.xclient.message_type = a; |
|---|
| 205 | ev.xclient.format = 32; |
|---|
| 206 | ev.xclient.data.l[0] = x; |
|---|
| 207 | ev.xclient.data.l[1] = CurrentTime; |
|---|
| 208 | |
|---|
| 209 | return XSendEvent(dpy, w, False, NoEventMask, &ev); |
|---|
| 210 | } |
|---|
| 211 | |
|---|
| 212 | #ifdef SHAPE |
|---|
| 213 | void set_shape(Client *c) { |
|---|
| 214 | int bounding_shaped; |
|---|
| 215 | int i, b; unsigned int u; /* dummies */ |
|---|
| 216 | |
|---|
| 217 | if (!have_shape) return; |
|---|
| 218 | /* Logic to decide if we have a shaped window cribbed from fvwm-2.5.10. |
|---|
| 219 | * Previous method (more than one rectangle returned from |
|---|
| 220 | * XShapeGetRectangles) worked _most_ of the time. */ |
|---|
| 221 | if (XShapeQueryExtents(dpy, c->window, &bounding_shaped, &i, &i, |
|---|
| 222 | &u, &u, &b, &i, &i, &u, &u) && bounding_shaped) { |
|---|
| 223 | LOG_DEBUG("%d shape extents\n", bounding_shaped); |
|---|
| 224 | XShapeCombineShape(dpy, c->parent, ShapeBounding, 0, 0, |
|---|
| 225 | c->window, ShapeBounding, ShapeSet); |
|---|
| 226 | } |
|---|
| 227 | } |
|---|
| 228 | #endif |
|---|