wmii

git clone git://oldgit.suckless.org/wmii/
Log | Files | Refs | README | LICENSE

frame.c (13941B)


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