wmii

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

mouse.c (13793B)


      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 
      9 enum {
     10 	ButtonMask =
     11 		ButtonPressMask | ButtonReleaseMask,
     12 	MouseMask =
     13 		ButtonMask | PointerMotionMask
     14 };
     15 
     16 static Cursor
     17 quad_cursor(Align align) {
     18 	switch(align) {
     19 	case NEast: return cursor[CurNECorner];
     20 	case NWest: return cursor[CurNWCorner];
     21 	case SEast: return cursor[CurSECorner];
     22 	case SWest: return cursor[CurSWCorner];
     23 	case South:
     24 	case North: return cursor[CurDVArrow];
     25 	case East:
     26 	case West:  return cursor[CurDHArrow];
     27 	default:    return cursor[CurMove];
     28 	}
     29 }
     30 
     31 static bool
     32 cwin_expose(Window *w, void *aux, XExposeEvent *e) {
     33 
     34 	fill(w, rectsubpt(w->r, w->r.min), &def.focuscolor.bg);
     35 	fill(w, w->r, &def.focuscolor.bg);
     36 	return false;
     37 }
     38 
     39 static Handlers chandler = {
     40 	.expose = cwin_expose,
     41 };
     42 
     43 Window*
     44 constraintwin(Rectangle r) {
     45 	Window *w;
     46 
     47 	w = createwindow(&scr.root, r, 0, InputOnly, nil, 0);
     48 	if(0) {
     49 		Window *w2;
     50 
     51 		w2 = createwindow(&scr.root, r, 0, InputOutput, nil, 0);
     52 		selectinput(w2, ExposureMask);
     53 		w->aux = w2;
     54 
     55 		setborder(w2, 1, &def.focuscolor.border);
     56 		sethandler(w2, &chandler);
     57 		mapwin(w2);
     58 		raisewin(w2);
     59 	}
     60 	mapwin(w);
     61 	return w;
     62 }
     63 
     64 void
     65 destroyconstraintwin(Window *w) {
     66 
     67 	if(w->aux)
     68 		destroywindow(w->aux);
     69 	destroywindow(w);
     70 }
     71 
     72 static Window*
     73 gethsep(Rectangle r) {
     74 	Window *w;
     75 	WinAttr wa;
     76 
     77 	wa.background_pixel = pixelvalue(&scr.root, &def.normcolor.border);
     78 	w = createwindow(&scr.root, r, scr.depth, InputOutput, &wa, CWBackPixel);
     79 	mapwin(w);
     80 	raisewin(w);
     81 	return w;
     82 }
     83 
     84 static void
     85 rect_morph(Rectangle *r, Point d, Align *mask) {
     86 	int n;
     87 
     88 	if(*mask & North)
     89 		r->min.y += d.y;
     90 	if(*mask & West)
     91 		r->min.x += d.x;
     92 	if(*mask & South)
     93 		r->max.y += d.y;
     94 	if(*mask & East)
     95 		r->max.x += d.x;
     96 
     97 	if(r->min.x > r->max.x) {
     98 		n = r->min.x;
     99 		r->min.x = r->max.x;
    100 		r->max.x = n;
    101 		*mask ^= East|West;
    102 	}
    103 	if(r->min.y > r->max.y) {
    104 		n = r->min.y;
    105 		r->min.y = r->max.y;
    106 		r->max.y = n;
    107 		*mask ^= North|South;
    108 	}
    109 }
    110 
    111 /* Yes, yes, macros are evil. So are patterns. */
    112 #define frob(x, y) \
    113 	const Rectangle *rp;                                              \
    114 	int i, tx;                                                        \
    115 									  \
    116 	for(i=0; i < nrect; i++) {                                        \
    117 		rp = &rects[i];                                           \
    118 		if((rp->min.y <= r->max.y) && (rp->max.y >= r->min.y)) {  \
    119 			tx = rp->min.x;                                   \
    120 			if(abs(tx - x) <= abs(dx))                        \
    121 				dx = tx - x;                              \
    122 									  \
    123 			tx = rp->max.x;                                   \
    124 			if(abs(tx - x) <= abs(dx))                        \
    125 				dx = tx - x;                              \
    126 		}                                                         \
    127 	}                                                                 \
    128 	return dx                                                         \
    129 
    130 static int
    131 snap_hline(const Rectangle *rects, int nrect, int dx, const Rectangle *r, int y) {
    132 	frob(y, x);
    133 }
    134 
    135 static int
    136 snap_vline(const Rectangle *rects, int nrect, int dx, const Rectangle *r, int x) {
    137 	frob(x, y);
    138 }
    139 
    140 #undef frob
    141 
    142 /* Returns a gravity for increment handling. It's normally the
    143  * opposite of the mask (the directions that we're resizing in),
    144  * unless a snap occurs, in which case, it's the direction of the
    145  * snap.
    146  */
    147 Align
    148 snap_rect(const Rectangle *rects, int num, Rectangle *r, Align *mask, int snap) {
    149 	Align ret;
    150 	Point d;
    151 
    152 	d.x = snap+1;
    153 	d.y = snap+1;
    154 
    155 	if(*mask&North)
    156 		d.y = snap_hline(rects, num, d.y, r, r->min.y);
    157 	if(*mask&South)
    158 		d.y = snap_hline(rects, num, d.y, r, r->max.y);
    159 
    160 	if(*mask&East)
    161 		d.x = snap_vline(rects, num, d.x, r, r->max.x);
    162 	if(*mask&West)
    163 		d.x = snap_vline(rects, num, d.x, r, r->min.x);
    164 
    165 	ret = Center;
    166 	if(abs(d.x) <= snap)
    167 		ret ^= East|West;
    168 	else
    169 		d.x = 0;
    170 
    171 	if(abs(d.y) <= snap)
    172 		ret ^= North|South;
    173 	else
    174 		d.y = 0;
    175 
    176 	rect_morph(r, d, mask);
    177 	return ret ^ *mask;
    178 }
    179 
    180 int
    181 readmouse(Point *p, uint *button) {
    182 	XEvent ev;
    183 
    184 	for(;;) {
    185 		XMaskEvent(display, MouseMask|ExposureMask|PropertyChangeMask, &ev);
    186 		debug_event(&ev);
    187 		switch(ev.type) {
    188 		case Expose:
    189 		case NoExpose:
    190 		case PropertyNotify:
    191 			event_dispatch(&ev);
    192 		default:
    193 			Dprint(DEvent, "readmouse(): ignored: %E\n", &ev);
    194 			continue;
    195 		case ButtonPress:
    196 		case ButtonRelease:
    197 			*button = ev.xbutton.button;
    198 		case MotionNotify:
    199 			p->x = ev.xmotion.x_root;
    200 			p->y = ev.xmotion.y_root;
    201 			if(p->x == scr.rect.max.x - 1)
    202 				p->x = scr.rect.max.x;
    203 			if(p->y == scr.rect.max.y - 1)
    204 				p->y = scr.rect.max.y;
    205 			break;
    206 		}
    207 		return ev.type;
    208 	}
    209 }
    210 
    211 bool
    212 readmotion(Point *p) {
    213 	uint button;
    214 
    215 	for(;;)
    216 		switch(readmouse(p, &button)) {
    217 		case MotionNotify:
    218 			return true;
    219 		case ButtonRelease:
    220 			return false;
    221 		}
    222 }
    223 
    224 static void
    225 mouse_resizecolframe(Frame *f, Align align) {
    226 	Window *cwin, *hwin = nil;
    227 	Divide *d;
    228 	View *v;
    229 	Area *a;
    230 	Rectangle r;
    231 	Point pt, min;
    232 	int s;
    233 
    234 	assert((align&(East|West)) != (East|West));
    235 	assert((align&(North|South)) != (North|South));
    236 
    237 	f->collapsed = false;
    238 
    239 	v = selview;
    240 	d = divs;
    241 	SET(a);
    242 	foreach_column(v, s, a) {
    243 		if(a == f->area)
    244 			break;
    245 		d = d->next;
    246 	}
    247 
    248 	if(align & East)
    249 		d = d->next;
    250 
    251 	min.x = column_minwidth();
    252 	min.y = /*frame_delta_h() +*/ labelh(def.font);
    253 	/* Set the limits of where this box may be dragged. */
    254 #define frob(pred, f, aprev, rmin, rmax, plus, minus, xy, use_screen) BLOCK( \
    255 		if(pred) {                                           \
    256 			r.rmin.xy = f->aprev->r.rmin.xy plus min.xy; \
    257 			r.rmax.xy = f->r.rmax.xy minus min.xy;       \
    258 		}else if(use_screen) {                               \
    259 			r.rmin.xy = v->r[f->screen].rmin.xy plus 1;  \
    260 			r.rmax.xy = a->r.rmax.xy minus min.xy;       \
    261 		}else {                                              \
    262 			r.rmin.xy = a->r.rmin.xy;                    \
    263 			r.rmax.xy = r.rmin.xy plus 1;                \
    264 		})
    265 
    266 	r = f->r;
    267 	if(align & North)
    268 		frob(f->aprev, f, aprev, min, max, +, -, y, false);
    269 	else if(align & South)
    270 		frob(f->anext, f, anext, max, min, -, +, y, false);
    271 	if(align & West)
    272 		frob(a->prev,  a, prev,  min, max, +, -, x, true);
    273 	else if(align & East)
    274 		frob(a->next,  a, next,  max, min, -, +, x, true);
    275 #undef frob
    276 
    277 	cwin = constraintwin(r);
    278 
    279 	r = f->r;
    280 	if(align & North)
    281 		r.min.y--;
    282 	else if(align & South)
    283 		r.min.y = r.max.y - 1;
    284 	r.max.y = r.min.y + 2;
    285 
    286 	if(align & (North|South))
    287 		hwin = gethsep(r);
    288 
    289 	if(!grabpointer(&scr.root, cwin, cursor[CurSizing], MouseMask))
    290 		goto done;
    291 
    292 	pt.x = (align & West ? f->r.min.x : f->r.max.x);
    293 	pt.y = (align & North ? f->r.min.y : f->r.max.y);
    294 	warppointer(pt);
    295 
    296 	while(readmotion(&pt)) {
    297 		if(align & West)
    298 			r.min.x = pt.x;
    299 		else if(align & East)
    300 			r.max.x = pt.x;
    301 
    302 		if(align & South)
    303 			r.min.y = pt.y;
    304 		else if(align & North)
    305 			r.min.y = pt.y - 1;
    306 		r.max.y = r.min.y+2;
    307 
    308 		if(align & (East|West))
    309 			div_set(d, pt.x);
    310 		if(hwin)
    311 			reshapewin(hwin, r);
    312 	}
    313 
    314 	r = f->r;
    315 	if(align & West)
    316 		r.min.x = pt.x;
    317 	else if(align & East)
    318 		r.max.x = pt.x;
    319 	if(align & North)
    320 		r.min.y = pt.y;
    321 	else if(align & South)
    322 		r.max.y = pt.y;
    323 	column_resizeframe(f, r);
    324 
    325 	/* XXX: Magic number... */
    326 	if(align & West)
    327 		pt.x = f->r.min.x + 4;
    328 	else if(align & East)
    329 		pt.x = f->r.max.x - 4;
    330 
    331 	if(align & North)
    332 		pt.y = f->r.min.y + 4;
    333 	else if(align & South)
    334 		pt.y = f->r.max.y - 4;
    335 	warppointer(pt);
    336 
    337 done:
    338 	ungrabpointer();
    339 	destroyconstraintwin(cwin);
    340 	if (hwin)
    341 		destroywindow(hwin);
    342 }
    343 
    344 void
    345 mouse_resizecol(Divide *d) {
    346 	Window *cwin;
    347 	View *v;
    348 	Rectangle r;
    349 	Point pt;
    350 	int minw, scrn;
    351 
    352 	v = selview;
    353 
    354 	scrn = (d->left ? d->left : d->right)->screen;
    355 
    356 	pt = querypointer(&scr.root);
    357 
    358 	minw = column_minwidth();
    359 	r.min.x = d->left  ? d->left->r.min.x + minw  : v->r[scrn].min.x;
    360 	r.max.x = d->right ? d->right->r.max.x - minw : v->r[scrn].max.x;
    361 	r.min.y = pt.y;
    362 	r.max.y = pt.y+1;
    363 
    364 	cwin = constraintwin(r);
    365 
    366 	if(!grabpointer(&scr.root, cwin, cursor[CurNone], MouseMask))
    367 		goto done;
    368 
    369 	while(readmotion(&pt))
    370 		div_set(d, pt.x);
    371 
    372 	if(d->left)
    373 		d->left->r.max.x = pt.x;
    374 	else
    375 		v->pad[scrn].min.x = pt.x - v->r[scrn].min.x;
    376 
    377 	if(d->right)
    378 		d->right->r.min.x = pt.x;
    379 	else
    380 		v->pad[scrn].max.x = pt.x - v->r[scrn].max.x;
    381 
    382 	view_arrange(v);
    383 
    384 done:
    385 	ungrabpointer();
    386 	destroyconstraintwin(cwin);
    387 }
    388 
    389 void
    390 mouse_resize(Client *c, Align align, bool grabmod) {
    391 	Rectangle *rects;
    392 	Rectangle frect, origin;
    393 	Align grav;
    394 	Cursor cur;
    395 	Point d, pt, hr;
    396 	float rx, ry, hrx, hry;
    397 	uint nrect;
    398 	Frame *f;
    399 
    400 	f = c->sel;
    401 	if(f->client->fullscreen >= 0) {
    402 		ungrabpointer();
    403 		return;
    404 	}
    405 	if(!f->area->floating) {
    406 		if(align==Center)
    407 			mouse_movegrabbox(c, grabmod);
    408 		else
    409 			mouse_resizecolframe(f, align);
    410 		return;
    411 	}
    412 
    413 	cur = quad_cursor(align);
    414 	if(align == Center)
    415 		cur = cursor[CurSizing];
    416 
    417 	if(!grabpointer(c->framewin, nil, cur, MouseMask))
    418 		return;
    419 
    420 	origin = f->r;
    421 	frect = f->r;
    422 	rects = view_rects(f->area->view, &nrect, c->frame);
    423 
    424 	pt = querypointer(c->framewin);
    425 	rx = (float)pt.x / Dx(frect);
    426 	ry = (float)pt.y / Dy(frect);
    427 
    428 	pt = querypointer(&scr.root);
    429 
    430 	SET(hrx);
    431 	SET(hry);
    432 
    433 	if(align != Center) {
    434 		hr = subpt(frect.max, frect.min);
    435 		hr = divpt(hr, Pt(2, 2));
    436 		d = hr;
    437 		if(align&North) d.y -= hr.y;
    438 		if(align&South) d.y += hr.y;
    439 		if(align&East) d.x += hr.x;
    440 		if(align&West) d.x -= hr.x;
    441 
    442 		pt = addpt(d, f->r.min);
    443 		warppointer(pt);
    444 	}else {
    445 		hrx = (double)(Dx(scr.rect)
    446 			     + Dx(frect)
    447 			     - 2 * labelh(def.font))
    448 		    / Dx(scr.rect);
    449 		hry = (double)(Dy(scr.rect)
    450 			     + Dy(frect)
    451 			     - 3 * labelh(def.font))
    452 		    / Dy(scr.rect);
    453 
    454 		pt.x = frect.max.x - labelh(def.font);
    455 		pt.y = frect.max.y - labelh(def.font);
    456 		d.x = pt.x / hrx;
    457 		d.y = pt.y / hry;
    458 
    459 		warppointer(d);
    460 	}
    461 	sync();
    462 	event_flush(PointerMotionMask, false);
    463 
    464 	while(readmotion(&d)) {
    465 		if(align == Center) {
    466 			d.x = (d.x * hrx) - pt.x;
    467 			d.y = (d.y * hry) - pt.y;
    468 		}else
    469 			d = subpt(d, pt);
    470 		pt = addpt(pt, d);
    471 
    472 		rect_morph(&origin, d, &align);
    473 		frect = constrain(origin, -1);
    474 
    475 		grav = snap_rect(rects, nrect, &frect, &align, def.snap);
    476 
    477 		frect = frame_hints(f, frect, grav);
    478 		frect = constrain(frect, -1);
    479 
    480 		client_resize(c, frect);
    481 	}
    482 
    483 	pt = addpt(c->framewin->r.min,
    484 		   Pt(Dx(frect) * rx,
    485 		      Dy(frect) * ry));
    486 	if(pt.y > scr.rect.max.y)
    487 		pt.y = scr.rect.max.y - 1;
    488 	warppointer(pt);
    489 
    490 	free(rects);
    491 	ungrabpointer();
    492 }
    493 
    494 static int
    495 pushstack_down(Frame *f, int y) {
    496 	int ret;
    497 	int dh, dy;
    498 
    499 	if(f == nil)
    500 		return 0;;
    501 	ret = 0;
    502 	dy = y - f->colr.min.y;
    503 	if(dy < 0)
    504 		return 0;
    505 	if(!f->collapsed) {
    506 		dh = Dy(f->colr) - labelh(def.font);
    507 		if(dy <= dh) {
    508 			f->colr.min.y += dy;
    509 			return dy;
    510 		}else {
    511 			f->collapsed = true;
    512 			f->colr.min.y += dh;
    513 			ret = dh;
    514 			dy -= dh;
    515 		}
    516 	}
    517 	dy = pushstack_down(f->anext, f->colr.max.y + dy);
    518 	f->colr.min.y += dy;
    519 	f->colr.max.y += dy;
    520 	return ret + dy;
    521 }
    522 
    523 static int
    524 pushstack_up(Frame *f, int y) {
    525 	int ret;
    526 	int dh, dy;
    527 
    528 	if(f == nil)
    529 		return 0;
    530 	ret = 0;
    531 	dy = f->colr.max.y - y;
    532 	if(dy < 0)
    533 		return 0;
    534 	if(!f->collapsed) {
    535 		dh = Dy(f->colr) - labelh(def.font);
    536 		if(dy <= dh) {
    537 			f->colr.max.y -= dy;
    538 			return dy;
    539 		}else {
    540 			f->collapsed = true;
    541 			f->colr.max.y -= dh;
    542 			ret = dh;
    543 			dy -= dh;
    544 		}
    545 	}
    546 	dy = pushstack_up(f->aprev, f->colr.min.y - dy);
    547 	f->colr.min.y -= dy;
    548 	f->colr.max.y -= dy;
    549 	return ret + dy;
    550 }
    551 
    552 static void
    553 mouse_tempvertresize(Area *a, Point p) {
    554 	Frame *fa, *fb, *f;
    555 	Window *cwin;
    556 	Rectangle r;
    557 	Point pt;
    558 	int incmode, nabove, nbelow;
    559 
    560 	if(a->mode != Coldefault)
    561 		return;
    562 
    563 	for(fa=a->frame; fa; fa=fa->anext)
    564 		if(p.y < fa->r.max.y + labelh(def.font)/2)
    565 			break;
    566 	if(!(fa && fa->anext))
    567 		return;
    568 	fb = fa->anext;
    569 	nabove=0;
    570 	nbelow=0;
    571 	for(f=fa; f; f=f->aprev)
    572 		nabove++;
    573 	for(f=fa->anext; f; f=f->anext)
    574 		nbelow++;
    575 
    576 	incmode = def.incmode;
    577 	def.incmode = IIgnore;
    578 	resizing = true;
    579 	column_arrange(a, false);
    580 
    581 	r.min.x = p.x;
    582 	r.max.x = p.x + 1;
    583 	r.min.y = a->r.min.y + labelh(def.font) * nabove;
    584 	r.max.y = a->r.max.y - labelh(def.font) * nbelow;
    585 	cwin = constraintwin(r);
    586 
    587 	if(!grabpointer(&scr.root, cwin, cursor[CurDVArrow], MouseMask))
    588 		goto done;
    589 
    590 	for(f=a->frame; f; f=f->anext)
    591 		f->colr_old = f->colr;
    592 
    593 	while(readmotion(&pt)) {
    594 		for(f=a->frame; f; f=f->anext) {
    595 			f->collapsed = false;
    596 			f->colr = f->colr_old;
    597 		}
    598 		if(pt.y > p.y)
    599 			pushstack_down(fb, pt.y);
    600 		else
    601 			pushstack_up(fa, pt.y);
    602 		fa->colr.max.y = pt.y;
    603 		fb->colr.min.y = pt.y;
    604 		column_frob(a);
    605 	}
    606 
    607 done:
    608 	ungrabpointer();
    609 	destroyconstraintwin(cwin);
    610 	def.incmode = incmode;
    611 	resizing = false;
    612 	column_arrange(a, false);
    613 }
    614 
    615 void
    616 mouse_checkresize(Frame *f, Point p, bool exec) {
    617 	Rectangle r;
    618 	Cursor cur;
    619 	int q;
    620 
    621 	cur = cursor[CurNormal];
    622 	if(rect_haspoint_p(f->crect, p)) {
    623 		client_setcursor(f->client, cur);
    624 		return;
    625 	}
    626 
    627 	r = rectsubpt(f->r, f->r.min);
    628 	q = quadrant(r, p);
    629 	if(rect_haspoint_p(f->grabbox, p)) {
    630 		cur = cursor[CurTCross];
    631 		if(exec)
    632 			mouse_movegrabbox(f->client, false);
    633 	}
    634 	else if(f->area->floating) {
    635 		if(p.x <= 2
    636 		|| p.y <= 2
    637 		|| r.max.x - p.x <= 2
    638 		|| r.max.y - p.y <= 2) {
    639 			cur = quad_cursor(q);
    640 			if(exec)
    641 				mouse_resize(f->client, q, false);
    642 		}
    643 		else if(exec && rect_haspoint_p(f->titlebar, p))
    644 			mouse_movegrabbox(f->client, true);
    645 	}else {
    646 		if(f->aprev && p.y <= 2
    647 		|| f->anext && r.max.y - p.y <= 2) {
    648 			cur = cursor[CurDVArrow];
    649 			if(exec)
    650 				mouse_tempvertresize(f->area, addpt(p, f->r.min));
    651 		}
    652 	}
    653 
    654 	if(!exec)
    655 		client_setcursor(f->client, cur);
    656 }
    657 
    658 static void
    659 _grab(XWindow w, uint button, ulong mod) {
    660 	XGrabButton(display, button, mod, w, false, ButtonMask,
    661 			GrabModeSync, GrabModeSync, None, None);
    662 }
    663 
    664 /* Doesn't belong here */
    665 void
    666 grab_button(XWindow w, uint button, ulong mod) {
    667 	_grab(w, button, mod);
    668 	if((mod != AnyModifier) && numlock_mask) {
    669 		_grab(w, button, mod | numlock_mask);
    670 		_grab(w, button, mod | numlock_mask | LockMask);
    671 	}
    672 }
    673