Blob


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"
10 int
11 nobuttons(XButtonEvent *e) /* Einstuerzende */
12 {
13 int state;
15 state = (e->state & AllButtonMask);
16 return (e->type == ButtonRelease) && (state & (state - 1)) == 0;
17 }
19 int
20 grab(Window w, Window constrain, int mask, Cursor curs, int t)
21 {
22 int status;
24 if(t == 0)
25 t = timestamp();
26 status = XGrabPointer(dpy, w, False, mask,
27 GrabModeAsync, GrabModeAsync, constrain, curs, t);
28 return status;
29 }
31 void
32 ungrab(XButtonEvent *e)
33 {
34 XEvent ev;
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 }
49 static void
50 drawstring(Display *dpy, ScreenInfo *s, Menu *m, int wide, int high, int i, int selected)
51 {
52 int tx, ty;
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 }
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;
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;
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;
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++;
102 if(y < 0){
103 e->y -= y;
104 y = 0;
105 warp++;
107 if(y+dy >= ymax){
108 e->y -= y+dy-ymax;
109 y = ymax-dy;
110 warp++;
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;
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;
180 Client *
181 selectwin(int release, int *shift, ScreenInfo *s)
183 XEvent ev;
184 XButtonEvent *e;
185 int status;
186 Window w;
187 Client *c;
189 status = grab(s->root, s->root, ButtonMask, s->target, 0);
190 if(status != GrabSuccess){
191 graberror("selectwin", status); /* */
192 return 0;
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;
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;
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);
225 int
226 sweepcalc(Client *c, int x, int y, BorderOrient bl, int ignored)
228 int dx, dy, sx, sy;
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;
238 y += dy;
239 if(dy < 0){
240 dy = -dy;
241 sy = -1;
244 dx -= 2*BORDER;
245 dy -= 2*BORDER;
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;
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;
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;
265 c->dx = sx*(dx + 2*BORDER);
266 c->dy = sy*(dy + 2*BORDER);
268 return ignored;
271 int
272 dragcalc(Client *c, int x, int y, BorderOrient bl, int ignored)
274 c->x += x;
275 c->y += y;
277 return ignored;
280 int
281 pullcalc(Client *c, int x, int y, BorderOrient bl, int init)
283 int dx, dy, sx, sy, px, py, spx, spy, rdx, rdy, xoff, yoff, xcorn, ycorn;
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;
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;
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;
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;
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;
382 if(dy < 0){
383 dy = -dy;
384 sy = -1;
387 /* remember requested size;
388 * after applying size hints we may have to correct position
389 */
390 rdx = sx*dx;
391 rdy = sy*dy;
393 /* apply size hints */
394 dx -= (2*BORDER - xoff);
395 dy -= (2*BORDER - yoff);
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;
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;
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;
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;
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;
428 return init;
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)
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);
440 void
441 drawbound(Client *c, int drawing)
443 int x, y, dx, dy;
444 ScreenInfo *s;
446 if(debug) fprintf(stderr, "drawbound %d %dx%d+%d+%d\n", drawing, c->dx, c->dy, c->x, c->y);
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;
457 if(dy < 0){
458 y += dy;
459 dy = -dy;
461 if(dx <= 2 || dy <= 2)
462 return;
464 if(solidsweep){
465 if(drawing == -1){
466 XUnmapWindow(dpy, s->sweepwin);
467 return;
470 x += BORDER;
471 y += BORDER;
472 dx -= 2*BORDER;
473 dy -= 2*BORDER;
475 if(drawing){
476 XMoveResizeWindow(dpy, s->sweepwin, x, y, dx, dy);
477 XSelectInput(dpy, s->sweepwin, MenuMask);
478 XMapRaised(dpy, s->sweepwin);
480 return;
483 if(drawing == -1)
484 return;
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);
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);
499 void
500 misleep(int msec)
502 struct timeval t;
504 t.tv_sec = msec/1000;
505 t.tv_usec = (msec%1000)*1000;
506 select(0, 0, 0, 0, &t);
509 int
510 sweepdrag(Client *c, int but, XButtonEvent *e0, BorderOrient bl, int (*recalc)(Client*, int, int, BorderOrient, int))
512 XEvent ev;
513 int idle;
514 int cx, cy, rx, ry;
515 int ox, oy, odx, ody;
516 XButtonEvent *e;
517 int notmoved;
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);
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;
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);
559 misleep(50);
560 continue;
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;
575 if(c->dy < 0){
576 c->y += c->dy;
577 c->dy = -c->dy;
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;
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;
598 int
599 sweep(Client *c, int but, XButtonEvent *ignored)
601 XEvent ev;
602 int status;
603 XButtonEvent *e;
604 ScreenInfo *s;
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;
615 XMaskEvent(dpy, ButtonMask, &ev);
616 e = &ev.xbutton;
617 if(e->button != but){
618 ungrab(e);
619 return 0;
621 XChangeActivePointerGrab(dpy, ButtonMask, s->boxcurs, e->time);
622 return sweepdrag(c, but, e, BorderUnknown, sweepcalc);
625 int
626 pull(Client *c, int but, XButtonEvent *e)
628 int status;
629 ScreenInfo *s;
630 BorderOrient bl;
632 bl = borderorient(c, e->x, e->y);
633 /* assert(bl > BorderUnknown && bl < NBorder); */
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;
642 return sweepdrag(c, but, 0, bl, pullcalc);
645 int
646 drag(Client *c, int but)
648 int status;
649 ScreenInfo *s;
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;
657 return sweepdrag(c, but, 0, BorderUnknown, dragcalc);
660 void
661 getmouse(int *x, int *y, ScreenInfo *s)
663 Window dw1, dw2;
664 int t1, t2;
665 unsigned int t3;
667 XQueryPointer(dpy, s->root, &dw1, &dw2, x, y, &t1, &t2, &t3);
668 if(debug) fprintf(stderr, "getmouse: %d %d\n", *x, *y);
671 void
672 setmouse(int x, int y, ScreenInfo *s)
674 XWarpPointer(dpy, None, s->root, None, None, None, None, x, y);