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