plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

grab.c (13301B)


      1 /* Copyright (c) 1994-1996 David Hogan, see README for licence details */
      2 #include <stdio.h>
      3 #include <X11/X.h>
      4 #include <X11/Xos.h>
      5 #include <X11/Xlib.h>
      6 #include <X11/Xutil.h>
      7 #include "dat.h"
      8 #include "fns.h"
      9 
     10 int
     11 nobuttons(XButtonEvent *e)	/* Einstuerzende */
     12 {
     13 	int state;
     14 
     15 	state = (e->state & AllButtonMask);
     16 	return (e->type == ButtonRelease) && (state & (state - 1)) == 0;
     17 }
     18 
     19 int
     20 grab(Window w, Window constrain, int mask, Cursor curs, int t)
     21 {
     22 	int status;
     23 
     24 	if(t == 0)
     25 		t = timestamp();
     26 	status = XGrabPointer(dpy, w, False, mask,
     27 		GrabModeAsync, GrabModeAsync, constrain, curs, t);
     28 	return status;
     29 }
     30 
     31 void
     32 ungrab(XButtonEvent *e)
     33 {
     34 	XEvent ev;
     35 
     36 	if(!nobuttons(e))
     37 		for(;;){
     38 			XMaskEvent(dpy, ButtonMask | ButtonMotionMask, &ev);
     39 			if(ev.type == MotionNotify)
     40 				continue;
     41 			e = &ev.xbutton;
     42 			if(nobuttons(e))
     43 				break;
     44 		}
     45 	XUngrabPointer(dpy, e->time);
     46 	curtime = e->time;
     47 }
     48 
     49 static void
     50 drawstring(Display *dpy, ScreenInfo *s, Menu *m, int wide, int high, int i, int selected)
     51 {
     52 	int tx, ty;
     53 
     54 	tx = (wide - XTextWidth(font, m->item[i], strlen(m->item[i])))/2;
     55 	ty = i*high + font->ascent + 1;
     56 	XFillRectangle(dpy, s->menuwin, selected ? s->gcmenubgs : s->gcmenubg, 0, i*high, wide, high);
     57 	XDrawString(dpy, s->menuwin, selected ? s->gcmenufgs : s->gcmenufg, tx, ty, m->item[i], strlen(m->item[i]));
     58 }
     59 
     60 int
     61 menuhit(XButtonEvent *e, Menu *m)
     62 {
     63 	XEvent ev;
     64 	int i, n, cur, old, wide, high, status, drawn, warp;
     65 	int x, y, dx, dy, xmax, ymax;
     66 	ScreenInfo *s;
     67 
     68 	if(font == 0)
     69 		return -1;
     70 	s = getscreen(e->root);
     71 	if(s == 0 || e->window == s->menuwin)	   /* ugly event mangling */
     72 		return -1;
     73 
     74 	dx = 0;
     75 	for(n = 0; m->item[n]; n++){
     76 		wide = XTextWidth(font, m->item[n], strlen(m->item[n])) + 4;
     77 		if(wide > dx)
     78 			dx = wide;
     79 	}
     80 	wide = dx;
     81 	cur = m->lasthit;
     82 	if(cur >= n)
     83 		cur = n - 1;
     84 
     85 	high = font->ascent + font->descent + 1;
     86 	dy = n*high;
     87 	x = e->x - wide/2;
     88 	y = e->y - cur*high - high/2;
     89 	warp = 0;
     90 	xmax = DisplayWidth(dpy, s->num);
     91 	ymax = DisplayHeight(dpy, s->num);
     92 	if(x < 0){
     93 		e->x -= x;
     94 		x = 0;
     95 		warp++;
     96 	}
     97 	if(x+wide >= xmax){
     98 		e->x -= x+wide-xmax;
     99 		x = xmax-wide;
    100 		warp++;
    101 	}
    102 	if(y < 0){
    103 		e->y -= y;
    104 		y = 0;
    105 		warp++;
    106 	}
    107 	if(y+dy >= ymax){
    108 		e->y -= y+dy-ymax;
    109 		y = ymax-dy;
    110 		warp++;
    111 	}
    112 	if(warp)
    113 		setmouse(e->x, e->y, s);
    114 	XMoveResizeWindow(dpy, s->menuwin, x, y, dx, dy);
    115 	XSelectInput(dpy, s->menuwin, MenuMask);
    116 	XMapRaised(dpy, s->menuwin);
    117 	status = grab(s->menuwin, None, MenuGrabMask, None, e->time);
    118 	if(status != GrabSuccess){
    119 		/* graberror("menuhit", status); */
    120 		XUnmapWindow(dpy, s->menuwin);
    121 		return -1;
    122 	}
    123 	drawn = 0;
    124 	for(;;){
    125 		XMaskEvent(dpy, MenuMask, &ev);
    126 		switch (ev.type){
    127 		default:
    128 			fprintf(stderr, "rio: menuhit: unknown ev.type %d\n", ev.type);
    129 			break;
    130 		case ButtonPress:
    131 			break;
    132 		case ButtonRelease:
    133 			if(ev.xbutton.button != e->button)
    134 				break;
    135 			x = ev.xbutton.x;
    136 			y = ev.xbutton.y;
    137 			i = y/high;
    138 			if(cur >= 0 && y >= cur*high-3 && y < (cur+1)*high+3)
    139 				i = cur;
    140 			if(x < 0 || x > wide || y < -3)
    141 				i = -1;
    142 			else if(i < 0 || i >= n)
    143 				i = -1;
    144 			else
    145 				m->lasthit = i;
    146 			if(!nobuttons(&ev.xbutton))
    147 				i = -1;
    148 			ungrab(&ev.xbutton);
    149 			XUnmapWindow(dpy, s->menuwin);
    150 			return i;
    151 		case MotionNotify:
    152 			if(!drawn)
    153 				break;
    154 			x = ev.xbutton.x;
    155 			y = ev.xbutton.y;
    156 			old = cur;
    157 			cur = y/high;
    158 			if(old >= 0 && y >= old*high-3 && y < (old+1)*high+3)
    159 				cur = old;
    160 			if(x < 0 || x > wide || y < -3)
    161 				cur = -1;
    162 			else if(cur < 0 || cur >= n)
    163 				cur = -1;
    164 			if(cur == old)
    165 				break;
    166 			if(old >= 0 && old < n)
    167 				drawstring(dpy, s, m, wide, high, old, 0);
    168 			if(cur >= 0 && cur < n)
    169 				drawstring(dpy, s, m, wide, high, cur, 1);
    170 			break;
    171 		case Expose:
    172 			XClearWindow(dpy, s->menuwin);
    173 			for(i = 0; i < n; i++)
    174 				drawstring(dpy, s, m, wide, high, i, cur==i);
    175 			drawn = 1;
    176 		}
    177 	}
    178 }
    179 
    180 Client *
    181 selectwin(int release, int *shift, ScreenInfo *s)
    182 {
    183 	XEvent ev;
    184 	XButtonEvent *e;
    185 	int status;
    186 	Window w;
    187 	Client *c;
    188 
    189 	status = grab(s->root, s->root, ButtonMask, s->target, 0);
    190 	if(status != GrabSuccess){
    191 		graberror("selectwin", status); /* */
    192 		return 0;
    193 	}
    194 	w = None;
    195 	for(;;){
    196 		XMaskEvent(dpy, ButtonMask, &ev);
    197 		e = &ev.xbutton;
    198 		switch (ev.type){
    199 		case ButtonPress:
    200 			if(e->button != Button3){
    201 				ungrab(e);
    202 				return 0;
    203 			}
    204 			w = e->subwindow;
    205 			if(!release){
    206 				c = getclient(w, 0);
    207 				if(c == 0)
    208 					ungrab(e);
    209 				if(shift != 0)
    210 					*shift = (e->state&ShiftMask) != 0;
    211 				return c;
    212 			}
    213 			break;
    214 		case ButtonRelease:
    215 			ungrab(e);
    216 			if(e->button != Button3 || e->subwindow != w)
    217 				return 0;
    218 			if(shift != 0)
    219 				*shift = (e->state&ShiftMask) != 0;
    220 			return getclient(w, 0);
    221 		}
    222 	}
    223 }
    224 
    225 int
    226 sweepcalc(Client *c, int x, int y, BorderOrient bl, int ignored)
    227 {
    228 	int dx, dy, sx, sy;
    229 
    230 	dx = x - c->x;
    231 	dy = y - c->y;
    232 	sx = sy = 1;
    233 	x += dx;
    234 	if(dx < 0){
    235 		dx = -dx;
    236 		sx = -1;
    237 	}
    238 	y += dy;
    239 	if(dy < 0){
    240 		dy = -dy;
    241 		sy = -1;
    242 	}
    243 
    244 	dx -= 2*BORDER;
    245 	dy -= 2*BORDER;
    246 
    247 	if(!c->is9term){
    248 		if(dx < c->min_dx)
    249 			dx = c->min_dx;
    250 		if(dy < c->min_dy)
    251 			dy = c->min_dy;
    252 	}
    253 
    254 	if(c->size.flags & PResizeInc){
    255 		dx = c->min_dx + (dx-c->min_dx)/c->size.width_inc*c->size.width_inc;
    256 		dy = c->min_dy + (dy-c->min_dy)/c->size.height_inc*c->size.height_inc;
    257 	}
    258 
    259 	if(c->size.flags & PMaxSize){
    260 		if(dx > c->size.max_width)
    261 			dx = c->size.max_width;
    262 		if(dy > c->size.max_height)
    263 			dy = c->size.max_height;
    264 	}
    265 	c->dx = sx*(dx + 2*BORDER);
    266 	c->dy = sy*(dy + 2*BORDER);
    267 
    268 	return ignored;
    269 }
    270 
    271 int
    272 dragcalc(Client *c, int x, int y, BorderOrient bl, int ignored)
    273 {
    274 	c->x += x;
    275 	c->y += y;
    276 
    277 	return ignored;
    278 }
    279 
    280 int
    281 pullcalc(Client *c, int x, int y, BorderOrient bl, int init)
    282 {
    283 	int dx, dy, sx, sy, px, py, spx, spy, rdx, rdy, xoff, yoff, xcorn, ycorn;
    284 
    285 	px = c->x;
    286 	py = c->y;
    287 	dx = c->dx;
    288 	dy = c->dy;
    289 	sx = sy = 1;
    290 	spx = spy = 0;
    291  	xoff = yoff = 0;
    292 	xcorn = ycorn = 0;
    293 
    294 	switch(bl){
    295 	case BorderN:
    296 		py = y;
    297 		dy = (c->y + c->dy)  - y;
    298 		spy = 1;
    299 		yoff = y - c->y;
    300 		break;
    301 	case BorderS:
    302 		dy = y - c->y;
    303 		yoff =  (c->y + c->dy) - y;
    304 		break;
    305 	case BorderE:
    306 		dx = x - c->x;
    307 		xoff = (c->x + c->dx) - x;
    308 		break;
    309 	case BorderW:
    310 		px = x;
    311 		dx = (c->x + c->dx) - x;
    312 		spx = 1;
    313 		xoff = x - c->x;
    314 		break;
    315 	case BorderNNW:
    316 	case BorderWNW:
    317 		px = x;
    318 		dx = (c->x + c->dx) - x;
    319 		spx = 1;
    320 		py = y;
    321 		dy = (c->y + c->dy)  - y;
    322 		spy = 1;
    323 		xoff = x - c->x;
    324 		yoff = y - c->y;
    325 		break;
    326 	case BorderNNE:
    327 	case BorderENE:
    328 		dx = x - c->x;
    329 		py = y;
    330 		dy = (c->y + c->dy)  - y;
    331 		spy = 1;
    332 		xoff = (c->x + c->dx) - x;
    333 		yoff = y - c->y;
    334 		break;
    335 	case BorderSSE:
    336 	case BorderESE:
    337 		dx = x - c->x;
    338 		dy = y - c->y;
    339 		xoff = (c->x + c->dx) - x;
    340 		yoff =  (c->y + c->dy) - y;
    341 		break;
    342 	case BorderSSW:
    343 	case BorderWSW:
    344 		px = x;
    345 		dx = (c->x + c->dx)  - x;
    346 		spx = 1;
    347 		dy = y - c->y;
    348 		xoff = x - c->x;
    349 		yoff =  (c->y + c->dy) - y;
    350 		break;
    351 	default:
    352 		break;
    353 	}
    354 	switch(bl){
    355 	case BorderNNW:
    356 	case BorderNNE:
    357 	case BorderSSW:
    358 	case BorderSSE:
    359 		xcorn = 1;
    360 		break;
    361 	case BorderWNW:
    362 	case BorderENE:
    363 	case BorderWSW:
    364 	case BorderESE:
    365 		ycorn = 1;
    366 		break;
    367 	}
    368 	if(!init
    369 		|| xoff < 0 || (xcorn && xoff > CORNER) || (!xcorn && xoff > BORDER)
    370 		|| yoff < 0 || (ycorn && yoff > CORNER) || (!ycorn && yoff > BORDER)){
    371 		xoff = 0;
    372 		yoff = 0;
    373 		init = 0;
    374 	}
    375 
    376 	if(debug) fprintf(stderr, "c %dx%d+%d+%d m +%d+%d r  %dx%d+%d+%d sp (%d,%d) bl %d\n",
    377 				c->dx, c->dy, c->x, c->y, x, y, dx, dy, px, py, spx, spy, bl);
    378 	if(dx < 0){
    379 		dx = -dx;
    380 		sx = -1;
    381 	}
    382 	if(dy < 0){
    383 		dy = -dy;
    384 		sy = -1;
    385 	}
    386 
    387 	/* remember requested size;
    388 	 * after applying size hints we may have to correct position
    389 	 */
    390 	rdx = sx*dx;
    391 	rdy = sy*dy;
    392 
    393 	/* apply size hints */
    394 	dx -= (2*BORDER - xoff);
    395 	dy -= (2*BORDER - yoff);
    396 
    397 	if(!c->is9term){
    398 		if(dx < c->min_dx)
    399 			dx = c->min_dx;
    400 		if(dy < c->min_dy)
    401 			dy = c->min_dy;
    402 	}
    403 
    404 	if(c->size.flags & PResizeInc){
    405 		dx = c->min_dx + (dx-c->min_dx)/c->size.width_inc*c->size.width_inc;
    406 		dy = c->min_dy + (dy-c->min_dy)/c->size.height_inc*c->size.height_inc;
    407 	}
    408 
    409 	if(c->size.flags & PMaxSize){
    410 		if(dx > c->size.max_width)
    411 			dx = c->size.max_width;
    412 		if(dy > c->size.max_height)
    413 			dy = c->size.max_height;
    414 	}
    415 
    416 	/* set size and position */
    417 	c->dx = sx*(dx + 2*BORDER );
    418 	c->dy = sy*(dy + 2*BORDER );
    419 	c->x = px;
    420 	c->y = py;
    421 
    422 	/* compensate position for size changed due to size hints */
    423 	if(spx)
    424 		c->x -= c->dx - rdx;
    425 	if(spy)
    426 		c->y -= c->dy - rdy;
    427 
    428 	return init;
    429 }
    430 
    431 static void
    432 xcopy(int fwd, Display *dpy, Drawable src, Drawable dst, GC gc, int x, int y, int dx, int dy, int x1, int y1)
    433 {
    434 	if(fwd)
    435 		XCopyArea(dpy, src, dst, gc, x, y, dx, dy, x1, y1);
    436 	else
    437 		XCopyArea(dpy, dst, src, gc, x1, y1, dx, dy, x, y);
    438 }
    439 
    440 void
    441 drawbound(Client *c, int drawing)
    442 {
    443 	int x, y, dx, dy;
    444 	ScreenInfo *s;
    445 
    446 	if(debug) fprintf(stderr, "drawbound %d %dx%d+%d+%d\n", drawing, c->dx, c->dy, c->x, c->y);
    447 
    448 	s = c->screen;
    449 	x = c->x;
    450 	y = c->y;
    451 	dx = c->dx;
    452 	dy = c->dy;
    453 	if(dx < 0){
    454 		x += dx;
    455 		dx = -dx;
    456 	}
    457 	if(dy < 0){
    458 		y += dy;
    459 		dy = -dy;
    460 	}
    461 	if(dx <= 2 || dy <= 2)
    462 		return;
    463 
    464 	if(solidsweep){
    465 		if(drawing == -1){
    466 			XUnmapWindow(dpy, s->sweepwin);
    467 			return;
    468 		}
    469 
    470 		x += BORDER;
    471 		y += BORDER;
    472 		dx -= 2*BORDER;
    473 		dy -= 2*BORDER;
    474 
    475 		if(drawing){
    476 			XMoveResizeWindow(dpy, s->sweepwin, x, y, dx, dy);
    477 			XSelectInput(dpy, s->sweepwin, MenuMask);
    478 			XMapRaised(dpy, s->sweepwin);
    479 		}
    480 		return;
    481 	}
    482 
    483 	if(drawing == -1)
    484 		return;
    485 
    486 	xcopy(drawing, dpy, s->root, s->bkup[0], s->gccopy, x, y, dx, BORDER, 0, 0);
    487 	xcopy(drawing, dpy, s->root, s->bkup[0], s->gccopy, x, y+dy-BORDER, dx, BORDER, dx, 0);
    488 	xcopy(drawing, dpy, s->root, s->bkup[1], s->gccopy, x, y, BORDER, dy, 0, 0);
    489 	xcopy(drawing, dpy, s->root, s->bkup[1], s->gccopy, x+dx-BORDER, y, BORDER, dy, 0, dy);
    490 
    491 	if(drawing){
    492 		XFillRectangle(dpy, s->root, s->gcred, x, y, dx, BORDER);
    493 		XFillRectangle(dpy, s->root, s->gcred, x, y+dy-BORDER, dx, BORDER);
    494 		XFillRectangle(dpy, s->root, s->gcred, x, y, BORDER, dy);
    495 		XFillRectangle(dpy, s->root, s->gcred, x+dx-BORDER, y, BORDER, dy);
    496 	}
    497 }
    498 
    499 void
    500 misleep(int msec)
    501 {
    502 	struct timeval t;
    503 
    504 	t.tv_sec = msec/1000;
    505 	t.tv_usec = (msec%1000)*1000;
    506 	select(0, 0, 0, 0, &t);
    507 }
    508 
    509 int
    510 sweepdrag(Client *c, int but, XButtonEvent *e0, BorderOrient bl, int (*recalc)(Client*, int, int, BorderOrient, int))
    511 {
    512 	XEvent ev;
    513 	int idle;
    514 	int cx, cy, rx, ry;
    515 	int ox, oy, odx, ody;
    516 	XButtonEvent *e;
    517 	int notmoved;
    518 
    519 	notmoved = 1;
    520 	ox = c->x;
    521 	oy = c->y;
    522 	odx = c->dx;
    523 	ody = c->dy;
    524 	c->x -= BORDER;
    525 	c->y -= BORDER;
    526 	c->dx += 2*BORDER;
    527 	c->dy += 2*BORDER;
    528 	if(bl != BorderUnknown || e0 == 0)
    529 		getmouse(&cx, &cy, c->screen);
    530 	else
    531 		getmouse(&c->x, &c->y, c->screen);
    532 	XGrabServer(dpy);
    533 	if(bl != BorderUnknown){
    534 		notmoved = recalc(c, cx, cy, bl, notmoved);
    535 	}
    536 	drawbound(c, 1);
    537 	idle = 0;
    538 	for(;;){
    539 		if(XCheckMaskEvent(dpy, ButtonMask, &ev) == 0){
    540 			getmouse(&rx, &ry, c->screen);
    541 			if(rx != cx || ry != cy || ++idle > 300){
    542 				drawbound(c, 0);
    543 				if(rx == cx && ry == cy){
    544 					XUngrabServer(dpy);
    545 					XFlush(dpy);
    546 					misleep(500);
    547 					XGrabServer(dpy);
    548 					idle = 0;
    549 				}
    550 				if(e0 || bl != BorderUnknown)
    551 					notmoved = recalc(c, rx, ry, bl, notmoved);
    552 				else
    553 					notmoved = recalc(c, rx-cx, ry-cy, bl, notmoved);
    554 				cx = rx;
    555 				cy = ry;
    556 				drawbound(c, 1);
    557 				XFlush(dpy);
    558 			}
    559 			misleep(50);
    560 			continue;
    561 		}
    562 		e = &ev.xbutton;
    563 		switch (ev.type){
    564 		case ButtonPress:
    565 		case ButtonRelease:
    566 			drawbound(c, 0);
    567 			ungrab(e);
    568 			XUngrabServer(dpy);
    569 			if(e->button != but && c->init)
    570 				goto bad;
    571 			if(c->dx < 0){
    572 				c->x += c->dx;
    573 				c->dx = -c->dx;
    574 			}
    575 			if(c->dy < 0){
    576 				c->y += c->dy;
    577 				c->dy = -c->dy;
    578 			}
    579 			c->x += BORDER;
    580 			c->y += BORDER;
    581 			c->dx -= 2*BORDER;
    582 			c->dy -= 2*BORDER;
    583 			if(c->dx < 4 || c->dy < 4 || c->dx < c->min_dx || c->dy < c->min_dy)
    584 				goto bad;
    585 			return 1;
    586 		}
    587 	}
    588 bad:
    589 	if(debug) fprintf(stderr, "sweepdrag bad\n");
    590 	c->x = ox;
    591 	c->y = oy;
    592 	c->dx = odx;
    593 	c->dy = ody;
    594 	drawbound(c, -1);
    595 	return 0;
    596 }
    597 
    598 int
    599 sweep(Client *c, int but, XButtonEvent *ignored)
    600 {
    601 	XEvent ev;
    602 	int status;
    603 	XButtonEvent *e;
    604 	ScreenInfo *s;
    605 
    606 	s = c->screen;
    607 	c->dx = 0;
    608 	c->dy = 0;
    609 	status = grab(s->root, s->root, ButtonMask, s->sweep0, 0);
    610 	if(status != GrabSuccess){
    611 		graberror("sweep", status); /* */
    612 		return 0;
    613 	}
    614 
    615 	XMaskEvent(dpy, ButtonMask, &ev);
    616 	e = &ev.xbutton;
    617 	if(e->button != but){
    618 		ungrab(e);
    619 		return 0;
    620 	}
    621 	XChangeActivePointerGrab(dpy, ButtonMask, s->boxcurs, e->time);
    622 	return sweepdrag(c, but, e, BorderUnknown, sweepcalc);
    623 }
    624 
    625 int
    626 pull(Client *c, int but, XButtonEvent *e)
    627 {
    628 	int status;
    629 	ScreenInfo *s;
    630 	BorderOrient bl;
    631 
    632 	bl = borderorient(c, e->x, e->y);
    633 	/* assert(bl > BorderUnknown && bl < NBorder); */
    634 
    635 	s = c->screen;
    636 	status = grab(s->root, s->root, ButtonMask, s->bordcurs[bl], 0);
    637 	if(status != GrabSuccess){
    638 		graberror("pull", status); /* */
    639 		return 0;
    640 	}
    641 
    642 	return sweepdrag(c, but, 0, bl, pullcalc);
    643 }
    644 
    645 int
    646 drag(Client *c, int but)
    647 {
    648 	int status;
    649 	ScreenInfo *s;
    650 
    651 	s = c->screen;
    652 	status = grab(s->root, s->root, ButtonMask, s->boxcurs, 0);
    653 	if(status != GrabSuccess){
    654 		graberror("drag", status); /* */
    655 		return 0;
    656 	}
    657 	return sweepdrag(c, but, 0, BorderUnknown, dragcalc);
    658 }
    659 
    660 void
    661 getmouse(int *x, int *y, ScreenInfo *s)
    662 {
    663 	Window dw1, dw2;
    664 	int t1, t2;
    665 	unsigned int t3;
    666 
    667 	XQueryPointer(dpy, s->root, &dw1, &dw2, x, y, &t1, &t2, &t3);
    668 	if(debug) fprintf(stderr, "getmouse: %d %d\n", *x, *y);
    669 }
    670 
    671 void
    672 setmouse(int x, int y, ScreenInfo *s)
    673 {
    674 	XWarpPointer(dpy, None, s->root, None, None, None, None, x, y);
    675 }