wmii

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

view.c (12918B)


      1 /* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com>
      2  * Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
      3  * See LICENSE file for license details.
      4  */
      5 #include "dat.h"
      6 #include "fns.h"
      7 
      8 static bool
      9 empty_p(View *v) {
     10 	Frame *f;
     11 	Area *a;
     12 	char **p;
     13 	int cmp;
     14 	int s;
     15 
     16 	foreach_frame(v, s, a, f) {
     17 		cmp = 1;
     18 		for(p=f->client->retags; *p; p++) {
     19 			cmp = strcmp(*p, v->name);
     20 			if(cmp >= 0)
     21 				break;
     22 		}
     23 		if(cmp)
     24 			return false;
     25 	}
     26 	return true;
     27 }
     28 
     29 static void
     30 _view_select(View *v) {
     31 	if(selview != v) {
     32 		if(selview)
     33 			event("UnfocusTag %s\n",selview->name);
     34 		selview = v;
     35 		event("FocusTag %s\n", v->name);
     36 		event("AreaFocus %a\n", v->sel);
     37 		ewmh_updateview();
     38 	}
     39 }
     40 
     41 Client*
     42 view_selclient(View *v) {
     43 	if(v->sel && v->sel->sel)
     44 		return v->sel->sel->client;
     45 	return nil;
     46 }
     47 
     48 bool
     49 view_fullscreen_p(View *v, int scrn) {
     50 	Frame *f;
     51 
     52 	for(f=v->floating->frame; f; f=f->anext)
     53 		if(f->client->fullscreen == scrn)
     54 			return true;
     55 	return false;
     56 }
     57 
     58 View*
     59 view_create(const char *name) {
     60 	static ushort id = 1;
     61 	View **vp;
     62 	Client *c;
     63 	View *v;
     64 	int i;
     65 
     66 	for(vp=&view; *vp; vp=&(*vp)->next) {
     67 		i = strcmp((*vp)->name, name);
     68 		if(i == 0)
     69 			return *vp;
     70 		if(i > 0)
     71 			break;
     72 	}
     73 
     74 	v = emallocz(sizeof *v);
     75 	v->id = id++;
     76 	v->r = emallocz(nscreens * sizeof *v->r);
     77 	v->pad = emallocz(nscreens * sizeof *v->pad);
     78 
     79 	utflcpy(v->name, name, sizeof v->name);
     80 
     81 	event("CreateTag %s\n", v->name);
     82 	area_create(v, nil, screen->idx, 0);
     83 
     84 	v->areas = emallocz(nscreens * sizeof *v->areas);
     85 
     86 	for(i=0; i < nscreens; i++)
     87 		view_init(v, i);
     88 
     89 	area_focus(v->firstarea);
     90 
     91 	v->next = *vp;
     92 	*vp = v;
     93 
     94 	/* FIXME: Belongs elsewhere */
     95 	/* FIXME: Can do better. */
     96 	for(c=client; c; c=c->next)
     97 		if(c != kludge)
     98 			client_applytags(c, c->tags);
     99 
    100 	view_arrange(v);
    101 	if(!selview)
    102 		_view_select(v);
    103 	ewmh_updateviews();
    104 	return v;
    105 }
    106 
    107 void
    108 view_init(View *v, int iscreen) {
    109 	v->r[iscreen] = screens[iscreen]->r;
    110 	v->pad[iscreen] = ZR;
    111 	v->areas[iscreen] = nil;
    112 	column_new(v, nil, iscreen, 0);
    113 }
    114 
    115 void
    116 view_destroy(View *v) {
    117 	View **vp;
    118 	Frame *f;
    119 	View *tv;
    120 	Area *a;
    121 	int s;
    122 
    123 	if(v->dead)
    124 		return;
    125 	v->dead = true;
    126 
    127 	for(vp=&view; *vp; vp=&(*vp)->next)
    128 		if(*vp == v) break;
    129 	*vp = v->next;
    130 	assert(v != v->next);
    131 
    132 	/* Detach frames held here by regex tags. */
    133 	/* FIXME: Can do better. */
    134 	foreach_frame(v, s, a, f)
    135 		client_applytags(f->client, f->client->tags);
    136 
    137 	foreach_area(v, s, a)
    138 		area_destroy(a);
    139 
    140 	event("DestroyTag %s\n", v->name);
    141 
    142 	if(v == selview) {
    143 		for(tv=view; tv; tv=tv->next)
    144 			if(tv->next == *vp) break;
    145 		if(tv == nil)
    146 			tv = view;
    147 		if(tv)
    148 			view_focus(screen, tv);
    149 	}
    150 	free(v->areas);
    151 	free(v->r);
    152 	free(v->pad);
    153 	free(v);
    154 	ewmh_updateviews();
    155 }
    156 
    157 Area*
    158 view_findarea(View *v, int screen, int idx, bool create) {
    159 	Area *a;
    160 
    161 	assert(screen >= 0 && screen < nscreens);
    162 
    163 	for(a=v->areas[screen]; a && --idx > 0; a=a->next)
    164 		if(create && a->next == nil)
    165 			return area_create(v, a, screen, 0);
    166 	return a;
    167 }
    168 
    169 static void
    170 frames_update_sel(View *v) {
    171 	Frame *f;
    172 	Area *a;
    173 	int s;
    174 
    175 	foreach_frame(v, s, a, f)
    176 		f->client->sel = f;
    177 }
    178 
    179 /* Don't let increment hints take up more than half
    180  * of the screen, in either direction.
    181  */
    182 static Rectangle
    183 fix_rect(Rectangle old, Rectangle new) {
    184 	double r;
    185 
    186 	new = rect_intersection(new, old);
    187 
    188 	r = (Dy(old) - Dy(new)) / Dy(old);
    189 	if(r > .5) {
    190 		r -= .5;
    191 		new.min.y -= r * (new.min.y - old.min.y);
    192 		new.max.y += r * (old.max.y - new.max.y);
    193 	}
    194 	r = (Dx(old) - Dx(new)) / Dx(old);
    195 	if(r > .5) {
    196 		r -= .5;
    197 		new.min.x -= r * (new.min.x - old.min.x);
    198 		new.max.x += r * (old.max.x - new.max.x);
    199 	}
    200 	return new;
    201 }
    202 
    203 void
    204 view_update_rect(View *v) {
    205 	static Vector_rect vec;
    206 	static Vector_rect *vp;
    207 	Rectangle r, sr, rr, brect, scrnr;
    208 	WMScreen *scrn;
    209 	Strut *strut;
    210 	Frame *f;
    211 	int left, right, top, bottom;
    212 	int s, i;
    213 
    214 	/* XXX:
    215 	if(v != selview)
    216 		return false;
    217 	*/
    218 
    219 	top = 0;
    220 	left = 0;
    221 	right = 0;
    222 	bottom = 0;
    223 	vec.n = 0;
    224 	for(f=v->floating->frame; f; f=f->anext) {
    225 		strut = f->client->strut;
    226 		if(!strut)
    227 			continue;
    228 		/* Can do better in the future. */
    229 		top = max(top, strut->top.max.y);
    230 		left = max(left, strut->left.max.x);
    231 		right = min(right, strut->right.min.x);
    232 		bottom = min(bottom, strut->bottom.min.y);
    233 		vector_rpush(&vec, strut->top);
    234 		vector_rpush(&vec, strut->left);
    235 		vector_rpush(&vec, rectaddpt(strut->right, Pt(scr.rect.max.x, 0)));
    236 		vector_rpush(&vec, rectaddpt(strut->bottom, Pt(0, scr.rect.max.y)));
    237 	}
    238 	vp = unique_rects(&vec, scr.rect);
    239 	scrnr = scr.rect;
    240 	scrnr.min.y += top;
    241 	scrnr.min.x += left;
    242 	scrnr.max.x += right;
    243 	scrnr.max.y += bottom;
    244 
    245 	/* FIXME: Multihead. */
    246 	v->floating->r = scr.rect;
    247 
    248 	for(s=0; s < nscreens; s++) {
    249 		scrn = screens[s];
    250 		r = fix_rect(scrn->r, scrnr);
    251 
    252 		/* Ugly. Very, very ugly. */
    253 		/*
    254 		 * Try to find some rectangle near the edge of the
    255 		 * screen where the bar will fit. This way, for
    256 		 * instance, a system tray can be placed there
    257 		 * without taking up too much extra screen real
    258 		 * estate.
    259 		 */
    260 		rr = r;
    261 		brect = scrn->brect;
    262 		for(i=0; i < vp->n; i++) {
    263 			sr = rect_intersection(vp->ary[i], scrn->r);
    264 			if(Dx(sr) < Dx(r)/2 || Dy(sr) < Dy(brect))
    265 				continue;
    266 			if(scrn->barpos == BTop && sr.min.y < rr.min.y
    267 			|| scrn->barpos != BTop && sr.max.y > rr.max.y)
    268 				rr = sr;
    269 		}
    270 
    271 		if(scrn->barpos == BTop) {
    272 			bar_sety(scrn, rr.min.y);
    273 			r.min.y = max(r.min.y, scrn->brect.max.y);
    274 		}else {
    275 			bar_sety(scrn, rr.max.y - Dy(brect));
    276 			r.max.y = min(r.max.y, scrn->brect.min.y);
    277 		}
    278 		bar_setbounds(scrn, rr.min.x, rr.max.x);
    279 		v->r[s] = r;
    280 	}
    281 }
    282 
    283 void
    284 view_update(View *v) {
    285 	Client *c;
    286 	Frame *f;
    287 	Area *a;
    288 	int s;
    289 
    290 	if(v == selview && !starting) {
    291 		frames_update_sel(v);
    292 
    293 		foreach_frame(v, s, a, f)
    294 			if(f->client->fullscreen >= 0) {
    295 				f->collapsed = false;
    296 				if(!f->area->floating) {
    297 					f->oldarea = area_idx(f->area);
    298 					f->oldscreen = f->area->screen;
    299 					area_moveto(v->floating, f);
    300 					area_setsel(v->floating, f);
    301 				}else if(f->oldarea == -1)
    302 					f->oldarea = 0;
    303 			}
    304 
    305 		view_arrange(v);
    306 
    307 		for(c=client; c; c=c->next) {
    308 			f = c->sel;
    309 			if((f && f->view == v)
    310 			&& (f->area == v->sel || !(f->area && f->area->max && f->area->floating))) {
    311 				if(f->area)
    312 					client_resize(c, f->r);
    313 			}else {
    314 				client_unmapframe(c);
    315 				client_unmap(c, IconicState);
    316 			}
    317 			ewmh_updatestate(c);
    318 			ewmh_updateclient(c);
    319 		}
    320 
    321 		view_restack(v);
    322 		if(!v->sel->floating && view_fullscreen_p(v, v->sel->screen))
    323 			area_focus(v->floating);
    324 		else
    325 			area_focus(v->sel);
    326 		frame_draw_all();
    327 	}
    328 	view_update_urgency(v, nil);
    329 }
    330 
    331 void
    332 view_update_urgency(View *v, char *from) {
    333 	Area *a;
    334 	Frame *f;
    335 	int s, urgent;
    336 
    337 	urgent = 0;
    338 	foreach_frame(v, s, a, f)
    339 		if (f->client->urgent) {
    340 			urgent++;
    341 			break;
    342 		}
    343 
    344 	if (urgent != v->urgent)
    345 		event("%sUrgentTag %s %s\n",
    346 		      urgent ? "" : "Not",
    347 		      from ? from : "Unknown",
    348 		      v->name);
    349 
    350 	v->urgent = urgent;
    351 }
    352 
    353 void
    354 view_focus(WMScreen *s, View *v) {
    355 
    356 	USED(s);
    357 
    358 	_view_select(v);
    359 	view_update(v);
    360 }
    361 
    362 void
    363 view_select(const char *arg) {
    364 	char buf[256];
    365 
    366 	utflcpy(buf, arg, sizeof buf);
    367 	trim(buf, " \t+/");
    368 
    369 	if(buf[0] == '\0')
    370 		return;
    371 	if(!strcmp(buf, ".") || !strcmp(buf, ".."))
    372 		return;
    373 
    374 	_view_select(view_create(buf));
    375 	view_update_all(); /* performs view_focus */
    376 }
    377 
    378 void
    379 view_attach(View *v, Frame *f) {
    380 	Client *c;
    381 	Frame *ff;
    382 	Area *a, *oldsel;
    383 
    384 	c = f->client;
    385 
    386 	oldsel = v->oldsel;
    387 	a = v->sel;
    388 	if(c->floating == Never)
    389 		a = view_findarea(v, v->selscreen, v->selcol, false);
    390 	else if(client_floats_p(c)) {
    391 		if(v->sel != v->floating && c->fullscreen < 0)
    392 			oldsel = v->sel;
    393 		a = v->floating;
    394 	}
    395 	else if((ff = client_groupframe(c, v))) {
    396 		a = ff->area;
    397 		if(v->oldsel && ff->client == view_selclient(v))
    398 			a = v->oldsel;
    399 	}
    400 	else if(v->sel->floating) {
    401 		if(v->oldsel)
    402 			a = v->oldsel;
    403 		/* Don't float a frame when starting or when its
    404 		 * last focused frame didn't float. Important when
    405 		 * tagging with +foo.
    406 		 */
    407 		else if(starting
    408 		     || c->sel && c->sel->area && !c->sel->area->floating)
    409 			a = v->firstarea;
    410 	}
    411 	if(!a->floating && c->floating != Never && view_fullscreen_p(v, a->screen))
    412 		a = v->floating;
    413 
    414 	event("ViewAttach %s %#C\n", v->name, c);
    415 	area_attach(a, f);
    416 	/* TODO: Decide whether to focus this frame */
    417 	bool newgroup = !c->group
    418 		     || c->group->ref == 1
    419 		     || view_selclient(v)
    420 		        && view_selclient(v)->group == c->group
    421 		     || group_leader(c->group)
    422 		        && !client_viewframe(group_leader(c->group),
    423 					     c->sel->view);
    424 	USED(newgroup);
    425 
    426 	if(!(c->w.ewmh.type & (TypeSplash|TypeDock))) {
    427 		if(!(c->tagre.regex && regexec(c->tagre.regc, v->name, nil, 0)))
    428 			frame_focus(f);
    429 		else if(c->group && f->area->sel->client->group == c->group)
    430 			/* XXX: Stack. */
    431 			area_setsel(f->area, f);
    432 	}
    433 
    434 	if(oldsel)
    435 		v->oldsel = oldsel;
    436 
    437 	if(c->sel == nil)
    438 		c->sel = f;
    439 	view_update(v);
    440 }
    441 
    442 void
    443 view_detach(Frame *f) {
    444 	Client *c;
    445 	View *v;
    446 
    447 	v = f->view;
    448 	c = f->client;
    449 
    450 	area_detach(f);
    451 	if(c->sel == f)
    452 		c->sel = f->cnext;
    453 
    454 	event("ViewDetach %s %#C\n", v->name, c);
    455 	view_update(v);
    456 	if(v != selview && empty_p(v))
    457 		view_destroy(v);
    458 }
    459 
    460 char**
    461 view_names(void) {
    462 	Vector_ptr vec;
    463 	View *v;
    464 
    465 	vector_pinit(&vec);
    466 	for(v=view; v; v=v->next)
    467 		vector_ppush(&vec, v->name);
    468 	vector_ppush(&vec, nil);
    469 	return erealloc(vec.ary, vec.n * sizeof *vec.ary);
    470 }
    471 
    472 void
    473 view_restack(View *v) {
    474 	static Vector_long wins;
    475 	Divide *d;
    476 	Frame *f;
    477 	Area *a;
    478 	int s;
    479 
    480 	if(v != selview)
    481 		return;
    482 
    483 	wins.n = 0;
    484 
    485 	for(f=v->floating->stack; f; f=f->snext)
    486 		vector_lpush(&wins, f->client->framewin->xid);
    487 
    488 	for(int s=0; s < nscreens; s++)
    489 		vector_lpush(&wins, screens[s]->barwin->xid);
    490 
    491 	for(d = divs; d && d->w->mapped; d = d->next)
    492 		vector_lpush(&wins, d->w->xid);
    493 
    494 	foreach_column(v, s, a)
    495 		if(a->frame) {
    496 			vector_lpush(&wins, a->sel->client->framewin->xid);
    497 			for(f=a->frame; f; f=f->anext)
    498 				if(f != a->sel)
    499 					vector_lpush(&wins, f->client->framewin->xid);
    500 		}
    501 
    502 	ewmh_updatestacking();
    503 	if(wins.n)
    504 		XRestackWindows(display, (ulong*)wins.ary, wins.n);
    505 }
    506 
    507 void
    508 view_scale(View *v, int scrn, int width) {
    509 	uint xoff, numcol;
    510 	uint minwidth;
    511 	Area *a;
    512 	float scale;
    513 	int dx, minx;
    514 
    515 	minwidth = column_minwidth();
    516 	minx = v->r[scrn].min.x + v->pad[scrn].min.x;
    517 
    518 	if(!v->areas[scrn])
    519 		return;
    520 
    521 	numcol = 0;
    522 	dx = 0;
    523 	for(a=v->areas[scrn]; a; a=a->next) {
    524 		numcol++;
    525 		dx += Dx(a->r);
    526 	}
    527 
    528 	scale = (float)width / dx;
    529 	xoff = minx;
    530 	for(a=v->areas[scrn]; a; a=a->next) {
    531 		a->r.max.x = xoff + Dx(a->r) * scale;
    532 		a->r.min.x = xoff;
    533 		if(!a->next)
    534 			a->r.max.x = v->r[scrn].min.x + width;
    535 		xoff = a->r.max.x;
    536 	}
    537 
    538 	if(numcol * minwidth > width)
    539 		return;
    540 
    541 	xoff = minx;
    542 	for(a=v->areas[scrn]; a; a=a->next) {
    543 		a->r.min.x = xoff;
    544 
    545 		if(Dx(a->r) < minwidth)
    546 			a->r.max.x = xoff + minwidth;
    547 		if(!a->next)
    548 			a->r.max.x = minx + width;
    549 		xoff = a->r.max.x;
    550 	}
    551 }
    552 
    553 /* XXX: Multihead. */
    554 void
    555 view_arrange(View *v) {
    556 	Area *a;
    557 	int s;
    558 
    559 	if(!v->firstarea)
    560 		return;
    561 
    562 	view_update_rect(v);
    563 	for(s=0; s < nscreens; s++)
    564 		view_scale(v, s, Dx(v->r[s]) + Dx(v->pad[s]));
    565 	foreach_area(v, s, a) {
    566 		if(a->floating)
    567 			continue;
    568 		/* This is wrong... */
    569 		a->r.min.y = v->r[s].min.y;
    570 		a->r.max.y = v->r[s].max.y;
    571 		column_arrange(a, false);
    572 	}
    573 	if(v == selview)
    574 		div_update_all();
    575 }
    576 
    577 Rectangle*
    578 view_rects(View *v, uint *num, Frame *ignore) {
    579 	Vector_rect result;
    580 	Frame *f;
    581 	int i;
    582 
    583 	vector_rinit(&result);
    584 
    585 	for(f=v->floating->frame; f; f=f->anext)
    586 		if(f != ignore)
    587 			vector_rpush(&result, f->r);
    588 	for(i=0; i < nscreens; i++) {
    589 		vector_rpush(&result, v->r[i]);
    590 		vector_rpush(&result, screens[i]->r);
    591 	}
    592 
    593 	*num = result.n;
    594 	return result.ary;
    595 }
    596 
    597 void
    598 view_update_all(void) {
    599 	View *n, *v, *old;
    600 
    601 	old = selview;
    602 	for(v=view; v; v=v->next)
    603 		frames_update_sel(v);
    604 
    605 	for(v=view; v; v=n) {
    606 		n=v->next;
    607 		if(v != old && empty_p(v))
    608 			view_destroy(v);
    609 	}
    610 
    611 	view_update(selview);
    612 }
    613 
    614 uint
    615 view_newcolwidth(View *v, int scrn, int num) {
    616 	Rule *r;
    617 	char *toks[16];
    618 	char buf[sizeof r->value];
    619 	ulong n;
    620 
    621 	/* XXX: Multihead. */
    622 	for(r=def.colrules.rule; r; r=r->next)
    623 		if(regexec(r->regex, v->name, nil, 0)) {
    624 			utflcpy(buf, r->value, sizeof buf);
    625 			n = tokenize(toks, 16, buf, '+');
    626 
    627 			if(num < n)
    628 				if(getulong(toks[num], &n))
    629 					return Dx(v->r[scrn]) * (n / 100.0);
    630 				else if(!strcmp("px", strend(toks[num], 2))) {
    631 					toks[num][strlen(toks[num]) - 2] = '\0';
    632 					if(getulong(toks[num], &n))
    633 						return n;
    634 				}
    635 			break;
    636 		}
    637 	return 0;
    638 }
    639 
    640 char*
    641 view_index(View *v) {
    642 	Rectangle *r;
    643 	Frame *f;
    644 	Area *a;
    645 	int s;
    646 
    647 	bufclear();
    648 	foreach_area(v, s, a) {
    649 		if(a->floating)
    650 			bufprint("# %a %d %d\n", a, Dx(a->r), Dy(a->r));
    651 		else
    652 			bufprint("# %a %d %d\n", a, a->r.min.x, Dx(a->r));
    653 
    654 		for(f=a->frame; f; f=f->anext) {
    655 			r = &f->r;
    656 			if(a->floating)
    657 				bufprint("%a %#C %d %d %d %d %s\n",
    658 						a, f->client,
    659 						r->min.x, r->min.y,
    660 						Dx(*r), Dy(*r),
    661 						f->client->props);
    662 			else
    663 				bufprint("%a %#C %d %d %s\n",
    664 						a, f->client,
    665 						r->min.y, Dy(*r),
    666 						f->client->props);
    667 		}
    668 	}
    669 	return buffer;
    670 }
    671