1 /* Copyright ©2006-2007 Kris Maglione <fbsdaemon@gmail.com>
2  * See LICENSE file for license details.
3  */
4 #include "dat.h"
5 #include <math.h>
6 #include <stdio.h>
7 #include "fns.h"
8 
9 uint
frame_idx(Frame * f)10 frame_idx(Frame *f) {
11 	Frame *fp;
12 	uint i;
13 
14 	fp = f->area->frame;
15 	for(i = 1; fp != f; fp = fp->anext)
16 		i++;
17 	return i;
18 }
19 
20 Frame *
create_frame(Client * c,View * v)21 create_frame(Client *c, View *v) {
22 	static ushort id = 1;
23 	Frame *f;
24 
25 	f = emallocz(sizeof *f);
26 	f->id = id++;
27 	f->client = c;
28 	f->view = v;
29 
30 	if(c->sel) {
31 		f->revert = c->sel->revert;
32 		f->r = c->sel->r;
33 	}
34 	else{
35 		f->r = client2frame(f, gravclient(c, ZR));
36 		f->revert = f->r;
37 		c->sel = f;
38 	}
39 	f->collapsed = False;
40 
41 	return f;
42 }
43 
44 void
remove_frame(Frame * f)45 remove_frame(Frame *f) {
46 	Area *a;
47 
48 	a = f->area;
49 	if(f->aprev)
50 		f->aprev->anext = f->anext;
51 	if(f->anext)
52 		f->anext->aprev = f->aprev;
53 	if(f == a->frame)
54 		a->frame = f->anext;
55 
56 	if(a->floating) {
57 		if(f->sprev)
58 			f->sprev->snext = f->snext;
59 		if(f->snext)
60 			f->snext->sprev = f->sprev;
61 		if(f == a->stack)
62 			a->stack = f->snext;
63 	}
64 	f->anext = f->aprev = f->snext = f->sprev = nil;
65 }
66 
67 void
insert_frame(Frame * pos,Frame * f)68 insert_frame(Frame *pos, Frame *f) {
69 	Area *a;
70 
71 	a = f->area;
72 
73 	if(pos) {
74 		f->aprev = pos;
75 		f->anext = pos->anext;
76 	}else {
77 		f->anext = f->area->frame;
78 		f->area->frame = f;
79 	}
80 	if(f->aprev)
81 		f->aprev->anext = f;
82 	if(f->anext)
83 		f->anext->aprev = f;
84 
85 	if(a->floating) {
86 		f->snext = a->stack;
87 		a->stack = f;
88 		if(f->snext)
89 			f->snext->sprev = f;
90 	}
91 }
92 
93 Bool
frame_to_top(Frame * f)94 frame_to_top(Frame *f) {
95 	Area *a;
96 
97 	a = f->area;
98 	if(!a->floating || f == a->stack)
99 		return False;
100 
101 	if(f->sprev)
102 		f->sprev->snext = f->snext;
103 	if(f->snext)
104 		f->snext->sprev = f->sprev;
105 
106 	f->snext = a->stack;
107 	a->stack = f;
108 	f->sprev = nil;
109 	if(f->snext)
110 		f->snext->sprev = f;
111 
112 	return True;
113 }
114 
115 /* Handlers */
116 static void
bup_event(Window * w,XButtonEvent * e)117 bup_event(Window *w, XButtonEvent *e) {
118 	write_event("ClientClick %C %d\n", w->aux, e->button);
119 }
120 
121 static void
bdown_event(Window * w,XButtonEvent * e)122 bdown_event(Window *w, XButtonEvent *e) {
123 	Frame *f;
124 	Client *c;
125 
126 	c = w->aux;
127 	f = c->sel;
128 
129 	if((e->state & def.mod) == def.mod) {
130 		switch(e->button) {
131 		case Button1:
132 			do_mouse_resize(c, False, CENTER);
133 			focus(c, True);
134 			frame_to_top(f);
135 			focus(c, True);
136 			break;
137 		case Button3:
138 			do_mouse_resize(c, False, quadrant(f->r, Pt(e->x_root, e->y_root)));
139 			frame_to_top(f);
140 			focus(c, True);
141 			break;
142 		default:
143 			XAllowEvents(display, ReplayPointer, e->time);
144 			break;
145 		}
146 		if(e->button != Button1)
147 			XUngrabPointer(display, e->time);
148 	}else{
149 		if(e->button == Button1) {
150 			if(frame_to_top(f))
151 				restack_view(f->view);
152 			else if(ptinrect(Pt(e->x, e->y), f->grabbox))
153 				do_mouse_resize(c, True, CENTER);
154 			else if(f->area->floating)
155 				if(!e->subwindow && !ptinrect(Pt(e->x, e->y), f->titlebar))
156 					do_mouse_resize(c, False, quadrant(f->r, Pt(e->x_root, e->y_root)));
157 
158 			if(f->client != selclient())
159 				focus(c, True);
160 		}
161 		if(e->subwindow)
162 			XAllowEvents(display, ReplayPointer, e->time);
163 		else {
164 			/* Ungrab so a menu can receive events before the button is released */
165 			XUngrabPointer(display, e->time);
166 			XSync(display, False);
167 
168 			write_event("ClientMouseDown %C %d\n", f->client, e->button);
169 		}
170 	}
171 }
172 
173 static void
enter_event(Window * w,XCrossingEvent * e)174 enter_event(Window *w, XCrossingEvent *e) {
175 	Client *c;
176 	Frame *f;
177 
178 	c = w->aux;
179 	f = c->sel;
180 	if(screen->focus != c) {
181 		Dprint("enter_notify(f) => %s\n", f->client->name);
182 		if(f->area->floating || !f->collapsed)
183 			focus(f->client, False);
184 	}
185 	set_frame_cursor(f, Pt(e->x, e->y));
186 }
187 
188 static void
expose_event(Window * w,XExposeEvent * e)189 expose_event(Window *w, XExposeEvent *e) {
190 	Client *c;
191 
192 	USED(e);
193 
194 	c = w->aux;
195 	if(c->sel)
196 		draw_frame(c->sel);
197 	else
198 		fprint(2, "Badness: Expose event on a client frame which shouldn't be visible: %C\n",
199 			c);
200 }
201 
202 static void
motion_event(Window * w,XMotionEvent * e)203 motion_event(Window *w, XMotionEvent *e) {
204 	Client *c;
205 
206 	c = w->aux;
207 	set_frame_cursor(c->sel, Pt(e->x, e->y));
208 }
209 
210 Handlers framehandler = {
211 	.bup = bup_event,
212 	.bdown = bdown_event,
213 	.enter = enter_event,
214 	.expose = expose_event,
215 	.motion = motion_event,
216 };
217 
218 Rectangle
frame2client(Frame * f,Rectangle r)219 frame2client(Frame *f, Rectangle r) {
220 	if(f == nil || f->area == nil || f->area->floating) {
221 		r.max.x -= def.border * 2;
222 		r.max.y -= frame_delta_h();
223 		if(f) {
224 			if(f->client->borderless) {
225 				r.max.x += 2 * def.border;
226 				r.max.y += def.border;
227 			}
228 			if(f->client->titleless)
229 				r.max.y += labelh(def.font);
230 		}
231 	}else {
232 		r.max.x -= 2;
233 		r.max.y -= labelh(def.font) + 1;
234 	}
235 	r.max.x = max(r.min.x+1, r.max.x);
236 	r.max.y = max(r.min.y+1, r.max.y);
237 	return r;
238 }
239 
240 Rectangle
client2frame(Frame * f,Rectangle r)241 client2frame(Frame *f, Rectangle r) {
242 	if(f == nil || f->area == nil ||  f->area->floating) {
243 		r.max.x += def.border * 2;
244 		r.max.y += frame_delta_h();
245 		if(f) {
246 			if(f->client->borderless) {
247 				r.max.x -= 2 * def.border;
248 				r.max.y -= def.border;
249 			}
250 			if(f->client->titleless)
251 				r.max.y -= labelh(def.font);
252 		}
253 	}else {
254 		r.max.x += 2;
255 		r.max.y += labelh(def.font) + 1;
256 	}
257 	return r;
258 }
259 
260 void
resize_frame(Frame * f,Rectangle r)261 resize_frame(Frame *f, Rectangle r) {
262 	Align stickycorner;
263 	Point pt;
264 	Client *c;
265 
266 	c = f->client;
267 	stickycorner = get_sticky(f->r, r);
268 
269 	f->crect = frame_hints(f, r, stickycorner);
270 
271 	if(Dx(r) <= 0 || Dy(r) <= 0)
272 		fprint(2, "Badness: Frame rect: %R\n", r);
273 
274 	if(f->area->floating)
275 		f->r = f->crect;
276 	else
277 		f->r = r;
278 
279 	f->crect = frame2client(f, f->crect);
280 	f->crect = rectsubpt(f->crect, f->crect.min);
281 
282 	if(!f->area->floating && f->area->mode == Coldefault) {
283 		if(Dy(f->r) < 2 * labelh(def.font))
284 			f->collapsed = True;
285 		else
286 			f->collapsed = False;
287 	}
288 
289 	if(Dx(f->crect) < labelh(def.font)) {
290 		f->r.max.x = f->r.min.x + frame_delta_h();
291 		f->collapsed = True;
292 	}
293 
294 	if(f->collapsed) {
295 		f->r.max.y= f->r.min.y + labelh(def.font);
296 		f->crect = f->r;
297 	}
298 
299 	pt = ZP;
300 	if(!f->client->borderless || !f->area->floating)
301 		pt.y += 1;
302 	if(!f->client->titleless || !f->area->floating)
303 		pt.y += labelh(def.font) - 1;
304 
305 	if(f->area->floating) {
306 		if(c->fullscreen) {
307 			f->crect = screen->r;
308 			f->r = client2frame(f, f->crect);
309 			pt.x = (Dx(f->r) - Dx(f->crect)) / 2;
310 			f->r = rectsubpt(f->r, pt);
311 		}else
312 			f->r = constrain(f->r);
313 	}
314 	pt.x = (Dx(f->r) - Dx(f->crect)) / 2;
315 	f->crect = rectaddpt(f->crect, pt);
316 }
317 
318 void
set_frame_cursor(Frame * f,Point pt)319 set_frame_cursor(Frame *f, Point pt) {
320 	Rectangle r;
321 	Cursor cur;
322 
323 	if(f->area->floating
324 	&& !ptinrect(pt, f->titlebar)
325 	&& !ptinrect(pt, f->crect)) {
326 	 	r = rectsubpt(f->r, f->r.min);
327 	 	cur = cursor_of_quad(quadrant(r, pt));
328 		set_cursor(f->client, cur);
329 	} else
330 		set_cursor(f->client, cursor[CurNormal]);
331 }
332 
333 void
swap_frames(Frame * fa,Frame * fb)334 swap_frames(Frame *fa, Frame *fb) {
335 	Frame **fp;
336 	Client *c;
337 
338 	if(fa == fb) return;
339 
340 	for(fp = &fa->client->frame; *fp; fp = &(*fp)->cnext)
341 		if(*fp == fa) break;
342 	*fp = (*fp)->cnext;
343 
344 	for(fp = &fb->client->frame; *fp; fp = &(*fp)->cnext)
345 		if(*fp == fb) break;
346 	*fp = (*fp)->cnext;
347 
348 	c = fa->client;
349 	fa->client = fb->client;
350 	fb->client = c;
351 	fb->cnext = c->frame;
352 	c->frame = fb;
353 
354 	c = fa->client;
355 	fa->cnext = c->frame;
356 	c->frame = fa;
357 
358 	if(c->sel && c->sel->view == screen->sel)
359 		focus_view(screen, c->sel->view);
360 }
361 
362 void
focus_frame(Frame * f,Bool restack)363 focus_frame(Frame *f, Bool restack) {
364 	View *v;
365 	Area *a, *old_a;
366 
367 	a = f->area;
368 	v = f->view;
369 	old_a = v->sel;
370 
371 	a->sel = f;
372 
373 	if(a != old_a)
374 		focus_area(f->area);
375 
376 	if(v != screen->sel)
377 		return;
378 
379 	focus_client(f->client);
380 
381 	if(!a->floating && ((a->mode == Colstack) || (a->mode == Colmax)))
382 		arrange_column(a, False);
383 
384 	if(restack)
385 		restack_view(v);
386 }
387 
388 int
frame_delta_h(void)389 frame_delta_h(void) {
390 	return def.border + labelh(def.font);
391 }
392 
393 void
draw_frame(Frame * f)394 draw_frame(Frame *f) {
395 	Rectangle r, fr;
396 	CTuple *col;
397 	Frame *tf;
398 
399 	if(f->view != screen->sel)
400 		return;
401 
402 	if(f->client == screen->focus)
403 		col = &def.focuscolor;
404 	else
405 		col = &def.normcolor;
406 	if(!f->area->floating && f->area->mode == Colmax)
407 		for(tf = f->area->frame; tf; tf=tf->anext)
408 			if(tf->client == screen->focus) {
409 				col = &def.focuscolor;
410 				break;
411 			}
412 	fr = f->client->framewin->r;
413 	fr = rectsubpt(fr, fr.min);
414 
415 	/* background */
416 	r = fr;
417 	fill(screen->ibuf, r, col->bg);
418 	border(screen->ibuf, r, 1, col->border);
419 
420 	r.max.y = r.min.y + labelh(def.font);
421 	border(screen->ibuf, r, 1, col->border);
422 
423 	f->titlebar = insetrect(r, 3);
424 	f->titlebar.max.y += 3;
425 
426 	/* grabbox */
427 	r.min = Pt(2, 2);
428 	r.max.x = r.min.x + def.font->height - 3;
429 	r.max.y -= 2;
430 	f->grabbox = r;
431 
432 	if(f->client->urgent)
433 		fill(screen->ibuf, r, col->fg);
434 	border(screen->ibuf, r, 1, col->border);
435 
436 	r.min.x = r.max.x;
437 	r.max.x = fr.max.x;
438 	r.min.y = 0;
439 	r.max.y = labelh(def.font);
440 	drawstring(screen->ibuf, def.font, r, WEST,
441 			f->client->name, col->fg);
442 
443 	XSetWindowBackgroundPixmap(display, f->client->framewin->w, None);
444 	copyimage(f->client->framewin, fr, screen->ibuf, ZP);
445 	XSync(display, False);
446 }
447 
448 void
draw_frames(void)449 draw_frames(void) {
450 	Client *c;
451 
452 	for(c=client; c; c=c->next)
453 		if(c->sel && c->sel->view == screen->sel)
454 			draw_frame(c->sel);
455 }
456 
457 Rectangle
constrain(Rectangle r)458 constrain(Rectangle r) {
459 	Rectangle sr;
460 	Point p;
461 
462 	sr = screen->r;
463 	sr.max.y = screen->brect.min.y;
464 
465 	if(Dx(r) > Dx(sr))
466 		r.max.x = r.min.x + Dx(sr);
467 	if(Dy(r) > Dy(sr))
468 		r.max.y = r.min.y + Dy(sr);
469 
470 	sr = insetrect(sr, Dy(screen->brect));
471 	p = ZP;
472 	p.x -= min(r.max.x - sr.min.x, 0);
473 	p.x -= max(r.min.x - sr.max.x, 0);
474 	p.y -= min(r.max.y - sr.min.y, 0);
475 	p.y -= max(r.min.y - sr.max.y, 0);
476 	return rectaddpt(r, p);
477 }
478