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 }