1 /* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
2  * See LICENSE file for license details.
3  */
4 #include "dat.h"
5 #include <math.h>
6 #include "fns.h"
7 
8 uint
frame_idx(Frame * f)9 frame_idx(Frame *f) {
10 	Frame *fp;
11 	uint i;
12 
13 	fp = f->area->frame;
14 	for(i = 1; fp != f; fp = fp->anext)
15 		i++;
16 	return i;
17 }
18 
19 Frame*
frame_create(Client * c,View * v)20 frame_create(Client *c, View *v) {
21 	static ushort id = 1;
22 	Frame *f;
23 
24 	f = emallocz(sizeof *f);
25 	f->id = id++;
26 	f->client = c;
27 	f->view = v;
28 
29 	if(c->sel) {
30 		f->floatr = c->sel->floatr;
31 		f->r = c->sel->r;
32 	}else {
33 		f->r = client_grav(c, c->r);
34 		f->floatr = f->r;
35 		c->sel = f;
36 	}
37 	f->collapsed = false;
38 	f->screen = -1;
39 	f->oldarea = -1;
40 	f->oldscreen = -1;
41 
42 	return f;
43 }
44 
45 void
frame_remove(Frame * f)46 frame_remove(Frame *f) {
47 	Area *a;
48 
49 	a = f->area;
50 	if(f->aprev)
51 		f->aprev->anext = f->anext;
52 	if(f->anext)
53 		f->anext->aprev = f->aprev;
54 	if(f == a->frame)
55 		a->frame = f->anext;
56 
57 	if(a->floating) {
58 		if(f->sprev)
59 			f->sprev->snext = f->snext;
60 		if(f->snext)
61 			f->snext->sprev = f->sprev;
62 		if(f == a->stack)
63 			a->stack = f->snext;
64 	}
65 	f->anext = f->aprev = f->snext = f->sprev = nil;
66 }
67 
68 void
frame_insert(Frame * f,Frame * pos)69 frame_insert(Frame *f, Frame *pos) {
70 	Area *a;
71 
72 	a = f->area;
73 
74 	if(pos) {
75 		assert(pos != f);
76 		f->aprev = pos;
77 		f->anext = pos->anext;
78 	}else {
79 		assert(f->area->frame != f);
80 		f->anext = f->area->frame;
81 		f->area->frame = f;
82 	}
83 	if(f->aprev)
84 		f->aprev->anext = f;
85 	if(f->anext)
86 		f->anext->aprev = f;
87 
88 	if(a->floating) {
89 		assert(f->sprev == nil);
90 		frame_restack(f, nil);
91 	}
92 }
93 
94 bool
frame_restack(Frame * f,Frame * above)95 frame_restack(Frame *f, Frame *above) {
96 	Client *c;
97 	Frame *fp;
98 	Area *a;
99 
100 	c = f->client;
101 	a = f->area;
102 	if(!a->floating)
103 		return false;
104 	if(f == above)
105 		return false;
106 
107 	if(above == nil && !(c->w.ewmh.type & TypeDock))
108 		for(fp=a->stack; fp; fp=fp->snext)
109 			if(fp->client->w.ewmh.type & TypeDock)
110 				above = fp;
111 			else
112 				break;
113 
114 	if(f->sprev || f == a->stack)
115 	if(f->sprev == above)
116 		return false;
117 
118 	if(f->sprev)
119 		f->sprev->snext = f->snext;
120 	else if(f->snext)
121 		a->stack = f->snext;
122 	if(f->snext)
123 		f->snext->sprev = f->sprev;
124 
125 	f->sprev = above;
126 	if(above == nil) {
127 		f->snext = a->stack;
128 		a->stack = f;
129 	}
130 	else {
131 		f->snext = above->snext;
132 		above->snext = f;
133 	}
134 	if(f->snext)
135 		f->snext->sprev = f;
136 	assert(f->snext != f && f->sprev != f);
137 
138 	return true;
139 }
140 
141 /* Handlers */
142 static void
bup_event(Window * w,XButtonEvent * e)143 bup_event(Window *w, XButtonEvent *e) {
144 	if((e->state & def.mod) != def.mod)
145 		XAllowEvents(display, ReplayPointer, e->time);
146 	else
147 		XUngrabPointer(display, e->time);
148 	event("ClientClick %C %d\n", w->aux, e->button);
149 }
150 
151 static void
bdown_event(Window * w,XButtonEvent * e)152 bdown_event(Window *w, XButtonEvent *e) {
153 	Frame *f;
154 	Client *c;
155 
156 	c = w->aux;
157 	f = c->sel;
158 
159 	if((e->state & def.mod) == def.mod) {
160 		switch(e->button) {
161 		case Button1:
162 			focus(c, false);
163 			mouse_resize(c, Center, true);
164 			break;
165 		case Button2:
166 			frame_restack(f, nil);
167 			view_restack(f->view);
168 			focus(c, false);
169 			grabpointer(c->framewin, nil, cursor[CurNone], ButtonReleaseMask);
170 			break;
171 		case Button3:
172 			focus(c, false);
173 			mouse_resize(c, quadrant(f->r, Pt(e->x_root, e->y_root)), true);
174 			break;
175 		default:
176 			XAllowEvents(display, ReplayPointer, e->time);
177 			break;
178 		}
179 	}else {
180 		if(e->button == Button1) {
181 			if(!e->subwindow) {
182 				frame_restack(f, nil);
183 				view_restack(f->view);
184 				mouse_checkresize(f, Pt(e->x, e->y), true);
185 			}
186 
187 			if(f->client != selclient())
188 				focus(c, false);
189 		}
190 		if(e->subwindow)
191 			XAllowEvents(display, ReplayPointer, e->time);
192 		else {
193 			/* Ungrab so a menu can receive events before the button is released */
194 			XUngrabPointer(display, e->time);
195 			sync();
196 
197 			event("ClientMouseDown %C %d\n", f->client, e->button);
198 		}
199 	}
200 }
201 
202 static void
config_event(Window * w,XConfigureEvent * e)203 config_event(Window *w, XConfigureEvent *e) {
204 
205 	USED(w, e);
206 }
207 
208 static void
enter_event(Window * w,XCrossingEvent * e)209 enter_event(Window *w, XCrossingEvent *e) {
210 	Client *c;
211 	Frame *f;
212 
213 	c = w->aux;
214 	f = c->sel;
215 	if(disp.focus != c || selclient() != c) {
216 		Dprint(DFocus, "enter_notify(f) => [%C]%s%s\n",
217 		       f->client, f->client->name,
218 		       ignoreenter == e->serial ? " (ignored)" : "");
219 		if(e->detail != NotifyInferior)
220 		if(e->serial != ignoreenter && (f->area->floating || !f->collapsed))
221 		if(!(c->w.ewmh.type & TypeSplash))
222 			focus(f->client, false);
223 	}
224 	mouse_checkresize(f, Pt(e->x, e->y), false);
225 }
226 
227 static void
expose_event(Window * w,XExposeEvent * e)228 expose_event(Window *w, XExposeEvent *e) {
229 	Client *c;
230 
231 	USED(e);
232 
233 	c = w->aux;
234 	if(c->sel)
235 		frame_draw(c->sel);
236 	else
237 		fprint(2, "Badness: Expose event on a client frame which shouldn't be visible: %C\n",
238 			c);
239 }
240 
241 static void
motion_event(Window * w,XMotionEvent * e)242 motion_event(Window *w, XMotionEvent *e) {
243 	Client *c;
244 
245 	c = w->aux;
246 	mouse_checkresize(c->sel, Pt(e->x, e->y), false);
247 }
248 
249 Handlers framehandler = {
250 	.bup = bup_event,
251 	.bdown = bdown_event,
252 	.config = config_event,
253 	.enter = enter_event,
254 	.expose = expose_event,
255 	.motion = motion_event,
256 };
257 
258 WinHints
frame_gethints(Frame * f)259 frame_gethints(Frame *f) {
260 	WinHints h;
261 	Client *c;
262 	Rectangle r;
263 	Point d;
264 	int minh;
265 
266 	minh = labelh(def.font);
267 
268 	c = f->client;
269 	h = *c->w.hints;
270 
271 	r = frame_client2rect(c, ZR, f->area->floating);
272 	d = subpt(r.max, r.min);
273 
274 	if(!f->area->floating && def.incmode == IIgnore)
275 		h.inc = Pt(1, 1);
276 
277 	if(h.min.x < 2*minh)
278 		h.min.x = minh + (2*minh) % h.inc.x;
279 	if(h.min.y < minh)
280 		h.min.y = minh + minh % h.inc.y;
281 
282 	h.min.x += d.x;
283 	h.min.y += d.y;
284 	/* Guard against overflow. */
285 	h.max.x = max(h.max.x + d.x, h.max.x);
286 	h.max.y = max(h.max.y + d.y, h.max.y);
287 
288 	h.base.x += d.x;
289 	h.base.y += d.y;
290 	h.baspect.x += d.x;
291 	h.baspect.y += d.y;
292 
293 	h.group = 0;
294 	h.grav = ZP;
295 	h.gravstatic = 0;
296 	h.position = 0;
297 	return h;
298 }
299 
300 #define ADJ(PE, ME) \
301 	if(c->fullscreen >= 0)                       \
302 		return r;                            \
303 						     \
304 	if(!floating) {                              \
305 		r.min.x PE 1;                        \
306 		r.min.y PE labelh(def.font);         \
307 		r.max.x ME 1;                        \
308 		r.max.y ME 1;                        \
309 	}else {                                      \
310 		if(!c->borderless) {                 \
311 			r.min.x PE def.border;       \
312 			r.max.x ME def.border;       \
313 			r.max.y ME def.border;       \
314 		}                                    \
315 		if(!c->titleless)                    \
316 			r.min.y PE labelh(def.font); \
317 	}                                            \
318 
319 Rectangle
frame_rect2client(Client * c,Rectangle r,bool floating)320 frame_rect2client(Client *c, Rectangle r, bool floating) {
321 
322 	ADJ(+=, -=)
323 
324 	/* Force clients to be at least 1x1 */
325 	r.max.x = max(r.max.x, r.min.x+1);
326 	r.max.y = max(r.max.y, r.min.y+1);
327 	return r;
328 }
329 
330 Rectangle
frame_client2rect(Client * c,Rectangle r,bool floating)331 frame_client2rect(Client *c, Rectangle r, bool floating) {
332 
333 	ADJ(-=, +=)
334 
335 	return r;
336 }
337 
338 #undef ADJ
339 
340 void
frame_resize(Frame * f,Rectangle r)341 frame_resize(Frame *f, Rectangle r) {
342 	Client *c;
343 	Rectangle fr, cr;
344 	int collapsed, dx;
345 
346 	if(btassert("8 full", Dx(r) <= 0 || Dy(r) < 0
347 		           || Dy(r) == 0 && (!f->area->max || resizing)
348 			      && !f->collapsed)) {
349 		fprint(2, "Frame rect: %R\n", r);
350 		r.max.x = min(r.min.x+1, r.max.x);
351 		r.max.y = min(r.min.y+1, r.max.y);
352 	}
353 
354 	c = f->client;
355 	if(c->fullscreen >= 0) {
356 		f->r = screens[c->fullscreen]->r;
357 		f->crect = rectsetorigin(f->r, ZP);
358 		return;
359 	}
360 
361 	/*
362 	if(f->area->floating)
363 		f->collapsed = false;
364 	*/
365 
366 	fr = frame_hints(f, r, get_sticky(f->r, r));
367 	if(f->area->floating && !c->strut)
368 		fr = constrain(fr, -1);
369 
370 	/* Collapse managed frames which are too small */
371 	/* XXX. */
372 	collapsed = f->collapsed;
373 	if(!f->area->floating && f->area->mode == Coldefault) {
374 		f->collapsed = false;
375 		if(Dy(r) < 2 * labelh(def.font))
376 			f->collapsed = true;
377 	}
378 	if(collapsed != f->collapsed)
379 		ewmh_updatestate(c);
380 
381 	fr.max.x = max(fr.max.x, fr.min.x + 2*labelh(def.font));
382 	if(f->collapsed && f->area->floating)
383 		fr.max.y = fr.min.y + labelh(def.font);
384 
385 	cr = frame_rect2client(c, fr, f->area->floating);
386 	if(f->area->floating)
387 		f->r = fr;
388 	else {
389 		f->r = r;
390 		dx = Dx(r) - Dx(cr);
391 		dx -= 2 * (cr.min.x - fr.min.x);
392 		cr.min.x += dx / 2;
393 		cr.max.x += dx / 2;
394 	}
395 	f->crect = rectsubpt(cr, f->r.min);
396 
397 	if(f->area->floating && !f->collapsed)
398 		f->floatr = f->r;
399 }
400 
401 static void
pushlabel(Image * img,Rectangle * rp,char * s,CTuple * col)402 pushlabel(Image *img, Rectangle *rp, char *s, CTuple *col) {
403 	Rectangle r;
404 	int w;
405 
406 	w = textwidth(def.font, s) + def.font->height;
407 	w = min(w, Dx(*rp) - 30); /* Magic number. */
408 	if(w > 0) {
409 		r = *rp;
410 		rp->max.x -= w;
411 		if(0)
412 		drawline(img, Pt(rp->max.x, r.min.y+2),
413 			      Pt(rp->max.x, r.max.y-2),
414 			      CapButt, 1, col->border);
415 		drawstring(img, def.font, r, East,
416 			   s, col->fg);
417 	}
418 }
419 
420 void
frame_draw(Frame * f)421 frame_draw(Frame *f) {
422 	Rectangle r, fr;
423 	Client *c;
424 	CTuple *col;
425 	Image *img;
426 	char *s;
427 	uint w;
428 	int n, m;
429 
430 	if(f->view != selview)
431 		return;
432 	if(f->area == nil) /* Blech. */
433 		return;
434 
435 	c = f->client;
436 	img = *c->ibuf;
437 	fr = rectsetorigin(c->framewin->r, ZP);
438 
439 	/* Pick colors. */
440 	if(c == selclient() || c == disp.focus)
441 		col = &def.focuscolor;
442 	else
443 		col = &def.normcolor;
444 
445 	/* Background/border */
446 	r = fr;
447 	fill(img, r, col->bg);
448 	border(img, r, 1, col->border);
449 
450 	/* Title border */
451 	r.max.y = r.min.y + labelh(def.font);
452 	border(img, r, 1, col->border);
453 
454 	f->titlebar = insetrect(r, 3);
455 	f->titlebar.max.y += 3;
456 
457 	/* Odd focus. Unselected, with keyboard focus. */
458 	/* Draw a border just inside the titlebar. */
459 	if(c != selclient() && c == disp.focus) {
460 		border(img, insetrect(r, 1), 1, def.normcolor.bg);
461 		border(img, insetrect(r, 2), 1, def.focuscolor.border);
462 	}
463 
464 	/* grabbox */
465 	r.min = Pt(2, 2);
466 	r.max.y -= 2;
467 	r.max.x = r.min.x + Dy(r);
468 	f->grabbox = r;
469 
470 	if(c->urgent)
471 		fill(img, r, col->fg);
472 	border(img, r, 1, col->border);
473 
474 	/* Odd focus. Selected, without keyboard focus. */
475 	/* Draw a border around the grabbox. */
476 	if(c != disp.focus && col == &def.focuscolor)
477 		border(img, insetrect(r, -1), 1, def.normcolor.bg);
478 
479 	/* Draw a border on borderless+titleless selected apps. */
480 	if(f->area->floating && c->borderless && c->titleless && !c->fullscreen && c == selclient())
481 		setborder(c->framewin, def.border, def.focuscolor.border);
482 	else
483 		setborder(c->framewin, 0, def.focuscolor.border);
484 
485 	/* Label */
486 	r.min.x = r.max.x;
487 	r.max.x = fr.max.x;
488 	r.min.y = 0;
489 	r.max.y = labelh(def.font);
490 	/* Draw count on frames in 'max' columns. */
491 	if(f->area->max && !resizing) {
492 		/* XXX */
493 		n = stack_count(f, &m);
494 		s = smprint("%d/%d", m, n);
495 		pushlabel(img, &r, s, col);
496 		free(s);
497 	}
498 	/* Label clients with extra tags. */
499 	if((s = client_extratags(c))) {
500 		pushlabel(img, &r, s, col);
501 		free(s);
502 	}else /* Make sure floating clients have room for their indicators. */
503 	if(c->floating)
504 		r.max.x -= Dx(f->grabbox);
505 	w = drawstring(img, def.font, r, West,
506 			c->name, col->fg);
507 
508 	/* Draw inner border on floating clients. */
509 	if(f->area->floating) {
510 		r.min.x = r.min.x + w + 10;
511 		r.max.x += Dx(f->grabbox) - 2;
512 		r.min.y = f->grabbox.min.y;
513 		r.max.y = f->grabbox.max.y;
514 		border(img, r, 1, col->border);
515 	}
516 
517 	/* Border increment gaps... */
518 	r.min.y = f->crect.min.y;
519 	r.min.x = max(1, f->crect.min.x - 1);
520 	r.max.x = min(fr.max.x - 1, f->crect.max.x + 1);
521 	r.max.y = min(fr.max.y - 1, f->crect.max.y + 1);
522 	border(img, r, 1, col->border);
523 
524 	/* Why? Because some non-ICCCM-compliant apps feel the need to
525 	 * change the background properties of all of their ancestor windows
526 	 * in order to implement pseudo-transparency.
527 	 * What's more, the designers of X11 felt that it would be unfair to
528 	 * implementers to make it possible to detect, or forbid, such changes.
529 	 */
530 	XSetWindowBackgroundPixmap(display, c->framewin->xid, None);
531 
532 	copyimage(c->framewin, fr, img, ZP);
533 }
534 
535 void
frame_draw_all(void)536 frame_draw_all(void) {
537 	Client *c;
538 
539 	for(c=client; c; c=c->next)
540 		if(c->sel && c->sel->view == selview)
541 			frame_draw(c->sel);
542 }
543 
544 void
frame_swap(Frame * fa,Frame * fb)545 frame_swap(Frame *fa, Frame *fb) {
546 	Frame **fp;
547 	Client *c;
548 
549 	if(fa == fb) return;
550 
551 	for(fp = &fa->client->frame; *fp; fp = &fp[0]->cnext)
552 		if(*fp == fa) break;
553 	fp[0] = fp[0]->cnext;
554 
555 	for(fp = &fb->client->frame; *fp; fp = &fp[0]->cnext)
556 		if(*fp == fb) break;
557 	fp[0] = fp[0]->cnext;
558 
559 	c = fa->client;
560 	fa->client = fb->client;
561 	fb->client = c;
562 	fb->cnext = c->frame;
563 	c->frame = fb;
564 
565 	c = fa->client;
566 	fa->cnext = c->frame;
567 	c->frame = fa;
568 
569 	if(c->sel)
570 		view_update(c->sel->view);
571 }
572 
573 void
move_focus(Frame * old_f,Frame * f)574 move_focus(Frame *old_f, Frame *f) {
575 	int noinput;
576 
577 	noinput = (old_f && old_f->client->noinput) ||
578 		  (f && f->client->noinput) ||
579 		  disp.hasgrab != &c_root;
580 	if(noinput) {
581 		if(old_f)
582 			frame_draw(old_f);
583 		if(f)
584 			frame_draw(f);
585 	}
586 }
587 
588 void
frame_focus(Frame * f)589 frame_focus(Frame *f) {
590 	Frame *old_f, *ff;
591 	View *v;
592 	Area *a, *old_a;
593 
594 	v = f->view;
595 	a = f->area;
596 	old_a = v->sel;
597 
598 	if(0 && f->collapsed) {
599 		for(ff=f; ff->collapsed && ff->anext; ff=ff->anext)
600 			;
601 		for(; ff->collapsed && ff->aprev; ff=ff->aprev)
602 			;
603 		/* XXX */
604 		f->colr.max.y = f->colr.min.y + Dy(ff->colr);
605 		ff->colr.max.y = ff->colr.min.y + labelh(def.font);
606 	}else if(f->area->mode == Coldefault) {
607 		for(; f->collapsed && f->anext; f=f->anext)
608 			;
609 		for(; f->collapsed && f->aprev; f=f->aprev)
610 			;
611 	}
612 
613 	old_f = old_a->sel;
614 	a->sel = f;
615 
616 	if(a != old_a)
617 		area_focus(f->area);
618 	if(old_a != v->oldsel && f != old_f)
619 		v->oldsel = nil;
620 
621 	if(v != selview || a != v->sel || resizing)
622 		return;
623 
624 	move_focus(old_f, f);
625 	if(a->floating)
626 		float_arrange(a);
627 	client_focus(f->client);
628 
629 	/*
630 	if(!a->floating && ((a->mode == Colstack) || (a->mode == Colmax)))
631 	*/
632 		column_arrange(a, false);
633 }
634 
635 int
frame_delta_h(void)636 frame_delta_h(void) {
637 	return def.border + labelh(def.font);
638 }
639 
640 Rectangle
constrain(Rectangle r,int inset)641 constrain(Rectangle r, int inset) {
642 	WMScreen **sp;
643 	WMScreen *s, *sbest;
644 	Rectangle isect;
645 	Point p;
646 	int best, n;
647 
648 	if(inset < 0)
649 		inset = Dy(screen->brect);
650 	/*
651 	 * FIXME: This will cause problems for windows with
652 	 * D(r) < 2 * inset
653 	 */
654 
655 	SET(best);
656 	sbest = nil;
657 	for(sp=screens; (s = *sp); sp++) {
658 		if (!screen->showing)
659 			continue;
660 		isect = rect_intersection(r, insetrect(s->r, inset));
661 		if(Dx(isect) >= 0 && Dy(isect) >= 0)
662 			return r;
663 		if(Dx(isect) <= 0 && Dy(isect) <= 0)
664 			n = max(Dx(isect), Dy(isect));
665 		else
666 			n = min(Dx(isect), Dy(isect));
667 		if(!sbest || n > best) {
668 			sbest = s;
669 			best = n;
670 		}
671 	}
672 
673 	isect = insetrect(sbest->r, inset);
674 	p = ZP;
675 	p.x -= min(r.max.x - isect.min.x, 0);
676 	p.x -= max(r.min.x - isect.max.x, 0);
677 	p.y -= min(r.max.y - isect.min.y, 0);
678 	p.y -= max(r.min.y - isect.max.y, 0);
679 	return rectaddpt(r, p);
680 }
681 
682