1 /* Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts <sabetts@vcn.bc.ca>
2 * Copyright (C) 2016 Mathieu OTHACEHE <m.othacehe@gmail.com>
3 *
4 * This file is part of ratpoison.
5 *
6 * ratpoison is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * ratpoison is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this software; see the file COPYING. If not, write to
18 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19 * Boston, MA 02111-1307 USA
20 */
21
22 #include "ratpoison.h"
23 #include <string.h>
24 #include <X11/cursorfont.h>
25
26 static void init_screen (rp_screen *s);
27
28 int
screen_width(rp_screen * s)29 screen_width (rp_screen *s)
30 {
31 return s->width - defaults.padding_right - defaults.padding_left;
32 }
33
34 int
screen_height(rp_screen * s)35 screen_height (rp_screen *s)
36 {
37 return s->height - defaults.padding_bottom - defaults.padding_top;
38 }
39
40 int
screen_left(rp_screen * s)41 screen_left (rp_screen *s)
42 {
43 return s->left + defaults.padding_left;
44 }
45
46 int
screen_right(rp_screen * s)47 screen_right (rp_screen *s)
48 {
49 return screen_left (s) + screen_width (s);
50 }
51
52 int
screen_top(rp_screen * s)53 screen_top (rp_screen *s)
54 {
55 return s->top + defaults.padding_top;
56 }
57
58 int
screen_bottom(rp_screen * s)59 screen_bottom (rp_screen *s)
60 {
61 return screen_top (s) + screen_height (s);
62 }
63
64 /* Returns a pointer to a list of frames. */
65 struct list_head *
screen_copy_frameset(rp_screen * s)66 screen_copy_frameset (rp_screen *s)
67 {
68 struct list_head *head;
69 rp_frame *cur;
70
71 /* Init our new list. */
72 head = xmalloc (sizeof (struct list_head));
73 INIT_LIST_HEAD (head);
74
75 /* Copy each frame to our new list. */
76 list_for_each_entry (cur, &s->frames, node)
77 {
78 list_add_tail (&(frame_copy (cur))->node, head);
79 }
80
81 return head;
82 }
83
84 /* Set head as the frameset, deleting the existing one. */
85 void
screen_restore_frameset(rp_screen * s,struct list_head * head)86 screen_restore_frameset (rp_screen *s, struct list_head *head)
87 {
88 frameset_free (&s->frames);
89 INIT_LIST_HEAD (&s->frames);
90
91 /* Hook in our new frameset. */
92 list_splice (head, &s->frames);
93 }
94
95
96 /* Given a screen, free the frames' numbers from the numset. */
97 void
screen_free_nums(rp_screen * s)98 screen_free_nums (rp_screen *s)
99 {
100 rp_frame *cur;
101
102 list_for_each_entry (cur, &s->frames, node)
103 {
104 numset_release (s->frames_numset, cur->number);
105 }
106 }
107
108 /* Given a list of frames, free them, but don't remove their numbers
109 from the numset. */
110 void
frameset_free(struct list_head * head)111 frameset_free (struct list_head *head)
112 {
113 rp_frame *frame;
114 struct list_head *iter, *tmp;
115
116 list_for_each_safe_entry (frame, iter, tmp, head, node)
117 {
118 /* FIXME: what if frames has memory inside its struct
119 that needs to be freed? */
120 free (frame);
121 }
122 }
123
124 rp_frame *
screen_get_frame(rp_screen * s,int frame_num)125 screen_get_frame (rp_screen *s, int frame_num)
126 {
127 rp_frame *cur;
128
129 list_for_each_entry (cur, &s->frames, node)
130 {
131 if (cur->number == frame_num)
132 return cur;
133 }
134
135 return NULL;
136 }
137
138 rp_frame *
screen_find_frame_by_frame(rp_screen * s,rp_frame * f)139 screen_find_frame_by_frame (rp_screen *s, rp_frame *f)
140 {
141 rp_frame *cur;
142
143 list_for_each_entry (cur, &s->frames, node)
144 {
145 PRINT_DEBUG (("cur=%p f=%p\n", cur, f));
146 if (cur == f)
147 return cur;
148 }
149
150 return NULL;
151 }
152
153 /* Given a root window, return the rp_screen struct */
154 rp_screen *
find_screen(Window w)155 find_screen (Window w)
156 {
157 rp_screen *cur;
158
159 list_for_each_entry (cur, &rp_screens, node)
160 {
161 if (cur->root == w)
162 return cur;
163 }
164
165 return NULL;
166 }
167
168 /* Given a window attr, return the rp_screen struct */
169 rp_screen *
find_screen_by_attr(XWindowAttributes attr)170 find_screen_by_attr (XWindowAttributes attr)
171 {
172 rp_screen *cur;
173
174 list_for_each_entry (cur, &rp_screens, node)
175 {
176 if (attr.x >= cur->left &&
177 attr.x <= cur->left + cur->width &&
178 attr.y >= cur->top &&
179 attr.y <= cur->top + cur->height)
180 return cur;
181 }
182
183 return NULL;
184 }
185
186 /* Return 1 if w is a root window of any of the screens. */
187 int
is_a_root_window(unsigned int w)188 is_a_root_window (unsigned int w)
189 {
190 rp_screen *cur;
191
192 list_for_each_entry (cur, &rp_screens, node)
193 {
194 if (cur->root == w)
195 return 1;
196 }
197
198 return 0;
199 }
200
201 rp_screen *
screen_number(int number)202 screen_number (int number)
203 {
204 rp_screen *cur;
205
206 list_for_each_entry (cur, &rp_screens, node)
207 {
208 if (cur->number == number)
209 return cur;
210 }
211
212 return NULL;
213 }
214
215 static int
screen_cmp(void * priv UNUSED,struct list_head * a,struct list_head * b)216 screen_cmp (void *priv UNUSED, struct list_head *a, struct list_head *b)
217 {
218 rp_screen *sc_a = container_of (a, typeof(*sc_a), node);
219 rp_screen *sc_b = container_of (b, typeof(*sc_b), node);
220
221 if (sc_a->left < sc_b->left)
222 return -1;
223 if (sc_a->left > sc_b->left)
224 return 1;
225
226 if (sc_a->top > sc_b->top)
227 return -1;
228 if (sc_a->top < sc_b->top)
229 return 1;
230
231 return 0;
232 }
233
234 void
screen_sort(void)235 screen_sort (void)
236 {
237 return list_sort (NULL, &rp_screens, screen_cmp);
238 }
239
240 static void
screen_set_numbers(void)241 screen_set_numbers (void)
242 {
243 rp_screen *cur;
244
245 list_for_each_entry (cur, &rp_screens, node)
246 {
247 cur->number = numset_request (rp_glob_screen.numset);
248 }
249 }
250
251 static void
screen_select_primary(void)252 screen_select_primary (void)
253 {
254 rp_screen *cur;
255
256 /* By default, take the first screen as current screen */
257 list_first(cur, &rp_screens, node);
258 if (!rp_current_screen)
259 rp_current_screen = cur;
260
261 #ifdef HAVE_XRANDR
262 if (!rp_have_xrandr)
263 return;
264
265 list_for_each_entry (cur, &rp_screens, node)
266 {
267 if (xrandr_is_primary(cur)) {
268 rp_current_screen = cur;
269 PRINT_DEBUG(("Xrandr primary screen %d detected\n",
270 rp_current_screen->number));
271 break;
272 }
273 }
274 #endif
275 }
276
277 static void
init_global_screen(rp_global_screen * s)278 init_global_screen (rp_global_screen *s)
279 {
280 int screen_num;
281
282 screen_num = DefaultScreen (dpy);
283 s->root = RootWindow (dpy, screen_num);
284
285 s->numset = numset_new ();
286 s->fg_color = BlackPixel (dpy, screen_num);
287 s->bg_color = WhitePixel (dpy, screen_num);
288 s->fw_color = BlackPixel (dpy, screen_num);
289 s->bw_color = BlackPixel (dpy, screen_num);
290 }
291
292 void
init_screens(void)293 init_screens (void)
294 {
295 int i;
296 int screen_count = 0;
297 int *rr_outputs = NULL;
298 rp_screen *screen;
299
300 /* Get the number of screens */
301 if (rp_have_xrandr) {
302 #ifdef HAVE_XRANDR
303 rr_outputs = xrandr_query_screen (&screen_count);
304 #endif
305 } else {
306 screen_count = ScreenCount (dpy);
307 }
308
309 /* Create our global frame numset */
310 rp_frame_numset = numset_new();
311
312 init_global_screen (&rp_glob_screen);
313
314 for (i = 0; i < screen_count; i++)
315 {
316 screen = xmalloc (sizeof(*screen));
317 list_add (&screen->node, &rp_screens);
318
319 #ifdef HAVE_XRANDR
320 if (rp_have_xrandr)
321 xrandr_fill_screen (rr_outputs[i], screen);
322 #endif
323 init_screen (screen);
324 }
325
326 screen_sort ();
327 screen_set_numbers ();
328 screen_select_primary ();
329
330 free (rr_outputs);
331 }
332
333 static void
init_rat_cursor(rp_screen * s)334 init_rat_cursor (rp_screen *s)
335 {
336 s->rat = XCreateFontCursor (dpy, XC_icon);
337 }
338
339 static void
init_screen(rp_screen * s)340 init_screen (rp_screen *s)
341 {
342 XGCValues gcv;
343 struct sbuf *buf;
344 char *colon;
345 int screen_num;
346
347 screen_num = DefaultScreen (dpy);
348
349 if (!rp_have_xrandr)
350 {
351 s->left = 0;
352 s->top = 0;
353 s->width = DisplayWidth (dpy, screen_num);
354 s->height = DisplayHeight (dpy, screen_num);
355 }
356
357 /* Select on some events on the root window, if this fails, then
358 there is already a WM running and the X Error handler will catch
359 it, terminating ratpoison. */
360 XSelectInput (dpy, RootWindow (dpy, screen_num),
361 PropertyChangeMask | ColormapChangeMask
362 | SubstructureRedirectMask | SubstructureNotifyMask
363 | StructureNotifyMask);
364 XSync (dpy, False);
365
366 /* Set the numset for the frames to our global numset. */
367 s->frames_numset = rp_frame_numset;
368
369 s->scratch_buffer = NULL;
370
371 /* Build the display string for each screen */
372 buf = sbuf_new (0);
373 sbuf_printf (buf, "DISPLAY=%s", DisplayString (dpy));
374 colon = strrchr (sbuf_get (buf), ':');
375 if (colon)
376 {
377 char *dot;
378
379 dot = strrchr (sbuf_get (buf), '.');
380 if (!dot || dot < colon)
381 {
382 /* no dot was found or it belongs to fqdn - append screen_num
383 to the end */
384 sbuf_printf_concat (buf, ".%d", screen_num);
385 }
386 }
387 s->display_string = sbuf_free_struct (buf);
388
389 PRINT_DEBUG (("display string: %s\n", s->display_string));
390
391 s->root = RootWindow (dpy, screen_num);
392 s->screen_num = screen_num;
393 s->def_cmap = DefaultColormap (dpy, screen_num);
394
395 init_rat_cursor (s);
396
397 /* Setup the GC for drawing the font. */
398 gcv.foreground = rp_glob_screen.fg_color;
399 gcv.background = rp_glob_screen.bg_color;
400 gcv.function = GXcopy;
401 gcv.line_width = 1;
402 gcv.subwindow_mode = IncludeInferiors;
403 s->normal_gc = XCreateGC(dpy, s->root,
404 GCForeground | GCBackground | GCFunction
405 | GCLineWidth | GCSubwindowMode,
406 &gcv);
407 gcv.foreground = rp_glob_screen.bg_color;
408 gcv.background = rp_glob_screen.fg_color;
409 s->inverse_gc = XCreateGC(dpy, s->root,
410 GCForeground | GCBackground | GCFunction
411 | GCLineWidth | GCSubwindowMode,
412 &gcv);
413
414 /* Create the program bar window. */
415 s->bar_is_raised = 0;
416 s->bar_window = XCreateSimpleWindow (dpy, s->root, 0, 0, 1, 1,
417 defaults.bar_border_width,
418 rp_glob_screen.fg_color, rp_glob_screen.bg_color);
419
420 /* Setup the window that will receive all keystrokes once the prefix
421 key has been pressed. */
422 s->key_window = XCreateSimpleWindow (dpy, s->root, 0, 0, 1, 1, 0,
423 WhitePixel (dpy, screen_num),
424 BlackPixel (dpy, screen_num));
425 XSelectInput (dpy, s->key_window, KeyPressMask | KeyReleaseMask);
426
427 /* Create the input window. */
428 s->input_window = XCreateSimpleWindow (dpy, s->root, 0, 0, 1, 1,
429 defaults.bar_border_width,
430 rp_glob_screen.fg_color, rp_glob_screen.bg_color);
431 XSelectInput (dpy, s->input_window, KeyPressMask | KeyReleaseMask);
432
433 /* Create the frame indicator window */
434 s->frame_window = XCreateSimpleWindow (dpy, s->root, 1, 1, 1, 1, defaults.bar_border_width,
435 rp_glob_screen.fg_color, rp_glob_screen.bg_color);
436
437 /* Create the help window */
438 s->help_window = XCreateSimpleWindow (dpy, s->root, s->left, s->top, s->width,
439 s->height, 0, rp_glob_screen.fg_color, rp_glob_screen.bg_color);
440 XSelectInput (dpy, s->help_window, KeyPressMask);
441
442 activate_screen(s);
443
444 XSync (dpy, 0);
445
446 #ifdef USE_XFT_FONT
447 {
448 s->xft_font = XftFontOpenName (dpy, screen_num, DEFAULT_XFT_FONT);
449 if (!s->xft_font)
450 {
451 PRINT_ERROR(("Failed to open font\n"));
452 }
453 else
454 {
455 if (!XftColorAllocName (dpy, DefaultVisual (dpy, screen_num),
456 DefaultColormap (dpy, screen_num),
457 defaults.fgcolor_string, &s->xft_fg_color))
458 {
459 PRINT_ERROR(("Failed to allocate font fg color\n"));
460 XftFontClose (dpy, s->xft_font);
461 s->xft_font = NULL;
462 }
463 if (!XftColorAllocName (dpy, DefaultVisual (dpy, screen_num),
464 DefaultColormap (dpy, screen_num),
465 defaults.bgcolor_string, &s->xft_bg_color))
466 {
467 PRINT_ERROR(("Failed to allocate font fg color\n"));
468 XftFontClose (dpy, s->xft_font);
469 s->xft_font = NULL;
470 }
471 }
472 }
473 #endif
474 }
475
476 void
activate_screen(rp_screen * s)477 activate_screen (rp_screen *s)
478 {
479 /* Add netwm support. FIXME: I think this is busted. */
480 XChangeProperty (dpy, RootWindow (dpy, s->screen_num),
481 _net_supported, XA_ATOM, 32, PropModeReplace,
482 (unsigned char*)&_net_wm_pid, 1);
483
484 /* set window manager name */
485 XChangeProperty (dpy, RootWindow (dpy, s->screen_num),
486 _net_wm_name, xa_utf8_string, 8, PropModeReplace,
487 (unsigned char*)"ratpoison", 9);
488 XMapWindow (dpy, s->key_window);
489 }
490
491 void
deactivate_screen(rp_screen * s)492 deactivate_screen (rp_screen *s)
493 {
494 /* Unmap its key window */
495 XUnmapWindow (dpy, s->key_window);
496
497 /* delete everything so noone sees them while we are not there */
498 XDeleteProperty (dpy, RootWindow (dpy, s->screen_num),
499 _net_supported);
500 XDeleteProperty (dpy, RootWindow (dpy, s->screen_num),
501 _net_wm_name);
502 }
503
504 static int
is_rp_window_for_given_screen(Window w,rp_screen * s)505 is_rp_window_for_given_screen (Window w, rp_screen *s)
506 {
507 if (w != s->key_window &&
508 w != s->bar_window &&
509 w != s->input_window &&
510 w != s->frame_window &&
511 w != s->help_window)
512 return 0;
513 return 1;
514 }
515
516 int
is_rp_window(Window w)517 is_rp_window (Window w)
518 {
519 rp_screen *cur;
520
521 list_for_each_entry (cur, &rp_screens, node)
522 {
523 if (is_rp_window_for_given_screen (w, cur))
524 return 1;
525 }
526
527 return 0;
528 }
529
530 char *
screen_dump(rp_screen * screen)531 screen_dump (rp_screen *screen)
532 {
533 char *tmp;
534 struct sbuf *s;
535
536 s = sbuf_new (0);
537 if (rp_have_xrandr)
538 sbuf_printf(s, "%s ", sbuf_get (screen->xrandr.name));
539
540 sbuf_printf_concat (s, "%d %d %d %d %d %d",
541 screen->number,
542 screen->left,
543 screen->top,
544 screen->width,
545 screen->height,
546 (rp_current_screen == screen)?1:0 /* is current? */
547 );
548
549 /* Extract the string and return it, and don't forget to free s. */
550 tmp = sbuf_get (s);
551 free (s);
552 return tmp;
553 }
554
555 int
screen_count(void)556 screen_count (void)
557 {
558 return list_size (&rp_screens);
559 }
560
561 rp_screen *
screen_next(void)562 screen_next (void)
563 {
564 return list_next_entry (rp_current_screen, &rp_screens, node);
565 }
566
567 rp_screen *
screen_prev(void)568 screen_prev (void)
569 {
570 return list_prev_entry (rp_current_screen, &rp_screens, node);
571 }
572
573 static void
screen_remove_current(void)574 screen_remove_current (void)
575 {
576 rp_screen *new_screen;
577 rp_frame *new_frame;
578 rp_window *cur_win;
579 int cur_frame;
580
581 cur_win = current_window ();
582 new_screen = screen_next ();
583
584 cur_frame = new_screen->current_frame;
585 new_frame = screen_get_frame (new_screen, cur_frame);
586
587 set_active_frame (new_frame, 1);
588
589 hide_window (cur_win);
590 }
591
592 void
screen_update(rp_screen * s,int left,int top,int width,int height)593 screen_update (rp_screen *s, int left, int top, int width, int height)
594 {
595 rp_frame *f;
596 int oldwidth, oldheight;
597
598 PRINT_DEBUG (("screen_update (left=%d, top=%d, width=%d, height=%d)\n",
599 left, top, width, height));
600
601 if (s->width == width &&
602 s->height == height &&
603 s->left == left &&
604 s->top == top)
605 /* nothing to do */
606 return;
607
608 oldwidth = s->width;
609 oldheight = s->height;
610
611 s->left = left;
612 s->top = top;
613 s->width = width;
614 s->height = height;
615
616 XMoveResizeWindow (dpy, s->help_window, s->left, s->top, s->width, s->height);
617
618 list_for_each_entry (f, &s->frames, node)
619 {
620 f->x = (f->x*width)/oldwidth;
621 f->width = (f->width*width)/oldwidth;
622 f->y = (f->y*height)/oldheight;
623 f->height = (f->height*height)/oldheight;
624 maximize_all_windows_in_frame (f);
625 }
626 }
627
628 rp_screen *
screen_add(int rr_output)629 screen_add (int rr_output)
630 {
631 rp_screen *screen;
632
633 screen = xmalloc (sizeof(*screen));
634 list_add (&screen->node, &rp_screens);
635
636 screen->number = numset_request (rp_glob_screen.numset);
637
638 #ifdef HAVE_XRANDR
639 if (rp_have_xrandr)
640 xrandr_fill_screen (rr_output, screen);
641 #endif
642 init_screen (screen);
643 init_frame_list (screen);
644
645 if (screen_count () == 1)
646 {
647 rp_current_screen = screen;
648 change_windows_screen (NULL, rp_current_screen);
649 set_window_focus (rp_current_screen->key_window);
650 }
651
652 return screen;
653 }
654
655 void
screen_del(rp_screen * s)656 screen_del (rp_screen *s)
657 {
658 if (s == rp_current_screen)
659 {
660 if (screen_count () == 1)
661 {
662 hide_screen_windows (s);
663 rp_current_screen = NULL;
664 }
665 else
666 {
667 /*
668 * The deleted screen cannot be the current screen anymore,
669 * focus the next one.
670 */
671 screen_remove_current ();
672 }
673 }
674 else
675 {
676 hide_screen_windows (s);
677 }
678
679 /* Affect window's screen backpointer to the new current screen */
680 change_windows_screen (s, rp_current_screen);
681
682 numset_release (rp_glob_screen.numset, s->number);
683
684 screen_free (s);
685
686 list_del (&s->node);
687 free (s);
688 }
689
690 void
screen_free(rp_screen * s)691 screen_free (rp_screen *s)
692 {
693 rp_frame *frame;
694 struct list_head *iter, *tmp;
695
696 list_for_each_safe_entry (frame, iter, tmp, &s->frames, node)
697 {
698 frame_free (s, frame);
699 }
700
701 deactivate_screen(s);
702
703 XDestroyWindow (dpy, s->bar_window);
704 XDestroyWindow (dpy, s->key_window);
705 XDestroyWindow (dpy, s->input_window);
706 XDestroyWindow (dpy, s->frame_window);
707 XDestroyWindow (dpy, s->help_window);
708
709 #ifdef USE_XFT_FONT
710 if (s->xft_font)
711 {
712 XftColorFree (dpy, DefaultVisual (dpy, s->screen_num),
713 DefaultColormap (dpy, s->screen_num), &s->xft_fg_color);
714 XftColorFree (dpy, DefaultVisual (dpy, s->screen_num),
715 DefaultColormap (dpy, s->screen_num), &s->xft_bg_color);
716 XftFontClose (dpy, s->xft_font);
717 }
718 #endif
719
720 XFreeCursor (dpy, s->rat);
721 XFreeColormap (dpy, s->def_cmap);
722 XFreeGC (dpy, s->normal_gc);
723 XFreeGC (dpy, s->inverse_gc);
724
725 free (s->display_string);
726
727 if (rp_have_xrandr)
728 free (s->xrandr.name);
729 }
730
731 void
screen_free_final(void)732 screen_free_final (void)
733 {
734 /* Relinquish our hold on the root window. */
735 XSelectInput(dpy, RootWindow (dpy, DefaultScreen (dpy)), 0);
736
737 numset_free (rp_glob_screen.numset);
738 }
739