wmii

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

layout.c (11685B)


      1 /* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
      2  * See LICENSE file for license details.
      3  */
      4 #include "dat.h"
      5 #include "fns.h"
      6 
      7 /* Here be dragons. */
      8 /* Actually, I'm happy to say, the dragons have dissipated. */
      9 
     10 enum {
     11 	ButtonMask =
     12 		ButtonPressMask | ButtonReleaseMask,
     13 	MouseMask =
     14 		ButtonMask | PointerMotionMask
     15 };
     16 
     17 static Handlers handlers;
     18 
     19 enum { OHoriz, OVert };
     20 typedef struct Framewin Framewin;
     21 struct Framewin {
     22 	/* Todo... give these better names. */
     23 	Window* w;
     24 	Rectangle grabbox;
     25 	Frame*	f;
     26 	Area*	ra;
     27 	Point	pt;
     28 	int	orientation;
     29 	int	xy;
     30 	int	screen;
     31 };
     32 
     33 static Rectangle
     34 framerect(Framewin *f) {
     35 	Rectangle r;
     36 	Point p;
     37 	int scrn;
     38 
     39 	r.min = ZP;
     40 	if(f->orientation == OHoriz) {
     41 		r.max.x = f->xy;
     42 		r.max.y = f->grabbox.max.y + f->grabbox.min.y;
     43 	}else {
     44 		r.max.x = f->grabbox.max.x + f->grabbox.min.x;
     45 		r.max.y = f->xy;
     46 		r = rectsubpt(r, Pt(Dx(r)/2, 0));
     47 	}
     48 	r = rectaddpt(r, f->pt);
     49 
     50 	scrn = f->screen;
     51 	if(scrn == -1)
     52 		scrn = max(ownerscreen(f->f->r), 0);
     53 
     54 	/* Keep onscreen */
     55 	p = ZP;
     56 	p.x -= min(0, r.min.x);
     57 	p.x -= max(0, r.max.x - screens[scrn]->r.max.x);
     58 	p.y -= max(0, r.max.y - screens[scrn]->brect.min.y - Dy(r)/2);
     59 	return rectaddpt(r, p);
     60 }
     61 
     62 static void
     63 frameadjust(Framewin *f, Point pt, int orientation, int xy) {
     64 	f->orientation = orientation;
     65 	f->xy = xy;
     66 	f->pt = pt;
     67 }
     68 
     69 static Framewin*
     70 framewin(Frame *f, Point pt, int orientation, int n) {
     71 	WinAttr wa;
     72 	Framewin *fw;
     73 
     74 	fw = emallocz(sizeof *fw);
     75 	wa.override_redirect = true;
     76 	wa.event_mask = ExposureMask;
     77 	fw->w = createwindow(&scr.root, Rect(0, 0, 1, 1),
     78 			scr.depth, InputOutput,
     79 			&wa, CWEventMask);
     80 	fw->w->aux = fw;
     81 	sethandler(fw->w, &handlers);
     82 
     83 	fw->f = f;
     84 	fw->screen = f->area->screen;
     85 	fw->grabbox = f->grabbox;
     86 	frameadjust(fw, pt, orientation, n);
     87 	reshapewin(fw->w, framerect(fw));
     88 
     89 	raisewin(fw->w);
     90 
     91 	return fw;
     92 }
     93 
     94 static void
     95 framedestroy(Framewin *f) {
     96 	destroywindow(f->w);
     97 	free(f);
     98 }
     99 
    100 static bool
    101 expose_event(Window *w, void *aux, XExposeEvent *e) {
    102 	Rectangle r;
    103 	Framewin *f;
    104 	Image *buf;
    105 	CTuple *c;
    106 
    107 	USED(e);
    108 
    109 	f = aux;
    110 	c = &def.focuscolor;
    111 	buf = disp.ibuf;
    112 
    113 	r = rectsubpt(w->r, w->r.min);
    114 	fill(buf, r, &c->bg);
    115 	border(buf, r, 1, &c->border);
    116 	border(buf, f->grabbox, 1, &c->border);
    117 	border(buf, insetrect(f->grabbox, -f->grabbox.min.x), 1, &c->border);
    118 
    119 	copyimage(w, r, buf, ZP);
    120 	return false;
    121 }
    122 
    123 static Handlers handlers = {
    124 	.expose = expose_event,
    125 };
    126 
    127 static Area*
    128 find_area(Point pt) {
    129 	View *v;
    130 	Area *a;
    131 	int s;
    132 
    133 	v = selview;
    134 	for(s=0; s < nscreens; s++) {
    135 		if(!rect_haspoint_p(screens[s]->r, pt))
    136 			continue;
    137 		for(a=v->areas[s]; a; a=a->next)
    138 			if(pt.x < a->r.max.x)
    139 				return a;
    140 	}
    141 	return nil;
    142 }
    143 
    144 static void
    145 vplace(Framewin *fw, Point pt) {
    146 	Vector_long vec = {0};
    147 	Rectangle r;
    148 	Frame *f;
    149 	Area *a;
    150 	long l;
    151 	int hr;
    152 
    153 	a = find_area(pt);
    154 	if(a == nil)
    155 		return;
    156 
    157 	fw->ra = a;
    158 	fw->screen = a->screen;
    159 
    160 	pt.x = a->r.min.x;
    161 	frameadjust(fw, pt, OHoriz, Dx(a->r));
    162 
    163 	r = fw->w->r;
    164 	hr = Dy(r)/2;
    165 	pt.y -= hr;
    166 
    167 	if(a->frame == nil)
    168 		goto done;
    169 
    170 	vector_lpush(&vec, a->frame->r.min.y);
    171 	for(f=a->frame; f; f=f->anext) {
    172 		if(f == fw->f)
    173 			vector_lpush(&vec, f->r.min.y + 0*hr);
    174 		else if(f->collapsed)
    175 			vector_lpush(&vec, f->r.min.y + 1*hr);
    176 		else
    177 			vector_lpush(&vec, f->r.min.y + 2*hr);
    178 		if(!f->collapsed && f->anext != fw->f)
    179 			vector_lpush(&vec, f->r.max.y - 2*hr);
    180 	}
    181 
    182 	for(int i=0; i < vec.n; i++) {
    183 		l = vec.ary[i];
    184 		if(abs(pt.y - l) < hr) {
    185 			pt.y = l;
    186 			break;
    187 		}
    188 	}
    189 	vector_lfree(&vec);
    190 
    191 done:
    192 	pt.x = a->r.min.x;
    193 	frameadjust(fw, pt, OHoriz, Dx(a->r));
    194 	reshapewin(fw->w, framerect(fw));
    195 }
    196 
    197 static void
    198 hplace(Framewin *fw, Point pt) {
    199 	Area *a;
    200 	int minw;
    201 
    202 	a = find_area(pt);
    203 	if(a == nil)
    204 		return; /* XXX: Multihead. */
    205 
    206 	fw->screen = a->screen;
    207 	fw->ra = nil;
    208 	minw = column_minwidth();
    209 	if(abs(pt.x - a->r.min.x) < minw/2) {
    210 		pt.x = a->r.min.x;
    211 		fw->ra = a->prev;
    212 	}
    213 	else if(abs(pt.x - a->r.max.x) < minw/2) {
    214 		pt.x = a->r.max.x;
    215 		fw->ra = a;
    216 	}
    217 
    218 	pt.y = a->r.min.y;
    219 	frameadjust(fw, pt, OVert, Dy(a->r));
    220 	reshapewin(fw->w, framerect(fw));
    221 }
    222 
    223 static Point
    224 grabboxcenter(Frame *f) {
    225 	Point p;
    226 
    227 	p = addpt(f->r.min, f->grabbox.min);
    228 	p.x += Dx(f->grabbox)/2;
    229 	p.y += Dy(f->grabbox)/2;
    230 	return p;
    231 }
    232 
    233 static int tvcol(Frame*, bool);
    234 static int thcol(Frame*, bool);
    235 static int tfloat(Frame*, bool);
    236 
    237 enum {
    238 	TDone,
    239 	TVCol,
    240 	THCol,
    241 	TFloat,
    242 };
    243 
    244 static int (*tramp[])(Frame*, bool) = {
    245 	0,
    246 	tvcol,
    247 	thcol,
    248 	tfloat,
    249 };
    250 
    251 /* Trampoline to allow properly tail recursive move/resize routines.
    252  * We could probably get away with plain tail calls, but I don't
    253  * like the idea.
    254  */
    255 static void
    256 trampoline(int fn, Frame *f, bool grabbox) {
    257 	int moved;
    258 
    259 	moved = 0;
    260 	while(fn > 0) {
    261 		if(grabbox)
    262 			warppointer(grabboxcenter(f));
    263 		//f->collapsed = false;
    264 		fn = tramp[fn](f, moved++);
    265 	}
    266 	ungrabpointer();
    267 }
    268 
    269 static void
    270 resizemode(int mode) {
    271 	bool orig;
    272 
    273 	orig = resizing;
    274 	resizing = mode && mode != TFloat;
    275 	if(resizing != orig)
    276 		view_update(selview);
    277 }
    278 
    279 void
    280 mouse_movegrabbox(Client *c, bool grabmod) {
    281 	Frame *f;
    282 	Point p;
    283 	float x, y;
    284 
    285 	f = c->sel;
    286 
    287 	SET(x);
    288 	SET(y);
    289 	if(grabmod) {
    290 		p = querypointer(f->client->framewin);
    291 		x = (float)p.x / Dx(f->r);
    292 		y = (float)p.y / Dy(f->r);
    293 	}
    294 
    295 	if(f->area->floating)
    296 		trampoline(TFloat, f, !grabmod);
    297 	else
    298 		trampoline(THCol, f, true);
    299 
    300 	if(grabmod)
    301 		warppointer(addpt(f->r.min, Pt(x * Dx(f->r),
    302 					       y * Dy(f->r))));
    303 	else
    304 		warppointer(grabboxcenter(f));
    305 }
    306 
    307 static int
    308 _openstack_down(Frame *f, int h) {
    309 	int ret;
    310 	int dy;
    311 
    312 	if(f == nil)
    313 		return 0;;
    314 	ret = 0;
    315 	if(!f->collapsed) {
    316 		dy = Dy(f->colr) - labelh(def.font);
    317 		if(dy >= h) {
    318 			f->colr.min.y += h;
    319 			return h;
    320 		}else {
    321 			f->collapsed = true;
    322 			f->colr.min.y += dy;
    323 			ret = dy;
    324 			h -= dy;
    325 		}
    326 	}
    327 	dy = _openstack_down(f->anext, h);
    328 	f->colr.min.y += dy;
    329 	f->colr.max.y += dy;
    330 	return ret + dy;
    331 }
    332 
    333 static int
    334 _openstack_up(Frame *f, int h) {
    335 	int ret;
    336 	int dy;
    337 
    338 	if(f == nil)
    339 		return 0;
    340 	ret = 0;
    341 	if(!f->collapsed) {
    342 		dy = Dy(f->colr) - labelh(def.font);
    343 		if(dy >= h) {
    344 			f->colr.max.y -= h;
    345 			return h;
    346 		}else {
    347 			f->collapsed = true;
    348 			f->colr.max.y -= dy;
    349 			ret = dy;
    350 			h -= dy;
    351 		}
    352 	}
    353 	dy = _openstack_up(f->aprev, h);
    354 	f->colr.min.y -= dy;
    355 	f->colr.max.y -= dy;
    356 	return ret + dy;
    357 }
    358 
    359 static void
    360 column_openstack(Area *a, Frame *f, int h) {
    361 
    362 	if(f == nil)
    363 		_openstack_down(a->frame, h);
    364 	else {
    365 		h -= _openstack_down(f->anext, h);
    366 		if(h)
    367 			_openstack_up(f->aprev, h);
    368 	}
    369 }
    370 
    371 static void
    372 column_drop(Area *a, Frame *f, int y) {
    373 	Frame *ff;
    374 	int dy, extra_y;
    375 
    376 	extra_y = Dy(a->r);
    377 	for(ff=a->frame; ff; ff=ff->anext) {
    378 		assert(ff != f);
    379 		extra_y -= Dy(ff->colr);
    380 	}
    381 
    382 	if(a->frame == nil || y <= a->frame->r.min.y) {
    383 		f->collapsed = true;
    384 		f->colr.min.y = 0;
    385 		f->colr.max.y = labelh(def.font);
    386 		column_openstack(a, nil, labelh(def.font));
    387 		column_insert(a, f, nil);
    388 		return;
    389 	}
    390 	for(ff=a->frame; ff->anext; ff=ff->anext)
    391 		if(y <= ff->colr.max.y) break;
    392 
    393 	y = max(y, ff->colr.min.y + labelh(def.font));
    394 	y = min(y, ff->colr.max.y);
    395 	dy = ff->colr.max.y - y;
    396 	if(dy <= labelh(def.font)) {
    397 		f->collapsed = true;
    398 		f->colr.min.y = 0;
    399 		f->colr.max.y = labelh(def.font);
    400 		column_openstack(a, ff, labelh(def.font) - dy);
    401 	}else {
    402 		f->colr.min.y = y;
    403 		f->colr.max.y = ff->colr.max.y + extra_y;
    404 		ff->colr.max.y = y;
    405 	}
    406 	column_insert(a, f, ff);
    407 }
    408 
    409 static int
    410 thcol(Frame *f, bool moved) {
    411 	Framewin *fw;
    412 	Frame *fp, *fn;
    413 	Area *a;
    414 	Point pt, pt2;
    415 	uint button;
    416 	int ret, collapsed;
    417 
    418 	focus(f->client, false);
    419 
    420 	ret = TDone;
    421 	if(!grabpointer(&scr.root, nil, None, MouseMask))
    422 		return TDone;
    423 
    424 	readmotion(&pt);
    425 	pt2.x = f->area->r.min.x;
    426 	pt2.y = pt.y;
    427 	fw = framewin(f, pt2, OHoriz, Dx(f->area->r));
    428 
    429 	if(moved)
    430 		goto casemotion;
    431 
    432 	vplace(fw, pt);
    433 	for(;;)
    434 		switch (readmouse(&pt, &button)) {
    435 		case MotionNotify:
    436 		casemotion:
    437 			moved = 1;
    438 			resizemode(THCol);
    439 			if(mapwin(fw->w))
    440 				grabpointer(&scr.root, nil, cursor[CurIcon], MouseMask);
    441 			vplace(fw, pt);
    442 			break;
    443 		case ButtonRelease:
    444 			if(!moved)
    445 				goto done;
    446 
    447 			if(button != 1)
    448 				continue;
    449 			SET(collapsed);
    450 			SET(fp);
    451 			SET(fn);
    452 			a = f->area;
    453 			if(a->floating)
    454 				area_detach(f);
    455 			else {
    456 				collapsed = f->collapsed;
    457 				fp = f->aprev;
    458 				fn = f->anext;
    459 				column_remove(f);
    460 				if(!f->collapsed)
    461 					if(fp)
    462 						fp->colr.max.y = f->colr.max.y;
    463 					else if(fn && fw->pt.y > fn->r.min.y)
    464 						fn->colr.min.y = f->colr.min.y;
    465 			}
    466 
    467 			column_drop(fw->ra, f, fw->pt.y);
    468 			if(!a->floating && collapsed) {
    469 				/* XXX */
    470 				for(; fn && fn->collapsed; fn=fn->anext)
    471 					;
    472 				if(fn == nil)
    473 					for(fn=fp; fn && fn->collapsed; fn=fn->aprev)
    474 						;
    475 				if(fp)
    476 					fp->colr.max.x += labelh(def.font);
    477 			}
    478 
    479 
    480  			if(!a->frame && !a->floating && a->view->areas[a->screen]->next)
    481  				area_destroy(a);
    482 
    483 			frame_focus(f);
    484 			goto done;
    485 		case ButtonPress:
    486 			if(button == 2)
    487 				ret = TVCol;
    488 			else if(button == 3)
    489 				ret = TFloat;
    490 			else
    491 				continue;
    492 			goto done;
    493 		}
    494 done:
    495 	resizemode(0);
    496 	framedestroy(fw);
    497 	return ret;
    498 }
    499 
    500 static int
    501 tvcol(Frame *f, bool moved) {
    502 	Framewin *fw;
    503 	Window *cwin;
    504 	Rectangle r;
    505 	Point pt, pt2;
    506 	uint button;
    507 	int ret, scrn;
    508 
    509 	focus(f->client, false);
    510 
    511 	pt = querypointer(&scr.root);
    512 	pt2.x = pt.x;
    513 	pt2.y = f->area->r.min.y;
    514 
    515 	scrn = f->area->screen > -1 ? f->area->screen : find_area(pt) ? find_area(pt)->screen : 0;
    516 	r = f->view->r[scrn];
    517 	fw = framewin(f, pt2, OVert, Dy(r));
    518 	mapwin(fw->w);
    519 
    520 	r.min.y += fw->grabbox.min.y + Dy(fw->grabbox)/2;
    521 	r.max.y = r.min.y + 1;
    522 	cwin = createwindow(&scr.root, r, 0, InputOnly, nil, 0);
    523 	mapwin(cwin);
    524 
    525 	ret = TDone;
    526 	if(!grabpointer(&scr.root, cwin, cursor[CurIcon], MouseMask))
    527 		goto done;
    528 
    529 	resizemode(TVCol);
    530 
    531 	hplace(fw, pt);
    532 	for(;;)
    533 		switch (readmouse(&pt, &button)) {
    534 		case MotionNotify:
    535 			moved = 1;
    536 			hplace(fw, pt);
    537 			continue;
    538 		case ButtonPress:
    539 			if(button == 2)
    540 				ret = THCol;
    541 			else if(button == 3)
    542 				ret = TFloat;
    543 			else
    544 				continue;
    545 			goto done;
    546 		case ButtonRelease:
    547 			if(button != 1)
    548 				continue;
    549 			if(fw->ra) {
    550 				fw->ra = column_new(f->view, fw->ra, screen->idx, 0);
    551 				area_moveto(fw->ra, f);
    552 			}
    553 			goto done;
    554 		}
    555 
    556 done:
    557 	framedestroy(fw);
    558 	destroywindow(cwin);
    559 	resizemode(0);
    560 	return ret;
    561 }
    562 
    563 static int
    564 tfloat(Frame *f, bool moved) {
    565 	Rectangle *rects;
    566 	Rectangle frect, origin;
    567 	Point pt, pt1;
    568 	Client *c;
    569 	Align align;
    570 	uint nrect, button;
    571 	int ret;
    572 
    573 	c = f->client;
    574 	if(!f->area->floating) {
    575 		if(f->anext)
    576 			f->anext->colr.min.y = f->colr.min.y;
    577 		else if(f->aprev)
    578 			f->aprev->colr.max.y = f->colr.max.y;
    579 		area_moveto(f->view->floating, f);
    580 		view_update(f->view);
    581 		warppointer(grabboxcenter(f));
    582 	}
    583 	client_mapframe(f->client);
    584 	if(!f->collapsed)
    585 		focus(f->client, false);
    586 
    587 	ret = TDone;
    588 	if(!grabpointer(c->framewin, nil, cursor[CurMove], MouseMask))
    589 		return TDone;
    590 
    591 	rects = view_rects(f->view, &nrect, f);
    592 	origin = f->r;
    593 	frect = f->r;
    594 
    595 	if(!readmotion(&pt)) {
    596 		focus(f->client, false);
    597 		goto done;
    598 	}
    599 	/* pt1 = grabboxcenter(f); */
    600 	pt1 = pt;
    601 	goto case_motion;
    602 
    603 shut_up_ken:
    604 	for(;;pt1=pt)
    605 		switch (readmouse(&pt, &button)) {
    606 		default: goto shut_up_ken;
    607 		case MotionNotify:
    608 			moved = 1;
    609 		case_motion:
    610 			origin = rectaddpt(origin, subpt(pt, pt1));
    611 			origin = constrain(origin, -1);
    612 			frect = origin;
    613 
    614 			align = Center;
    615 			snap_rect(rects, nrect, &frect, &align, def.snap);
    616 
    617 			frect = frame_hints(f, frect, Center);
    618 			frect = constrain(frect, -1);
    619 			client_resize(c, frect);
    620 			continue;
    621 		case ButtonRelease:
    622 			if(button != 1)
    623 				continue;
    624 			if(!moved) {
    625 				f->collapsed = !f->collapsed;
    626 				client_resize(f->client, f->floatr);
    627 			}
    628 			goto done;
    629 		case ButtonPress:
    630 			if(button != 3)
    631 				continue;
    632 			client_unmapframe(f->client);
    633 			ret = THCol;
    634 			goto done;
    635 		}
    636 done:
    637 	free(rects);
    638 	return ret;
    639 }
    640