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