A tiling window manager
at master 799 lines 17 kB view raw
1/* 2 * Copyright (c) 2019 joshua stein <jcs@jcs.org> 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License as published by the Free 6 * Software Foundation; either version 2 of the License, or (at your option) 7 * any later version. 8 * 9 * This program is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 * more details. 13 * 14 * You should have received a copy of the GNU General Public License along with 15 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 16 * Place, Suite 330, Boston, MA 02111-1307 USA. 17 */ 18 19#include <err.h> 20#include "sdorfehs.h" 21 22rp_vscreen *vscreen_next(void); 23rp_vscreen *vscreen_prev(void); 24 25static int vscreen_access = 1; 26 27void 28init_vscreen(rp_vscreen *v, rp_screen *s) 29{ 30 v->screen = s; 31 v->number = numset_request(s->vscreens_numset); 32 v->frames_numset = numset_new(); 33 v->numset = numset_new(); 34 v->last_access = 0; 35 36 if (v->number == 0) 37 v->name = xstrdup(DEFAULT_VSCREEN_NAME); 38 else 39 v->name = xsprintf("%d", v->number); 40 41 INIT_LIST_HEAD(&v->unmapped_windows); 42 INIT_LIST_HEAD(&v->mapped_windows); 43 44 init_frame_list(v); 45} 46 47void 48vscreen_del(rp_vscreen *v) 49{ 50 rp_screen *s = v->screen; 51 rp_vscreen *target = NULL; 52 rp_window *cur; 53 54 if (v->number != 0) 55 target = screen_find_vscreen_by_number(s, 0); 56 57 list_for_each_entry(cur, &rp_mapped_window, node) { 58 if (cur->vscreen == v && target) 59 vscreen_move_window(target, cur); 60 } 61 62 if (s->current_vscreen == v && target) 63 set_current_vscreen(target); 64 65 numset_release(s->vscreens_numset, v->number); 66 67 vscreen_free(v); 68 69 list_del(&v->node); 70 free(v); 71} 72 73rp_vscreen * 74vscreen_next(void) 75{ 76 return list_next_entry(rp_current_screen->current_vscreen, 77 &rp_current_screen->vscreens, node); 78} 79 80rp_vscreen * 81vscreen_prev(void) 82{ 83 return list_prev_entry(rp_current_screen->current_vscreen, 84 &rp_current_screen->vscreens, node); 85} 86 87void 88vscreen_free(rp_vscreen *v) 89{ 90 rp_frame *frame; 91 struct list_head *iter, *tmp; 92 93 list_for_each_safe_entry(frame, iter, tmp, &v->frames, node) 94 frame_free(v, frame); 95} 96 97int 98vscreens_resize(int n) 99{ 100 struct list_head *tmp, *iter; 101 rp_vscreen *cur; 102 rp_screen *scr; 103 int x; 104 105 PRINT_DEBUG(("Resizing vscreens from %d to %d\n", defaults.vscreens, 106 n)); 107 108 if (n < 1) 109 return 1; 110 111 if (n < defaults.vscreens) { 112 list_for_each_entry(scr, &rp_screens, node) { 113 list_for_each_safe_entry(cur, iter, tmp, &scr->vscreens, 114 node) { 115 if (cur->number < n) 116 continue; 117 118 if (scr->current_vscreen == cur) 119 set_current_vscreen( 120 screen_find_vscreen_by_number(scr, 121 0)); 122 123 vscreen_del(cur); 124 } 125 } 126 } else if (n > defaults.vscreens) { 127 list_for_each_entry(scr, &rp_screens, node) { 128 for (x = defaults.vscreens; x < n; x++) { 129 cur = xmalloc(sizeof(rp_vscreen)); 130 init_vscreen(cur, scr); 131 list_add_tail(&cur->node, &scr->vscreens); 132 } 133 } 134 } 135 136 defaults.vscreens = n; 137 138 set_atom(rp_glob_screen.root, _net_number_of_desktops, XA_CARDINAL, 139 (unsigned long *)&defaults.vscreens, 1); 140 141 return 0; 142} 143 144/* Returns a pointer to a list of frames. */ 145struct list_head * 146vscreen_copy_frameset(rp_vscreen *v) 147{ 148 struct list_head *head; 149 rp_frame *cur; 150 151 /* Init our new list. */ 152 head = xmalloc(sizeof(struct list_head)); 153 INIT_LIST_HEAD(head); 154 155 /* Copy each frame to our new list. */ 156 list_for_each_entry(cur, &v->frames, node) { 157 list_add_tail(&(frame_copy(cur))->node, head); 158 } 159 160 return head; 161} 162 163/* Set head as the frameset, deleting the existing one. */ 164void 165vscreen_restore_frameset(rp_vscreen *v, struct list_head *head) 166{ 167 frameset_free(&v->frames); 168 INIT_LIST_HEAD(&v->frames); 169 170 /* Hook in our new frameset. */ 171 list_splice(head, &v->frames); 172} 173 174/* Given a screen, free the frames' numbers from the numset. */ 175void 176vscreen_free_nums(rp_vscreen *v) 177{ 178 rp_frame *cur; 179 180 list_for_each_entry(cur, &v->frames, node) { 181 numset_release(v->frames_numset, cur->number); 182 } 183} 184 185/* 186 * Given a list of frames, free them, but don't remove their numbers from the 187 * numset. 188 */ 189void 190frameset_free(struct list_head *head) 191{ 192 rp_frame *frame; 193 struct list_head *iter, *tmp; 194 195 list_for_each_safe_entry(frame, iter, tmp, head, node) { 196 /* 197 * FIXME: what if frames has memory inside its struct that 198 * needs to be freed? 199 */ 200 free(frame); 201 } 202} 203 204rp_frame * 205vscreen_get_frame(rp_vscreen *v, int frame_num) 206{ 207 rp_frame *cur; 208 209 list_for_each_entry(cur, &v->frames, node) { 210 if (cur->number == frame_num) 211 return cur; 212 } 213 214 return NULL; 215} 216 217rp_frame * 218vscreen_find_frame_by_frame(rp_vscreen *v, rp_frame *f) 219{ 220 rp_frame *cur; 221 222 list_for_each_entry(cur, &v->frames, node) { 223 PRINT_DEBUG(("cur=%p f=%p\n", cur, f)); 224 if (cur == f) 225 return cur; 226 } 227 228 return NULL; 229} 230 231void 232set_current_vscreen(rp_vscreen *v) 233{ 234 rp_window *win; 235 rp_frame *frame; 236 237 if (v == NULL || (v->screen == rp_current_screen && 238 v->screen->current_vscreen == v)) 239 return; 240 241 window_full_screen(NULL); 242 243 list_for_each_entry(frame, &rp_current_vscreen->frames, node) 244 frame->restore_win_number = frame->win_number; 245 246 hide_vscreen_windows(rp_current_vscreen); 247 248 rp_current_screen = v->screen; 249 v->screen->current_vscreen = v; 250 v->last_access = vscreen_access++; 251 252 list_for_each_entry(frame, &rp_current_vscreen->frames, node) { 253 if (frame->restore_win_number == EMPTY) 254 continue; 255 256 win = find_window_number(frame->restore_win_number); 257 if (win && win->vscreen != v) { 258 warnx("restore win for frame %d on incorrect vscreen\n", 259 frame->number); 260 win = NULL; 261 } 262 263 frame->restore_win_number = 0; 264 265 if (!win) 266 /* previously focused window disappeared */ 267 win = find_window_for_frame(frame); 268 269 if (!win) 270 continue; 271 272 win->frame_number = frame->number; 273 274 maximize(win); 275 unhide_window(win); 276 } 277 278 set_window_focus(v->screen->key_window); 279 280 if ((frame = current_frame(v))) 281 set_active_frame(frame, 0); 282 283 update_bar(v->screen); 284 show_frame_indicator(0); 285 286 raise_utility_windows(); 287 288 vscreen_announce_current(v); 289 290 hook_run(&rp_switch_vscreen_hook); 291} 292 293void 294vscreen_move_window(rp_vscreen *to, rp_window *w) 295{ 296 rp_vscreen *from = w->vscreen; 297 rp_frame *f; 298 rp_window_elem *we; 299 struct rp_child_info *child; 300 301 we = vscreen_find_window(&from->mapped_windows, w); 302 if (we == NULL) { 303 PRINT_DEBUG(("Unable to find window in mapped window lists.\n")); 304 return; 305 } 306 307 f = find_windows_frame(w); 308 w->vscreen = to; 309 w->sticky_frame = EMPTY; 310 311 /* forget that this window was in the frame it was in */ 312 if (f) 313 f->win_number = EMPTY; 314 315 numset_release(from->numset, we->number); 316 list_del(&we->node); 317 318 we->number = numset_request(to->numset); 319 vscreen_insert_window(&to->mapped_windows, we); 320 321 if (to == rp_current_vscreen) 322 set_active_window_force(w); 323 else 324 hide_window(w); 325 326 /* 327 * Update rp_children so that any new windows from this application 328 * will appear on the vscreen we just moved to 329 */ 330 child = get_child_info(w->w, 0); 331 if (!child) 332 return; 333 334 child->frame = current_frame(to); 335 child->vscreen = to; 336 child->screen = to->screen; 337} 338 339struct numset * 340vscreen_get_numset(rp_vscreen *v) 341{ 342 return v->numset; 343} 344 345/* 346 * Get the vscreen list and store it in buffer delimiting each window with 347 * delim. mark_start and mark_end will be filled with the text positions for 348 * the start and end of the current window. 349 */ 350void 351get_vscreen_list(rp_screen *screen, char *delim, struct sbuf *buffer, 352 int *mark_start, int *mark_end) 353{ 354 rp_vscreen *cur, *last; 355 356 if (buffer == NULL) 357 return; 358 359 sbuf_clear(buffer); 360 361 last = screen_last_vscreen(screen); 362 list_for_each_entry(cur, &screen->vscreens, node) { 363 char *fmt; 364 char separator; 365 366 if (cur == screen->current_vscreen) { 367 *mark_start = strlen(sbuf_get(buffer)); 368 separator = '*'; 369 } else if (cur == last) 370 separator = '+'; 371 else 372 separator = '-'; 373 374 /* 375 * A hack, pad the vscreen with a space at the beginning and end 376 * if there is no delimiter. 377 */ 378 if (!delim) 379 sbuf_concat(buffer, " "); 380 381 fmt = xsprintf("%d%c%s", cur->number, separator, cur->name); 382 sbuf_concat(buffer, fmt); 383 free(fmt); 384 385 /* 386 * A hack, pad the vscreen with a space at the beginning and 387 * end if there is no delimiter. 388 */ 389 if (!delim) 390 sbuf_concat(buffer, " "); 391 392 /* 393 * Only put the delimiter between the vscreen, and not after 394 * the the last vscreen. 395 */ 396 if (delim && cur->node.next != &screen->vscreens) 397 sbuf_concat(buffer, delim); 398 399 if (cur == screen->current_vscreen) 400 *mark_end = strlen(sbuf_get(buffer)); 401 } 402} 403 404void 405vscreen_rename(rp_vscreen *v, char *name) 406{ 407 free(v->name); 408 v->name = xstrdup(name); 409} 410 411rp_vscreen * 412vscreen_next_vscreen(rp_vscreen *vscreen) 413{ 414 return list_next_entry(vscreen->screen->current_vscreen, 415 &vscreen->screen->vscreens, node); 416} 417 418rp_vscreen * 419vscreen_prev_vscreen(rp_vscreen *vscreen) 420{ 421 return list_prev_entry(vscreen->screen->current_vscreen, 422 &vscreen->screen->vscreens, node); 423} 424 425rp_vscreen * 426screen_last_vscreen(rp_screen *screen) 427{ 428 int last_access = 0; 429 rp_vscreen *most_recent = NULL; 430 rp_vscreen *cur; 431 432 list_for_each_entry(cur, &screen->vscreens, node) { 433 if (cur != screen->current_vscreen && 434 cur->last_access > last_access) { 435 most_recent = cur; 436 last_access = cur->last_access; 437 } 438 } 439 return most_recent; 440} 441 442rp_vscreen * 443screen_find_vscreen_by_number(rp_screen *s, int n) 444{ 445 rp_vscreen *cur; 446 447 list_for_each_entry(cur, &s->vscreens, node) { 448 if (cur->number == n) 449 return cur; 450 } 451 452 return NULL; 453} 454 455rp_vscreen * 456screen_find_vscreen_by_name(rp_screen *s, char *name, int exact_match) 457{ 458 rp_vscreen *cur; 459 460 if (exact_match) { 461 list_for_each_entry(cur, &s->vscreens, node) { 462 if (cur->name && !strcmp(cur->name, name)) 463 return cur; 464 } 465 } else { 466 list_for_each_entry(cur, &s->vscreens, node) { 467 if (cur->name && str_comp(name, cur->name, 468 strlen(name))) 469 return cur; 470 } 471 } 472 473 return NULL; 474} 475 476rp_window_elem * 477vscreen_find_window(struct list_head *list, rp_window *win) 478{ 479 rp_window_elem *cur; 480 481 list_for_each_entry(cur, list, node) { 482 if (cur->win == win) 483 return cur; 484 } 485 486 return NULL; 487} 488 489rp_window_elem * 490vscreen_find_window_by_number(rp_vscreen *v, int num) 491{ 492 rp_window_elem *cur; 493 494 list_for_each_entry(cur, &v->mapped_windows, node) { 495 if (cur->number == num) 496 return cur; 497 } 498 499 return NULL; 500 501} 502 503/* 504 * Insert a window_elem into the correct spot in the vscreen's window list to 505 * preserve window number ordering. 506 */ 507void 508vscreen_insert_window(struct list_head *h, rp_window_elem *w) 509{ 510 rp_window_elem *cur; 511 512 list_for_each_entry(cur, h, node) { 513 if (cur->number > w->number) { 514 list_add_tail(&w->node, &cur->node); 515 return; 516 } 517 } 518 519 list_add_tail(&w->node, h); 520} 521 522static int 523vscreen_in_list(struct list_head *h, rp_window_elem *w) 524{ 525 rp_window_elem *cur; 526 527 list_for_each_entry(cur, h, node) { 528 if (cur == w) 529 return 1; 530 } 531 532 return 0; 533} 534 535/* 536 * If a window_elem's number has changed then the list has to be resorted. 537 */ 538void 539vscreen_resort_window(rp_vscreen *v, rp_window_elem *w) 540{ 541 /* Only a mapped window can be resorted. */ 542 if (!vscreen_in_list(&v->mapped_windows, w)) { 543 PRINT_DEBUG(("Attempting to resort an unmapped window!\n")); 544 return; 545 } 546 list_del(&w->node); 547 vscreen_insert_window(&v->mapped_windows, w); 548} 549 550void 551vscreen_add_window(rp_vscreen *v, rp_window *w) 552{ 553 rp_window_elem *we; 554 555 /* Create our container structure for the window. */ 556 we = xmalloc(sizeof(rp_window_elem)); 557 we->win = w; 558 we->number = -1; 559 560 /* Finally, add it to our list. */ 561 list_add_tail(&we->node, &v->unmapped_windows); 562} 563 564void 565vscreen_map_window(rp_vscreen *v, rp_window *win) 566{ 567 rp_window_elem *we; 568 569 we = vscreen_find_window(&v->unmapped_windows, win); 570 if (we) { 571 we->number = numset_request(v->numset); 572 list_del(&we->node); 573 vscreen_insert_window(&v->mapped_windows, we); 574 } 575} 576 577void 578vscreen_unmap_window(rp_vscreen *v, rp_window *win) 579{ 580 rp_window_elem *we; 581 582 we = vscreen_find_window(&v->mapped_windows, win); 583 if (we) { 584 numset_release(v->numset, we->number); 585 list_move_tail(&we->node, &v->unmapped_windows); 586 } 587} 588 589void 590vscreen_del_window(rp_vscreen *v, rp_window *win) 591{ 592 rp_window_elem *cur; 593 struct list_head *iter, *tmp; 594 595 /* The assumption is that a window is unmapped before it's deleted. */ 596 list_for_each_safe_entry(cur, iter, tmp, &v->unmapped_windows, node) { 597 if (cur->win == win) { 598 list_del(&cur->node); 599 free(cur); 600 } 601 } 602 603 /* 604 * Make sure the window isn't in the list of mapped windows. This would 605 * mean there is a bug. 606 */ 607#ifdef DEBUG 608 list_for_each_entry(cur, &v->mapped_windows, node) { 609 if (cur->win == win) 610 PRINT_DEBUG(("This window wasn't removed from the " 611 "mapped window list.\n")); 612 } 613#endif 614} 615 616rp_window * 617vscreen_last_window(rp_vscreen *v) 618{ 619 rp_frame *f; 620 rp_window_elem *most_recent = NULL; 621 rp_window_elem *cur; 622 int last_access = 0; 623 624 f = current_frame(v); 625 list_for_each_entry(cur, &v->mapped_windows, node) { 626 if (cur->win->sticky_frame != EMPTY && 627 (!f || (cur->win->sticky_frame != f->number))) 628 continue; 629 630 if (cur->win->last_access >= last_access 631 && cur->win != current_window() 632 && !find_windows_frame(cur->win) 633 && (cur->win->vscreen == v || rp_have_xrandr)) { 634 most_recent = cur; 635 last_access = cur->win->last_access; 636 } 637 } 638 639 if (most_recent) 640 return most_recent->win; 641 642 return NULL; 643} 644 645rp_window * 646vscreen_next_window(rp_vscreen *v, rp_window *win) 647{ 648 rp_window_elem *cur, *we; 649 rp_frame *f; 650 651 /* If there is no window, then get the last accessed one. */ 652 if (win == NULL) 653 return vscreen_last_window(v); 654 655 /* 656 * If we can't find the window, then it's in a different vscreen, so 657 * get the last accessed one in this vscreen. 658 */ 659 we = vscreen_find_window(&v->mapped_windows, win); 660 if (we == NULL) 661 return vscreen_last_window(v); 662 663 /* 664 * The window is in this vscreen, so find the next one in the list that 665 * isn't already displayed. 666 */ 667 f = current_frame(v); 668 for (cur = list_next_entry(we, &v->mapped_windows, node); 669 cur != we; 670 cur = list_next_entry(cur, &v->mapped_windows, node)) { 671 if (cur->win->sticky_frame != EMPTY && 672 (!f || (cur->win->sticky_frame != f->number))) 673 continue; 674 675 if (!find_windows_frame(cur->win) && 676 (cur->win->vscreen == win->vscreen || rp_have_xrandr)) 677 return cur->win; 678 } 679 680 return NULL; 681} 682 683rp_window * 684vscreen_prev_window(rp_vscreen *v, rp_window *win) 685{ 686 rp_window_elem *cur, *we; 687 rp_frame *f; 688 689 /* If there is no window, then get the last accessed one. */ 690 if (win == NULL) 691 return vscreen_last_window(v); 692 693 /* 694 * If we can't find the window, then it's in a different vscreen, so 695 * get the last accessed one in this vscreen. 696 */ 697 we = vscreen_find_window(&v->mapped_windows, win); 698 if (we == NULL) 699 return vscreen_last_window(v); 700 701 /* 702 * The window is in this vscreen, so find the previous one in the list 703 * that isn't already displayed. 704 */ 705 f = current_frame(v); 706 for (cur = list_prev_entry(we, &v->mapped_windows, node); 707 cur != we; 708 cur = list_prev_entry(cur, &v->mapped_windows, node)) { 709 if (cur->win->sticky_frame != EMPTY && 710 (!f || (cur->win->sticky_frame != f->number))) 711 continue; 712 713 if (!find_windows_frame(cur->win) && rp_have_xrandr) 714 return cur->win; 715 } 716 717 return NULL; 718} 719 720void 721vscreens_merge(rp_vscreen *from, rp_vscreen *to) 722{ 723 rp_window_elem *cur; 724 struct list_head *iter, *tmp; 725 726 /* Merging a vscreen with itself makes no sense. */ 727 if (from == to) 728 return; 729 730 /* Move the unmapped windows. */ 731 list_for_each_safe_entry(cur, iter, tmp, &from->unmapped_windows, node) { 732 list_del(&cur->node); 733 list_add_tail(&cur->node, &to->unmapped_windows); 734 } 735 736 /* Move the mapped windows. */ 737 list_for_each_safe_entry(cur, iter, tmp, &from->mapped_windows, node) { 738 numset_release(from->numset, cur->number); 739 list_del(&cur->node); 740 741 cur->number = numset_request(to->numset); 742 vscreen_insert_window(&to->mapped_windows, cur); 743 } 744} 745 746/* Used by :cother / :iother */ 747rp_window * 748vscreen_last_window_by_class(rp_vscreen *v, char *class) 749{ 750 int last_access = 0; 751 rp_window_elem *most_recent = NULL; 752 rp_window_elem *cur; 753 754 list_for_each_entry(cur, &v->mapped_windows, node) { 755 if (cur->win->last_access >= last_access 756 && cur->win != current_window() 757 && !find_windows_frame(cur->win) 758 && strcmp(class, cur->win->res_class)) { 759 most_recent = cur; 760 last_access = cur->win->last_access; 761 } 762 } 763 764 if (most_recent) 765 return most_recent->win; 766 767 return NULL; 768} 769 770/* Used by :cother / :iother */ 771rp_window * 772vscreen_last_window_by_class_complement(rp_vscreen *v, char *class) 773{ 774 int last_access = 0; 775 rp_window_elem *most_recent = NULL; 776 rp_window_elem *cur; 777 778 list_for_each_entry(cur, &v->mapped_windows, node) { 779 if (cur->win->last_access >= last_access 780 && cur->win != current_window() 781 && !find_windows_frame(cur->win) 782 && !strcmp(class, cur->win->res_class)) { 783 most_recent = cur; 784 last_access = cur->win->last_access; 785 } 786 } 787 788 if (most_recent) 789 return most_recent->win; 790 791 return NULL; 792} 793 794void 795vscreen_announce_current(rp_vscreen *v) 796{ 797 set_atom(v->screen->root, _net_current_desktop, XA_CARDINAL, 798 (unsigned long *)&v->number, 1); 799}