randr: Properly update things if the number of monitors changes
[vd_agent/hramrach.git] / src / vdagent-x11-randr.c
blob6a9a4ed993023023998fc7999a9c9143f4e79be9
1 /* vdagent-x11-randr.c vdagent Xrandr integration code
3 Copyright 2012 Red Hat, Inc.
5 Red Hat Authors:
6 Alon Levy <alevy@redhat.com>
7 Hans de Goede <hdegoede@redhat.com>
8 Marc-André Lureau <mlureau@redhat.com>
10 This program is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include <string.h>
25 #include <syslog.h>
26 #include <stdlib.h>
28 #include <X11/extensions/Xinerama.h>
30 #include "vdagentd-proto.h"
31 #include "vdagent-x11.h"
32 #include "vdagent-x11-priv.h"
34 static int caught_error;
35 static int (*old_error_handler)(Display *, XErrorEvent *);
37 static int error_handler(Display *display, XErrorEvent *error)
39 caught_error = 1;
40 return 0;
43 static void arm_error_handler(struct vdagent_x11 *x11)
45 caught_error = 0;
46 XSync(x11->display, False);
47 old_error_handler = XSetErrorHandler(error_handler);
50 static int check_error_handler(struct vdagent_x11 *x11)
52 int error;
54 XSync(x11->display, False);
55 XSetErrorHandler(old_error_handler);
56 error = caught_error;
57 caught_error = 0;
59 return error;
62 static XRRModeInfo *mode_from_id(struct vdagent_x11 *x11, int id)
64 int i;
66 for (i = 0 ; i < x11->randr.res->nmode ; ++i) {
67 if (id == x11->randr.res->modes[i].id) {
68 return &x11->randr.res->modes[i];
71 return NULL;
74 static XRRCrtcInfo *crtc_from_id(struct vdagent_x11 *x11, int id)
76 int i;
78 for (i = 0 ; i < x11->randr.res->ncrtc ; ++i) {
79 if (id == x11->randr.res->crtcs[i]) {
80 return x11->randr.crtcs[i];
83 return NULL;
86 static void free_randr_resources(struct vdagent_x11 *x11)
88 int i;
90 if (!x11->randr.res) {
91 return;
93 if (x11->randr.outputs != NULL) {
94 for (i = 0 ; i < x11->randr.res->noutput; ++i) {
95 XRRFreeOutputInfo(x11->randr.outputs[i]);
97 free(x11->randr.outputs);
99 if (x11->randr.crtcs != NULL) {
100 for (i = 0 ; i < x11->randr.res->ncrtc; ++i) {
101 XRRFreeCrtcInfo(x11->randr.crtcs[i]);
103 free(x11->randr.crtcs);
105 XRRFreeScreenResources(x11->randr.res);
106 x11->randr.res = NULL;
107 x11->randr.outputs = NULL;
108 x11->randr.crtcs = NULL;
109 x11->randr.num_monitors = 0;
112 static void update_randr_res(struct vdagent_x11 *x11)
114 int i;
116 free_randr_resources(x11);
117 /* Note: we don't need XRRGetScreenResourcesCurrent since we cache the changes */
118 x11->randr.res = XRRGetScreenResources(x11->display, x11->root_window);
119 x11->randr.outputs = malloc(x11->randr.res->noutput * sizeof(*x11->randr.outputs));
120 x11->randr.crtcs = malloc(x11->randr.res->ncrtc * sizeof(*x11->randr.crtcs));
121 for (i = 0 ; i < x11->randr.res->noutput; ++i) {
122 x11->randr.outputs[i] = XRRGetOutputInfo(x11->display, x11->randr.res,
123 x11->randr.res->outputs[i]);
124 if (x11->randr.outputs[i]->connection == RR_Connected)
125 x11->randr.num_monitors++;
127 for (i = 0 ; i < x11->randr.res->ncrtc; ++i) {
128 x11->randr.crtcs[i] = XRRGetCrtcInfo(x11->display, x11->randr.res,
129 x11->randr.res->crtcs[i]);
131 /* XXX is this dynamic? should it be cached? */
132 if (XRRGetScreenSizeRange(x11->display, x11->root_window,
133 &x11->randr.min_width,
134 &x11->randr.min_height,
135 &x11->randr.max_width,
136 &x11->randr.max_height) != 1) {
137 syslog(LOG_ERR, "update_randr_res: RRGetScreenSizeRange failed");
141 void vdagent_x11_randr_init(struct vdagent_x11 *x11)
143 int i;
145 if (XRRQueryExtension(x11->display, &i, &i)) {
146 x11->has_xrandr = 1;
147 update_randr_res(x11);
148 XRRQueryVersion(x11->display, &x11->xrandr_major, &x11->xrandr_minor);
149 } else {
150 x11->randr.res = NULL;
153 if (XineramaQueryExtension(x11->display, &i, &i))
154 x11->has_xinerama = 1;
156 switch (x11->has_xrandr << 4 | x11->has_xinerama) {
157 case 0x00:
158 syslog(LOG_ERR, "Neither Xrandr nor Xinerama found, assuming single monitor setup");
159 break;
160 case 0x01:
161 if (x11->debug)
162 syslog(LOG_DEBUG, "Found Xinerama extension without Xrandr, assuming Xinerama multi monitor setup");
163 break;
164 case 0x10:
165 syslog(LOG_ERR, "Found Xrandr but no Xinerama, weird!");
166 break;
167 case 0x11:
168 /* Standard xrandr setup, nothing to see here */
169 break;
173 static XRRModeInfo *
174 find_mode_by_name (struct vdagent_x11 *x11, char *name)
176 int m;
177 XRRModeInfo *ret = NULL;
179 for (m = 0; m < x11->randr.res->nmode; m++) {
180 XRRModeInfo *mode = &x11->randr.res->modes[m];
181 if (!strcmp (name, mode->name)) {
182 ret = mode;
183 break;
186 return ret;
189 static XRRModeInfo *
190 find_mode_by_size (struct vdagent_x11 *x11, int width, int height)
192 int m;
193 XRRModeInfo *ret = NULL;
195 for (m = 0; m < x11->randr.res->nmode; m++) {
196 XRRModeInfo *mode = &x11->randr.res->modes[m];
197 if (mode->width == width && mode->height == height) {
198 ret = mode;
199 break;
202 return ret;
205 static void delete_mode(struct vdagent_x11 *x11, int output_index, const char* name)
207 int m;
208 XRRModeInfo *mode;
209 XRROutputInfo *output_info;
210 XRRCrtcInfo *crtc_info;
211 RRCrtc crtc;
212 int current_mode = -1;
214 if (x11->debug)
215 syslog(LOG_DEBUG, "Deleting mode %s", name);
217 output_info = x11->randr.outputs[output_index];
218 if (output_info->ncrtc != 1) {
219 syslog(LOG_ERR, "output has %d crtcs, expected exactly 1, "
220 "failed to delete mode", output_info->ncrtc);
221 return;
223 crtc_info = crtc_from_id(x11, output_info->crtcs[0]);
224 current_mode = crtc_info->mode;
225 crtc = output_info->crtc;
226 for (m = 0 ; m < x11->randr.res->nmode; ++m) {
227 mode = &x11->randr.res->modes[m];
228 if (strcmp(mode->name, name) == 0)
229 break;
231 if (m < x11->randr.res->nmode) {
232 arm_error_handler(x11);
233 XRRDeleteOutputMode (x11->display, x11->randr.res->outputs[output_index],
234 mode->id);
235 XRRDestroyMode (x11->display, mode->id);
236 // ignore race error, if mode is created by others
237 check_error_handler(x11);
240 /* silly to update everytime for more then one monitor */
241 update_randr_res(x11);
244 static void set_reduced_cvt_mode(XRRModeInfo *mode, int width, int height)
246 /* Code taken from hw/xfree86/modes/xf86cvt.c
247 * See that file for lineage. Originated in public domain code
248 * Would be nice if xorg exported this in a library */
250 /* 1) top/bottom margin size (% of height) - default: 1.8 */
251 #define CVT_MARGIN_PERCENTAGE 1.8
253 /* 2) character cell horizontal granularity (pixels) - default 8 */
254 #define CVT_H_GRANULARITY 8
256 /* 4) Minimum vertical porch (lines) - default 3 */
257 #define CVT_MIN_V_PORCH 3
259 /* 4) Minimum number of vertical back porch lines - default 6 */
260 #define CVT_MIN_V_BPORCH 6
262 /* Pixel Clock step (kHz) */
263 #define CVT_CLOCK_STEP 250
265 /* Minimum vertical blanking interval time (µs) - default 460 */
266 #define CVT_RB_MIN_VBLANK 460.0
268 /* Fixed number of clocks for horizontal sync */
269 #define CVT_RB_H_SYNC 32.0
271 /* Fixed number of clocks for horizontal blanking */
272 #define CVT_RB_H_BLANK 160.0
274 /* Fixed number of lines for vertical front porch - default 3 */
275 #define CVT_RB_VFPORCH 3
277 int VBILines;
278 float VFieldRate = 60.0;
279 int VSync;
280 float HPeriod;
282 /* 2. Horizontal pixels */
283 width = width - (width % CVT_H_GRANULARITY);
285 mode->width = width;
286 mode->height = height;
287 VSync = 10;
289 /* 8. Estimate Horizontal period. */
290 HPeriod = ((float) (1000000.0 / VFieldRate - CVT_RB_MIN_VBLANK)) / height;
292 /* 9. Find number of lines in vertical blanking */
293 VBILines = ((float) CVT_RB_MIN_VBLANK) / HPeriod + 1;
295 /* 10. Check if vertical blanking is sufficient */
296 if (VBILines < (CVT_RB_VFPORCH + VSync + CVT_MIN_V_BPORCH))
297 VBILines = CVT_RB_VFPORCH + VSync + CVT_MIN_V_BPORCH;
299 /* 11. Find total number of lines in vertical field */
300 mode->vTotal = height + VBILines;
302 /* 12. Find total number of pixels in a line */
303 mode->hTotal = mode->width + CVT_RB_H_BLANK;
305 /* Fill in HSync values */
306 mode->hSyncEnd = mode->width + CVT_RB_H_BLANK / 2;
307 mode->hSyncStart = mode->hSyncEnd - CVT_RB_H_SYNC;
309 /* Fill in VSync values */
310 mode->vSyncStart = mode->height + CVT_RB_VFPORCH;
311 mode->vSyncEnd = mode->vSyncStart + VSync;
313 /* 15/13. Find pixel clock frequency (kHz for xf86) */
314 mode->dotClock = mode->hTotal * 1000.0 / HPeriod;
315 mode->dotClock -= mode->dotClock % CVT_CLOCK_STEP;
319 static XRRModeInfo *create_new_mode(struct vdagent_x11 *x11, int output_index,
320 int width, int height)
322 char modename[20];
323 XRRModeInfo mode;
325 snprintf(modename, sizeof(modename), "%dx%d-%d", width, height, output_index);
326 mode.hSkew = 0;
327 mode.name = modename;
328 mode.nameLength = strlen(mode.name);
329 set_reduced_cvt_mode(&mode, width, height);
330 mode.modeFlags = 0;
331 mode.id = 0;
332 arm_error_handler(x11);
333 XRRCreateMode (x11->display, x11->root_window, &mode);
334 // ignore race error, if mode is created by others
335 check_error_handler(x11);
337 /* silly to update everytime for more then one monitor */
338 update_randr_res(x11);
340 return find_mode_by_name(x11, modename);
343 static int xrandr_add_and_set(struct vdagent_x11 *x11, int output, int x, int y,
344 int width, int height)
346 XRRModeInfo *mode;
347 int xid;
348 Status s;
349 RROutput outputs[1];
350 char modename[20];
352 if (!x11->randr.res || output >= x11->randr.res->noutput || output < 0) {
353 syslog(LOG_ERR, "%s: program error: missing RANDR or bad output",
354 __FUNCTION__);
355 return 0;
357 if (x11->set_crtc_config_not_functional) {
358 /* fail, set_best_mode will find something close. */
359 return 0;
361 xid = x11->randr.res->outputs[output];
362 mode = find_mode_by_size(x11, width, height);
363 if (!mode) {
364 mode = create_new_mode(x11, output, width, height);
366 if (!mode) {
367 syslog(LOG_ERR, "failed to add a new mode");
368 return 0;
370 XRRAddOutputMode(x11->display, xid, mode->id);
371 outputs[0] = xid;
372 s = XRRSetCrtcConfig(x11->display, x11->randr.res, x11->randr.res->crtcs[output],
373 CurrentTime, x, y, mode->id, RR_Rotate_0, outputs,
376 if (s != RRSetConfigSuccess) {
377 syslog(LOG_ERR, "failed to XRRSetCrtcConfig");
378 x11->set_crtc_config_not_functional = 1;
379 return 0;
382 /* clean the previous name, if any */
383 snprintf(modename, sizeof(modename), "%dx%d-%d", x11->width, x11->height, output);
384 delete_mode(x11, output, modename);
386 return 1;
389 static void xrandr_disable_output(struct vdagent_x11 *x11, int output)
391 Status s;
393 if (!x11->randr.res || output >= x11->randr.res->noutput || output < 0) {
394 syslog(LOG_ERR, "%s: program error: missing RANDR or bad output",
395 __FUNCTION__);
396 return;
399 s = XRRSetCrtcConfig(x11->display, x11->randr.res,
400 x11->randr.res->crtcs[output],
401 CurrentTime, 0, 0, None, RR_Rotate_0,
402 NULL, 0);
404 if (s != RRSetConfigSuccess)
405 syslog(LOG_ERR, "failed to disable monitor");
408 static int set_screen_to_best_size(struct vdagent_x11 *x11, int width, int height,
409 int *out_width, int *out_height){
410 int i, num_sizes = 0;
411 int best = -1;
412 unsigned int closest_diff = -1;
413 XRRScreenSize *sizes;
414 XRRScreenConfiguration *config;
415 Rotation rotation;
417 sizes = XRRSizes(x11->display, x11->screen, &num_sizes);
418 if (!sizes || !num_sizes) {
419 syslog(LOG_ERR, "XRRSizes failed");
420 return 0;
422 if (x11->debug)
423 syslog(LOG_DEBUG, "set_screen_to_best_size found %d modes\n", num_sizes);
425 /* Find the closest size which will fit within the monitor */
426 for (i = 0; i < num_sizes; i++) {
427 if (sizes[i].width > width ||
428 sizes[i].height > height)
429 continue; /* Too large for the monitor */
431 unsigned int wdiff = width - sizes[i].width;
432 unsigned int hdiff = height - sizes[i].height;
433 unsigned int diff = wdiff * wdiff + hdiff * hdiff;
434 if (diff < closest_diff) {
435 closest_diff = diff;
436 best = i;
440 if (best == -1) {
441 syslog(LOG_ERR, "no suitable resolution found for monitor");
442 return 0;
445 config = XRRGetScreenInfo(x11->display, x11->root_window);
446 if(!config) {
447 syslog(LOG_ERR, "get screen info failed");
448 return 0;
450 XRRConfigCurrentConfiguration(config, &rotation);
451 XRRSetScreenConfig(x11->display, config, x11->root_window, best,
452 rotation, CurrentTime);
453 XRRFreeScreenConfigInfo(config);
455 if (x11->debug)
456 syslog(LOG_DEBUG, "set_screen_to_best_size set size to: %dx%d\n",
457 sizes[best].width, sizes[best].height);
458 *out_width = sizes[best].width;
459 *out_height = sizes[best].height;
460 return 1;
463 void vdagent_x11_randr_handle_root_size_change(struct vdagent_x11 *x11,
464 int width, int height)
466 if (width == x11->width && height == x11->height) {
467 return;
470 if (x11->debug)
471 syslog(LOG_DEBUG, "Root size changed to %dx%d send %d",
472 width, height, !x11->dont_send_guest_xorg_res);
474 x11->width = width;
475 x11->height = height;
476 if (!x11->dont_send_guest_xorg_res) {
477 vdagent_x11_send_daemon_guest_xorg_res(x11);
481 static uint32_t min_int(uint32_t x, uint32_t y)
483 return x > y ? y : x;
486 static uint32_t max_int(uint32_t x, uint32_t y)
488 return x > y ? x : y;
491 static int constrain_to_range(uint32_t low, uint32_t *val, uint32_t high)
493 if (low <= *val && *val <= high) {
494 return 0;
496 if (low > *val) {
497 *val = low;
499 if (*val > high) {
500 *val = high;
502 return 1;
505 static void constrain_to_screen(struct vdagent_x11 *x11, uint32_t *w, uint32_t *h)
507 uint32_t lx, ly, hx, hy;
508 uint32_t orig_h = *h;
509 uint32_t orig_w = *w;
511 lx = x11->randr.min_width;
512 hx = x11->randr.max_width;
513 ly = x11->randr.min_height;
514 hy = x11->randr.max_height;
515 if (constrain_to_range(lx, w, hx)) {
516 syslog(LOG_ERR, "width not in driver range: ! %d < %d < %d",
517 lx, orig_w, hx);
519 if (constrain_to_range(ly, h, hy)) {
520 syslog(LOG_ERR, "height not in driver range: ! %d < %d < %d",
521 ly, orig_h, hy);
526 * The agent config doesn't contain a primary size, just the monitors, but
527 * we need to total size as well, to make sure we have enough memory and
528 * because X needs it.
530 * At the same pass constrain any provided size to what the server accepts.
532 * Exit axioms:
533 * x >= 0, y >= 0 for all x, y in mon_config
534 * max_width >= width >= min_width,
535 * max_height >= height >= min_height for all monitors in mon_config
537 static void zero_base_monitors(struct vdagent_x11 *x11,
538 VDAgentMonitorsConfig *mon_config,
539 uint32_t *width, uint32_t *height)
541 int i = 0;
542 uint32_t min_x, min_y, max_x, max_y;
543 uint32_t *mon_height, *mon_width;
545 mon_config->monitors[0].x &= ~7;
546 mon_config->monitors[0].width &= ~7;
547 mon_width = &mon_config->monitors[i].width;
548 mon_height = &mon_config->monitors[i].height;
549 constrain_to_screen(x11, mon_width, mon_height);
550 min_x = mon_config->monitors[0].x;
551 min_y = mon_config->monitors[0].y;
552 max_x = mon_config->monitors[0].width + min_x;
553 max_y = mon_config->monitors[0].height + min_y;
554 for (++i ; i < mon_config->num_of_monitors; ++i) {
555 mon_config->monitors[i].x &= ~7;
556 mon_config->monitors[i].width &= ~7;
557 mon_width = &mon_config->monitors[i].width;
558 mon_height = &mon_config->monitors[i].height;
559 constrain_to_screen(x11, mon_width, mon_height);
560 min_x = min_int(mon_config->monitors[i].x, min_x);
561 min_y = min_int(mon_config->monitors[i].y, min_y);
562 max_x = max_int(mon_config->monitors[i].x + *mon_width, max_x);
563 max_y = max_int(mon_config->monitors[i].y + *mon_height, max_y);
565 if (min_x != 0 || min_y != 0) {
566 syslog(LOG_ERR, "%s: agent config %d,%d rooted, adjusting to 0,0.",
567 __FUNCTION__, min_x, min_y);
568 for (i = 0 ; i < mon_config->num_of_monitors; ++i) {
569 mon_config->monitors[i].x -= min_x;
570 mon_config->monitors[i].y -= min_y;
573 max_x -= min_x;
574 max_y -= min_y;
575 *width = max_x;
576 *height = max_y;
579 static int same_monitor_configs(struct vdagent_x11 *x11,
580 VDAgentMonitorsConfig *mon,
581 uint32_t primary_w, uint32_t primary_h)
583 int i;
584 XRRModeInfo *mode;
585 XRRCrtcInfo *crtc;
586 VDAgentMonConfig *client_mode;
587 XRRScreenResources *res;
589 update_randr_res(x11);
590 res = x11->randr.res;
592 if (res->noutput > res->ncrtc) {
593 syslog(LOG_ERR, "error: unexpected noutput > ncrtc in driver");
594 return 0;
597 if (mon->num_of_monitors > res->noutput) {
598 for (i = res->noutput; i < mon->num_of_monitors; i++) {
599 if (mon->monitors[i].width || mon->monitors[i].height) {
600 syslog(LOG_WARNING,
601 "warning: unexpected client request: #mon %d > driver output %d",
602 mon->num_of_monitors, res->noutput);
603 break;
606 mon->num_of_monitors = res->noutput;
609 if (x11->width != primary_w || x11->height != primary_h)
610 return 0;
612 if (x11->randr.num_monitors != mon->num_of_monitors)
613 return 0;
615 for (i = 0 ; i < mon->num_of_monitors; ++i) {
616 if (x11->randr.outputs[i]->ncrtc == 0) {
617 return 0;
619 if (x11->randr.outputs[i]->ncrtc != 1) {
620 syslog(LOG_ERR, "error: unexpected driver config, ncrtc %d != 1",
621 x11->randr.outputs[i]->ncrtc);
622 return 0;
624 crtc = crtc_from_id(x11, x11->randr.outputs[i]->crtcs[0]);
625 if (!crtc) {
626 syslog(LOG_ERR, "error: inconsistent or stale data from X");
627 return 0;
629 client_mode = &mon->monitors[i];
630 mode = mode_from_id(x11, crtc->mode);
631 if (!mode) {
632 return 0;
634 /* NOTE: we don't compare depth. */
635 /* NOTE 2: width set by X is a multiple of 8, so ignore lower 3 bits */
636 if ((mode->width & ~7) != (client_mode->width & ~7) ||
637 mode->height != client_mode->height ||
638 (crtc->x & ~7) != (client_mode->x & ~7) ||
639 crtc->y != client_mode->y) {
640 return 0;
643 return 1;
646 static void dump_monitors_config(struct vdagent_x11 *x11,
647 VDAgentMonitorsConfig *mon_config,
648 const char *prefix)
650 int i;
651 VDAgentMonConfig *m;
653 syslog(LOG_DEBUG, "%s: %d, %x", prefix, mon_config->num_of_monitors,
654 mon_config->flags);
655 for (i = 0 ; i < mon_config->num_of_monitors; ++i) {
656 m = &mon_config->monitors[i];
657 syslog(LOG_DEBUG, "received monitor %d config %dx%d+%d+%d", i,
658 m->width, m->height, m->x, m->y);
663 * Set monitor configuration according to client request.
665 * On exit send current configuration to client, regardless of error.
667 * Errors:
668 * screen size too large for driver to handle. (we set the largest/smallest possible)
669 * no randr support in X server.
670 * invalid configuration request from client.
672 void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
673 VDAgentMonitorsConfig *mon_config)
675 int i;
676 int width, height;
677 int x, y;
678 uint32_t primary_w, primary_h;
680 if (!x11->has_xrandr)
681 goto exit;
683 if (mon_config->num_of_monitors < 1) {
684 syslog(LOG_ERR, "client sent invalid monitor config number %d",
685 mon_config->num_of_monitors);
686 goto exit;
689 if (x11->debug) {
690 dump_monitors_config(x11, mon_config, "from guest");
693 zero_base_monitors(x11, mon_config, &primary_w, &primary_h);
695 constrain_to_screen(x11, &primary_w, &primary_h);
697 if (same_monitor_configs(x11, mon_config, primary_w, primary_h)) {
698 goto exit;
701 if (x11->debug) {
702 dump_monitors_config(x11, mon_config, "after zeroing");
705 for (i = mon_config->num_of_monitors; i < x11->randr.res->noutput; i++)
706 xrandr_disable_output(x11, i);
708 for (i = 0; i < mon_config->num_of_monitors; ++i) {
709 /* Try to create the requested resolution */
710 width = mon_config->monitors[i].width;
711 height = mon_config->monitors[i].height;
712 x = mon_config->monitors[i].x;
713 y = mon_config->monitors[i].y;
714 if (!xrandr_add_and_set(x11, i, x, y, width, height) &&
715 mon_config->num_of_monitors == 1) {
716 set_screen_to_best_size(x11, width, height,
717 &primary_w, &primary_h);
718 goto update;
722 if (primary_w != x11->width || primary_h != x11->height) {
723 if (x11->debug)
724 syslog(LOG_DEBUG, "Changing screen size to %dx%d",
725 primary_w, primary_h);
726 arm_error_handler(x11);
727 XRRSetScreenSize(x11->display, x11->root_window, primary_w, primary_h,
728 DisplayWidthMM(x11->display, x11->screen),
729 DisplayHeightMM(x11->display, x11->screen));
730 if (check_error_handler(x11)) {
731 syslog(LOG_ERR, "failed to XRRSetScreenSize");
732 x11->set_crtc_config_not_functional = 1;
736 update:
737 update_randr_res(x11);
738 x11->width = primary_w;
739 x11->height = primary_h;
741 /* Flush output buffers and consume any pending events (ConfigureNotify) */
742 x11->dont_send_guest_xorg_res = 1;
743 vdagent_x11_do_read(x11);
744 x11->dont_send_guest_xorg_res = 0;
746 exit:
747 vdagent_x11_send_daemon_guest_xorg_res(x11);
749 /* Flush output buffers and consume any pending events */
750 vdagent_x11_do_read(x11);
753 void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11)
755 struct vdagentd_guest_xorg_resolution *res = NULL;
756 XineramaScreenInfo *screen_info = NULL;
757 int i, screen_count = 0;
759 if (x11->has_xinerama) {
760 /* Xinerama reports the same information RANDR reports, so stay
761 * with Xinerama for support of Xinerama only setups */
762 screen_info = XineramaQueryScreens(x11->display, &screen_count);
765 if (screen_count == 0)
766 screen_count = 1;
768 res = malloc(screen_count * sizeof(*res));
769 if (!res) {
770 syslog(LOG_ERR, "out of memory while trying to send resolutions, not sending resolutions.");
771 if (screen_info)
772 XFree(screen_info);
773 return;
776 if (screen_info) {
777 for (i = 0; i < screen_count; i++) {
778 if (screen_info[i].screen_number >= screen_count) {
779 syslog(LOG_ERR, "Invalid screen number in xinerama screen info (%d >= %d)",
780 screen_info[i].screen_number, screen_count);
781 XFree(screen_info);
782 free(res);
783 return;
785 res[screen_info[i].screen_number].width = screen_info[i].width;
786 res[screen_info[i].screen_number].height = screen_info[i].height;
787 res[screen_info[i].screen_number].x = screen_info[i].x_org;
788 res[screen_info[i].screen_number].y = screen_info[i].y_org;
790 XFree(screen_info);
791 } else {
792 res[0].width = x11->width;
793 res[0].height = x11->height;
794 res[0].x = 0;
795 res[0].y = 0;
798 udscs_write(x11->vdagentd, VDAGENTD_GUEST_XORG_RESOLUTION, x11->width,
799 x11->height, (uint8_t *)res, screen_count * sizeof(*res));
800 free(res);