A tiling window manager
1/*
2 * Functions for handling window splitting and tiling.
3 * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts <sabetts@vcn.bc.ca>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA.
18 */
19
20#include <unistd.h>
21#include <err.h>
22#include <string.h>
23
24#include "sdorfehs.h"
25
26#define VERTICALLY 0
27#define HORIZONTALLY 1
28
29static void
30update_last_access(rp_frame *frame)
31{
32 static int counter = 0;
33
34 frame->last_access = counter;
35 counter++;
36}
37
38rp_frame *
39current_frame(rp_vscreen *v)
40{
41 return vscreen_get_frame(v, v->current_frame);
42}
43
44void
45cleanup_frame(rp_frame *frame)
46{
47 rp_window *win;
48 rp_vscreen *vscreen = frame->vscreen;
49
50 win = find_window_other(vscreen);
51 if (win == NULL) {
52 set_frames_window(frame, NULL);
53 return;
54 }
55 set_frames_window(frame, win);
56
57 maximize(win);
58 unhide_window(win);
59
60 if (!window_is_transient(win))
61 hide_others(win);
62}
63
64rp_window *
65set_frames_window(rp_frame *frame, rp_window *win)
66{
67 int last_win;
68
69 last_win = frame->win_number;
70 if (win) {
71 frame->win_number = win->number;
72 win->frame_number = frame->number;
73
74 /*
75 * We need to make sure that win and frame are on the same
76 * screen, since with Xrandr, windows can move from one screen
77 * to another.
78 */
79 win->vscreen = frame->vscreen;
80 } else {
81 frame->win_number = EMPTY;
82 }
83
84 return find_window_number(last_win);
85}
86
87void
88maximize_all_windows_in_frame(rp_frame *frame)
89{
90 rp_window *win;
91
92 list_for_each_entry(win, &rp_mapped_window, node) {
93 if (win->frame_number == frame->number) {
94 maximize(win);
95 }
96 }
97}
98
99/* Make the frame occupy the entire screen */
100void
101maximize_frame(rp_frame *frame)
102{
103 rp_vscreen *v = frame->vscreen;
104
105 frame->x = screen_left(v->screen);
106 frame->y = screen_top(v->screen);
107
108 frame->width = screen_width(v->screen);
109 frame->height = screen_height(v->screen);
110}
111
112/* Create a full screen frame */
113static void
114create_initial_frame(rp_vscreen *vscreen)
115{
116 rp_frame *frame;
117
118 frame = frame_new(vscreen);
119 vscreen->current_frame = frame->number;
120 list_add_tail(&frame->node, &vscreen->frames);
121
122 update_last_access(frame);
123
124 maximize_frame(frame);
125 set_frames_window(frame, NULL);
126}
127
128void
129init_frame_list(rp_vscreen *vscreen)
130{
131 INIT_LIST_HEAD(&vscreen->frames);
132
133 create_initial_frame(vscreen);
134}
135
136rp_frame *
137find_last_frame(rp_vscreen *v)
138{
139 rp_frame *cur_frame, *last = NULL;
140 int last_access = -1;
141
142 list_for_each_entry(cur_frame, &v->frames, node) {
143 if (cur_frame->number != v->current_frame &&
144 cur_frame->last_access > last_access) {
145 last_access = cur_frame->last_access;
146 last = cur_frame;
147 }
148 }
149
150 return last;
151}
152
153/* Return the frame that contains the window. */
154rp_frame *
155find_windows_frame(rp_window *win)
156{
157 rp_vscreen *v;
158 rp_frame *cur;
159
160 v = win->vscreen;
161
162 list_for_each_entry(cur, &v->frames, node) {
163 if (cur->win_number == win->number)
164 return cur;
165 }
166
167 return NULL;
168}
169
170int
171num_frames(rp_vscreen *v)
172{
173 return list_size(&v->frames);
174}
175
176rp_frame *
177find_frame_next(rp_frame *frame)
178{
179 if (frame == NULL)
180 return NULL;
181 return list_next_entry(frame, &frame->vscreen->frames, node);
182}
183
184rp_frame *
185find_frame_prev(rp_frame *frame)
186{
187 if (frame == NULL)
188 return NULL;
189 return list_prev_entry(frame, &frame->vscreen->frames, node);
190}
191
192rp_window *
193current_window(void)
194{
195 return find_window_number(current_frame(rp_current_vscreen)->win_number);
196}
197
198static int
199window_fits_in_frame(rp_window *win, rp_frame *frame)
200{
201 /*
202 * If the window has minimum size hints, make sure they are smaller
203 * than the frame.
204 */
205 if (win->hints->flags & PMinSize) {
206 if (win->hints->min_width > frame->width
207 ||
208 win->hints->min_height > frame->height) {
209 return 0;
210 }
211 }
212 return 1;
213}
214
215/*
216 * Search the list of mapped windows for a window that will fit in the
217 * specified frame.
218 */
219rp_window *
220find_window_for_frame(rp_frame *frame)
221{
222 rp_vscreen *v = frame->vscreen;
223 int last_access = 0;
224 rp_window_elem *most_recent = NULL;
225 rp_window_elem *cur;
226
227 list_for_each_entry(cur, &v->mapped_windows, node) {
228 if (cur->win != current_window()
229 && cur->win->sticky_frame != frame->number
230 && !find_windows_frame(cur->win)
231 && cur->win->last_access >= last_access
232 && window_fits_in_frame(cur->win, frame)
233 && cur->win->frame_number == EMPTY) {
234 most_recent = cur;
235 last_access = cur->win->last_access;
236 }
237 }
238
239 if (most_recent)
240 return most_recent->win;
241
242 return NULL;
243}
244
245/*
246 * Splits the frame in 2. if way is 0 then split vertically otherwise split it
247 * horizontally.
248 */
249static void
250split_frame(rp_frame *frame, int way, int pixels)
251{
252 rp_vscreen *v;
253 rp_window *win;
254 rp_frame *new_frame;
255
256 v = frame->vscreen;
257
258 /* Make our new frame. */
259 new_frame = frame_new(v);
260
261 /* Add the frame to the frameset. */
262 list_add(&new_frame->node, ¤t_frame(rp_current_vscreen)->node);
263
264 set_frames_window(new_frame, NULL);
265
266 if (way == HORIZONTALLY) {
267 new_frame->x = frame->x;
268 new_frame->y = frame->y + pixels;
269 new_frame->width = frame->width;
270 new_frame->height = frame->height - pixels;
271
272 frame->height = pixels;
273 } else {
274 new_frame->x = frame->x + pixels;
275 new_frame->y = frame->y;
276 new_frame->width = frame->width - pixels;
277 new_frame->height = frame->height;
278
279 frame->width = pixels;
280 }
281
282 win = find_window_for_frame(new_frame);
283 if (win) {
284 PRINT_DEBUG(("Found a window for the frame!\n"));
285
286 set_frames_window(new_frame, win);
287
288 maximize(win);
289 unhide_window(win);
290 } else {
291 PRINT_DEBUG(("No window fits the frame.\n"));
292
293 set_frames_window(new_frame, NULL);
294 }
295
296 /* resize the existing frame */
297 if (frame->win_number != EMPTY) {
298 maximize_all_windows_in_frame(frame);
299 XRaiseWindow(dpy, find_window_number(frame->win_number)->w);
300 }
301 update_bar(v->screen);
302 show_frame_indicator(0);
303}
304
305/*
306 * Splits the window vertically leaving the original with 'pixels' pixels .
307 */
308void
309v_split_frame(rp_frame *frame, int pixels)
310{
311 split_frame(frame, VERTICALLY, pixels);
312}
313
314/*
315 * Splits the frame horizontally leaving the original with 'pixels' pixels .
316 */
317void
318h_split_frame(rp_frame *frame, int pixels)
319{
320 split_frame(frame, HORIZONTALLY, pixels);
321}
322
323void
324remove_all_splits(void)
325{
326 struct list_head *tmp, *iter;
327 rp_vscreen *v = rp_current_vscreen;
328 rp_frame *frame;
329 rp_window *win;
330
331 /* Hide all the windows not in the current frame. */
332 list_for_each_entry(win, &rp_mapped_window, node) {
333 if (win->frame_number != v->current_frame && win->vscreen == v)
334 hide_window(win);
335
336 if (win->sticky_frame != EMPTY &&
337 win->sticky_frame != v->current_frame && win->vscreen == v)
338 win->sticky_frame = EMPTY;
339 }
340
341 /* Delete all the frames except the current one. */
342 list_for_each_safe_entry(frame, iter, tmp, &v->frames, node) {
343 if (frame->number != v->current_frame) {
344 list_del(&frame->node);
345 frame_free(v, frame);
346 }
347 }
348
349 /* Maximize the frame and the windows in the frame. */
350 maximize_frame(current_frame(rp_current_vscreen));
351 maximize_all_windows_in_frame(current_frame(rp_current_vscreen));
352}
353
354/* Shrink the size of the frame to fit it's current window. */
355void
356resize_shrink_to_window(rp_frame *frame)
357{
358 rp_window *win;
359
360 if (frame->win_number == EMPTY)
361 return;
362
363 win = find_window_number(frame->win_number);
364
365 resize_frame_horizontally(frame,
366 win->width + (win->border * 2) + (defaults.gap * 2) - frame->width);
367 resize_frame_vertically(frame,
368 win->height + (win->border * 2) + (defaults.gap * 2) -
369 frame->height);
370}
371
372/*
373 * resize_frame is a generic frame resizer that can resize vertically,
374 * horizontally, to the right, to the left, etc. It all depends on the
375 * functions passed to it. Returns -1 if the resize failed, 0 for success.
376 */
377static int
378resize_frame(rp_frame *frame, rp_frame *pusher, int diff,
379 int (*c1)(rp_frame *), int (c2)(rp_frame *),
380 int (*c3)(rp_frame *), int (c4)(rp_frame *),
381 void (*resize1)(rp_frame *, int),
382 void (*resize2)(rp_frame *, int),
383 int (*resize3)(rp_frame *, rp_frame *, int))
384{
385 rp_vscreen *v = frame->vscreen;
386 rp_frame *cur;
387
388 /*
389 * Loop through the frames and determine which ones are affected by
390 * resizing frame.
391 */
392 list_for_each_entry(cur, &v->frames, node) {
393 if (cur == frame || cur == pusher)
394 continue;
395 /*
396 * If cur is touching frame along the axis that is being moved
397 * then this frame is affected by the resize.
398 */
399 if ((*c1)(cur) == (*c3)(frame)) {
400 /* If the frame can't get any smaller, then fail. */
401 if (diff > 0 && abs((*c3)(cur) - (*c1)(cur)) - diff <=
402 (defaults.window_border_width * 2) +
403 (defaults.gap * 2))
404 return -1;
405
406 /*
407 * Test for this circumstance:
408 * --+ | |+-+ |f||c| | |+-+ --+
409 *
410 * In this case, resizing cur will not affect any other
411 * frames, so just do the resize.
412 */
413 if (((*c2)(cur) >= (*c2)(frame))
414 && (*c4)(cur) <= (*c4)(frame)) {
415 (*resize2)(cur, -diff);
416 maximize_all_windows_in_frame(cur);
417 }
418 /*
419 * Otherwise, cur's corners are either strictly outside
420 * frame's corners, or one of them is inside and the
421 * other isn't. In either of these cases, resizing cur
422 * will affect other adjacent frames, so find them and
423 * resize them first (recursive step) and then resize
424 * cur.
425 */
426 else if (((*c2)(cur) < (*c2)(frame)
427 && (*c4)(cur) > (*c4)(frame))
428 || ((*c2)(cur) >= (*c2)(frame)
429 && (*c2)(cur) < (*c4)(frame))
430 || ((*c4)(cur) > (*c2)(frame)
431 && (*c4)(cur) <= (*c4)(frame))) {
432 /* Attempt to resize cur. */
433 if (resize3(cur, frame, -diff) == -1)
434 return -1;
435 }
436 }
437 }
438
439 /* Finally, resize the frame and the windows inside. */
440 (*resize1)(frame, diff);
441 maximize_all_windows_in_frame(frame);
442
443 return 0;
444}
445
446static int resize_frame_bottom(rp_frame *frame, rp_frame *pusher, int diff);
447static int resize_frame_top(rp_frame *frame, rp_frame *pusher, int diff);
448static int resize_frame_left(rp_frame *frame, rp_frame *pusher, int diff);
449static int resize_frame_right(rp_frame *frame, rp_frame *pusher, int diff);
450
451/* Resize frame by moving it's right side. */
452static int
453resize_frame_right(rp_frame *frame, rp_frame *pusher, int diff)
454{
455 return resize_frame(frame, pusher, diff,
456 frame_left, frame_top, frame_right, frame_bottom,
457 frame_resize_right, frame_resize_left, resize_frame_left);
458}
459
460/* Resize frame by moving it's left side. */
461static int
462resize_frame_left(rp_frame *frame, rp_frame *pusher, int diff)
463{
464 return resize_frame(frame, pusher, diff,
465 frame_right, frame_top, frame_left, frame_bottom,
466 frame_resize_left, frame_resize_right, resize_frame_right);
467}
468
469/* Resize frame by moving it's top side. */
470static int
471resize_frame_top(rp_frame *frame, rp_frame *pusher, int diff)
472{
473 return resize_frame(frame, pusher, diff,
474 frame_bottom, frame_left, frame_top, frame_right,
475 frame_resize_up, frame_resize_down, resize_frame_bottom);
476}
477
478/* Resize frame by moving it's bottom side. */
479static int
480resize_frame_bottom(rp_frame *frame, rp_frame *pusher, int diff)
481{
482 return resize_frame(frame, pusher, diff,
483 frame_top, frame_left, frame_bottom, frame_right,
484 frame_resize_down, frame_resize_up, resize_frame_top);
485}
486
487/*
488 * Resize frame diff pixels by expanding it to the right. If the frame is
489 * against the right side of the screen, expand it to the left.
490 */
491void
492resize_frame_horizontally(rp_frame *frame, int diff)
493{
494 int (*resize_fn)(rp_frame *, rp_frame *, int);
495 struct list_head *l;
496 rp_vscreen *v = frame->vscreen;
497
498 if (num_frames(v) < 2 || diff == 0)
499 return;
500
501 if (frame_width(frame) + diff <= (defaults.window_border_width * 2) +
502 (defaults.gap * 2))
503 return;
504
505 /* Find out which resize function to use. */
506 if (frame_right(frame) < screen_right(v->screen)) {
507 resize_fn = resize_frame_right;
508 } else if (frame_left(frame) > screen_left(v->screen)) {
509 resize_fn = resize_frame_left;
510 } else {
511 return;
512 }
513
514 /*
515 * Copy the frameset. If the resize fails, then we restore the original
516 * one.
517 */
518 l = vscreen_copy_frameset(v);
519
520 if ((*resize_fn)(frame, NULL, diff) == -1) {
521 vscreen_restore_frameset(v, l);
522 } else {
523 frameset_free(l);
524 }
525
526 /* It's our responsibility to free this. */
527 free(l);
528}
529
530/*
531 * Resize frame diff pixels by expanding it down. If the frame is against the
532 * bottom of the screen, expand it up.
533 */
534void
535resize_frame_vertically(rp_frame *frame, int diff)
536{
537 int (*resize_fn)(rp_frame *, rp_frame *, int);
538 struct list_head *l;
539 rp_vscreen *v = frame->vscreen;
540
541 if (num_frames(v) < 2 || diff == 0)
542 return;
543
544 if (frame_height(frame) + diff <= (defaults.window_border_width * 2) +
545 (defaults.gap * 2))
546 return;
547
548 /* Find out which resize function to use. */
549 if (frame_bottom(frame) < screen_bottom(v->screen)) {
550 resize_fn = resize_frame_bottom;
551 } else if (frame_top(frame) > screen_top(v->screen)) {
552 resize_fn = resize_frame_top;
553 } else {
554 return;
555 }
556
557 /*
558 * Copy the frameset. If the resize fails, then we restore the original
559 * one.
560 */
561 l = vscreen_copy_frameset(v);
562
563 if ((*resize_fn)(frame, NULL, diff) == -1) {
564 vscreen_restore_frameset(v, l);
565 } else {
566 frameset_free(l);
567 }
568
569 /* It's our responsibility to free this. */
570 free(l);
571}
572
573static int
574frame_is_below(rp_frame *src, rp_frame *frame)
575{
576 if (frame->y > src->y)
577 return 1;
578 return 0;
579}
580
581static int
582frame_is_above(rp_frame *src, rp_frame *frame)
583{
584 if (frame->y < src->y)
585 return 1;
586 return 0;
587}
588
589static int
590frame_is_left(rp_frame *src, rp_frame *frame)
591{
592 if (frame->x < src->x)
593 return 1;
594 return 0;
595}
596
597static int
598frame_is_right(rp_frame *src, rp_frame *frame)
599{
600 if (frame->x > src->x)
601 return 1;
602 return 0;
603}
604
605static int
606total_frame_area(rp_vscreen *v)
607{
608 int area = 0;
609 rp_frame *cur;
610
611 list_for_each_entry(cur, &v->frames, node) {
612 area += cur->width * cur->height;
613 }
614
615 return area;
616}
617
618/* Return 1 if frames f1 and f2 overlap */
619static int
620frames_overlap(rp_frame *f1, rp_frame *f2)
621{
622 if (f1->x >= f2->x + f2->width
623 || f1->y >= f2->y + f2->height
624 || f2->x >= f1->x + f1->width
625 || f2->y >= f1->y + f1->height) {
626 return 0;
627 }
628 return 1;
629}
630
631/* Return 1 if w's frame overlaps any other window's frame */
632static int
633frame_overlaps(rp_frame *frame)
634{
635 rp_vscreen *v;
636 rp_frame *cur;
637
638 v = frame->vscreen;
639
640 list_for_each_entry(cur, &v->frames, node) {
641 if (cur != frame && frames_overlap(cur, frame)) {
642 return 1;
643 }
644 }
645 return 0;
646}
647
648void
649remove_frame(rp_frame *frame)
650{
651 rp_vscreen *v;
652 int area;
653 rp_frame *cur;
654 rp_window *win;
655
656 if (frame == NULL)
657 return;
658
659 v = frame->vscreen;
660
661 area = total_frame_area(v);
662 PRINT_DEBUG(("Total Area: %d\n", area));
663
664 list_del(&frame->node);
665 win = find_window_number(frame->win_number);
666 hide_window(win);
667 hide_others(win);
668
669 list_for_each_entry(win, &rp_mapped_window, node) {
670 if (win->sticky_frame == frame->number && win->vscreen == v)
671 win->sticky_frame = EMPTY;
672 }
673
674 list_for_each_entry(cur, &v->frames, node) {
675 rp_frame tmp_frame;
676 int fits = 0;
677
678 /* Backup the frame */
679 memcpy(&tmp_frame, cur, sizeof(rp_frame));
680
681 if (frame_is_below(frame, cur)
682 || frame_is_above(frame, cur)) {
683 if (frame_is_below(frame, cur))
684 cur->y = frame->y;
685 cur->height += frame->height;
686 }
687 PRINT_DEBUG(("Attempting vertical Frame y=%d height=%d\n",
688 cur->y, cur->height));
689 PRINT_DEBUG(("New Total Area: %d\n", total_frame_area(v)));
690
691 /*
692 * If the area is bigger than before, the frame takes up too
693 * much space. If the current frame and the deleted frame
694 * DON'T overlap then the current window took up just the right
695 * amount of space but didn't take up the space left behind by
696 * the deleted window. If any active frames overlap, it could
697 * have taken up the right amount of space, overlaps with the
698 * deleted frame but obviously didn't fit.
699 */
700 if (total_frame_area(v) > area || !frames_overlap(cur, frame) ||
701 frame_overlaps(cur)) {
702 PRINT_DEBUG(("Didn't fit vertically\n"));
703
704 /* Restore the current window's frame */
705 memcpy(cur, &tmp_frame, sizeof(rp_frame));
706 } else {
707 PRINT_DEBUG(("It fit vertically!!\n"));
708
709 /* update the frame backup */
710 memcpy(&tmp_frame, cur, sizeof(rp_frame));
711 fits = 1;
712 }
713
714 if (frame_is_left(frame, cur)
715 || frame_is_right(frame, cur)) {
716 if (frame_is_right(frame, cur))
717 cur->x = frame->x;
718 cur->width += frame->width;
719 }
720 PRINT_DEBUG(("Attempting horizontal Frame x=%d width=%d\n",
721 cur->x, cur->width));
722 PRINT_DEBUG(("New Total Area: %d\n", total_frame_area(v)));
723
724 /* Same test as the vertical test, above. */
725 if (total_frame_area(v) > area || !frames_overlap(cur, frame) ||
726 frame_overlaps(cur)) {
727 PRINT_DEBUG(("Didn't fit horizontally\n"));
728
729 /* Restore the current window's frame */
730 memcpy(cur, &tmp_frame, sizeof(rp_frame));
731 } else {
732 PRINT_DEBUG(("It fit horizontally!!\n"));
733 fits = 1;
734 }
735
736 if (fits) {
737 /*
738 * The current frame fits into the new space so keep
739 * its new frame parameters and maximize the window to
740 * fit the new frame size.
741 */
742 if (cur->win_number != EMPTY) {
743 rp_window *new =
744 find_window_number(cur->win_number);
745 maximize_all_windows_in_frame(cur);
746 XRaiseWindow(dpy, new->w);
747 }
748 } else {
749 memcpy(cur, &tmp_frame, sizeof(rp_frame));
750 }
751 }
752
753 frame_free(v, frame);
754}
755
756/*
757 * Switch the input focus to another frame, and therefore a different window.
758 */
759void
760set_active_frame(rp_frame *frame, int force_indicator)
761{
762 rp_vscreen *old_v = rp_current_vscreen;
763 rp_vscreen *v = frame->vscreen;
764 int old = old_v->current_frame;
765 rp_window *win, *old_win;
766 rp_frame *old_frame;
767
768 win = find_window_number(frame->win_number);
769 old_frame = current_frame(rp_current_vscreen);
770 if (old_frame) {
771 old_win = find_window_number(old_frame->win_number);
772 } else {
773 old_win = NULL;
774 }
775
776 /* Make the switch */
777 give_window_focus(win, old_win);
778 update_last_access(frame);
779 v->current_frame = frame->number;
780
781 /* If frame->win == NULL, then rp_current_screen is not updated. */
782 rp_current_screen = v->screen;
783 rp_current_screen->current_vscreen = v;
784
785 update_bar(rp_current_screen);
786
787 /* Possibly show the frame indicator. */
788 if ((old != v->current_frame && num_frames(v) > 1) || v != old_v) {
789 if (v != old_v)
790 force_indicator = 1;
791
792 show_frame_indicator(force_indicator);
793
794 /*
795 * run the frame switch hook. We call it in here because this
796 * is when a frame switch ACTUALLY (for sure) happens.
797 */
798 hook_run(&rp_switch_frame_hook);
799 }
800 /*
801 * If the frame has no window to give focus to, give the key window
802 * focus.
803 */
804 if (frame->win_number == EMPTY) {
805 set_window_focus(v->screen->key_window);
806 }
807 /* Call the switchscreen hook, when appropriate. */
808 if (v->screen != old_v->screen) {
809 hook_run(&rp_switch_screen_hook);
810 hook_run(&rp_switch_vscreen_hook);
811 }
812}
813
814void
815exchange_with_frame(rp_frame *cur, rp_frame *frame)
816{
817 rp_window *win, *last_win;
818
819 if (frame == NULL || frame == cur)
820 return;
821
822 /* Exchange the windows in the frames */
823 win = find_window_number(cur->win_number);
824 last_win = set_frames_window(frame, win);
825 set_frames_window(cur, last_win);
826
827 /* Make sure the windows comes up full screen */
828 if (last_win)
829 maximize(last_win);
830 if (win) {
831 maximize(win);
832 /* Make sure the program bar is always on the top */
833 update_window_names(win->vscreen->screen, defaults.window_fmt);
834 }
835 /* Make the switch */
836 update_last_access(frame);
837
838 set_active_frame(frame, 0);
839}
840
841void
842blank_frame(rp_frame *frame)
843{
844 rp_vscreen *v;
845 rp_window *win;
846
847 if (frame->win_number == EMPTY)
848 return;
849
850 v = frame->vscreen;
851
852 win = find_window_number(frame->win_number);
853 hide_window(win);
854 hide_others(win);
855
856 set_frames_window(frame, NULL);
857
858 update_bar(v->screen);
859
860 /* Give the key window focus. */
861 set_window_focus(v->screen->key_window);
862}
863
864void
865hide_frame_indicator(void)
866{
867 rp_screen *cur;
868
869 list_for_each_entry(cur, &rp_screens, node) {
870 XUnmapWindow(dpy, cur->frame_window);
871 }
872}
873
874void
875show_frame_indicator(int force)
876{
877 if (num_frames(rp_current_vscreen) > 1 || force) {
878 hide_frame_indicator();
879 if (defaults.frame_indicator_timeout != -1) {
880 show_frame_message(defaults.frame_fmt);
881 alarm(defaults.frame_indicator_timeout);
882 }
883 }
884}
885
886void
887show_frame_message(char *msg)
888{
889 rp_screen *s = rp_current_screen;
890 int width, height;
891 rp_frame *frame;
892 rp_window *win;
893 rp_window_elem *elem = NULL;
894 struct sbuf *msgbuf;
895
896 frame = current_frame(rp_current_vscreen);
897 win = current_window();
898 if (win) {
899 elem = vscreen_find_window(&win->vscreen->mapped_windows, win);
900 if (!elem)
901 warnx("window 0x%lx not on any vscreen\n",
902 (unsigned long)win->w);
903 }
904 /* A frame doesn't always contain a window. */
905 msgbuf = sbuf_new(0);
906 if (elem)
907 format_string(msg, elem, msgbuf);
908 else
909 sbuf_concat(msgbuf, EMPTY_FRAME_MESSAGE);
910
911 width = defaults.bar_x_padding * 2
912 + rp_text_width(s, msgbuf->data, msgbuf->len, NULL);
913 height = (FONT_HEIGHT(s) + defaults.bar_y_padding * 2);
914
915 /*
916 * We don't want another frame indicator to be displayed on another
917 * screen at the same time, so we hide it before bringing it back
918 * again.
919 */
920 hide_frame_indicator();
921
922 XMoveResizeWindow(dpy, s->frame_window,
923 frame->x + frame->width / 2 - width / 2,
924 frame->y + frame->height / 2 - height / 2,
925 width, height);
926
927 XMapRaised(dpy, s->frame_window);
928 XClearWindow(dpy, s->frame_window);
929 XSync(dpy, False);
930
931 rp_draw_string(s, s->frame_window, STYLE_NORMAL,
932 defaults.bar_x_padding,
933 defaults.bar_y_padding + FONT_ASCENT(s),
934 msgbuf->data, msgbuf->len, NULL, NULL);
935
936 sbuf_free(msgbuf);
937}
938
939rp_frame *
940find_frame_up(rp_frame *frame)
941{
942 rp_frame *cur, *winner = NULL;
943 rp_vscreen *v = frame->vscreen;
944 int wingap = 0, curgap;
945
946 list_for_each_entry(cur, &v->frames, node) {
947 if (frame_top(frame) != frame_bottom(cur))
948 continue;
949
950 curgap = abs(frame_left(frame) - frame_left(cur));
951 if (!winner || (curgap < wingap)) {
952 winner = cur;
953 wingap = curgap;
954 }
955 }
956
957 return winner;
958}
959
960rp_frame *
961find_frame_down(rp_frame *frame)
962{
963 rp_frame *cur, *winner = NULL;
964 rp_vscreen *v = frame->vscreen;
965 int wingap = 0, curgap;
966
967 list_for_each_entry(cur, &v->frames, node) {
968 if (frame_bottom(frame) != frame_top(cur))
969 continue;
970
971 curgap = abs(frame_left(frame) - frame_left(cur));
972 if (!winner || (curgap < wingap)) {
973 winner = cur;
974 wingap = curgap;
975 }
976 }
977
978 return winner;
979}
980
981rp_frame *
982find_frame_left(rp_frame *frame)
983{
984 rp_frame *cur, *winner = NULL;
985 rp_vscreen *v = frame->vscreen;
986 int wingap = 0, curgap;
987
988 list_for_each_entry(cur, &v->frames, node) {
989 if (frame_left(frame) != frame_right(cur))
990 continue;
991
992 curgap = abs(frame_top(frame) - frame_top(cur));
993 if (!winner || (curgap < wingap)) {
994 winner = cur;
995 wingap = curgap;
996 }
997 }
998
999 return winner;
1000}
1001
1002rp_frame *
1003find_frame_right(rp_frame *frame)
1004{
1005 rp_frame *cur, *winner = NULL;
1006 rp_vscreen *v = frame->vscreen;
1007 int wingap = 0, curgap;
1008
1009 list_for_each_entry(cur, &v->frames, node) {
1010 if (frame_right(frame) != frame_left(cur))
1011 continue;
1012
1013 curgap = abs(frame_top(frame) - frame_top(cur));
1014 if (!winner || (curgap < wingap)) {
1015 winner = cur;
1016 wingap = curgap;
1017 }
1018 }
1019
1020 return winner;
1021}
1022
1023rp_frame *
1024find_frame_number(rp_vscreen *v, int num)
1025{
1026 rp_frame *cur_frame;
1027
1028 list_for_each_entry(cur_frame, &v->frames, node) {
1029 if (cur_frame->number == num)
1030 return cur_frame;
1031 }
1032
1033 return NULL;
1034}