1 /*
2 Copyright (C) 2002-2010 Karl J. Runge <runge@karlrunge.com>
3 All rights reserved.
4
5 This file is part of x11vnc.
6
7 x11vnc is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or (at
10 your option) any later version.
11
12 x11vnc is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with x11vnc; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
20 or see <http://www.gnu.org/licenses/>.
21
22 In addition, as a special exception, Karl J. Runge
23 gives permission to link the code of its release of x11vnc with the
24 OpenSSL project's "OpenSSL" library (or with modified versions of it
25 that use the same license as the "OpenSSL" library), and distribute
26 the linked executables. You must obey the GNU General Public License
27 in all respects for all of the code used other than "OpenSSL". If you
28 modify this file, you may extend this exception to your version of the
29 file, but you are not obligated to do so. If you do not wish to do
30 so, delete this exception statement from your version.
31 */
32
33 /* -- xrandr.c -- */
34
35 #include "x11vnc.h"
36 #include "cleanup.h"
37 #include "connections.h"
38 #include "remote.h"
39 #include "screen.h"
40 #include "win_utils.h"
41
42 time_t last_subwin_trap = 0;
43 int subwin_trap_count = 0;
44 XErrorHandler old_getimage_handler;
45
46 int xrandr_present = 0;
47 int xrandr_width = -1;
48 int xrandr_height = -1;
49 int xrandr_rotation = -1;
50 Time xrandr_timestamp = 0;
51 Time xrandr_cfg_time = 0;
52
53 void initialize_xrandr(void);
54 int check_xrandr_event(char *msg);
55 int known_xrandr_mode(char *s);
56
57 static int handle_subwin_resize(char *msg);
58 static void handle_xrandr_change(int new_x, int new_y);
59
60
initialize_xrandr(void)61 void initialize_xrandr(void) {
62 if (xrandr_present && dpy) {
63 #if LIBVNCSERVER_HAVE_LIBXRANDR
64 Rotation rot;
65
66 X_LOCK;
67 xrandr_width = XDisplayWidth(dpy, scr);
68 xrandr_height = XDisplayHeight(dpy, scr);
69 XRRRotations(dpy, scr, &rot);
70 xrandr_rotation = (int) rot;
71 if (xrandr || xrandr_maybe) {
72 XRRSelectInput(dpy, rootwin, RRScreenChangeNotifyMask);
73 } else {
74 XRRSelectInput(dpy, rootwin, 0);
75 }
76 X_UNLOCK;
77 #endif
78 } else if (xrandr) {
79 rfbLog("-xrandr mode specified, but no RANDR support on\n");
80 rfbLog(" display or in client library. Disabling -xrandr "
81 "mode.\n");
82 xrandr = 0;
83 }
84 }
85
handle_subwin_resize(char * msg)86 static int handle_subwin_resize(char *msg) {
87 int new_x, new_y;
88 int i, check = 10, ms = 250; /* 2.5 secs total... */
89
90 if (msg) {} /* unused vars warning: */
91 if (! subwin) {
92 return 0; /* hmmm... */
93 }
94 if (! valid_window(subwin, NULL, 0)) {
95 rfbLogEnable(1);
96 rfbLog("subwin 0x%lx went away!\n", subwin);
97 X_UNLOCK;
98 clean_up_exit(1);
99 }
100 if (! get_window_size(subwin, &new_x, &new_y)) {
101 rfbLogEnable(1);
102 rfbLog("could not get size of subwin 0x%lx\n", subwin);
103 X_UNLOCK;
104 clean_up_exit(1);
105 }
106 if (wdpy_x == new_x && wdpy_y == new_y) {
107 /* no change */
108 return 0;
109 }
110
111 /* window may still be changing (e.g. drag resize) */
112 for (i=0; i < check; i++) {
113 int newer_x, newer_y;
114 usleep(ms * 1000);
115
116 if (! get_window_size(subwin, &newer_x, &newer_y)) {
117 rfbLogEnable(1);
118 rfbLog("could not get size of subwin 0x%lx\n", subwin);
119 clean_up_exit(1);
120 }
121 if (new_x == newer_x && new_y == newer_y) {
122 /* go for it... */
123 break;
124 } else {
125 rfbLog("subwin 0x%lx still changing size...\n", subwin);
126 new_x = newer_x;
127 new_y = newer_y;
128 }
129 }
130
131 rfbLog("subwin 0x%lx new size: x: %d -> %d, y: %d -> %d\n",
132 subwin, wdpy_x, new_x, wdpy_y, new_y);
133 rfbLog("calling handle_xrandr_change() for resizing\n");
134
135 X_UNLOCK;
136 handle_xrandr_change(new_x, new_y);
137 return 1;
138 }
139
handle_xrandr_change(int new_x,int new_y)140 static void handle_xrandr_change(int new_x, int new_y) {
141 rfbClientIteratorPtr iter;
142 rfbClientPtr cl;
143
144 RAWFB_RET_VOID
145
146 /* assumes no X_LOCK */
147
148 /* sanity check xrandr_mode */
149 if (! xrandr_mode) {
150 xrandr_mode = strdup("default");
151 } else if (! known_xrandr_mode(xrandr_mode)) {
152 free(xrandr_mode);
153 xrandr_mode = strdup("default");
154 }
155 rfbLog("xrandr_mode: %s\n", xrandr_mode);
156 if (!strcmp(xrandr_mode, "exit")) {
157 close_all_clients();
158 rfbLog(" shutting down due to XRANDR event.\n");
159 clean_up_exit(0);
160 }
161 if (!strcmp(xrandr_mode, "newfbsize") && screen) {
162 iter = rfbGetClientIterator(screen);
163 while( (cl = rfbClientIteratorNext(iter)) ) {
164 if (cl->useNewFBSize) {
165 continue;
166 }
167 rfbLog(" closing client %s (no useNewFBSize"
168 " support).\n", cl->host);
169 rfbCloseClient(cl);
170 rfbClientConnectionGone(cl);
171 }
172 rfbReleaseClientIterator(iter);
173 }
174
175 /* default, resize, and newfbsize create a new fb: */
176 rfbLog("check_xrandr_event: trying to create new framebuffer...\n");
177 if (new_x < wdpy_x || new_y < wdpy_y) {
178 check_black_fb();
179 }
180 do_new_fb(1);
181 rfbLog("check_xrandr_event: fb WxH: %dx%d\n", wdpy_x, wdpy_y);
182 }
183
check_xrandr_event(char * msg)184 int check_xrandr_event(char *msg) {
185 XEvent xev;
186
187 RAWFB_RET(0)
188
189 /* it is assumed that X_LOCK is on at this point. */
190
191 if (subwin) {
192 return handle_subwin_resize(msg);
193 }
194 #if LIBVNCSERVER_HAVE_LIBXRANDR
195 if (! xrandr_present) {
196 return 0;
197 }
198 if (! xrandr && ! xrandr_maybe) {
199 return 0;
200 }
201
202
203 if (xrandr_base_event_type && XCheckTypedEvent(dpy,
204 xrandr_base_event_type + RRScreenChangeNotify, &xev)) {
205 int do_change, qout = 0;
206 static int first = 1;
207 XRRScreenChangeNotifyEvent *rev;
208
209 rev = (XRRScreenChangeNotifyEvent *) &xev;
210
211 if (first && ! xrandr) {
212 fprintf(stderr, "\n");
213 if (getenv("X11VNC_DEBUG_XRANDR") == NULL) {
214 qout = 1;
215 }
216 }
217 first = 0;
218
219 rfbLog("check_xrandr_event():\n");
220 rfbLog("Detected XRANDR event at location '%s':\n", msg);
221
222 if (qout) {
223 ;
224 } else {
225 rfbLog(" serial: %d\n", (int) rev->serial);
226 rfbLog(" timestamp: %d\n", (int) rev->timestamp);
227 rfbLog(" cfg_timestamp: %d\n", (int) rev->config_timestamp);
228 rfbLog(" size_id: %d\n", (int) rev->size_index);
229 rfbLog(" sub_pixel: %d\n", (int) rev->subpixel_order);
230 rfbLog(" rotation: %d\n", (int) rev->rotation);
231 rfbLog(" width: %d\n", (int) rev->width);
232 rfbLog(" height: %d\n", (int) rev->height);
233 rfbLog(" mwidth: %d mm\n", (int) rev->mwidth);
234 rfbLog(" mheight: %d mm\n", (int) rev->mheight);
235 rfbLog("\n");
236 rfbLog("check_xrandr_event: previous WxH: %dx%d\n",
237 wdpy_x, wdpy_y);
238 }
239
240 if (wdpy_x == rev->width && wdpy_y == rev->height &&
241 xrandr_rotation == (int) rev->rotation) {
242 rfbLog("check_xrandr_event: no change detected.\n");
243 do_change = 0;
244 if (! xrandr) {
245 rfbLog("check_xrandr_event: "
246 "enabling full XRANDR trapping anyway.\n");
247 xrandr = 1;
248 }
249 } else {
250 do_change = 1;
251 if (! xrandr) {
252 rfbLog("check_xrandr_event: Resize; "
253 "enabling full XRANDR trapping.\n");
254 xrandr = 1;
255 }
256 }
257
258 xrandr_width = rev->width;
259 xrandr_height = rev->height;
260 xrandr_timestamp = rev->timestamp;
261 xrandr_cfg_time = rev->config_timestamp;
262 xrandr_rotation = (int) rev->rotation;
263
264 if (! qout) rfbLog("check_xrandr_event: updating config...\n");
265 XRRUpdateConfiguration(&xev);
266
267 if (do_change) {
268 /* under do_change caller normally returns before its X_UNLOCK */
269 X_UNLOCK;
270 handle_xrandr_change(rev->width, rev->height);
271 }
272 if (qout) {
273 return do_change;
274 }
275 rfbLog("check_xrandr_event: current WxH: %dx%d\n",
276 XDisplayWidth(dpy, scr), XDisplayHeight(dpy, scr));
277 rfbLog("check_xrandr_event(): returning control to"
278 " caller...\n");
279
280
281 return do_change;
282 }
283 #else
284 xev.type = 0;
285 #endif
286
287
288 return 0;
289 }
290
known_xrandr_mode(char * s)291 int known_xrandr_mode(char *s) {
292 /*
293 * default:
294 * resize: the default
295 * exit: shutdown clients and exit.
296 * newfbsize: shutdown clients that do not support NewFBSize encoding.
297 */
298 if (strcmp(s, "default") && strcmp(s, "resize") &&
299 strcmp(s, "exit") && strcmp(s, "newfbsize")) {
300 return 0;
301 } else {
302 return 1;
303 }
304 }
305
306
307