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