jcs ratpoison hax
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}