view.c (12918B)
1 /* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> 2 * Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail> 3 * See LICENSE file for license details. 4 */ 5 #include "dat.h" 6 #include "fns.h" 7 8 static bool 9 empty_p(View *v) { 10 Frame *f; 11 Area *a; 12 char **p; 13 int cmp; 14 int s; 15 16 foreach_frame(v, s, a, f) { 17 cmp = 1; 18 for(p=f->client->retags; *p; p++) { 19 cmp = strcmp(*p, v->name); 20 if(cmp >= 0) 21 break; 22 } 23 if(cmp) 24 return false; 25 } 26 return true; 27 } 28 29 static void 30 _view_select(View *v) { 31 if(selview != v) { 32 if(selview) 33 event("UnfocusTag %s\n",selview->name); 34 selview = v; 35 event("FocusTag %s\n", v->name); 36 event("AreaFocus %a\n", v->sel); 37 ewmh_updateview(); 38 } 39 } 40 41 Client* 42 view_selclient(View *v) { 43 if(v->sel && v->sel->sel) 44 return v->sel->sel->client; 45 return nil; 46 } 47 48 bool 49 view_fullscreen_p(View *v, int scrn) { 50 Frame *f; 51 52 for(f=v->floating->frame; f; f=f->anext) 53 if(f->client->fullscreen == scrn) 54 return true; 55 return false; 56 } 57 58 View* 59 view_create(const char *name) { 60 static ushort id = 1; 61 View **vp; 62 Client *c; 63 View *v; 64 int i; 65 66 for(vp=&view; *vp; vp=&(*vp)->next) { 67 i = strcmp((*vp)->name, name); 68 if(i == 0) 69 return *vp; 70 if(i > 0) 71 break; 72 } 73 74 v = emallocz(sizeof *v); 75 v->id = id++; 76 v->r = emallocz(nscreens * sizeof *v->r); 77 v->pad = emallocz(nscreens * sizeof *v->pad); 78 79 utflcpy(v->name, name, sizeof v->name); 80 81 event("CreateTag %s\n", v->name); 82 area_create(v, nil, screen->idx, 0); 83 84 v->areas = emallocz(nscreens * sizeof *v->areas); 85 86 for(i=0; i < nscreens; i++) 87 view_init(v, i); 88 89 area_focus(v->firstarea); 90 91 v->next = *vp; 92 *vp = v; 93 94 /* FIXME: Belongs elsewhere */ 95 /* FIXME: Can do better. */ 96 for(c=client; c; c=c->next) 97 if(c != kludge) 98 client_applytags(c, c->tags); 99 100 view_arrange(v); 101 if(!selview) 102 _view_select(v); 103 ewmh_updateviews(); 104 return v; 105 } 106 107 void 108 view_init(View *v, int iscreen) { 109 v->r[iscreen] = screens[iscreen]->r; 110 v->pad[iscreen] = ZR; 111 v->areas[iscreen] = nil; 112 column_new(v, nil, iscreen, 0); 113 } 114 115 void 116 view_destroy(View *v) { 117 View **vp; 118 Frame *f; 119 View *tv; 120 Area *a; 121 int s; 122 123 if(v->dead) 124 return; 125 v->dead = true; 126 127 for(vp=&view; *vp; vp=&(*vp)->next) 128 if(*vp == v) break; 129 *vp = v->next; 130 assert(v != v->next); 131 132 /* Detach frames held here by regex tags. */ 133 /* FIXME: Can do better. */ 134 foreach_frame(v, s, a, f) 135 client_applytags(f->client, f->client->tags); 136 137 foreach_area(v, s, a) 138 area_destroy(a); 139 140 event("DestroyTag %s\n", v->name); 141 142 if(v == selview) { 143 for(tv=view; tv; tv=tv->next) 144 if(tv->next == *vp) break; 145 if(tv == nil) 146 tv = view; 147 if(tv) 148 view_focus(screen, tv); 149 } 150 free(v->areas); 151 free(v->r); 152 free(v->pad); 153 free(v); 154 ewmh_updateviews(); 155 } 156 157 Area* 158 view_findarea(View *v, int screen, int idx, bool create) { 159 Area *a; 160 161 assert(screen >= 0 && screen < nscreens); 162 163 for(a=v->areas[screen]; a && --idx > 0; a=a->next) 164 if(create && a->next == nil) 165 return area_create(v, a, screen, 0); 166 return a; 167 } 168 169 static void 170 frames_update_sel(View *v) { 171 Frame *f; 172 Area *a; 173 int s; 174 175 foreach_frame(v, s, a, f) 176 f->client->sel = f; 177 } 178 179 /* Don't let increment hints take up more than half 180 * of the screen, in either direction. 181 */ 182 static Rectangle 183 fix_rect(Rectangle old, Rectangle new) { 184 double r; 185 186 new = rect_intersection(new, old); 187 188 r = (Dy(old) - Dy(new)) / Dy(old); 189 if(r > .5) { 190 r -= .5; 191 new.min.y -= r * (new.min.y - old.min.y); 192 new.max.y += r * (old.max.y - new.max.y); 193 } 194 r = (Dx(old) - Dx(new)) / Dx(old); 195 if(r > .5) { 196 r -= .5; 197 new.min.x -= r * (new.min.x - old.min.x); 198 new.max.x += r * (old.max.x - new.max.x); 199 } 200 return new; 201 } 202 203 void 204 view_update_rect(View *v) { 205 static Vector_rect vec; 206 static Vector_rect *vp; 207 Rectangle r, sr, rr, brect, scrnr; 208 WMScreen *scrn; 209 Strut *strut; 210 Frame *f; 211 int left, right, top, bottom; 212 int s, i; 213 214 /* XXX: 215 if(v != selview) 216 return false; 217 */ 218 219 top = 0; 220 left = 0; 221 right = 0; 222 bottom = 0; 223 vec.n = 0; 224 for(f=v->floating->frame; f; f=f->anext) { 225 strut = f->client->strut; 226 if(!strut) 227 continue; 228 /* Can do better in the future. */ 229 top = max(top, strut->top.max.y); 230 left = max(left, strut->left.max.x); 231 right = min(right, strut->right.min.x); 232 bottom = min(bottom, strut->bottom.min.y); 233 vector_rpush(&vec, strut->top); 234 vector_rpush(&vec, strut->left); 235 vector_rpush(&vec, rectaddpt(strut->right, Pt(scr.rect.max.x, 0))); 236 vector_rpush(&vec, rectaddpt(strut->bottom, Pt(0, scr.rect.max.y))); 237 } 238 vp = unique_rects(&vec, scr.rect); 239 scrnr = scr.rect; 240 scrnr.min.y += top; 241 scrnr.min.x += left; 242 scrnr.max.x += right; 243 scrnr.max.y += bottom; 244 245 /* FIXME: Multihead. */ 246 v->floating->r = scr.rect; 247 248 for(s=0; s < nscreens; s++) { 249 scrn = screens[s]; 250 r = fix_rect(scrn->r, scrnr); 251 252 /* Ugly. Very, very ugly. */ 253 /* 254 * Try to find some rectangle near the edge of the 255 * screen where the bar will fit. This way, for 256 * instance, a system tray can be placed there 257 * without taking up too much extra screen real 258 * estate. 259 */ 260 rr = r; 261 brect = scrn->brect; 262 for(i=0; i < vp->n; i++) { 263 sr = rect_intersection(vp->ary[i], scrn->r); 264 if(Dx(sr) < Dx(r)/2 || Dy(sr) < Dy(brect)) 265 continue; 266 if(scrn->barpos == BTop && sr.min.y < rr.min.y 267 || scrn->barpos != BTop && sr.max.y > rr.max.y) 268 rr = sr; 269 } 270 271 if(scrn->barpos == BTop) { 272 bar_sety(scrn, rr.min.y); 273 r.min.y = max(r.min.y, scrn->brect.max.y); 274 }else { 275 bar_sety(scrn, rr.max.y - Dy(brect)); 276 r.max.y = min(r.max.y, scrn->brect.min.y); 277 } 278 bar_setbounds(scrn, rr.min.x, rr.max.x); 279 v->r[s] = r; 280 } 281 } 282 283 void 284 view_update(View *v) { 285 Client *c; 286 Frame *f; 287 Area *a; 288 int s; 289 290 if(v == selview && !starting) { 291 frames_update_sel(v); 292 293 foreach_frame(v, s, a, f) 294 if(f->client->fullscreen >= 0) { 295 f->collapsed = false; 296 if(!f->area->floating) { 297 f->oldarea = area_idx(f->area); 298 f->oldscreen = f->area->screen; 299 area_moveto(v->floating, f); 300 area_setsel(v->floating, f); 301 }else if(f->oldarea == -1) 302 f->oldarea = 0; 303 } 304 305 view_arrange(v); 306 307 for(c=client; c; c=c->next) { 308 f = c->sel; 309 if((f && f->view == v) 310 && (f->area == v->sel || !(f->area && f->area->max && f->area->floating))) { 311 if(f->area) 312 client_resize(c, f->r); 313 }else { 314 client_unmapframe(c); 315 client_unmap(c, IconicState); 316 } 317 ewmh_updatestate(c); 318 ewmh_updateclient(c); 319 } 320 321 view_restack(v); 322 if(!v->sel->floating && view_fullscreen_p(v, v->sel->screen)) 323 area_focus(v->floating); 324 else 325 area_focus(v->sel); 326 frame_draw_all(); 327 } 328 view_update_urgency(v, nil); 329 } 330 331 void 332 view_update_urgency(View *v, char *from) { 333 Area *a; 334 Frame *f; 335 int s, urgent; 336 337 urgent = 0; 338 foreach_frame(v, s, a, f) 339 if (f->client->urgent) { 340 urgent++; 341 break; 342 } 343 344 if (urgent != v->urgent) 345 event("%sUrgentTag %s %s\n", 346 urgent ? "" : "Not", 347 from ? from : "Unknown", 348 v->name); 349 350 v->urgent = urgent; 351 } 352 353 void 354 view_focus(WMScreen *s, View *v) { 355 356 USED(s); 357 358 _view_select(v); 359 view_update(v); 360 } 361 362 void 363 view_select(const char *arg) { 364 char buf[256]; 365 366 utflcpy(buf, arg, sizeof buf); 367 trim(buf, " \t+/"); 368 369 if(buf[0] == '\0') 370 return; 371 if(!strcmp(buf, ".") || !strcmp(buf, "..")) 372 return; 373 374 _view_select(view_create(buf)); 375 view_update_all(); /* performs view_focus */ 376 } 377 378 void 379 view_attach(View *v, Frame *f) { 380 Client *c; 381 Frame *ff; 382 Area *a, *oldsel; 383 384 c = f->client; 385 386 oldsel = v->oldsel; 387 a = v->sel; 388 if(c->floating == Never) 389 a = view_findarea(v, v->selscreen, v->selcol, false); 390 else if(client_floats_p(c)) { 391 if(v->sel != v->floating && c->fullscreen < 0) 392 oldsel = v->sel; 393 a = v->floating; 394 } 395 else if((ff = client_groupframe(c, v))) { 396 a = ff->area; 397 if(v->oldsel && ff->client == view_selclient(v)) 398 a = v->oldsel; 399 } 400 else if(v->sel->floating) { 401 if(v->oldsel) 402 a = v->oldsel; 403 /* Don't float a frame when starting or when its 404 * last focused frame didn't float. Important when 405 * tagging with +foo. 406 */ 407 else if(starting 408 || c->sel && c->sel->area && !c->sel->area->floating) 409 a = v->firstarea; 410 } 411 if(!a->floating && c->floating != Never && view_fullscreen_p(v, a->screen)) 412 a = v->floating; 413 414 event("ViewAttach %s %#C\n", v->name, c); 415 area_attach(a, f); 416 /* TODO: Decide whether to focus this frame */ 417 bool newgroup = !c->group 418 || c->group->ref == 1 419 || view_selclient(v) 420 && view_selclient(v)->group == c->group 421 || group_leader(c->group) 422 && !client_viewframe(group_leader(c->group), 423 c->sel->view); 424 USED(newgroup); 425 426 if(!(c->w.ewmh.type & (TypeSplash|TypeDock))) { 427 if(!(c->tagre.regex && regexec(c->tagre.regc, v->name, nil, 0))) 428 frame_focus(f); 429 else if(c->group && f->area->sel->client->group == c->group) 430 /* XXX: Stack. */ 431 area_setsel(f->area, f); 432 } 433 434 if(oldsel) 435 v->oldsel = oldsel; 436 437 if(c->sel == nil) 438 c->sel = f; 439 view_update(v); 440 } 441 442 void 443 view_detach(Frame *f) { 444 Client *c; 445 View *v; 446 447 v = f->view; 448 c = f->client; 449 450 area_detach(f); 451 if(c->sel == f) 452 c->sel = f->cnext; 453 454 event("ViewDetach %s %#C\n", v->name, c); 455 view_update(v); 456 if(v != selview && empty_p(v)) 457 view_destroy(v); 458 } 459 460 char** 461 view_names(void) { 462 Vector_ptr vec; 463 View *v; 464 465 vector_pinit(&vec); 466 for(v=view; v; v=v->next) 467 vector_ppush(&vec, v->name); 468 vector_ppush(&vec, nil); 469 return erealloc(vec.ary, vec.n * sizeof *vec.ary); 470 } 471 472 void 473 view_restack(View *v) { 474 static Vector_long wins; 475 Divide *d; 476 Frame *f; 477 Area *a; 478 int s; 479 480 if(v != selview) 481 return; 482 483 wins.n = 0; 484 485 for(f=v->floating->stack; f; f=f->snext) 486 vector_lpush(&wins, f->client->framewin->xid); 487 488 for(int s=0; s < nscreens; s++) 489 vector_lpush(&wins, screens[s]->barwin->xid); 490 491 for(d = divs; d && d->w->mapped; d = d->next) 492 vector_lpush(&wins, d->w->xid); 493 494 foreach_column(v, s, a) 495 if(a->frame) { 496 vector_lpush(&wins, a->sel->client->framewin->xid); 497 for(f=a->frame; f; f=f->anext) 498 if(f != a->sel) 499 vector_lpush(&wins, f->client->framewin->xid); 500 } 501 502 ewmh_updatestacking(); 503 if(wins.n) 504 XRestackWindows(display, (ulong*)wins.ary, wins.n); 505 } 506 507 void 508 view_scale(View *v, int scrn, int width) { 509 uint xoff, numcol; 510 uint minwidth; 511 Area *a; 512 float scale; 513 int dx, minx; 514 515 minwidth = column_minwidth(); 516 minx = v->r[scrn].min.x + v->pad[scrn].min.x; 517 518 if(!v->areas[scrn]) 519 return; 520 521 numcol = 0; 522 dx = 0; 523 for(a=v->areas[scrn]; a; a=a->next) { 524 numcol++; 525 dx += Dx(a->r); 526 } 527 528 scale = (float)width / dx; 529 xoff = minx; 530 for(a=v->areas[scrn]; a; a=a->next) { 531 a->r.max.x = xoff + Dx(a->r) * scale; 532 a->r.min.x = xoff; 533 if(!a->next) 534 a->r.max.x = v->r[scrn].min.x + width; 535 xoff = a->r.max.x; 536 } 537 538 if(numcol * minwidth > width) 539 return; 540 541 xoff = minx; 542 for(a=v->areas[scrn]; a; a=a->next) { 543 a->r.min.x = xoff; 544 545 if(Dx(a->r) < minwidth) 546 a->r.max.x = xoff + minwidth; 547 if(!a->next) 548 a->r.max.x = minx + width; 549 xoff = a->r.max.x; 550 } 551 } 552 553 /* XXX: Multihead. */ 554 void 555 view_arrange(View *v) { 556 Area *a; 557 int s; 558 559 if(!v->firstarea) 560 return; 561 562 view_update_rect(v); 563 for(s=0; s < nscreens; s++) 564 view_scale(v, s, Dx(v->r[s]) + Dx(v->pad[s])); 565 foreach_area(v, s, a) { 566 if(a->floating) 567 continue; 568 /* This is wrong... */ 569 a->r.min.y = v->r[s].min.y; 570 a->r.max.y = v->r[s].max.y; 571 column_arrange(a, false); 572 } 573 if(v == selview) 574 div_update_all(); 575 } 576 577 Rectangle* 578 view_rects(View *v, uint *num, Frame *ignore) { 579 Vector_rect result; 580 Frame *f; 581 int i; 582 583 vector_rinit(&result); 584 585 for(f=v->floating->frame; f; f=f->anext) 586 if(f != ignore) 587 vector_rpush(&result, f->r); 588 for(i=0; i < nscreens; i++) { 589 vector_rpush(&result, v->r[i]); 590 vector_rpush(&result, screens[i]->r); 591 } 592 593 *num = result.n; 594 return result.ary; 595 } 596 597 void 598 view_update_all(void) { 599 View *n, *v, *old; 600 601 old = selview; 602 for(v=view; v; v=v->next) 603 frames_update_sel(v); 604 605 for(v=view; v; v=n) { 606 n=v->next; 607 if(v != old && empty_p(v)) 608 view_destroy(v); 609 } 610 611 view_update(selview); 612 } 613 614 uint 615 view_newcolwidth(View *v, int scrn, int num) { 616 Rule *r; 617 char *toks[16]; 618 char buf[sizeof r->value]; 619 ulong n; 620 621 /* XXX: Multihead. */ 622 for(r=def.colrules.rule; r; r=r->next) 623 if(regexec(r->regex, v->name, nil, 0)) { 624 utflcpy(buf, r->value, sizeof buf); 625 n = tokenize(toks, 16, buf, '+'); 626 627 if(num < n) 628 if(getulong(toks[num], &n)) 629 return Dx(v->r[scrn]) * (n / 100.0); 630 else if(!strcmp("px", strend(toks[num], 2))) { 631 toks[num][strlen(toks[num]) - 2] = '\0'; 632 if(getulong(toks[num], &n)) 633 return n; 634 } 635 break; 636 } 637 return 0; 638 } 639 640 char* 641 view_index(View *v) { 642 Rectangle *r; 643 Frame *f; 644 Area *a; 645 int s; 646 647 bufclear(); 648 foreach_area(v, s, a) { 649 if(a->floating) 650 bufprint("# %a %d %d\n", a, Dx(a->r), Dy(a->r)); 651 else 652 bufprint("# %a %d %d\n", a, a->r.min.x, Dx(a->r)); 653 654 for(f=a->frame; f; f=f->anext) { 655 r = &f->r; 656 if(a->floating) 657 bufprint("%a %#C %d %d %d %d %s\n", 658 a, f->client, 659 r->min.x, r->min.y, 660 Dx(*r), Dy(*r), 661 f->client->props); 662 else 663 bufprint("%a %#C %d %d %s\n", 664 a, f->client, 665 r->min.y, Dy(*r), 666 f->client->props); 667 } 668 } 669 return buffer; 670 } 671