2wm

dual window manager prototype (minimalist dwm with no tags, just one view)
git clone git://git.suckless.org/2wm
Log | Files | Refs | README | LICENSE

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 }