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