jcs ratpoison hax
at master 726 lines 16 kB view raw
1/* Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts <sabetts@vcn.bc.ca> 2 * 3 * This file is part of ratpoison. 4 * 5 * ratpoison is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2, or (at your option) 8 * any later version. 9 * 10 * ratpoison is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this software; see the file COPYING. If not, write to 17 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, 18 * Boston, MA 02111-1307 USA 19 */ 20 21#include "ratpoison.h" 22 23#include <string.h> 24 25static struct numset *group_numset; 26 27static void 28set_current_group_1 (rp_group *g) 29{ 30 static int counter = 1; 31 rp_current_group = g; 32 if (g) 33 g->last_access = counter++; 34} 35 36void 37init_groups(void) 38{ 39 rp_group *g; 40 41 group_numset = numset_new(); 42 INIT_LIST_HEAD (&rp_groups); 43 44 /* Create the first group in the list (We always need at least 45 one). */ 46 g = group_new (numset_request (group_numset), DEFAULT_GROUP_NAME); 47 set_current_group_1 (g); 48 list_add_tail (&g->node, &rp_groups); 49} 50 51void 52free_groups(void) 53{ 54 rp_group *cur; 55 struct list_head *iter, *tmp; 56 57 list_for_each_safe_entry (cur, iter, tmp, &rp_groups, node) 58 { 59 group_free (cur); 60 } 61} 62 63struct numset * 64group_get_numset(void) 65{ 66 return group_numset; 67} 68 69/* get the group list and store it in buffer delimiting each window 70 with delim. mark_start and mark_end will be filled with the text 71 positions for the start and end of the current window. */ 72void 73get_group_list (char *delim, struct sbuf *buffer, 74 int *mark_start, int *mark_end) 75{ 76 rp_group *cur, *last; 77 78 if (buffer == NULL) return; 79 80 sbuf_clear (buffer); 81 82 last = group_last_group (); 83 84 /* Generate the string. */ 85 list_for_each_entry (cur, &rp_groups, node) 86 { 87 char *fmt; 88 char separator; 89 90 if (cur == rp_current_group) 91 *mark_start = strlen (sbuf_get (buffer)); 92 93 if(cur == rp_current_group) 94 separator = '*'; 95 else if(cur == last) 96 separator = '+'; 97 else 98 separator = '-'; 99 100 /* A hack, pad the group with a space at the beginning and end 101 if there is no delimiter. */ 102 if (!delim) 103 sbuf_concat (buffer, " "); 104 105 fmt = xsprintf ("%d%c%s", cur->number, separator, cur->name); 106 sbuf_concat (buffer, fmt); 107 free (fmt); 108 109 /* A hack, pad the group with a space at the beginning and end 110 if there is no delimiter. */ 111 if (!delim) 112 sbuf_concat (buffer, " "); 113 114 /* Only put the delimiter between the group, and not after the the last 115 group. */ 116 if (delim && cur->node.next != &rp_groups) 117 sbuf_concat (buffer, delim); 118 119 if (cur == rp_current_group) 120 *mark_end = strlen (sbuf_get (buffer)); 121 } 122} 123 124rp_group * 125group_new (int number, char *name) 126{ 127 rp_group *g; 128 129 g = xmalloc (sizeof (rp_group)); 130 131 if (name) 132 g->name = xstrdup (name); 133 else 134 g->name = NULL; 135 g->last_access = 0; 136 g->number = number; 137 g->numset = numset_new(); 138 INIT_LIST_HEAD (&g->unmapped_windows); 139 INIT_LIST_HEAD (&g->mapped_windows); 140 141 return g; 142} 143 144void 145group_free (rp_group *g) 146{ 147 free (g->name); 148 numset_free (g->numset); 149 numset_release (group_numset, g->number); 150 free (g); 151} 152 153rp_group * 154group_add_new_group (char *name) 155{ 156 rp_group *g; 157 rp_group *cur; 158 159 g = group_new (numset_request (group_numset), name); 160 161 list_for_each_entry (cur, &rp_groups, node) 162 { 163 if (cur->number > g->number) 164 { 165 list_add_tail (&g->node, &cur->node); 166 return g; 167 } 168 } 169 170 list_add_tail (&g->node, &rp_groups); 171 172 return g; 173} 174 175void 176group_resort_group (rp_group *g) 177{ 178 rp_group *cur; 179 struct list_head *last = &rp_groups; 180 181 list_del (&g->node); 182 list_for_each_entry (cur, &rp_groups, node) 183 { 184 if (cur->number > g->number) 185 { 186 list_add (&g->node, last); 187 return; 188 } 189 last = &cur->node; 190 } 191 list_add (&g->node, last); 192} 193 194void 195group_rename (rp_group *g, char *name) 196{ 197 free (g->name); 198 g->name = xstrdup (name); 199} 200 201rp_group * 202group_next_group (void) 203{ 204 return list_next_entry (rp_current_group, &rp_groups, node); 205} 206 207rp_group * 208group_prev_group (void) 209{ 210 return list_prev_entry (rp_current_group, &rp_groups, node); 211} 212 213rp_group * 214group_last_group (void) 215{ 216 int last_access = 0; 217 rp_group *most_recent = NULL; 218 rp_group *cur; 219 220 list_for_each_entry (cur, &rp_groups, node) 221 { 222 if (cur != rp_current_group && cur->last_access > last_access) { 223 most_recent = cur; 224 last_access = cur->last_access; 225 } 226 } 227 return most_recent; 228} 229 230rp_group * 231groups_find_group_by_name (char *s, int exact_match) 232{ 233 rp_group *cur; 234 235 if (!exact_match) 236 { 237 list_for_each_entry (cur, &rp_groups, node) 238 { 239 if (cur->name && str_comp (s, cur->name, strlen (s))) 240 return cur; 241 } 242 } 243 else 244 { 245 list_for_each_entry (cur, &rp_groups, node) 246 { 247 if (cur->name && !strcmp (cur->name, s)) 248 return cur; 249 } 250 } 251 252 return NULL; 253} 254 255rp_group * 256groups_find_group_by_number (int n) 257{ 258 rp_group *cur; 259 260 list_for_each_entry (cur, &rp_groups, node) 261 { 262 if (cur->number == n) 263 return cur; 264 } 265 266 return NULL; 267} 268 269/* Return the first group that contains the window. */ 270rp_group * 271groups_find_group_by_window (rp_window *win) 272{ 273 rp_group *cur; 274 rp_window_elem *elem; 275 276 list_for_each_entry (cur, &rp_groups, node) 277 { 278 elem = group_find_window (&cur->mapped_windows, win); 279 if (elem) 280 return cur; 281 } 282 283 return NULL; 284} 285 286 287/* Return the first group that is g. */ 288rp_group * 289groups_find_group_by_group (rp_group *g) 290{ 291 rp_group *cur; 292 293 list_for_each_entry (cur, &rp_groups, node) 294 { 295 if (cur == g) 296 return cur; 297 } 298 299 return NULL; 300} 301 302rp_window_elem * 303group_find_window (struct list_head *list, rp_window *win) 304{ 305 rp_window_elem *cur; 306 307 list_for_each_entry (cur, list, node) 308 { 309 if (cur->win == win) 310 return cur; 311 } 312 313 return NULL; 314} 315 316rp_window_elem * 317group_find_window_by_number (rp_group *g, int num) 318{ 319 rp_window_elem *cur; 320 321 list_for_each_entry (cur, &g->mapped_windows, node) 322 { 323 if (cur->number == num) 324 return cur; 325 } 326 327 return NULL; 328 329} 330 331 332/* Insert a window_elem into the correct spot in the group's window 333 list to preserve window number ordering. */ 334static void 335group_insert_window (struct list_head *h, rp_window_elem *w) 336{ 337 rp_window_elem *cur; 338 339 list_for_each_entry (cur, h, node) 340 { 341 if (cur->number > w->number) 342 { 343 list_add_tail (&w->node, &cur->node); 344 return; 345 } 346 } 347 348 list_add_tail(&w->node, h); 349} 350 351static int 352group_in_list (struct list_head *h, rp_window_elem *w) 353{ 354 rp_window_elem *cur; 355 356 list_for_each_entry (cur, h, node) 357 { 358 if (cur == w) 359 return 1; 360 } 361 362 return 0; 363} 364 365/* If a window_elem's number has changed then the list has to be 366 resorted. */ 367void 368group_resort_window (rp_group *g, rp_window_elem *w) 369{ 370 /* Only a mapped window can be resorted. */ 371 if (!group_in_list (&g->mapped_windows, w)) 372 { 373 PRINT_DEBUG (("Attempting to restort an unmapped window!\n")); 374 return; 375 } 376 377 list_del (&w->node); 378 group_insert_window (&g->mapped_windows, w); 379} 380 381void 382group_add_window (rp_group *g, rp_window *w) 383{ 384 rp_window_elem *we; 385 386 /* Create our container structure for the window. */ 387 we = xmalloc (sizeof (rp_window_elem)); 388 we->win = w; 389 we->number = -1; 390 391 /* Finally, add it to our list. */ 392 list_add_tail (&we->node, &g->unmapped_windows); 393} 394 395void 396group_map_window (rp_group *g, rp_window *win) 397{ 398 rp_window_elem *we; 399 400 we = group_find_window (&g->unmapped_windows, win); 401 402 if (we) 403 { 404 we->number = numset_request (g->numset); 405 list_del (&we->node); 406 group_insert_window (&g->mapped_windows, we); 407 } 408} 409 410void 411groups_map_window (rp_window *win) 412{ 413 rp_group *cur; 414 415 list_for_each_entry (cur, &rp_groups, node) 416 { 417 group_map_window (cur, win); 418 } 419} 420 421void 422group_unmap_window (rp_group *g, rp_window *win) 423{ 424 rp_window_elem *we; 425 426 we = group_find_window (&g->mapped_windows, win); 427 428 if (we) 429 { 430 numset_release (g->numset, we->number); 431 list_move_tail (&we->node, &g->unmapped_windows); 432 } 433} 434 435void 436groups_unmap_window (rp_window *win) 437{ 438 rp_group *cur; 439 440 list_for_each_entry (cur, &rp_groups, node) 441 { 442 group_unmap_window (cur, win); 443 } 444} 445 446void 447group_del_window (rp_group *g, rp_window *win) 448{ 449 rp_window_elem *cur; 450 struct list_head *iter, *tmp; 451 452 /* The assumption is that a window is unmapped before it's deleted. */ 453 list_for_each_safe_entry (cur, iter, tmp, &g->unmapped_windows, node) 454 { 455 if (cur->win == win) 456 { 457 list_del (&cur->node); 458 free (cur); 459 } 460 } 461 462 /* Make sure the window isn't in the list of mapped windows. This 463 would mean there is a bug. */ 464#ifdef DEBUG 465 list_for_each_entry (cur, &g->mapped_windows, node) 466 { 467 if (cur->win == win) 468 PRINT_DEBUG (("This window wasn't removed from the mapped window list.\n")); 469 } 470#endif 471} 472 473/* Remove the window from any groups in resides in. */ 474void 475groups_del_window (rp_window *win) 476{ 477 rp_group *cur; 478 479 list_for_each_entry (cur, &rp_groups, node) 480 { 481 group_del_window (cur, win); 482 } 483} 484 485rp_window * 486group_last_window (rp_group *g, rp_screen *s) 487{ 488 int last_access = 0; 489 rp_window_elem *most_recent = NULL; 490 rp_window_elem *cur; 491 492 list_for_each_entry (cur, &g->mapped_windows, node) 493 { 494 if (cur->win->last_access >= last_access 495 && cur->win != current_window() 496 && !find_windows_frame (cur->win) 497 && (cur->win->scr == s || rp_have_xrandr)) 498 { 499 most_recent = cur; 500 last_access = cur->win->last_access; 501 } 502 } 503 504 if (most_recent) 505 return most_recent->win; 506 507 return NULL; 508} 509 510rp_window * 511group_next_window (rp_group *g, rp_window *win) 512{ 513 rp_window_elem *cur, *we; 514 515 /* If there is no window, then get the last accessed one. */ 516 if (win == NULL) 517 return group_last_window (g, rp_current_screen); 518 519 /* If we can't find the window, then it's in a different group, so 520 get the last accessed one in this group. */ 521 we = group_find_window (&g->mapped_windows, win); 522 if (we == NULL) 523 return group_last_window (g, win->scr); 524 525 /* The window is in this group, so find the next one in the list 526 that isn't already displayed. */ 527 for (cur = list_next_entry (we, &g->mapped_windows, node); 528 cur != we; 529 cur = list_next_entry (cur, &g->mapped_windows, node)) 530 { 531 if (!find_windows_frame (cur->win) && (cur->win->scr == win->scr || rp_have_xrandr)) 532 { 533 return cur->win; 534 } 535 } 536 537 return NULL; 538} 539 540rp_window * 541group_prev_window (rp_group *g, rp_window *win) 542{ 543 rp_window_elem *cur, *we; 544 545 /* If there is no window, then get the last accessed one. */ 546 if (win == NULL) 547 return group_last_window (g, rp_current_screen); 548 549 /* If we can't find the window, then it's in a different group, so 550 get the last accessed one in this group. */ 551 we = group_find_window (&g->mapped_windows, win); 552 if (we == NULL) 553 return group_last_window (g, win->scr); 554 555 /* The window is in this group, so find the previous one in the list 556 that isn't already displayed. */ 557 for (cur = list_prev_entry (we, &g->mapped_windows, node); 558 cur != we; 559 cur = list_prev_entry (cur, &g->mapped_windows, node)) 560 { 561 if (!find_windows_frame (cur->win) && (cur->win->scr == win->scr || rp_have_xrandr)) 562 { 563 return cur->win; 564 } 565 } 566 567 return NULL; 568 569} 570 571void 572group_move_window (rp_group *to, rp_window *win) 573{ 574 rp_group *cur, *from = NULL; 575 rp_window_elem *we = NULL; 576 577 /* Find the group that the window belongs to. FIXME: If the window 578 exists in multiple groups, then we're going to find the first 579 group with this window in it. */ 580 list_for_each_entry (cur, &rp_groups, node) 581 { 582 we = group_find_window (&cur->mapped_windows, win); 583 if (we) 584 { 585 from = cur; 586 break; 587 } 588 } 589 590 if (we == NULL || from == NULL) 591 { 592 PRINT_DEBUG (("Unable to find window in mapped window lists.\n")); 593 return; 594 } 595 596 /* Manually remove the window from one group...*/ 597 numset_release (from->numset, we->number); 598 list_del (&we->node); 599 600 /* and shove it into the other one. */ 601 we->number = numset_request (to->numset); 602 group_insert_window (&to->mapped_windows, we); 603} 604 605void 606groups_merge (rp_group *from, rp_group *to) 607{ 608 rp_window_elem *cur; 609 struct list_head *iter, *tmp; 610 611 /* Merging a group with itself makes no sense. */ 612 if (from == to) 613 return; 614 615 /* Move the unmapped windows. */ 616 list_for_each_safe_entry (cur, iter, tmp, &from->unmapped_windows, node) 617 { 618 list_del (&cur->node); 619 list_add_tail (&cur->node, &to->unmapped_windows); 620 } 621 622 /* Move the mapped windows. */ 623 list_for_each_safe_entry (cur, iter, tmp, &from->mapped_windows, node) 624 { 625 numset_release (from->numset, cur->number); 626 list_del (&cur->node); 627 628 cur->number = numset_request (to->numset); 629 group_insert_window (&to->mapped_windows, cur); 630 } 631} 632 633void 634set_current_group (rp_group *g) 635{ 636 if (rp_current_group == g || g == NULL) 637 return; 638 639 set_current_group_1 (g); 640 641 /* Call the switch group hook. */ 642 hook_run (&rp_switch_group_hook); 643} 644 645int 646group_delete_group (rp_group *g) 647{ 648 if (list_empty (&(g->mapped_windows)) 649 && list_empty (&(g->unmapped_windows))) 650 { 651 /* don't delete the last group */ 652 if (list_size (&rp_groups) == 1) 653 return GROUP_DELETE_LAST_GROUP; 654 655 /* we can safely delete the group */ 656 if (g == rp_current_group) 657 { 658 rp_group *next = group_last_group (); 659 set_current_group (next ? next : group_next_group ()); 660 } 661 662 list_del (&(g->node)); 663 group_free (g); 664 return GROUP_DELETE_GROUP_OK; 665 } 666 else 667 { 668 return GROUP_DELETE_GROUP_NONEMPTY; 669 } 670} 671 672/* Used by :cother / :iother */ 673rp_window * 674group_last_window_by_class (rp_group *g, char *class) 675{ 676 int last_access = 0; 677 rp_window_elem *most_recent = NULL; 678 rp_window_elem *cur; 679 rp_screen *s = rp_current_screen; 680 681 list_for_each_entry (cur, &g->mapped_windows, node) 682 { 683 if (cur->win->last_access >= last_access 684 && cur->win != current_window() 685 && !find_windows_frame (cur->win) 686 && (cur->win->scr == s || rp_have_xrandr) 687 && strcmp(class, cur->win->res_class)) 688 { 689 most_recent = cur; 690 last_access = cur->win->last_access; 691 } 692 } 693 694 if (most_recent) 695 return most_recent->win; 696 697 return NULL; 698} 699 700/* Used by :cother / :iother */ 701rp_window * 702group_last_window_by_class_complement (rp_group *g, char *class) 703{ 704 int last_access = 0; 705 rp_window_elem *most_recent = NULL; 706 rp_window_elem *cur; 707 rp_screen *s = rp_current_screen; 708 709 list_for_each_entry (cur, &g->mapped_windows, node) 710 { 711 if (cur->win->last_access >= last_access 712 && cur->win != current_window() 713 && !find_windows_frame (cur->win) 714 && (cur->win->scr == s || rp_have_xrandr) 715 && !strcmp(class, cur->win->res_class)) 716 { 717 most_recent = cur; 718 last_access = cur->win->last_access; 719 } 720 } 721 722 if (most_recent) 723 return most_recent->win; 724 725 return NULL; 726}