frame.c (13941B)
1 /* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail> 2 * See LICENSE file for license details. 3 */ 4 #include "dat.h" 5 #include <math.h> 6 #include "fns.h" 7 8 uint 9 frame_idx(Frame *f) { 10 Frame *fp; 11 uint i; 12 13 fp = f->area->frame; 14 for(i = 1; fp != f; fp = fp->anext) 15 i++; 16 return i; 17 } 18 19 Frame* 20 frame_create(Client *c, View *v) { 21 static ushort id = 1; 22 Frame *f; 23 24 f = emallocz(sizeof *f); 25 f->id = id++; 26 f->client = c; 27 f->view = v; 28 29 if(c->sel) { 30 f->floatr = c->sel->floatr; 31 f->r = c->sel->r; 32 }else if(c->sel) { 33 f->floatr = c->frame->floatr; 34 f->r = c->frame->r; 35 }else { 36 f->r = client_grav(c, c->r); 37 f->floatr = f->r; 38 c->sel = f; 39 } 40 f->collapsed = false; 41 f->screen = -1; 42 f->oldarea = -1; 43 f->oldscreen = -1; 44 45 return f; 46 } 47 48 void 49 frame_remove(Frame *f) { 50 Area *a; 51 52 a = f->area; 53 if(f->aprev) 54 f->aprev->anext = f->anext; 55 if(f->anext) 56 f->anext->aprev = f->aprev; 57 if(f == a->frame) 58 a->frame = f->anext; 59 60 if(a->floating) { 61 if(f->sprev) 62 f->sprev->snext = f->snext; 63 if(f->snext) 64 f->snext->sprev = f->sprev; 65 if(f == a->stack) 66 a->stack = f->snext; 67 } 68 f->anext = f->aprev = f->snext = f->sprev = nil; 69 } 70 71 void 72 frame_insert(Frame *f, Frame *pos) { 73 Area *a; 74 75 a = f->area; 76 77 if(pos) { 78 assert(pos != f); 79 f->aprev = pos; 80 f->anext = pos->anext; 81 }else { 82 assert(f->area->frame != f); 83 f->anext = f->area->frame; 84 f->area->frame = f; 85 } 86 if(f->aprev) 87 f->aprev->anext = f; 88 if(f->anext) 89 f->anext->aprev = f; 90 91 if(a->floating) { 92 assert(f->sprev == nil); 93 frame_restack(f, nil); 94 } 95 } 96 97 bool 98 frame_restack(Frame *f, Frame *above) { 99 Area *a; 100 101 a = f->area; 102 if(!a->floating) 103 return false; 104 if(f == above) 105 return false; 106 107 if(f->sprev || f == a->stack) 108 if(f->sprev == above) 109 return false; 110 111 if(f->sprev) 112 f->sprev->snext = f->snext; 113 else if(f->snext) 114 a->stack = f->snext; 115 if(f->snext) 116 f->snext->sprev = f->sprev; 117 118 f->sprev = above; 119 if(above == nil) { 120 f->snext = a->stack; 121 a->stack = f; 122 } 123 else { 124 f->snext = above->snext; 125 above->snext = f; 126 } 127 if(f->snext) 128 f->snext->sprev = f; 129 assert(f->snext != f && f->sprev != f); 130 131 return true; 132 } 133 134 /* Handlers */ 135 static bool 136 bup_event(Window *w, void *aux, XButtonEvent *e) { 137 if((e->state & def.mod) != def.mod) 138 XAllowEvents(display, ReplayPointer, e->time); 139 else 140 XUngrabPointer(display, e->time); 141 if(!e->subwindow) 142 event("ClientClick %#C %d\n", aux, e->button); 143 return false; 144 } 145 146 static bool 147 bdown_event(Window *w, void *aux, XButtonEvent *e) { 148 Frame *f; 149 Client *c; 150 151 c = aux; 152 f = c->sel; 153 154 if((e->state & def.mod) == def.mod) { 155 switch(e->button) { 156 case Button1: 157 focus(c, false); 158 mouse_resize(c, Center, true); 159 break; 160 case Button2: 161 frame_restack(f, nil); 162 view_restack(f->view); 163 focus(c, false); 164 grabpointer(c->framewin, nil, cursor[CurNone], ButtonReleaseMask); 165 break; 166 case Button3: 167 focus(c, false); 168 mouse_resize(c, quadrant(f->r, Pt(e->x_root, e->y_root)), true); 169 break; 170 default: 171 XAllowEvents(display, ReplayPointer, e->time); 172 break; 173 } 174 }else { 175 if(e->button == Button1) { 176 if(!e->subwindow) { 177 frame_restack(f, nil); 178 view_restack(f->view); 179 mouse_checkresize(f, Pt(e->x, e->y), true); 180 } 181 182 if(f->client != selclient()) 183 focus(c, false); 184 } 185 if(e->subwindow) 186 XAllowEvents(display, ReplayPointer, e->time); 187 else { 188 /* Ungrab so a menu can receive events before the button is released */ 189 XUngrabPointer(display, e->time); 190 sync(); 191 192 event("ClientMouseDown %#C %d\n", f->client, e->button); 193 } 194 } 195 return false; 196 } 197 198 static bool 199 enter_event(Window *w, void *aux, XCrossingEvent *e) { 200 Client *c; 201 Frame *f; 202 203 c = aux; 204 f = c->sel; 205 if(disp.focus != c || selclient() != c) { 206 Dprint(DFocus, "%E\n", e); 207 Dprint(DFocus, "enter_notify(f) => [%#C]%s%s\n", 208 f->client, f->client->name, 209 e->serial <= event_lastconfigure ? " (ignored)" : ""); 210 if(e->detail != NotifyInferior) 211 if(e->serial > event_lastconfigure && !f->collapsed) 212 focus(f->client, false); 213 } 214 mouse_checkresize(f, Pt(e->x, e->y), false); 215 return false; 216 } 217 218 static bool 219 expose_event(Window *w, void *aux, XExposeEvent *e) { 220 Client *c; 221 222 USED(e); 223 224 c = aux; 225 if(c->sel) 226 frame_draw(c->sel); 227 return false; 228 } 229 230 static bool 231 motion_event(Window *w, void *aux, XMotionEvent *e) { 232 Client *c; 233 234 c = aux; 235 mouse_checkresize(c->sel, Pt(e->x, e->y), false); 236 return false; 237 } 238 239 Handlers framehandler = { 240 .bup = bup_event, 241 .bdown = bdown_event, 242 .enter = enter_event, 243 .expose = expose_event, 244 .motion = motion_event, 245 }; 246 247 WinHints 248 frame_gethints(Frame *f) { 249 WinHints h; 250 Client *c; 251 Rectangle r; 252 Point d; 253 int minh; 254 255 minh = labelh(def.font); 256 257 c = f->client; 258 h = *c->w.hints; 259 260 r = frame_client2rect(c, ZR, f->area->floating); 261 d = subpt(r.max, r.min); 262 263 if(!f->area->floating && def.incmode == IIgnore) 264 h.inc = Pt(1, 1); 265 266 if(h.min.x < 2*minh) 267 h.min.x = minh + (2*minh) % h.inc.x; 268 if(h.min.y < minh) 269 h.min.y = minh + minh % h.inc.y; 270 271 h.min.x += d.x; 272 h.min.y += d.y; 273 /* Guard against overflow. */ 274 h.max.x = max(h.max.x + d.x, h.max.x); 275 h.max.y = max(h.max.y + d.y, h.max.y); 276 277 h.base.x += d.x; 278 h.base.y += d.y; 279 h.baspect.x += d.x; 280 h.baspect.y += d.y; 281 282 h.group = 0; 283 h.grav = ZP; 284 h.gravstatic = 0; 285 h.position = 0; 286 return h; 287 } 288 289 #define ADJ(PE, ME) \ 290 if(c->fullscreen >= 0) \ 291 return r; \ 292 \ 293 if(!floating) { \ 294 r.min.x PE 1; \ 295 r.min.y PE labelh(def.font); \ 296 r.max.x ME 1; \ 297 r.max.y ME 1; \ 298 }else { \ 299 if(!c->borderless) { \ 300 r.min.x PE def.border; \ 301 r.max.x ME def.border; \ 302 r.max.y ME def.border; \ 303 } \ 304 if(!c->titleless) \ 305 r.min.y PE labelh(def.font); \ 306 } \ 307 308 Rectangle 309 frame_rect2client(Client *c, Rectangle r, bool floating) { 310 311 ADJ(+=, -=) 312 313 /* Force clients to be at least 1x1 */ 314 r.max.x = max(r.max.x, r.min.x+1); 315 r.max.y = max(r.max.y, r.min.y+1); 316 return r; 317 } 318 319 Rectangle 320 frame_client2rect(Client *c, Rectangle r, bool floating) { 321 322 ADJ(-=, +=) 323 324 return r; 325 } 326 327 #undef ADJ 328 329 void 330 frame_resize(Frame *f, Rectangle r) { 331 Client *c; 332 Rectangle fr, cr; 333 int collapsed, dx; 334 335 if(btassert("8 full", Dx(r) <= 0 || Dy(r) < 0 336 || Dy(r) == 0 && (!f->area->max || resizing) 337 && !f->collapsed)) { 338 fprint(2, "Frame rect: %R\n", r); 339 r.max.x = max(r.min.x+1, r.max.x); 340 r.max.y = max(r.min.y+1, r.max.y); 341 } 342 343 c = f->client; 344 if(c->fullscreen >= 0) { 345 f->r = screens[c->fullscreen]->r; 346 f->crect = rectsetorigin(f->r, ZP); 347 return; 348 } 349 350 /* 351 if(f->area->floating) 352 f->collapsed = false; 353 */ 354 355 fr = frame_hints(f, r, get_sticky(f->r, r)); 356 if(f->area->floating && !c->strut) 357 fr = constrain(fr, -1); 358 359 /* Collapse managed frames which are too small */ 360 /* XXX. */ 361 collapsed = f->collapsed; 362 if(!f->area->floating && f->area->mode == Coldefault) { 363 f->collapsed = false; 364 if(Dy(r) < 2 * labelh(def.font)) 365 f->collapsed = true; 366 } 367 if(collapsed != f->collapsed) 368 ewmh_updatestate(c); 369 370 fr.max.x = max(fr.max.x, fr.min.x + 2*labelh(def.font)); 371 if(f->collapsed && f->area->floating) 372 fr.max.y = fr.min.y + labelh(def.font); 373 374 cr = frame_rect2client(c, fr, f->area->floating); 375 if(f->area->floating) 376 f->r = fr; 377 else { 378 f->r = r; 379 dx = Dx(r) - Dx(cr); 380 dx -= 2 * (cr.min.x - fr.min.x); 381 cr.min.x += dx / 2; 382 cr.max.x += dx / 2; 383 } 384 f->crect = rectsubpt(cr, f->r.min); 385 386 if(f->area->floating && !f->collapsed) 387 f->floatr = f->r; 388 } 389 390 static void 391 pushlabel(Image *img, Rectangle *rp, char *s, CTuple *col) { 392 Rectangle r; 393 int w; 394 395 w = textwidth(def.font, s) + def.font->height; 396 w = min(w, Dx(*rp) - 30); /* Magic number. */ 397 if(w > 0) { 398 r = *rp; 399 r.min.x = r.max.x - w; 400 rp->max.x -= w; 401 if(0) 402 drawline(img, Pt(rp->max.x, r.min.y+2), 403 Pt(rp->max.x, r.max.y-2), 404 CapButt, 1, &col->border); 405 drawstring(img, def.font, r, East, 406 s, &col->fg); 407 } 408 free(s); 409 } 410 411 void 412 frame_draw(Frame *f) { 413 Rectangle r, fr; 414 Client *c; 415 CTuple *col; 416 Image *img; 417 char *s; 418 int n, m; 419 420 if(f == nil || f->view != selview || f->area == nil) 421 return; 422 423 c = f->client; 424 img = c->framewin->depth == 32 ? disp.ibuf32 : disp.ibuf; 425 fr = rectsetorigin(c->framewin->r, ZP); 426 427 /* Pick colors. */ 428 if((c == selclient() || c == disp.focus) && disp.sel) 429 col = &def.focuscolor; 430 else 431 col = &def.normcolor; 432 433 /* Background/border */ 434 r = fr; 435 fill(img, r, &col->bg); 436 border(img, r, 1, &col->border); 437 438 /* Title border */ 439 r.max.y = r.min.y + labelh(def.font); 440 border(img, r, 1, &col->border); 441 442 f->titlebar = insetrect(r, 3); 443 f->titlebar.max.y += 3; 444 445 f->grabbox = insetrect(r, 2); 446 f->grabbox.max.x = f->grabbox.min.x + Dy(f->grabbox); 447 448 /* Odd focus. Unselected, with keyboard focus. */ 449 /* Draw a border just inside the titlebar. */ 450 if(c != selclient() && c == disp.focus) { 451 border(img, insetrect(r, 1), 1, &def.normcolor.bg); 452 border(img, insetrect(r, 2), 1, &def.focuscolor.border); 453 } 454 455 if(c->urgent) 456 fill(img, f->grabbox, &col->fg); 457 border(img, f->grabbox, 1, &col->border); 458 459 /* Odd focus. Selected, without keyboard focus. */ 460 /* Draw a border around the grabbox. */ 461 if(c != disp.focus && col == &def.focuscolor) 462 border(img, insetrect(r, -1), 1, &def.normcolor.bg); 463 464 /* Draw a border on borderless+titleless selected apps. */ 465 if(c->borderless && c->titleless && f->area->floating && !c->fullscreen && c == selclient()) 466 setborder(c->framewin, def.border, &def.focuscolor.border); 467 else 468 setborder(c->framewin, 0, &def.focuscolor.border); 469 470 /* Label */ 471 r = Rect(f->grabbox.max.x, 0, fr.max.x, labelh(def.font)); 472 473 /* Draw count on frames in 'max' columns. */ 474 if(f->area->max && !resizing) { 475 n = stack_count(f, &m); 476 pushlabel(img, &r, smprint("%d/%d", m, n), col); 477 } 478 479 /* Label clients with extra tags. */ 480 if((s = client_extratags(c))) 481 pushlabel(img, &r, s, col); 482 483 if(f->area->floating) /* Make sure floating clients have room for their indicators. */ 484 r.max.x -= f->grabbox.max.x; 485 486 if(!ewmh_responsive_p(c)) 487 r.min.x += drawstring(img, def.font, r, West, "(wedged) ", &col->fg); 488 r.min.x += drawstring(img, def.font, r, West, c->name, &col->fg); 489 490 /* Draw inner border on floating clients. */ 491 if(f->area->floating) { 492 r.min.x += 10; 493 r.max.x += Dx(f->grabbox); 494 r.min.y = f->grabbox.min.y; 495 r.max.y = f->grabbox.max.y; 496 border(img, r, 1, &col->border); 497 } 498 499 /* Border increment gaps... */ 500 r.min.y = f->crect.min.y; 501 r.min.x = max(1, f->crect.min.x - 1); 502 r.max.x = min(fr.max.x - 1, f->crect.max.x + 1); 503 r.max.y = min(fr.max.y - 1, f->crect.max.y + 1); 504 border(img, r, 1, &col->border); 505 506 /* Why? Because some non-ICCCM-compliant apps feel the need to 507 * change the background properties of all of their ancestor windows 508 * in order to implement pseudo-transparency. 509 * What's more, the designers of X11 felt that it would be unfair to 510 * implementers to make it possible to detect, or forbid, such changes. 511 */ 512 XSetWindowBackgroundPixmap(display, c->framewin->xid, None); 513 514 copyimage(c->framewin, fr, img, ZP); 515 } 516 517 void 518 frame_draw_all(void) { 519 Client *c; 520 521 for(c=client; c; c=c->next) 522 if(c->sel && c->sel->view == selview) 523 frame_draw(c->sel); 524 } 525 526 void 527 frame_swap(Frame *fa, Frame *fb) { 528 Frame **fp; 529 Client *c; 530 531 if(fa == fb) return; 532 533 for(fp = &fa->client->frame; *fp; fp = &fp[0]->cnext) 534 if(*fp == fa) break; 535 fp[0] = fp[0]->cnext; 536 537 for(fp = &fb->client->frame; *fp; fp = &fp[0]->cnext) 538 if(*fp == fb) break; 539 fp[0] = fp[0]->cnext; 540 541 c = fa->client; 542 fa->client = fb->client; 543 fb->client = c; 544 fb->cnext = c->frame; 545 c->frame = fb; 546 547 c = fa->client; 548 fa->cnext = c->frame; 549 c->frame = fa; 550 551 if(c->sel) 552 view_update(c->sel->view); 553 } 554 555 void 556 move_focus(Frame *old_f, Frame *f) { 557 int noinput; 558 559 noinput = (old_f && old_f->client->noinput) || 560 (f && f->client->noinput) || 561 disp.hasgrab != &c_root; 562 if(noinput) { 563 if(old_f) 564 frame_draw(old_f); 565 if(f) 566 frame_draw(f); 567 } 568 } 569 570 void 571 frame_focus(Frame *f) { 572 Frame *old_f, *ff; 573 View *v; 574 Area *a, *old_a; 575 576 v = f->view; 577 a = f->area; 578 old_a = v->sel; 579 580 if(0 && f->collapsed) { 581 for(ff=f; ff->collapsed && ff->anext; ff=ff->anext) 582 ; 583 for(; ff->collapsed && ff->aprev; ff=ff->aprev) 584 ; 585 /* XXX */ 586 f->colr.max.y = f->colr.min.y + Dy(ff->colr); 587 ff->colr.max.y = ff->colr.min.y + labelh(def.font); 588 } 589 else if(f->area->mode == Coldefault) { 590 /* XXX */ 591 for(; f->collapsed && f->anext; f=f->anext) 592 ; 593 for(; f->collapsed && f->aprev; f=f->aprev) 594 ; 595 } 596 597 old_f = old_a->sel; 598 a->sel = f; 599 600 if(a != old_a) 601 area_focus(f->area); 602 if(old_a != v->oldsel && f != old_f) 603 v->oldsel = nil; 604 605 if(f->area->floating) 606 f->collapsed = false; 607 608 if(v == selview && a == v->sel && !resizing) { 609 move_focus(old_f, f); 610 if(a->floating) 611 float_arrange(a); 612 613 // if(!a->floating && ((a->mode == Colstack) || (a->mode == Colmax))) 614 if(true) 615 column_arrange(a, false); 616 617 client_focus(f->client); 618 } 619 } 620 621 int 622 frame_delta_h(void) { 623 return def.border + labelh(def.font); 624 } 625 626 Rectangle 627 constrain(Rectangle r, int inset) { 628 WMScreen **sp; 629 WMScreen *s, *sbest; 630 Rectangle isect; 631 Point p; 632 int best, n; 633 634 if(inset < 0) 635 inset = Dy(screen->brect); 636 /* 637 * FIXME: This will cause problems for windows with 638 * D(r) < 2 * inset 639 */ 640 641 SET(best); 642 sbest = nil; 643 for(sp=screens; (s = *sp); sp++) { 644 if(!screen->showing) 645 continue; 646 647 isect = rect_intersection(r, insetrect(s->r, inset)); 648 if(Dx(isect) >= 0 && Dy(isect) >= 0) 649 return r; 650 651 if(Dx(isect) <= 0 && Dy(isect) <= 0) 652 n = max(Dx(isect), Dy(isect)); 653 else 654 n = min(Dx(isect), Dy(isect)); 655 656 if(!sbest || n > best) { 657 sbest = s; 658 best = n; 659 } 660 } 661 662 isect = insetrect(sbest->r, inset); 663 p = ZP; 664 p.x -= min(r.max.x - isect.min.x, 0); 665 p.x -= max(r.min.x - isect.max.x, 0); 666 p.y -= min(r.max.y - isect.min.y, 0); 667 p.y -= max(r.min.y - isect.max.y, 0); 668 return rectaddpt(r, p); 669 } 670