1 /* Copyright ©2007 Kris Maglione <fbsdaemon@gmail.com>
2  * See LICENSE file for license details.
3  */
4 #include "dat.h"
5 #include <assert.h>
6 #include <math.h>
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <bio.h>
12 #include "fns.h"
13 
14 Point ZP = {0, 0};
15 Rectangle ZR = {{0, 0}, {0, 0}};
16 
17 static Map wmap, amap;
18 static MapEnt *wbucket[137];
19 static MapEnt *abucket[137];
20 
21 #ifdef NO_UTF8
22 #define	XTEXT(func)	Xmb##func
23 #else
24 #define	XTEXT(func)	Xutf8##func
25 #endif
26 
27 XRectangle
XRect(Rectangle r)28 XRect(Rectangle r) {
29 	XRectangle xr;
30 
31 	xr.x = r.min.x;
32 	xr.y = r.min.y;
33 	xr.width = Dx(r);
34 	xr.height = Dy(r);
35 	return xr;
36 }
37 
38 int
eqrect(Rectangle a,Rectangle b)39 eqrect(Rectangle a, Rectangle b) {
40 	return a.min.x==b.min.x && a.max.x==b.max.x
41 		&& a.min.y==b.min.y && a.max.y==b.max.y;
42 }
43 
44 int
eqpt(Point p,Point q)45 eqpt(Point p, Point q) {
46 	return p.x==q.x && p.y==q.y;
47 }
48 
49 Point
addpt(Point p,Point q)50 addpt(Point p, Point q) {
51 	p.x += q.x;
52 	p.y += q.y;
53 	return p;
54 }
55 
56 Point
subpt(Point p,Point q)57 subpt(Point p, Point q) {
58 	p.x -= q.x;
59 	p.y -= q.y;
60 	return p;
61 }
62 
63 Point
mulpt(Point p,Point q)64 mulpt(Point p, Point q) {
65 	p.x *= q.x;
66 	p.y *= q.y;
67 	return p;
68 }
69 
70 Point
divpt(Point p,Point q)71 divpt(Point p, Point q) {
72 	p.x /= q.x;
73 	p.y /= q.y;
74 	return p;
75 }
76 
77 Rectangle
insetrect(Rectangle r,int n)78 insetrect(Rectangle r, int n) {
79 	r.min.x += n;
80 	r.min.y += n;
81 	r.max.x -= n;
82 	r.max.y -= n;
83 	return r;
84 }
85 
86 Rectangle
rectaddpt(Rectangle r,Point p)87 rectaddpt(Rectangle r, Point p) {
88 	r.min.x += p.x;
89 	r.max.x += p.x;
90 	r.min.y += p.y;
91 	r.max.y += p.y;
92 	return r;
93 }
94 
95 Rectangle
rectsubpt(Rectangle r,Point p)96 rectsubpt(Rectangle r, Point p) {
97 	r.min.x -= p.x;
98 	r.max.x -= p.x;
99 	r.min.y -= p.y;
100 	r.max.y -= p.y;
101 	return r;
102 }
103 
104 static int
Rfmt(Fmt * f)105 Rfmt(Fmt *f) {
106 	Rectangle r;
107 
108 	r = va_arg(f->args, Rectangle);
109 	return fmtprint(f, "%P+%dx%d", r.min, Dx(r), Dy(r));
110 }
111 
112 static int
Pfmt(Fmt * f)113 Pfmt(Fmt *f) {
114 	Point p;
115 
116 	p = va_arg(f->args, Point);
117 	return fmtprint(f, "(%d,%d)", p.x, p.y);
118 }
119 
120 static int
Wfmt(Fmt * f)121 Wfmt(Fmt *f) {
122 	Window *w;
123 
124 	w = va_arg(f->args, Window*);
125 	return fmtprint(f, "0x%ulx", w->w);
126 }
127 
128 /* Init */
129 void
initdisplay(void)130 initdisplay(void) {
131 	if(!(display = XOpenDisplay(nil)))
132 		fatal("couldn't open display");
133 	scr.screen = DefaultScreen(display);
134 	scr.colormap = DefaultColormap(display, scr.screen);
135 	scr.visual = DefaultVisual(display, scr.screen);
136 	scr.gc = DefaultGC(display, scr.screen);
137 	scr.depth = DefaultDepth(display, scr.screen);
138 
139 	scr.white = WhitePixel(display, scr.screen);
140 	scr.black = BlackPixel(display, scr.screen);
141 
142 	scr.root.w = RootWindow(display, scr.screen);
143 	scr.root.r = Rect(0, 0, DisplayWidth(display, scr.screen), DisplayHeight(display, scr.screen));
144 	scr.rect = scr.root.r;
145 
146 	scr.root.parent = &scr.root;
147 
148 	wmap.bucket = wbucket;
149 	wmap.nhash = nelem(wbucket);
150 	amap.bucket = abucket;
151 	amap.nhash = nelem(abucket);
152 
153 	fmtinstall('R', Rfmt);
154 	fmtinstall('P', Pfmt);
155 	fmtinstall('W', Wfmt);
156 }
157 
158 /* Images */
159 Image *
allocimage(int w,int h,int depth)160 allocimage(int w, int h, int depth) {
161 	Image *img;
162 
163 	img = emallocz(sizeof *img);
164 	img->type = WImage;
165 	img->image = XCreatePixmap(display, scr.root.w, w, h, depth);
166 	img->gc = XCreateGC(display, img->image, 0, nil);
167 	img->depth = depth;
168 	img->r = Rect(0, 0, w, h);
169 	return img;
170 }
171 
172 void
freeimage(Image * img)173 freeimage(Image *img) {
174 	assert(img->type == WImage);
175 
176 	XFreePixmap(display, img->image);
177 	XFreeGC(display, img->gc);
178 	free(img);
179 }
180 
181 /* Windows */
182 Window *
createwindow(Window * parent,Rectangle r,int depth,uint class,WinAttr * wa,int valmask)183 createwindow(Window *parent, Rectangle r, int depth, uint class,
184 		WinAttr *wa, int valmask)
185 		{
186 	Window *w;
187 
188 	assert(parent->type == WWindow);
189 
190 	w = emallocz(sizeof *w);
191 	w->type = WWindow;
192 	w->parent = parent;
193 
194 	w->w =  XCreateWindow(display, parent->w, r.min.x, r.min.y, Dx(r), Dy(r),
195 				0 /* border */, depth, class, scr.visual, valmask, wa);
196 
197 	if(class != InputOnly) {
198 		w->gc = XCreateGC(display, w->w, 0, nil);
199 		w->image = w->w;
200 	}
201 
202 	w->r = r;
203 	w->depth = depth;
204 	return w;
205 }
206 
207 void
reparentwindow(Window * w,Window * par,Point p)208 reparentwindow(Window *w, Window *par, Point p) {
209 	XReparentWindow(display, w->w, par->w, p.x, p.y);
210 	w->parent = par;
211 	w->r = rectsubpt(w->r, w->r.min);
212 	w->r = rectaddpt(w->r, p);
213 }
214 
215 void
destroywindow(Window * w)216 destroywindow(Window *w) {
217 	assert(w->type == WWindow);
218 	sethandler(w, nil);
219 	if(w->gc)
220 		XFreeGC(display, w->gc);
221 	XDestroyWindow(display, w->w);
222 	free(w);
223 }
224 
225 void
setwinattr(Window * w,WinAttr * wa,int valmask)226 setwinattr(Window *w, WinAttr *wa, int valmask) {
227 	assert(w->type == WWindow);
228 	XChangeWindowAttributes(display, w->w, valmask, wa);
229 }
230 
231 void
reshapewin(Window * w,Rectangle r)232 reshapewin(Window *w, Rectangle r) {
233 	assert(w->type == WWindow);
234 	if(!eqrect(r, w->r))
235 		XMoveResizeWindow(display, w->w, r.min.x, r.min.y, Dx(r), Dy(r));
236 	w->r = r;
237 }
238 
239 void
movewin(Window * w,Point pt)240 movewin(Window *w, Point pt) {
241 	Rectangle r;
242 
243 	assert(w->type == WWindow);
244 	r = rectsubpt(w->r, w->r.min);
245 	r = rectaddpt(r, pt);
246 	reshapewin(w, r);
247 }
248 
249 int
mapwin(Window * w)250 mapwin(Window *w) {
251 	if(!w->mapped) {
252 		XMapWindow(display, w->w);
253 		w->mapped = 1;
254 		return 1;
255 	}
256 	return 0;
257 }
258 
259 int
unmapwin(Window * w)260 unmapwin(Window *w) {
261 	if(w->mapped) {
262 		XUnmapWindow(display, w->w);
263 		w->mapped = 0;
264 		w->unmapped++;
265 		return 1;
266 	}
267 	return 0;
268 }
269 
270 void
raisewin(Window * w)271 raisewin(Window *w) {
272 	XRaiseWindow(display, w->w);
273 }
274 
275 void
lowerwin(Window * w)276 lowerwin(Window *w) {
277 	XLowerWindow(display, w->w);
278 }
279 
280 Handlers*
sethandler(Window * w,Handlers * new)281 sethandler(Window *w, Handlers *new) {
282 	Handlers *old;
283 	MapEnt *e;
284 
285 	assert(w->type == WWindow);
286 	assert((w->prev != nil && w->next != nil) || w->next == w->prev);
287 
288 	if(new == nil)
289 		maprm(&wmap, (ulong)w->w);
290 	else {
291 		e = mapget(&wmap, (ulong)w->w, 1);
292 		e->val = w;
293 	}
294 	old = w->handler;
295 	w->handler = new;
296 	return old;
297 }
298 
299 Window*
findwin(XWindow w)300 findwin(XWindow w) {
301 	MapEnt *e;
302 
303 	e = mapget(&wmap, (ulong)w, 0);
304 	if(e)
305 		return e->val;
306 	return nil;
307 }
308 
309 uint
winprotocols(Window * w)310 winprotocols(Window *w) {
311 	Atom *protocols;
312 	Atom actual, delete;
313 	int i, n, protos;
314 
315 	n = getproperty(w, "WM_PROTOCOLS", "ATOM", &actual, 0L, (void*)&protocols, 20L);
316 	if(n == 0)
317 		return 0;
318 
319 	protos = 0;
320 	delete = xatom("WM_DELETE_WINDOW");
321 	for(i = 0; i < n; i++) {
322 		if(protocols[i] == delete)
323 			protos |= WM_PROTOCOL_DELWIN;
324 	}
325 
326 	free(protocols);
327 	return protos;
328 }
329 
330 /* Shape */
331 void
setshapemask(Window * dst,Image * src,Point pt)332 setshapemask(Window *dst, Image *src, Point pt) {
333 	XShapeCombineMask (display, dst->w,
334 		ShapeBounding, pt.x, pt.y, src->image, ShapeSet);
335 }
336 
337 static void
setgccol(Image * dst,ulong col)338 setgccol(Image *dst, ulong col) {
339 	XSetForeground(display, dst->gc, col);
340 }
341 
342 /* Drawing */
343 void
border(Image * dst,Rectangle r,int w,ulong col)344 border(Image *dst, Rectangle r, int w, ulong col) {
345 	if(w == 0)
346 		return;
347 
348 	r = insetrect(r, w/2);
349 	r.max.x -= w%2;
350 	r.max.y -= w%2;
351 
352 	XSetLineAttributes(display, dst->gc, w, LineSolid, CapButt, JoinMiter);
353 	setgccol(dst, col);
354 	XDrawRectangle(display, dst->image, dst->gc,
355 			r.min.x, r.min.y, Dx(r), Dy(r));
356 }
357 
358 void
fill(Image * dst,Rectangle r,ulong col)359 fill(Image *dst, Rectangle r, ulong col) {
360 	setgccol(dst, col);
361 	XFillRectangle(display, dst->image, dst->gc,
362 		r.min.x, r.min.y, Dx(r), Dy(r));
363 }
364 
365 static XPoint*
convpts(Point * pt,int np)366 convpts(Point *pt, int np) {
367 	XPoint *rp;
368 	int i;
369 
370 	rp = emalloc(np * sizeof(*rp));
371 	for(i = 0; i < np; i++) {
372 		rp[i].x = pt[i].x;
373 		rp[i].y = pt[i].y;
374 	}
375 	return rp;
376 }
377 
378 void
drawpoly(Image * dst,Point * pt,int np,int cap,int w,ulong col)379 drawpoly(Image *dst, Point *pt, int np, int cap, int w, ulong col) {
380 	XPoint *xp;
381 
382 	xp = convpts(pt, np);
383 	XSetLineAttributes(display, dst->gc, w, LineSolid, cap, JoinMiter);
384 	setgccol(dst, col);
385 	XDrawLines(display, dst->image, dst->gc, xp, np, CoordModeOrigin);
386 	free(xp);
387 }
388 
389 void
fillpoly(Image * dst,Point * pt,int np,ulong col)390 fillpoly(Image *dst, Point *pt, int np, ulong col) {
391 	XPoint *xp;
392 
393 	xp = convpts(pt, np);
394 	setgccol(dst, col);
395 	XFillPolygon(display, dst->image, dst->gc, xp, np, Complex, CoordModeOrigin);
396 	free(xp);
397 }
398 
399 void
drawline(Image * dst,Point p1,Point p2,int cap,int w,ulong col)400 drawline(Image *dst, Point p1, Point p2, int cap, int w, ulong col) {
401 	XSetLineAttributes(display, dst->gc, w, LineSolid, cap, JoinMiter);
402 	setgccol(dst, col);
403 	XDrawLine(display, dst->image, dst->gc, p1.x, p1.y, p2.x, p2.y);
404 }
405 
406 void
drawstring(Image * dst,Font * font,Rectangle r,Align align,char * text,ulong col)407 drawstring(Image *dst, Font *font,
408 		Rectangle r, Align align,
409 		char *text, ulong col) {
410 	char *buf;
411 	uint x, y, w, h, len;
412 	int shortened;
413 
414 	shortened = 0;
415 
416 	len = strlen(text);
417 	buf = emalloc(len+1);
418 	memcpy(buf, text, len+1);
419 
420 	h = font->ascent + font->descent;
421 	y = r.min.y + Dy(r) / 2 - h / 2 + font->ascent;
422 
423 	/* shorten text if necessary */
424 	SET(w);
425 	while(len > 0) {
426 		w = textwidth_l(font, buf, len + min(shortened, 3));
427 		if(w <= Dx(r) - (font->height & ~1))
428 			break;
429 		while(len > 0 && (buf[--len]&0xC0) == 0x80)
430 			buf[len] = '.';
431 		buf[len] = '.';
432 		shortened++;
433 	}
434 
435 	if(len == 0 || w > Dx(r))
436 		goto done;
437 
438 	/* mark shortened info in the string */
439 	if(shortened)
440 		len += min(shortened, 3);
441 
442 	switch (align) {
443 	case EAST:
444 		x = r.max.x - (w + (font->height / 2));
445 		break;
446 	default:
447 		x = r.min.x + (font->height / 2);
448 		break;
449 	}
450 
451 	setgccol(dst, col);
452 	if(font->set)
453 		XTEXT(DrawString)(display, dst->image,
454 				font->set, dst->gc,
455 				x, y,
456 				buf, len);
457 	else {
458 		XSetFont(display, dst->gc, font->xfont->fid);
459 		XDrawString(display, dst->image, dst->gc,
460 				x, y,
461 				buf, len);
462 	}
463 
464 done:
465 	free(buf);
466 }
467 
468 void
copyimage(Image * dst,Rectangle r,Image * src,Point p)469 copyimage(Image *dst, Rectangle r, Image *src, Point p) {
470 	XCopyArea(display,
471 		src->image, dst->image,
472 		dst->gc,
473 		r.min.x, r.min.y, Dx(r), Dy(r),
474 		p.x, p.y);
475 }
476 
477 /* Colors */
478 Bool
namedcolor(char * name,ulong * ret)479 namedcolor(char *name, ulong *ret) {
480 	XColor c, c2;
481 
482 	if(XAllocNamedColor(display, scr.colormap, name, &c, &c2)) {
483 		*ret = c.pixel;
484 		return 1;
485 	}
486 	return 0;
487 }
488 
489 Bool
loadcolor(CTuple * c,char * str)490 loadcolor(CTuple *c, char *str) {
491 	char buf[24];
492 
493 	utflcpy(buf, str, sizeof(buf));
494 	memcpy(c->colstr, str, sizeof c->colstr);
495 
496 	buf[7] = buf[15] = buf[23] = '\0';
497 	return namedcolor(buf, &c->fg)
498 		&& namedcolor(buf+8, &c->bg)
499 		&& namedcolor(buf+16, &c->border);
500 }
501 
502 /* Fonts */
503 Font *
loadfont(char * name)504 loadfont(char *name) {
505 	Biobuf *b;
506 	Font *f;
507 	XFontStruct **xfonts;
508 	char **missing, **font_names;
509 	int n, i;
510 
511 	missing = nil;
512 	f = emallocz(sizeof *f);
513 	f->name = estrdup(name);
514 	f->set = XCreateFontSet(display, name, &missing, &n, nil);
515 	if(missing) {
516 		b = Bfdopen(dup(2), O_WRONLY);
517 		Bprint(b, "%s: note: missing fontset%s for '%s':", argv0,
518 				(n > 1 ? "s" : ""), name);
519 		for(i = 0; i < n; i++)
520 			Bprint(b, "%s %s", (i ? "," : ""), missing[i]);
521 		Bprint(b, "\n");
522 		Bterm(b);
523 		freestringlist(missing);
524 	}
525 
526 	if(f->set) {
527 		XFontsOfFontSet(f->set, &xfonts, &font_names);
528 		f->ascent = xfonts[0]->ascent;
529 		f->descent = xfonts[0]->descent;
530 	}
531 	else {
532 		f->xfont = XLoadQueryFont(display, name);
533 		if(!f->xfont) {
534 			fprint(2, "%s: cannot load font: %s\n", argv0, name);
535 			freefont(f);
536 			return nil;
537 		}
538 
539 		f->ascent = f->xfont->ascent;
540 		f->descent = f->xfont->descent;
541 	}
542 	f->height = f->ascent + f->descent;
543 	return f;
544 }
545 
546 void
freefont(Font * f)547 freefont(Font *f) {
548 	if(f->set)
549 		XFreeFontSet(display, f->set);
550 	if(f->xfont)
551 		XFreeFont(display, f->xfont);
552 	free(f->name);
553 	free(f);
554 }
555 
556 uint
textwidth_l(Font * font,char * text,uint len)557 textwidth_l(Font *font, char *text, uint len) {
558 	XRectangle r;
559 
560 	if(font->set) {
561 		XTEXT(TextExtents)(font->set, text, len, &r, nil);
562 		return r.width;
563 	}
564 	return XTextWidth(font->xfont, text, len);
565 }
566 
567 uint
textwidth(Font * font,char * text)568 textwidth(Font *font, char *text) {
569 	return textwidth_l(font, text, strlen(text));
570 }
571 
572 uint
labelh(Font * font)573 labelh(Font *font) {
574 	return font->height + 2;
575 }
576 
577 /* Misc */
578 Atom
xatom(char * name)579 xatom(char *name) {
580 	MapEnt *e;
581 
582 	e = hashget(&amap, name, 1);
583 	if(e->val == nil)
584 		e->val = (void*)XInternAtom(display, name, False);
585 	return (Atom)e->val;
586 }
587 
588 void
changeproperty(Window * w,char * prop,char * type,int width,uchar data[],int n)589 changeproperty(Window *w, char *prop, char *type, int width, uchar data[], int n) {
590 	XChangeProperty(display, w->w, xatom(prop), xatom(type), width, PropModeReplace, data, n);
591 }
592 
593 void
changeprop_char(Window * w,char * prop,char * type,char data[],int len)594 changeprop_char(Window *w, char *prop, char *type, char data[], int len) {
595 	changeproperty(w, prop, type, 8, (uchar*)data, len);
596 }
597 
598 void
changeprop_short(Window * w,char * prop,char * type,short data[],int len)599 changeprop_short(Window *w, char *prop, char *type, short data[], int len) {
600 	changeproperty(w, prop, type, 16, (uchar*)data, len);
601 }
602 
603 void
changeprop_long(Window * w,char * prop,char * type,long data[],int len)604 changeprop_long(Window *w, char *prop, char *type, long data[], int len) {
605 	changeproperty(w, prop, type, 32, (uchar*)data, len);
606 }
607 
608 void
freestringlist(char * list[])609 freestringlist(char *list[]) {
610 	XFreeStringList(list);
611 }
612 
613 ulong
getproperty(Window * w,char * prop,char * type,Atom * actual,ulong offset,uchar ** ret,ulong length)614 getproperty(Window *w, char *prop, char *type, Atom *actual, ulong offset, uchar **ret, ulong length) {
615 	Atom typea;
616 	ulong n, extra;
617 	int status, format;
618 
619 	typea = (type ? xatom(type) : 0L);
620 
621 	status = XGetWindowProperty(display, w->w,
622 		xatom(prop), offset, length, False /* delete */,
623 		typea, actual, &format, &n, &extra, ret);
624 
625 	if(status != Success) {
626 		*ret = nil;
627 		return 0;
628 	}
629 	if(n == 0) {
630 		free(*ret);
631 		*ret = nil;
632 	}
633 	return n;
634 }
635 
636 int
gettextlistproperty(Window * w,char * name,char ** ret[])637 gettextlistproperty(Window *w, char *name, char **ret[]) {
638 	XTextProperty prop;
639 	char **list;
640 	int n;
641 
642 	*ret = nil;
643 	n = 0;
644 
645 	XGetTextProperty(display, w->w, &prop, xatom(name));
646 	if(prop.nitems > 0) {
647 		if(XTEXT(TextPropertyToTextList)(display, &prop, &list, &n) == Success)
648 			*ret = list;
649 		XFree(prop.value);
650 	}
651 	return n;
652 }
653 
654 char *
gettextproperty(Window * w,char * name)655 gettextproperty(Window *w, char *name) {
656 	char **list, *str;
657 	int n;
658 
659 	str = nil;
660 
661 	n = gettextlistproperty(w, name, &list);
662 	if(n > 0 && list) {
663 		str = estrdup(*list);
664 		freestringlist(list);
665 	}
666 
667 	return str;
668 }
669 
670 void
setfocus(Window * w,int mode)671 setfocus(Window *w, int mode) {
672 	if(w) {
673 		XSetInputFocus(display, w->w, mode, CurrentTime);
674 	} else { /* "relinquish" focus */
675 		XSetInputFocus(display, PointerRoot, mode, CurrentTime);
676 	}
677 }
678 
679 /* Mouse */
680 Point
querypointer(Window * w)681 querypointer(Window *w) {
682 	XWindow dummy;
683 	Point pt;
684 	uint ui;
685 	int i;
686 
687 	XQueryPointer(display, w->w, &dummy, &dummy, &i, &i, &pt.x, &pt.y, &ui);
688 	return pt;
689 }
690 
691 void
warppointer(Point pt)692 warppointer(Point pt) {
693 	XWarpPointer(display,
694 		/* src, dest w */ None, scr.root.w,
695 		/* src_rect */	0, 0, 0, 0,
696 		/* target */	pt.x, pt.y);
697 }
698 
699 Point
translate(Window * src,Window * dst,Point sp)700 translate(Window *src, Window *dst, Point sp) {
701 	Point pt;
702 	XWindow w;
703 
704 	XTranslateCoordinates(display, src->w, dst->w, sp.x, sp.y, &pt.x, &pt.y, &w);
705 	return pt;
706 }
707 
708 int
grabpointer(Window * w,Window * confine,Cursor cur,int mask)709 grabpointer(Window *w, Window *confine, Cursor cur, int mask) {
710 	XWindow cw;
711 
712 	cw = None;
713 	if(confine)
714 		cw = confine->w;
715 	return XGrabPointer(display, w->w, False /* owner events */, mask,
716 		GrabModeAsync, GrabModeAsync, cw, cur, CurrentTime
717 		) == GrabSuccess;
718 }
719 
720 void
ungrabpointer(void)721 ungrabpointer(void) {
722 	XUngrabPointer(display, CurrentTime);
723 }
724 
725 /* Insanity */
726 void
sethints(Window * w)727 sethints(Window *w) {
728 	enum { MaxInt = ((uint)(1<<(8*sizeof(int)-1))-1) };
729 	XSizeHints xs;
730 	WinHints *h;
731 	Point p;
732 	long size;
733 
734 	if(w->hints == nil)
735 		w->hints = emalloc(sizeof *h);
736 
737 	h = w->hints;
738 	memset(h, 0, sizeof *h);
739 
740 	h->max = Pt(MaxInt, MaxInt);
741 	h->inc = Pt(1,1);
742 
743 	if(!XGetWMNormalHints(display, w->w, &xs, &size))
744 		return;
745 
746 	if(xs.flags&PMinSize) {
747 		p.x = xs.min_width;
748 		p.y = xs.min_height;
749 		h->min = p;
750 	}
751 	if(xs.flags&PMaxSize) {
752 		p.x = xs.max_width;
753 		p.y = xs.max_height;
754 		h->max = p;
755 	}
756 
757 	h->base = h->min;
758 	if(xs.flags&PBaseSize) {
759 		p.x = xs.base_width;
760 		p.y = xs.base_height;
761 		h->base = p;
762 		h->baspect = p;
763 	}
764 
765 	if(xs.flags&PResizeInc) {
766 		h->inc.x = max(xs.width_inc, 1);
767 		h->inc.y = max(xs.height_inc, 1);
768 	}
769 
770 	if(xs.flags&PAspect) {
771 		p.x = xs.min_aspect.x;
772 		p.y = xs.min_aspect.y;
773 		h->aspect.min = p;
774 		p.x = xs.max_aspect.x;
775 		p.y = xs.max_aspect.y;
776 		h->aspect.max = p;
777 	}
778 
779 	h->position = ((xs.flags&(USPosition|PPosition)) != 0);
780 
781 	p = ZP;
782 	if((xs.flags&PWinGravity) == 0)
783 		xs.win_gravity = NorthWestGravity;
784 
785 	switch (xs.win_gravity) {
786 	case EastGravity: case CenterGravity: case WestGravity:
787 		p.y = 1;
788 		break;
789 	case SouthEastGravity: case SouthGravity: case SouthWestGravity:
790 		p.y = 2;
791 		break;
792 	}
793 	switch (xs.win_gravity) {
794 	case NorthGravity: case CenterGravity: case SouthGravity:
795 		p.x = 1;
796 		break;
797 	case NorthEastGravity: case EastGravity: case SouthEastGravity:
798 		p.x = 2;
799 		break;
800 	}
801 	h->grav = p;
802 	h->gravstatic = (xs.win_gravity==StaticGravity);
803 }
804 
805 Rectangle
sizehint(WinHints * h,Rectangle r)806 sizehint(WinHints *h, Rectangle r) {
807 	Point p, p2, o;
808 
809 	if(h == nil)
810 		return r;
811 
812 	o = r.min;
813 	r = rectsubpt(r, o);
814 
815 	/* Min/max */
816 	r.max.x = max(r.max.x, h->min.x);
817 	r.max.y = max(r.max.y, h->min.y);
818 	r.max.x = min(r.max.x, h->max.x);
819 	r.max.y = min(r.max.y, h->max.y);
820 
821 	/* Increment */
822 	p = subpt(r.max, h->base);
823 	r.max.x -= p.x % h->inc.x;
824 	r.max.y -= p.y % h->inc.y;
825 
826 	/* Aspect */
827 	p = subpt(r.max, h->baspect);
828 	p.y = max(p.y, 1);
829 	p2 = h->aspect.min;
830 	if(p.x * p2.y / p.y < p2.x)
831 		r.max.y = h->baspect.y + p.x * p2.y / p2.x;
832 	p2 = h->aspect.max;
833 	if(p.x * p2.y / p.y > p2.x)
834 		r.max.x = h->baspect.x + p.y * p2.x / p2.y;
835 
836 	return rectaddpt(r, o);
837 }
838 
839 Rectangle
gravitate(Rectangle rc,Rectangle rf,Point grav)840 gravitate(Rectangle rc, Rectangle rf, Point grav) {
841 	Point d;
842 
843 	rf = rectsubpt(rf, rf.min);
844 
845 	/* Get delta between frame and client rectangles */
846 	d = subpt(rc.max, rc.min);
847 	d = subpt(rf.max, d);
848 
849 	/* Divide by 2 and apply gravity */
850 	d = divpt(d, Pt(2, 2));
851 	d = mulpt(d, grav);
852 
853 	return rectsubpt(rc, d);
854 }
855