1 /* functions for grabbing information about xrandr screens
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 moify
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 
24 #include <X11/extensions/Xrandr.h>
25 
26 static int xrandr_evbase;
27 
28 #define XRANDR_MAJOR 1
29 #define XRANDR_MINOR 3
30 
31 void
init_xrandr(void)32 init_xrandr (void)
33 {
34   int errbase, major, minor;
35 
36   if (!XRRQueryExtension (dpy, &xrandr_evbase, &errbase)) {
37     return;
38   }
39 
40   if (XRRQueryVersion (dpy, &major, &minor) == 0) {
41     return;
42   }
43 
44   if (major != XRANDR_MAJOR ||
45       (major == XRANDR_MAJOR && minor < XRANDR_MINOR)) {
46     PRINT_ERROR (("Xrandr version %d.%d is not supported\n", major, minor));
47     return;
48   }
49 
50   XRRSelectInput (dpy, RootWindow (dpy, DefaultScreen(dpy)),
51                   RRCrtcChangeNotifyMask | RROutputChangeNotifyMask);
52 
53   rp_have_xrandr = 1;
54 }
55 
56 int *
xrandr_query_screen(int * screen_count)57 xrandr_query_screen (int *screen_count)
58 {
59   XRRScreenResources *res;
60   XRROutputInfo *outinfo;
61   int *output_array;
62   int count = 0;
63   int i;
64 
65   res = XRRGetScreenResources (dpy, RootWindow (dpy, DefaultScreen (dpy)));
66   output_array = xmalloc (res->noutput * sizeof(int));
67 
68   for (i = 0; i < res->noutput; i++) {
69     outinfo = XRRGetOutputInfo (dpy, res, res->outputs[i]);
70     if (!outinfo->crtc)
71       continue;
72 
73     output_array[count] = res->outputs[i];
74     count++;
75 
76     XRRFreeOutputInfo (outinfo);
77   }
78 
79   *screen_count = count;
80   XRRFreeScreenResources (res);
81 
82   return output_array;
83 }
84 
85 static rp_screen *
xrandr_screen_output(int rr_output)86 xrandr_screen_output (int rr_output)
87 {
88   rp_screen *cur;
89 
90   list_for_each_entry (cur, &rp_screens, node)
91     {
92       if (cur->xrandr.output == rr_output)
93         return cur;
94     }
95 
96   return NULL;
97 }
98 
99 static rp_screen *
xrandr_screen_crtc(int rr_crtc)100 xrandr_screen_crtc (int rr_crtc)
101 {
102   rp_screen *cur;
103 
104   list_for_each_entry (cur, &rp_screens, node)
105     {
106       if (cur->xrandr.crtc == rr_crtc)
107         return cur;
108     }
109 
110   return NULL;
111 }
112 
113 int
xrandr_is_primary(rp_screen * screen)114 xrandr_is_primary (rp_screen *screen)
115 {
116   return screen->xrandr.primary;
117 }
118 
119 void
xrandr_fill_screen(int rr_output,rp_screen * screen)120 xrandr_fill_screen (int rr_output, rp_screen *screen)
121 {
122   XRRScreenResources *res;
123   XRROutputInfo *outinfo;
124   XRRCrtcInfo *crtinfo;
125   RROutput primary;
126 
127   res = XRRGetScreenResourcesCurrent (dpy, RootWindow (dpy, DefaultScreen (dpy)));
128   outinfo = XRRGetOutputInfo (dpy, res, rr_output);
129   if (!outinfo->crtc)
130     goto free_res;
131 
132   crtinfo = XRRGetCrtcInfo (dpy, res, outinfo->crtc);
133   if (!crtinfo)
134     goto free_out;
135 
136   primary = XRRGetOutputPrimary (dpy, RootWindow (dpy, DefaultScreen (dpy)));
137   if (rr_output == primary)
138     screen->xrandr.primary = 1;
139   else
140     screen->xrandr.primary = 0;
141 
142   screen->xrandr.name = sbuf_new (0);
143   sbuf_concat (screen->xrandr.name, outinfo->name);
144 
145   screen->xrandr.output  = rr_output;
146   screen->xrandr.crtc    = outinfo->crtc;
147 
148   screen->left   = crtinfo->x;
149   screen->top    = crtinfo->y;
150   screen->width  = crtinfo->width;
151   screen->height = crtinfo->height;
152 
153   XRRFreeCrtcInfo (crtinfo);
154  free_out:
155   XRRFreeOutputInfo (outinfo);
156  free_res:
157   XRRFreeScreenResources (res);
158 }
159 
160 static void
xrandr_output_change(XRROutputChangeNotifyEvent * ev)161 xrandr_output_change (XRROutputChangeNotifyEvent *ev)
162 {
163   XRRScreenResources *res;
164   XRROutputInfo *outinfo;
165   rp_screen *screen;
166 
167   res = XRRGetScreenResourcesCurrent (dpy, RootWindow (dpy, DefaultScreen (dpy)));
168   outinfo = XRRGetOutputInfo (dpy, res, ev->output);
169 
170   screen = xrandr_screen_output (ev->output);
171 
172   if (!screen && outinfo->crtc) {
173     screen = screen_add (ev->output);
174     screen_sort ();
175     PRINT_DEBUG (("%s: Added screen %s with crtc %lu\n", __func__,
176                   sbuf_get (screen->xrandr.name),
177                   (unsigned long)outinfo->crtc));
178   } else if (screen && !outinfo->crtc) {
179     PRINT_DEBUG (("%s: Removing screen %s\n", __func__,
180                   sbuf_get (screen->xrandr.name)));
181     screen_del (screen);
182   }
183 
184   XRRFreeOutputInfo (outinfo);
185   XRRFreeScreenResources (res);
186 }
187 
188 #ifdef DEBUG
189 static const char *
xrandr_rotation_string(Rotation r)190 xrandr_rotation_string (Rotation r)
191 {
192   static char buf[64];
193 
194 #define CASE(c) case c : return #c
195   switch (r)
196     {
197       CASE(RR_Rotate_0);
198       CASE(RR_Rotate_90);
199       CASE(RR_Rotate_180);
200       CASE(RR_Rotate_270);
201 #undef CASE
202     default:
203       snprintf(buf, sizeof buf, "Unknown rotation %hu", (unsigned short)r);
204       return buf;
205   }
206 }
207 #endif
208 
209 static void
xrandr_crtc_change(XRRCrtcChangeNotifyEvent * ev)210 xrandr_crtc_change (XRRCrtcChangeNotifyEvent *ev)
211 {
212   rp_screen *screen;
213 
214   if (!ev->crtc || !ev->width || !ev->height)
215     return;
216 
217   screen = xrandr_screen_crtc (ev->crtc);
218 
219   PRINT_DEBUG (("%s: crtc %s, rotation %s "
220                 "ev->x %d, ev->y %d, ev->width %d, ev->height %d\n",
221                 __func__, screen ? "found" : "not found",
222                 xrandr_rotation_string (ev->rotation),
223                 ev->x, ev->y, ev->width, ev->height));
224 
225   if (!screen)
226     return;
227 
228   if (ev->rotation == RR_Rotate_90 || ev->rotation == RR_Rotate_270)
229     screen_update (screen, ev->x, ev->y, ev->height, ev->width);
230   else
231     screen_update (screen, ev->x, ev->y, ev->width, ev->height);
232 }
233 
234 void
xrandr_notify(XEvent * ev)235 xrandr_notify (XEvent *ev)
236 {
237   XRRNotifyEvent *n_event;
238   XRROutputChangeNotifyEvent *o_event;
239   XRRCrtcChangeNotifyEvent *c_event;
240 
241   if (ev->type != xrandr_evbase + RRNotify)
242     return;
243 
244   PRINT_DEBUG (("--- Handling RRNotify ---\n"));
245 
246   n_event = (XRRNotifyEvent *)ev;
247   switch (n_event->subtype) {
248   case RRNotify_OutputChange:
249     PRINT_DEBUG (("---          XRROutputChangeNotifyEvent ---\n"));
250     o_event = (XRROutputChangeNotifyEvent *)ev;
251     xrandr_output_change (o_event);
252     break;
253   case RRNotify_CrtcChange:
254     PRINT_DEBUG (("---          XRRCrtcChangeNotifyEvent ---\n"));
255     c_event = (XRRCrtcChangeNotifyEvent *)ev;
256     xrandr_crtc_change (c_event);
257     break;
258   case RRNotify_OutputProperty:
259     PRINT_DEBUG (("---          RRNotify_OutputProperty ---\n"));
260     break;
261   default:
262     PRINT_DEBUG (("---          Unknown subtype %d ---\n", n_event->subtype));
263     break;
264   }
265 }
266