client.c (8782B)
1 /* (C)opyright MMVI-MMVII Anselm R. Garbe <garbeam at gmail dot com> 2 * See LICENSE file for license details. 3 */ 4 #include "2wm.h" 5 #include <stdlib.h> 6 #include <string.h> 7 #include <X11/Xatom.h> 8 #include <X11/Xutil.h> 9 10 /* static */ 11 12 static void 13 detachstack(Client *c) { 14 Client **tc; 15 for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext); 16 *tc = c->snext; 17 } 18 19 static void 20 grabbuttons(Client *c, Bool focused) { 21 XUngrabButton(dpy, AnyButton, AnyModifier, c->win); 22 23 if(focused) { 24 XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK, 25 GrabModeAsync, GrabModeSync, None, None); 26 XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK, 27 GrabModeAsync, GrabModeSync, None, None); 28 XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK, 29 GrabModeAsync, GrabModeSync, None, None); 30 XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, 31 GrabModeAsync, GrabModeSync, None, None); 32 33 XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK, 34 GrabModeAsync, GrabModeSync, None, None); 35 XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK, 36 GrabModeAsync, GrabModeSync, None, None); 37 XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK, 38 GrabModeAsync, GrabModeSync, None, None); 39 XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, 40 GrabModeAsync, GrabModeSync, None, None); 41 42 XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK, 43 GrabModeAsync, GrabModeSync, None, None); 44 XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK, 45 GrabModeAsync, GrabModeSync, None, None); 46 XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK, 47 GrabModeAsync, GrabModeSync, None, None); 48 XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, 49 GrabModeAsync, GrabModeSync, None, None); 50 } 51 else 52 XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK, 53 GrabModeAsync, GrabModeSync, None, None); 54 } 55 56 static void 57 setclientstate(Client *c, long state) { 58 long data[] = {state, None}; 59 XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, 60 PropModeReplace, (unsigned char *)data, 2); 61 } 62 63 static int 64 xerrordummy(Display *dsply, XErrorEvent *ee) { 65 return 0; 66 } 67 68 /* extern */ 69 70 void 71 configure(Client *c) { 72 XEvent synev; 73 74 synev.type = ConfigureNotify; 75 synev.xconfigure.display = dpy; 76 synev.xconfigure.event = c->win; 77 synev.xconfigure.window = c->win; 78 synev.xconfigure.x = c->x; 79 synev.xconfigure.y = c->y; 80 synev.xconfigure.width = c->w; 81 synev.xconfigure.height = c->h; 82 synev.xconfigure.border_width = c->border; 83 synev.xconfigure.above = None; 84 XSendEvent(dpy, c->win, True, NoEventMask, &synev); 85 } 86 87 void 88 detachclient(Client *c) { 89 if(c->prev) 90 c->prev->next = c->next; 91 if(c->next) 92 c->next->prev = c->prev; 93 if(c == clients) 94 clients = c->next; 95 c->next = c->prev = NULL; 96 } 97 98 void 99 focus(Client *c) { 100 if(c && c->view != view) 101 return; 102 if(sel && sel != c) { 103 grabbuttons(sel, False); 104 XSetWindowBorder(dpy, sel->win, normcol); 105 } 106 if(c) { 107 detachstack(c); 108 c->snext = stack; 109 stack = c; 110 grabbuttons(c, True); 111 } 112 sel = c; 113 if(!selscreen) 114 return; 115 if(c) { 116 XSetWindowBorder(dpy, c->win, selcol); 117 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); 118 } 119 else 120 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); 121 } 122 123 Client * 124 getclient(Window w) { 125 Client *c; 126 127 for(c = clients; c; c = c->next) 128 if(c->win == w) 129 return c; 130 return NULL; 131 } 132 133 Bool 134 isprotodel(Client *c) { 135 int i, n; 136 Atom *protocols; 137 Bool ret = False; 138 139 if(XGetWMProtocols(dpy, c->win, &protocols, &n)) { 140 for(i = 0; !ret && i < n; i++) 141 if(protocols[i] == wmatom[WMDelete]) 142 ret = True; 143 XFree(protocols); 144 } 145 return ret; 146 } 147 148 void 149 killclient(Arg *arg) { 150 if(!sel) 151 return; 152 if(isprotodel(sel)) 153 sendevent(sel->win, wmatom[WMProtocols], wmatom[WMDelete]); 154 else 155 XKillClient(dpy, sel->win); 156 } 157 158 void 159 manage(Window w, XWindowAttributes *wa) { 160 Client *c, *t; 161 Window trans; 162 163 c = emallocz(sizeof(Client)); 164 c->win = w; 165 c->x = wa->x; 166 c->y = wa->y; 167 c->w = wa->width; 168 c->h = wa->height; 169 if(c->w == sw && c->h == sh) { 170 c->border = 0; 171 c->x = sx; 172 c->y = sy; 173 } 174 else { 175 c->border = BORDERPX; 176 if(c->x + c->w + 2 * c->border > sx + sw) 177 c->x = sx + sw - c->w - 2 * c->border; 178 if(c->y + c->h + 2 * c->border > sy + sh) 179 c->y = sy + sh - c->h - 2 * c->border; 180 if(c->x < sx) 181 c->x = sx; 182 if(c->y < sy) 183 c->y = sy; 184 } 185 updatesizehints(c); 186 XSelectInput(dpy, c->win, 187 StructureNotifyMask | PropertyChangeMask | EnterWindowMask); 188 XGetTransientForHint(dpy, c->win, &trans); 189 grabbuttons(c, False); 190 XSetWindowBorder(dpy, c->win, normcol); 191 updatetitle(c); 192 if((t = getclient(trans))) 193 c->view = t->view; 194 else 195 c->view = view; 196 if(!(c->isfloat = isfloat(c))) 197 c->isfloat = t || c->isfixed; 198 if(clients) 199 clients->prev = c; 200 c->next = clients; 201 c->snext = stack; 202 stack = clients = c; 203 XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); 204 XMapWindow(dpy, c->win); 205 setclientstate(c, NormalState); 206 if(c->view == view) 207 focus(c); 208 arrange(); 209 } 210 211 void 212 resize(Client *c, Bool sizehints) { 213 float actual, dx, dy, max, min; 214 XWindowChanges wc; 215 216 if(c->w <= 0 || c->h <= 0) 217 return; 218 if(sizehints) { 219 if(c->minw && c->w < c->minw) 220 c->w = c->minw; 221 if(c->minh && c->h < c->minh) 222 c->h = c->minh; 223 if(c->maxw && c->w > c->maxw) 224 c->w = c->maxw; 225 if(c->maxh && c->h > c->maxh) 226 c->h = c->maxh; 227 /* inspired by algorithm from fluxbox */ 228 if(c->minay > 0 && c->maxay && (c->h - c->baseh) > 0) { 229 dx = (float)(c->w - c->basew); 230 dy = (float)(c->h - c->baseh); 231 min = (float)(c->minax) / (float)(c->minay); 232 max = (float)(c->maxax) / (float)(c->maxay); 233 actual = dx / dy; 234 if(max > 0 && min > 0 && actual > 0) { 235 if(actual < min) { 236 dy = (dx * min + dy) / (min * min + 1); 237 dx = dy * min; 238 c->w = (int)dx + c->basew; 239 c->h = (int)dy + c->baseh; 240 } 241 else if(actual > max) { 242 dy = (dx * min + dy) / (max * max + 1); 243 dx = dy * min; 244 c->w = (int)dx + c->basew; 245 c->h = (int)dy + c->baseh; 246 } 247 } 248 } 249 if(c->incw) 250 c->w -= (c->w - c->basew) % c->incw; 251 if(c->inch) 252 c->h -= (c->h - c->baseh) % c->inch; 253 } 254 if(c->w == sw && c->h == sh) 255 c->border = 0; 256 else 257 c->border = BORDERPX; 258 /* offscreen appearance fixes */ 259 if(c->x > sw) 260 c->x = sw - c->w - 2 * c->border; 261 if(c->y > sh) 262 c->y = sh - c->h - 2 * c->border; 263 if(c->x + c->w + 2 * c->border < sx) 264 c->x = sx; 265 if(c->y + c->h + 2 * c->border < sy) 266 c->y = sy; 267 wc.x = c->x; 268 wc.y = c->y; 269 wc.width = c->w; 270 wc.height = c->h; 271 wc.border_width = c->border; 272 XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc); 273 configure(c); 274 XSync(dpy, False); 275 } 276 277 void 278 updatesizehints(Client *c) { 279 long msize; 280 XSizeHints size; 281 282 if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags) 283 size.flags = PSize; 284 c->flags = size.flags; 285 if(c->flags & PBaseSize) { 286 c->basew = size.base_width; 287 c->baseh = size.base_height; 288 } 289 else 290 c->basew = c->baseh = 0; 291 if(c->flags & PResizeInc) { 292 c->incw = size.width_inc; 293 c->inch = size.height_inc; 294 } 295 else 296 c->incw = c->inch = 0; 297 if(c->flags & PMaxSize) { 298 c->maxw = size.max_width; 299 c->maxh = size.max_height; 300 } 301 else 302 c->maxw = c->maxh = 0; 303 if(c->flags & PMinSize) { 304 c->minw = size.min_width; 305 c->minh = size.min_height; 306 } 307 else 308 c->minw = c->minh = 0; 309 if(c->flags & PAspect) { 310 c->minax = size.min_aspect.x; 311 c->minay = size.min_aspect.y; 312 c->maxax = size.max_aspect.x; 313 c->maxay = size.max_aspect.y; 314 } 315 else 316 c->minax = c->minay = c->maxax = c->maxay = 0; 317 c->isfixed = (c->maxw && c->minw && c->maxh && c->minh && 318 c->maxw == c->minw && c->maxh == c->minh); 319 } 320 321 void 322 updatetitle(Client *c) { 323 char **list = NULL; 324 int n; 325 XTextProperty name; 326 327 name.nitems = 0; 328 c->name[0] = 0; 329 XGetTextProperty(dpy, c->win, &name, netatom[NetWMName]); 330 if(!name.nitems) 331 XGetWMName(dpy, c->win, &name); 332 if(!name.nitems) 333 return; 334 if(name.encoding == XA_STRING) 335 strncpy(c->name, (char *)name.value, sizeof c->name); 336 else { 337 if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success 338 && n > 0 && *list) 339 { 340 strncpy(c->name, *list, sizeof c->name); 341 XFreeStringList(list); 342 } 343 } 344 XFree(name.value); 345 } 346 347 void 348 unmanage(Client *c) { 349 Client *nc; 350 351 /* The server grab construct avoids race conditions. */ 352 XGrabServer(dpy); 353 XSetErrorHandler(xerrordummy); 354 detachstack(c); 355 detachclient(c); 356 if(sel == c) { 357 for(nc = stack; nc && (nc->view != view); nc = nc->snext); 358 focus(nc); 359 } 360 XUngrabButton(dpy, AnyButton, AnyModifier, c->win); 361 setclientstate(c, WithdrawnState); 362 free(c); 363 XSync(dpy, False); 364 XSetErrorHandler(xerror); 365 XUngrabServer(dpy); 366 arrange(); 367 }