layout.c (11685B)
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 /* Actually, I'm happy to say, the dragons have dissipated. */ 9 10 enum { 11 ButtonMask = 12 ButtonPressMask | ButtonReleaseMask, 13 MouseMask = 14 ButtonMask | PointerMotionMask 15 }; 16 17 static Handlers handlers; 18 19 enum { OHoriz, OVert }; 20 typedef struct Framewin Framewin; 21 struct Framewin { 22 /* Todo... give these better names. */ 23 Window* w; 24 Rectangle grabbox; 25 Frame* f; 26 Area* ra; 27 Point pt; 28 int orientation; 29 int xy; 30 int screen; 31 }; 32 33 static Rectangle 34 framerect(Framewin *f) { 35 Rectangle r; 36 Point p; 37 int scrn; 38 39 r.min = ZP; 40 if(f->orientation == OHoriz) { 41 r.max.x = f->xy; 42 r.max.y = f->grabbox.max.y + f->grabbox.min.y; 43 }else { 44 r.max.x = f->grabbox.max.x + f->grabbox.min.x; 45 r.max.y = f->xy; 46 r = rectsubpt(r, Pt(Dx(r)/2, 0)); 47 } 48 r = rectaddpt(r, f->pt); 49 50 scrn = f->screen; 51 if(scrn == -1) 52 scrn = max(ownerscreen(f->f->r), 0); 53 54 /* Keep onscreen */ 55 p = ZP; 56 p.x -= min(0, r.min.x); 57 p.x -= max(0, r.max.x - screens[scrn]->r.max.x); 58 p.y -= max(0, r.max.y - screens[scrn]->brect.min.y - Dy(r)/2); 59 return rectaddpt(r, p); 60 } 61 62 static void 63 frameadjust(Framewin *f, Point pt, int orientation, int xy) { 64 f->orientation = orientation; 65 f->xy = xy; 66 f->pt = pt; 67 } 68 69 static Framewin* 70 framewin(Frame *f, Point pt, int orientation, int n) { 71 WinAttr wa; 72 Framewin *fw; 73 74 fw = emallocz(sizeof *fw); 75 wa.override_redirect = true; 76 wa.event_mask = ExposureMask; 77 fw->w = createwindow(&scr.root, Rect(0, 0, 1, 1), 78 scr.depth, InputOutput, 79 &wa, CWEventMask); 80 fw->w->aux = fw; 81 sethandler(fw->w, &handlers); 82 83 fw->f = f; 84 fw->screen = f->area->screen; 85 fw->grabbox = f->grabbox; 86 frameadjust(fw, pt, orientation, n); 87 reshapewin(fw->w, framerect(fw)); 88 89 raisewin(fw->w); 90 91 return fw; 92 } 93 94 static void 95 framedestroy(Framewin *f) { 96 destroywindow(f->w); 97 free(f); 98 } 99 100 static bool 101 expose_event(Window *w, void *aux, XExposeEvent *e) { 102 Rectangle r; 103 Framewin *f; 104 Image *buf; 105 CTuple *c; 106 107 USED(e); 108 109 f = aux; 110 c = &def.focuscolor; 111 buf = disp.ibuf; 112 113 r = rectsubpt(w->r, w->r.min); 114 fill(buf, r, &c->bg); 115 border(buf, r, 1, &c->border); 116 border(buf, f->grabbox, 1, &c->border); 117 border(buf, insetrect(f->grabbox, -f->grabbox.min.x), 1, &c->border); 118 119 copyimage(w, r, buf, ZP); 120 return false; 121 } 122 123 static Handlers handlers = { 124 .expose = expose_event, 125 }; 126 127 static Area* 128 find_area(Point pt) { 129 View *v; 130 Area *a; 131 int s; 132 133 v = selview; 134 for(s=0; s < nscreens; s++) { 135 if(!rect_haspoint_p(screens[s]->r, pt)) 136 continue; 137 for(a=v->areas[s]; a; a=a->next) 138 if(pt.x < a->r.max.x) 139 return a; 140 } 141 return nil; 142 } 143 144 static void 145 vplace(Framewin *fw, Point pt) { 146 Vector_long vec = {0}; 147 Rectangle r; 148 Frame *f; 149 Area *a; 150 long l; 151 int hr; 152 153 a = find_area(pt); 154 if(a == nil) 155 return; 156 157 fw->ra = a; 158 fw->screen = a->screen; 159 160 pt.x = a->r.min.x; 161 frameadjust(fw, pt, OHoriz, Dx(a->r)); 162 163 r = fw->w->r; 164 hr = Dy(r)/2; 165 pt.y -= hr; 166 167 if(a->frame == nil) 168 goto done; 169 170 vector_lpush(&vec, a->frame->r.min.y); 171 for(f=a->frame; f; f=f->anext) { 172 if(f == fw->f) 173 vector_lpush(&vec, f->r.min.y + 0*hr); 174 else if(f->collapsed) 175 vector_lpush(&vec, f->r.min.y + 1*hr); 176 else 177 vector_lpush(&vec, f->r.min.y + 2*hr); 178 if(!f->collapsed && f->anext != fw->f) 179 vector_lpush(&vec, f->r.max.y - 2*hr); 180 } 181 182 for(int i=0; i < vec.n; i++) { 183 l = vec.ary[i]; 184 if(abs(pt.y - l) < hr) { 185 pt.y = l; 186 break; 187 } 188 } 189 vector_lfree(&vec); 190 191 done: 192 pt.x = a->r.min.x; 193 frameadjust(fw, pt, OHoriz, Dx(a->r)); 194 reshapewin(fw->w, framerect(fw)); 195 } 196 197 static void 198 hplace(Framewin *fw, Point pt) { 199 Area *a; 200 int minw; 201 202 a = find_area(pt); 203 if(a == nil) 204 return; /* XXX: Multihead. */ 205 206 fw->screen = a->screen; 207 fw->ra = nil; 208 minw = column_minwidth(); 209 if(abs(pt.x - a->r.min.x) < minw/2) { 210 pt.x = a->r.min.x; 211 fw->ra = a->prev; 212 } 213 else if(abs(pt.x - a->r.max.x) < minw/2) { 214 pt.x = a->r.max.x; 215 fw->ra = a; 216 } 217 218 pt.y = a->r.min.y; 219 frameadjust(fw, pt, OVert, Dy(a->r)); 220 reshapewin(fw->w, framerect(fw)); 221 } 222 223 static Point 224 grabboxcenter(Frame *f) { 225 Point p; 226 227 p = addpt(f->r.min, f->grabbox.min); 228 p.x += Dx(f->grabbox)/2; 229 p.y += Dy(f->grabbox)/2; 230 return p; 231 } 232 233 static int tvcol(Frame*, bool); 234 static int thcol(Frame*, bool); 235 static int tfloat(Frame*, bool); 236 237 enum { 238 TDone, 239 TVCol, 240 THCol, 241 TFloat, 242 }; 243 244 static int (*tramp[])(Frame*, bool) = { 245 0, 246 tvcol, 247 thcol, 248 tfloat, 249 }; 250 251 /* Trampoline to allow properly tail recursive move/resize routines. 252 * We could probably get away with plain tail calls, but I don't 253 * like the idea. 254 */ 255 static void 256 trampoline(int fn, Frame *f, bool grabbox) { 257 int moved; 258 259 moved = 0; 260 while(fn > 0) { 261 if(grabbox) 262 warppointer(grabboxcenter(f)); 263 //f->collapsed = false; 264 fn = tramp[fn](f, moved++); 265 } 266 ungrabpointer(); 267 } 268 269 static void 270 resizemode(int mode) { 271 bool orig; 272 273 orig = resizing; 274 resizing = mode && mode != TFloat; 275 if(resizing != orig) 276 view_update(selview); 277 } 278 279 void 280 mouse_movegrabbox(Client *c, bool grabmod) { 281 Frame *f; 282 Point p; 283 float x, y; 284 285 f = c->sel; 286 287 SET(x); 288 SET(y); 289 if(grabmod) { 290 p = querypointer(f->client->framewin); 291 x = (float)p.x / Dx(f->r); 292 y = (float)p.y / Dy(f->r); 293 } 294 295 if(f->area->floating) 296 trampoline(TFloat, f, !grabmod); 297 else 298 trampoline(THCol, f, true); 299 300 if(grabmod) 301 warppointer(addpt(f->r.min, Pt(x * Dx(f->r), 302 y * Dy(f->r)))); 303 else 304 warppointer(grabboxcenter(f)); 305 } 306 307 static int 308 _openstack_down(Frame *f, int h) { 309 int ret; 310 int dy; 311 312 if(f == nil) 313 return 0;; 314 ret = 0; 315 if(!f->collapsed) { 316 dy = Dy(f->colr) - labelh(def.font); 317 if(dy >= h) { 318 f->colr.min.y += h; 319 return h; 320 }else { 321 f->collapsed = true; 322 f->colr.min.y += dy; 323 ret = dy; 324 h -= dy; 325 } 326 } 327 dy = _openstack_down(f->anext, h); 328 f->colr.min.y += dy; 329 f->colr.max.y += dy; 330 return ret + dy; 331 } 332 333 static int 334 _openstack_up(Frame *f, int h) { 335 int ret; 336 int dy; 337 338 if(f == nil) 339 return 0; 340 ret = 0; 341 if(!f->collapsed) { 342 dy = Dy(f->colr) - labelh(def.font); 343 if(dy >= h) { 344 f->colr.max.y -= h; 345 return h; 346 }else { 347 f->collapsed = true; 348 f->colr.max.y -= dy; 349 ret = dy; 350 h -= dy; 351 } 352 } 353 dy = _openstack_up(f->aprev, h); 354 f->colr.min.y -= dy; 355 f->colr.max.y -= dy; 356 return ret + dy; 357 } 358 359 static void 360 column_openstack(Area *a, Frame *f, int h) { 361 362 if(f == nil) 363 _openstack_down(a->frame, h); 364 else { 365 h -= _openstack_down(f->anext, h); 366 if(h) 367 _openstack_up(f->aprev, h); 368 } 369 } 370 371 static void 372 column_drop(Area *a, Frame *f, int y) { 373 Frame *ff; 374 int dy, extra_y; 375 376 extra_y = Dy(a->r); 377 for(ff=a->frame; ff; ff=ff->anext) { 378 assert(ff != f); 379 extra_y -= Dy(ff->colr); 380 } 381 382 if(a->frame == nil || y <= a->frame->r.min.y) { 383 f->collapsed = true; 384 f->colr.min.y = 0; 385 f->colr.max.y = labelh(def.font); 386 column_openstack(a, nil, labelh(def.font)); 387 column_insert(a, f, nil); 388 return; 389 } 390 for(ff=a->frame; ff->anext; ff=ff->anext) 391 if(y <= ff->colr.max.y) break; 392 393 y = max(y, ff->colr.min.y + labelh(def.font)); 394 y = min(y, ff->colr.max.y); 395 dy = ff->colr.max.y - y; 396 if(dy <= labelh(def.font)) { 397 f->collapsed = true; 398 f->colr.min.y = 0; 399 f->colr.max.y = labelh(def.font); 400 column_openstack(a, ff, labelh(def.font) - dy); 401 }else { 402 f->colr.min.y = y; 403 f->colr.max.y = ff->colr.max.y + extra_y; 404 ff->colr.max.y = y; 405 } 406 column_insert(a, f, ff); 407 } 408 409 static int 410 thcol(Frame *f, bool moved) { 411 Framewin *fw; 412 Frame *fp, *fn; 413 Area *a; 414 Point pt, pt2; 415 uint button; 416 int ret, collapsed; 417 418 focus(f->client, false); 419 420 ret = TDone; 421 if(!grabpointer(&scr.root, nil, None, MouseMask)) 422 return TDone; 423 424 readmotion(&pt); 425 pt2.x = f->area->r.min.x; 426 pt2.y = pt.y; 427 fw = framewin(f, pt2, OHoriz, Dx(f->area->r)); 428 429 if(moved) 430 goto casemotion; 431 432 vplace(fw, pt); 433 for(;;) 434 switch (readmouse(&pt, &button)) { 435 case MotionNotify: 436 casemotion: 437 moved = 1; 438 resizemode(THCol); 439 if(mapwin(fw->w)) 440 grabpointer(&scr.root, nil, cursor[CurIcon], MouseMask); 441 vplace(fw, pt); 442 break; 443 case ButtonRelease: 444 if(!moved) 445 goto done; 446 447 if(button != 1) 448 continue; 449 SET(collapsed); 450 SET(fp); 451 SET(fn); 452 a = f->area; 453 if(a->floating) 454 area_detach(f); 455 else { 456 collapsed = f->collapsed; 457 fp = f->aprev; 458 fn = f->anext; 459 column_remove(f); 460 if(!f->collapsed) 461 if(fp) 462 fp->colr.max.y = f->colr.max.y; 463 else if(fn && fw->pt.y > fn->r.min.y) 464 fn->colr.min.y = f->colr.min.y; 465 } 466 467 column_drop(fw->ra, f, fw->pt.y); 468 if(!a->floating && collapsed) { 469 /* XXX */ 470 for(; fn && fn->collapsed; fn=fn->anext) 471 ; 472 if(fn == nil) 473 for(fn=fp; fn && fn->collapsed; fn=fn->aprev) 474 ; 475 if(fp) 476 fp->colr.max.x += labelh(def.font); 477 } 478 479 480 if(!a->frame && !a->floating && a->view->areas[a->screen]->next) 481 area_destroy(a); 482 483 frame_focus(f); 484 goto done; 485 case ButtonPress: 486 if(button == 2) 487 ret = TVCol; 488 else if(button == 3) 489 ret = TFloat; 490 else 491 continue; 492 goto done; 493 } 494 done: 495 resizemode(0); 496 framedestroy(fw); 497 return ret; 498 } 499 500 static int 501 tvcol(Frame *f, bool moved) { 502 Framewin *fw; 503 Window *cwin; 504 Rectangle r; 505 Point pt, pt2; 506 uint button; 507 int ret, scrn; 508 509 focus(f->client, false); 510 511 pt = querypointer(&scr.root); 512 pt2.x = pt.x; 513 pt2.y = f->area->r.min.y; 514 515 scrn = f->area->screen > -1 ? f->area->screen : find_area(pt) ? find_area(pt)->screen : 0; 516 r = f->view->r[scrn]; 517 fw = framewin(f, pt2, OVert, Dy(r)); 518 mapwin(fw->w); 519 520 r.min.y += fw->grabbox.min.y + Dy(fw->grabbox)/2; 521 r.max.y = r.min.y + 1; 522 cwin = createwindow(&scr.root, r, 0, InputOnly, nil, 0); 523 mapwin(cwin); 524 525 ret = TDone; 526 if(!grabpointer(&scr.root, cwin, cursor[CurIcon], MouseMask)) 527 goto done; 528 529 resizemode(TVCol); 530 531 hplace(fw, pt); 532 for(;;) 533 switch (readmouse(&pt, &button)) { 534 case MotionNotify: 535 moved = 1; 536 hplace(fw, pt); 537 continue; 538 case ButtonPress: 539 if(button == 2) 540 ret = THCol; 541 else if(button == 3) 542 ret = TFloat; 543 else 544 continue; 545 goto done; 546 case ButtonRelease: 547 if(button != 1) 548 continue; 549 if(fw->ra) { 550 fw->ra = column_new(f->view, fw->ra, screen->idx, 0); 551 area_moveto(fw->ra, f); 552 } 553 goto done; 554 } 555 556 done: 557 framedestroy(fw); 558 destroywindow(cwin); 559 resizemode(0); 560 return ret; 561 } 562 563 static int 564 tfloat(Frame *f, bool moved) { 565 Rectangle *rects; 566 Rectangle frect, origin; 567 Point pt, pt1; 568 Client *c; 569 Align align; 570 uint nrect, button; 571 int ret; 572 573 c = f->client; 574 if(!f->area->floating) { 575 if(f->anext) 576 f->anext->colr.min.y = f->colr.min.y; 577 else if(f->aprev) 578 f->aprev->colr.max.y = f->colr.max.y; 579 area_moveto(f->view->floating, f); 580 view_update(f->view); 581 warppointer(grabboxcenter(f)); 582 } 583 client_mapframe(f->client); 584 if(!f->collapsed) 585 focus(f->client, false); 586 587 ret = TDone; 588 if(!grabpointer(c->framewin, nil, cursor[CurMove], MouseMask)) 589 return TDone; 590 591 rects = view_rects(f->view, &nrect, f); 592 origin = f->r; 593 frect = f->r; 594 595 if(!readmotion(&pt)) { 596 focus(f->client, false); 597 goto done; 598 } 599 /* pt1 = grabboxcenter(f); */ 600 pt1 = pt; 601 goto case_motion; 602 603 shut_up_ken: 604 for(;;pt1=pt) 605 switch (readmouse(&pt, &button)) { 606 default: goto shut_up_ken; 607 case MotionNotify: 608 moved = 1; 609 case_motion: 610 origin = rectaddpt(origin, subpt(pt, pt1)); 611 origin = constrain(origin, -1); 612 frect = origin; 613 614 align = Center; 615 snap_rect(rects, nrect, &frect, &align, def.snap); 616 617 frect = frame_hints(f, frect, Center); 618 frect = constrain(frect, -1); 619 client_resize(c, frect); 620 continue; 621 case ButtonRelease: 622 if(button != 1) 623 continue; 624 if(!moved) { 625 f->collapsed = !f->collapsed; 626 client_resize(f->client, f->floatr); 627 } 628 goto done; 629 case ButtonPress: 630 if(button != 3) 631 continue; 632 client_unmapframe(f->client); 633 ret = THCol; 634 goto done; 635 } 636 done: 637 free(rects); 638 return ret; 639 } 640