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