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