uploaded spice-vdagent
[platform/adaptation/emulator/spice-vdagent.git] / src / vdagent-x11-randr.c
1 /*  vdagent-x11-randr.c vdagent Xrandr integration code
2
3     Copyright 2012 Red Hat, Inc.
4
5     Red Hat Authors:
6     Alon Levy <alevy@redhat.com>
7     Hans de Goede <hdegoede@redhat.com>
8     Marc-AndrĂ© Lureau <mlureau@redhat.com>
9
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.
14
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.
19
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/>.
22 */
23
24 #include <string.h>
25 #include <syslog.h>
26 #include <stdlib.h>
27 #include <limits.h>
28
29 #include <X11/extensions/Xinerama.h>
30
31 #include "vdagentd-proto.h"
32 #include "vdagent-x11.h"
33 #include "vdagent-x11-priv.h"
34
35 static int error_handler(Display *display, XErrorEvent *error)
36 {
37     vdagent_x11_caught_error = 1;
38     return 0;
39 }
40
41 static XRRModeInfo *mode_from_id(struct vdagent_x11 *x11, int id)
42 {
43     int i;
44
45     for (i = 0 ; i < x11->randr.res->nmode ; ++i) {
46         if (id == x11->randr.res->modes[i].id) {
47             return &x11->randr.res->modes[i];
48         }
49     }
50     return NULL;
51 }
52
53 static XRRCrtcInfo *crtc_from_id(struct vdagent_x11 *x11, int id)
54 {
55     int i;
56
57     for (i = 0 ; i < x11->randr.res->ncrtc ; ++i) {
58         if (id == x11->randr.res->crtcs[i]) {
59             return x11->randr.crtcs[i];
60         }
61     }
62     return NULL;
63 }
64
65 static void free_randr_resources(struct vdagent_x11 *x11)
66 {
67     int i;
68
69     if (!x11->randr.res) {
70         return;
71     }
72     if (x11->randr.outputs != NULL) {
73         for (i = 0 ; i < x11->randr.res->noutput; ++i) {
74             XRRFreeOutputInfo(x11->randr.outputs[i]);
75         }
76         free(x11->randr.outputs);
77     }
78     if (x11->randr.crtcs != NULL) {
79         for (i = 0 ; i < x11->randr.res->ncrtc; ++i) {
80             XRRFreeCrtcInfo(x11->randr.crtcs[i]);
81         }
82         free(x11->randr.crtcs);
83     }
84     XRRFreeScreenResources(x11->randr.res);
85     x11->randr.res = NULL;
86     x11->randr.outputs = NULL;
87     x11->randr.crtcs = NULL;
88     x11->randr.num_monitors = 0;
89 }
90
91 static void update_randr_res(struct vdagent_x11 *x11, int poll)
92 {
93     int i;
94
95     free_randr_resources(x11);
96     if (poll)
97         x11->randr.res = XRRGetScreenResources(x11->display, x11->root_window[0]);
98     else
99         x11->randr.res = XRRGetScreenResourcesCurrent(x11->display, x11->root_window[0]);
100     x11->randr.outputs = malloc(x11->randr.res->noutput * sizeof(*x11->randr.outputs));
101     x11->randr.crtcs = malloc(x11->randr.res->ncrtc * sizeof(*x11->randr.crtcs));
102     for (i = 0 ; i < x11->randr.res->noutput; ++i) {
103         x11->randr.outputs[i] = XRRGetOutputInfo(x11->display, x11->randr.res,
104                                                  x11->randr.res->outputs[i]);
105         if (x11->randr.outputs[i]->connection == RR_Connected)
106             x11->randr.num_monitors++;
107     }
108     for (i = 0 ; i < x11->randr.res->ncrtc; ++i) {
109         x11->randr.crtcs[i] = XRRGetCrtcInfo(x11->display, x11->randr.res,
110                                              x11->randr.res->crtcs[i]);
111     }
112     /* XXX is this dynamic? should it be cached? */
113     if (XRRGetScreenSizeRange(x11->display, x11->root_window[0],
114                               &x11->randr.min_width,
115                               &x11->randr.min_height,
116                               &x11->randr.max_width,
117                               &x11->randr.max_height) != 1) {
118         syslog(LOG_ERR, "update_randr_res: RRGetScreenSizeRange failed");
119     }
120 }
121
122 void vdagent_x11_randr_init(struct vdagent_x11 *x11)
123 {
124     int i;
125
126     if (x11->screen_count > 1) {
127         syslog(LOG_WARNING, "X-server has more then 1 screen, "
128                "disabling client -> guest resolution syncing");
129         return;
130     }
131
132     if (XRRQueryExtension(x11->display, &i, &i)) {
133         XRRQueryVersion(x11->display, &x11->xrandr_major, &x11->xrandr_minor);
134         if (x11->xrandr_major == 1 && x11->xrandr_minor >= 3)
135             x11->has_xrandr = 1;
136     }
137
138     if (x11->has_xrandr) {
139         update_randr_res(x11, 0);
140     } else {
141         x11->randr.res = NULL;
142     }
143
144     if (XineramaQueryExtension(x11->display, &i, &i))
145         x11->has_xinerama = 1;
146
147     switch (x11->has_xrandr << 4 | x11->has_xinerama) {
148     case 0x00:
149         syslog(LOG_ERR, "Neither Xrandr nor Xinerama found, assuming single monitor setup");
150         break;
151     case 0x01:
152         if (x11->debug)
153             syslog(LOG_DEBUG, "Found Xinerama extension without Xrandr, assuming Xinerama multi monitor setup");
154         break;
155     case 0x10:
156         syslog(LOG_ERR, "Found Xrandr but no Xinerama, weird!");
157         break;
158     case 0x11:
159         /* Standard xrandr setup, nothing to see here */
160         break;
161     }
162 }
163
164 static XRRModeInfo *
165 find_mode_by_name (struct vdagent_x11 *x11, char *name)
166 {
167     int         m;
168     XRRModeInfo        *ret = NULL;
169
170     for (m = 0; m < x11->randr.res->nmode; m++) {
171         XRRModeInfo *mode = &x11->randr.res->modes[m];
172         if (!strcmp (name, mode->name)) {
173             ret = mode;
174             break;
175         }
176     }
177     return ret;
178 }
179
180 static XRRModeInfo *
181 find_mode_by_size (struct vdagent_x11 *x11, int output, int width, int height)
182 {
183     int         m;
184     XRRModeInfo        *ret = NULL;
185
186     for (m = 0; m < x11->randr.outputs[output]->nmode; m++) {
187         XRRModeInfo *mode = mode_from_id(x11,
188                                          x11->randr.outputs[output]->modes[m]);
189         if (mode && mode->width == width && mode->height == height) {
190             ret = mode;
191             break;
192         }
193     }
194     return ret;
195 }
196
197 static void delete_mode(struct vdagent_x11 *x11, int output_index,
198                         int width, int height)
199 {
200     int m;
201     XRRModeInfo *mode;
202     XRROutputInfo *output_info;
203     char name[20];
204
205     if (width == 0 || height == 0)
206         return;
207
208     snprintf(name, sizeof(name), "%dx%d-%d", width, height, output_index);
209     if (x11->debug)
210         syslog(LOG_DEBUG, "Deleting mode %s", name);
211
212     output_info = x11->randr.outputs[output_index];
213     if (output_info->ncrtc != 1) {
214         syslog(LOG_ERR, "output has %d crtcs, expected exactly 1, "
215                "failed to delete mode", output_info->ncrtc);
216         return;
217     }
218     for (m = 0 ; m < x11->randr.res->nmode; ++m) {
219         mode = &x11->randr.res->modes[m];
220         if (strcmp(mode->name, name) == 0)
221             break;
222     }
223     if (m < x11->randr.res->nmode) {
224         vdagent_x11_set_error_handler(x11, error_handler);
225         XRRDeleteOutputMode (x11->display, x11->randr.res->outputs[output_index],
226                              mode->id);
227         XRRDestroyMode (x11->display, mode->id);
228         // ignore race error, if mode is created by others
229         vdagent_x11_restore_error_handler(x11);
230     }
231
232     /* silly to update everytime for more then one monitor */
233     update_randr_res(x11, 0);
234 }
235
236 static void set_reduced_cvt_mode(XRRModeInfo *mode, int width, int height)
237 {
238     /* Code taken from hw/xfree86/modes/xf86cvt.c
239      * See that file for lineage. Originated in public domain code
240      * Would be nice if xorg exported this in a library */
241
242     /* 1) top/bottom margin size (% of height) - default: 1.8 */
243 #define CVT_MARGIN_PERCENTAGE 1.8
244
245     /* 2) character cell horizontal granularity (pixels) - default 8 */
246 #define CVT_H_GRANULARITY 8
247
248     /* 4) Minimum vertical porch (lines) - default 3 */
249 #define CVT_MIN_V_PORCH 3
250
251     /* 4) Minimum number of vertical back porch lines - default 6 */
252 #define CVT_MIN_V_BPORCH 6
253
254     /* Pixel Clock step (kHz) */
255 #define CVT_CLOCK_STEP 250
256
257     /* Minimum vertical blanking interval time (µs) - default 460 */
258 #define CVT_RB_MIN_VBLANK 460.0
259
260     /* Fixed number of clocks for horizontal sync */
261 #define CVT_RB_H_SYNC 32.0
262
263     /* Fixed number of clocks for horizontal blanking */
264 #define CVT_RB_H_BLANK 160.0
265
266     /* Fixed number of lines for vertical front porch - default 3 */
267 #define CVT_RB_VFPORCH 3
268
269     int VBILines;
270     float VFieldRate = 60.0;
271     int VSync;
272     float HPeriod;
273
274     /* 2. Horizontal pixels */
275     width = width - (width % CVT_H_GRANULARITY);
276
277     mode->width = width;
278     mode->height = height;
279     VSync = 10;
280
281     /* 8. Estimate Horizontal period. */
282     HPeriod = ((float) (1000000.0 / VFieldRate - CVT_RB_MIN_VBLANK)) / height;
283
284     /* 9. Find number of lines in vertical blanking */
285     VBILines = ((float) CVT_RB_MIN_VBLANK) / HPeriod + 1;
286
287     /* 10. Check if vertical blanking is sufficient */
288     if (VBILines < (CVT_RB_VFPORCH + VSync + CVT_MIN_V_BPORCH))
289         VBILines = CVT_RB_VFPORCH + VSync + CVT_MIN_V_BPORCH;
290
291     /* 11. Find total number of lines in vertical field */
292     mode->vTotal = height + VBILines;
293
294     /* 12. Find total number of pixels in a line */
295     mode->hTotal = mode->width + CVT_RB_H_BLANK;
296
297     /* Fill in HSync values */
298     mode->hSyncEnd = mode->width + CVT_RB_H_BLANK / 2;
299     mode->hSyncStart = mode->hSyncEnd - CVT_RB_H_SYNC;
300
301     /* Fill in VSync values */
302     mode->vSyncStart = mode->height + CVT_RB_VFPORCH;
303     mode->vSyncEnd = mode->vSyncStart + VSync;
304
305     /* 15/13. Find pixel clock frequency (kHz for xf86) */
306     mode->dotClock = mode->hTotal * 1000.0 / HPeriod;
307     mode->dotClock -= mode->dotClock % CVT_CLOCK_STEP;
308
309 }
310
311 static XRRModeInfo *create_new_mode(struct vdagent_x11 *x11, int output_index,
312                                     int width, int height)
313 {
314     char modename[20];
315     XRRModeInfo mode;
316
317     snprintf(modename, sizeof(modename), "%dx%d-%d", width, height, output_index);
318     mode.hSkew = 0;
319     mode.name = modename;
320     mode.nameLength = strlen(mode.name);
321     set_reduced_cvt_mode(&mode, width, height);
322     mode.modeFlags = 0;
323     mode.id = 0;
324     vdagent_x11_set_error_handler(x11, error_handler);
325     XRRCreateMode (x11->display, x11->root_window[0], &mode);
326     // ignore race error, if mode is created by others
327     vdagent_x11_restore_error_handler(x11);
328
329     /* silly to update everytime for more then one monitor */
330     update_randr_res(x11, 0);
331
332     return find_mode_by_name(x11, modename);
333 }
334
335 static int xrandr_add_and_set(struct vdagent_x11 *x11, int output, int x, int y,
336                               int width, int height)
337 {
338     XRRModeInfo *mode;
339     int xid;
340     Status s;
341     RROutput outputs[1];
342     int old_width  = x11->randr.monitor_sizes[output].width;
343     int old_height = x11->randr.monitor_sizes[output].height;
344
345     if (!x11->randr.res || output >= x11->randr.res->noutput || output < 0) {
346         syslog(LOG_ERR, "%s: program error: missing RANDR or bad output",
347                __FUNCTION__);
348         return 0;
349     }
350     if (x11->set_crtc_config_not_functional) {
351         /* fail, set_best_mode will find something close. */
352         return 0;
353     }
354     xid = x11->randr.res->outputs[output];
355     mode = find_mode_by_size(x11, output, width, height);
356     if (!mode) {
357         mode = create_new_mode(x11, output, width, height);
358     }
359     if (!mode) {
360         syslog(LOG_ERR, "failed to add a new mode");
361         return 0;
362     }
363     XRRAddOutputMode(x11->display, xid, mode->id);
364     x11->randr.monitor_sizes[output].width = width;
365     x11->randr.monitor_sizes[output].height = height;
366     outputs[0] = xid;
367     s = XRRSetCrtcConfig(x11->display, x11->randr.res, x11->randr.res->crtcs[output],
368                          CurrentTime, x, y, mode->id, RR_Rotate_0, outputs,
369                          1);
370
371     if (s != RRSetConfigSuccess) {
372         syslog(LOG_ERR, "failed to XRRSetCrtcConfig");
373         x11->set_crtc_config_not_functional = 1;
374         return 0;
375     }
376
377     /* clean the previous name, if any */
378     if (width != old_width || height != old_height)
379         delete_mode(x11, output, old_width, old_height);
380
381     return 1;
382 }
383
384 static void xrandr_disable_output(struct vdagent_x11 *x11, int output)
385 {
386     Status s;
387
388     if (!x11->randr.res || output >= x11->randr.res->noutput || output < 0) {
389         syslog(LOG_ERR, "%s: program error: missing RANDR or bad output",
390                __FUNCTION__);
391         return;
392     }
393
394     s = XRRSetCrtcConfig(x11->display, x11->randr.res,
395                          x11->randr.res->crtcs[output],
396                          CurrentTime, 0, 0, None, RR_Rotate_0,
397                          NULL, 0);
398
399     if (s != RRSetConfigSuccess)
400         syslog(LOG_ERR, "failed to disable monitor");
401
402     delete_mode(x11, output, x11->randr.monitor_sizes[output].width,
403                              x11->randr.monitor_sizes[output].height);
404     x11->randr.monitor_sizes[output].width  = 0;
405     x11->randr.monitor_sizes[output].height = 0;
406 }
407
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;
416
417     sizes = XRRSizes(x11->display, 0, &num_sizes);
418     if (!sizes || !num_sizes) {
419         syslog(LOG_ERR, "XRRSizes failed");
420         return 0;
421     }
422     if (x11->debug)
423         syslog(LOG_DEBUG, "set_screen_to_best_size found %d modes\n", num_sizes);
424
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 */
430
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;
437         }
438     }
439
440     if (best == -1) {
441         syslog(LOG_ERR, "no suitable resolution found for monitor");
442         return 0;
443     }
444
445     config = XRRGetScreenInfo(x11->display, x11->root_window[0]);
446     if(!config) {
447         syslog(LOG_ERR, "get screen info failed");
448         return 0;
449     }
450     XRRConfigCurrentConfiguration(config, &rotation);
451     XRRSetScreenConfig(x11->display, config, x11->root_window[0], best,
452                        rotation, CurrentTime);
453     XRRFreeScreenConfigInfo(config);
454
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;
461 }
462
463 void vdagent_x11_randr_handle_root_size_change(struct vdagent_x11 *x11,
464     int screen, int width, int height)
465 {
466     if (width == x11->width[screen] && height == x11->height[screen]) {
467         return;
468     }
469
470     if (x11->debug)
471         syslog(LOG_DEBUG, "Root size of screen %d changed to %dx%d send %d",
472               screen,  width, height, !x11->dont_send_guest_xorg_res);
473
474     x11->width[screen]  = width;
475     x11->height[screen] = height;
476     if (!x11->dont_send_guest_xorg_res) {
477         vdagent_x11_send_daemon_guest_xorg_res(x11, 1);
478     }
479 }
480
481 static int min_int(int x, int y)
482 {
483     return x > y ? y : x;
484 }
485
486 static int max_int(int x, int y)
487 {
488     return x > y ? x : y;
489 }
490
491 static int constrain_to_range(int low, int *val, int high)
492 {
493     if (low <= *val && *val <= high) {
494         return 0;
495     }
496     if (low > *val) {
497         *val = low;
498     }
499     if (*val > high) {
500         *val = high;
501     }
502     return 1;
503 }
504
505 static void constrain_to_screen(struct vdagent_x11 *x11, int *w, int *h)
506 {
507     int lx, ly, hx, hy;
508     int orig_h = *h;
509     int orig_w = *w;
510
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);
518     }
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);
522     }
523 }
524
525 static int monitor_enabled(VDAgentMonConfig *mon)
526 {
527     return mon->width != 0 && mon->height != 0;
528 }
529
530 /*
531  * The agent config doesn't contain a primary size, just the monitors, but
532  * we need to total size as well, to make sure we have enough memory and
533  * because X needs it.
534  *
535  * At the same pass constrain any provided size to what the server accepts.
536  *
537  * Exit axioms:
538  *  x >= 0, y >= 0 for all x, y in mon_config
539  *  max_width >= width >= min_width,
540  *  max_height >= height >= min_height for all monitors in mon_config
541  */
542 static void zero_base_monitors(struct vdagent_x11 *x11,
543                                VDAgentMonitorsConfig *mon_config,
544                                int *width, int *height)
545 {
546     int i, min_x = INT_MAX, min_y = INT_MAX, max_x = INT_MIN, max_y = INT_MIN;
547     int *mon_height, *mon_width;
548
549     for (i = 0; i < mon_config->num_of_monitors; i++) {
550         if (!monitor_enabled(&mon_config->monitors[i]))
551             continue;
552         mon_config->monitors[i].x &= ~7;
553         mon_config->monitors[i].width &= ~7;
554         mon_width = (int *)&mon_config->monitors[i].width;
555         mon_height = (int *)&mon_config->monitors[i].height;
556         constrain_to_screen(x11, mon_width, mon_height);
557         min_x = min_int(mon_config->monitors[i].x, min_x);
558         min_y = min_int(mon_config->monitors[i].y, min_y);
559         max_x = max_int(mon_config->monitors[i].x + *mon_width, max_x);
560         max_y = max_int(mon_config->monitors[i].y + *mon_height, max_y);
561     }
562     if (min_x != 0 || min_y != 0) {
563         syslog(LOG_ERR, "%s: agent config %d,%d rooted, adjusting to 0,0.",
564                __FUNCTION__, min_x, min_y);
565         for (i = 0 ; i < mon_config->num_of_monitors; ++i) {
566             if (!monitor_enabled(&mon_config->monitors[i]))
567                 continue;
568             mon_config->monitors[i].x -= min_x;
569             mon_config->monitors[i].y -= min_y;
570         }
571     }
572     max_x -= min_x;
573     max_y -= min_y;
574     *width = max_x;
575     *height = max_y;
576 }
577
578 static int enabled_monitors(VDAgentMonitorsConfig *mon)
579 {
580     int i, enabled = 0;
581
582     for (i = 0; i < mon->num_of_monitors; i++) {
583         if (monitor_enabled(&mon->monitors[i]))
584             enabled++;
585     }
586     return enabled;
587 }
588
589 static int same_monitor_configs(VDAgentMonitorsConfig *conf1,
590                                 VDAgentMonitorsConfig *conf2)
591 {
592     int i;
593
594     if (conf1 == NULL || conf2 == NULL ||
595             conf1->num_of_monitors != conf2->num_of_monitors)
596         return 0;
597
598     for (i = 0; i < conf1->num_of_monitors; i++) {
599         VDAgentMonConfig *mon1 = &conf1->monitors[i];
600         VDAgentMonConfig *mon2 = &conf2->monitors[i];
601         /* NOTE: we don't compare depth. */
602         if (mon1->x != mon2->x || mon1->y != mon2->y ||
603                mon1->width != mon2->width || mon1->height != mon2->height)
604             return 0;
605     }
606     return 1;
607 }
608
609 static int config_size(int num_of_monitors)
610 {
611     return sizeof(VDAgentMonitorsConfig) +
612                            num_of_monitors * sizeof(VDAgentMonConfig);
613 }
614
615 static VDAgentMonitorsConfig *get_current_mon_config(struct vdagent_x11 *x11)
616 {
617     int i, num_of_monitors = 0;
618     XRRModeInfo *mode;
619     XRRCrtcInfo *crtc;
620     XRRScreenResources *res = x11->randr.res;
621     VDAgentMonitorsConfig *mon_config;
622
623     mon_config = calloc(1, config_size(res->noutput));
624     if (!mon_config) {
625         syslog(LOG_ERR, "out of memory allocating current monitor config");
626         return NULL;
627     }
628
629     for (i = 0 ; i < res->noutput; i++) {
630         if (x11->randr.outputs[i]->ncrtc == 0)
631             continue; /* Monitor disabled, already zero-ed by calloc */
632         if (x11->randr.outputs[i]->ncrtc != 1)
633             goto error;
634
635         crtc = crtc_from_id(x11, x11->randr.outputs[i]->crtcs[0]);
636         if (!crtc)
637             goto error;
638
639         mode = mode_from_id(x11, crtc->mode);
640         if (!mode)
641             continue; /* Monitor disabled, already zero-ed by calloc */
642
643         mon_config->monitors[i].x      = crtc->x;
644         mon_config->monitors[i].y      = crtc->y;
645         mon_config->monitors[i].width  = mode->width;
646         mon_config->monitors[i].height = mode->height;
647         num_of_monitors = i + 1;
648     }
649     mon_config->num_of_monitors = num_of_monitors;
650     mon_config->flags = VD_AGENT_CONFIG_MONITORS_FLAG_USE_POS;
651     return mon_config;
652
653 error:
654     syslog(LOG_ERR, "error: inconsistent or stale data from X");
655     free(mon_config);
656     return NULL;
657 }
658
659 static void dump_monitors_config(struct vdagent_x11 *x11,
660                                  VDAgentMonitorsConfig *mon_config,
661                                  const char *prefix)
662 {
663     int i;
664     VDAgentMonConfig *m;
665
666     syslog(LOG_DEBUG, "%s: %d, %x", prefix, mon_config->num_of_monitors,
667            mon_config->flags);
668     for (i = 0 ; i < mon_config->num_of_monitors; ++i) {
669         m = &mon_config->monitors[i];
670         if (!monitor_enabled(m))
671             continue;
672         syslog(LOG_DEBUG, "received monitor %d config %dx%d+%d+%d", i,
673                m->width, m->height, m->x, m->y);
674     }
675 }
676
677 /*
678  * Set monitor configuration according to client request.
679  *
680  * On exit send current configuration to client, regardless of error.
681  *
682  * Errors:
683  *  screen size too large for driver to handle. (we set the largest/smallest possible)
684  *  no randr support in X server.
685  *  invalid configuration request from client.
686  */
687 void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
688                                     VDAgentMonitorsConfig *mon_config,
689                                     int fallback)
690 {
691     int width, height;
692     int x, y;
693     int primary_w, primary_h;
694     int i, real_num_of_monitors = 0;
695     VDAgentMonitorsConfig *curr = NULL;
696
697     if (!x11->has_xrandr)
698         goto exit;
699
700     if (enabled_monitors(mon_config) < 1) {
701         syslog(LOG_ERR, "client sent config with all monitors disabled");
702         goto exit;
703     }
704
705     if (x11->debug) {
706         dump_monitors_config(x11, mon_config, "from guest");
707     }
708
709     for (i = 0; i < mon_config->num_of_monitors; i++) {
710         if (monitor_enabled(&mon_config->monitors[i]))
711             real_num_of_monitors = i + 1;
712     }
713     mon_config->num_of_monitors = real_num_of_monitors;
714
715     update_randr_res(x11, 0);
716     if (mon_config->num_of_monitors > x11->randr.res->noutput) {
717         syslog(LOG_WARNING,
718                "warning unexpected client request: #mon %d > driver output %d",
719                mon_config->num_of_monitors, x11->randr.res->noutput);
720         mon_config->num_of_monitors = x11->randr.res->noutput;
721     }
722
723     if (mon_config->num_of_monitors > MONITOR_SIZE_COUNT) {
724         syslog(LOG_WARNING, "warning: client send %d monitors, capping at %d",
725                mon_config->num_of_monitors, MONITOR_SIZE_COUNT);
726         mon_config->num_of_monitors = MONITOR_SIZE_COUNT;
727     }
728
729     zero_base_monitors(x11, mon_config, &primary_w, &primary_h);
730
731     constrain_to_screen(x11, &primary_w, &primary_h);
732
733     if (x11->debug) {
734         dump_monitors_config(x11, mon_config, "after zeroing");
735     }
736
737     curr = get_current_mon_config(x11);
738     if (same_monitor_configs(mon_config, curr) &&
739            x11->width[0] == primary_w && x11->height[0] == primary_h) {
740         goto exit;
741     }
742
743     if (same_monitor_configs(mon_config, x11->randr.failed_conf)) {
744         syslog(LOG_WARNING, "Ignoring previous failed client monitor config");
745         goto exit;
746     }
747
748     for (i = mon_config->num_of_monitors; i < x11->randr.res->noutput; i++)
749         xrandr_disable_output(x11, i);
750
751     for (i = 0; i < mon_config->num_of_monitors; ++i) {
752         if (!monitor_enabled(&mon_config->monitors[i])) {
753             xrandr_disable_output(x11, i);
754             continue;
755         }
756         /* Try to create the requested resolution */
757         width = mon_config->monitors[i].width;
758         height = mon_config->monitors[i].height;
759         x = mon_config->monitors[i].x;
760         y = mon_config->monitors[i].y;
761         if (!xrandr_add_and_set(x11, i, x, y, width, height) &&
762                 enabled_monitors(mon_config) == 1) {
763             set_screen_to_best_size(x11, width, height,
764                                     &primary_w, &primary_h);
765             goto update;
766         }
767     }
768
769     if (primary_w != x11->width[0] || primary_h != x11->height[0]) {
770         if (x11->debug)
771             syslog(LOG_DEBUG, "Changing screen size to %dx%d",
772                    primary_w, primary_h);
773         vdagent_x11_set_error_handler(x11, error_handler);
774         XRRSetScreenSize(x11->display, x11->root_window[0], primary_w, primary_h,
775                          DisplayWidthMM(x11->display, 0),
776                          DisplayHeightMM(x11->display, 0));
777         if (vdagent_x11_restore_error_handler(x11)) {
778             syslog(LOG_ERR, "XRRSetScreenSize failed, not enough mem?");
779             if (!fallback && curr) {
780                 syslog(LOG_WARNING, "Restoring previous config");
781                 vdagent_x11_set_monitor_config(x11, curr, 1);
782                 free(curr);
783                 /* Remember this config failed, if the client is maximized or
784                    fullscreen it will keep sending the failing config. */
785                 free(x11->randr.failed_conf);
786                 x11->randr.failed_conf =
787                     malloc(config_size(mon_config->num_of_monitors));
788                 if (x11->randr.failed_conf)
789                     memcpy(x11->randr.failed_conf, mon_config,
790                            config_size(mon_config->num_of_monitors));
791                 return;
792             }
793         }
794     }
795
796 update:
797     update_randr_res(x11,
798         x11->randr.num_monitors != enabled_monitors(mon_config));
799     x11->width[0] = primary_w;
800     x11->height[0] = primary_h;
801
802     /* Flush output buffers and consume any pending events (ConfigureNotify) */
803     x11->dont_send_guest_xorg_res = 1;
804     vdagent_x11_do_read(x11);
805     x11->dont_send_guest_xorg_res = 0;
806
807 exit:
808     vdagent_x11_send_daemon_guest_xorg_res(x11, 0);
809
810     /* Flush output buffers and consume any pending events */
811     vdagent_x11_do_read(x11);
812     free(curr);
813 }
814
815 void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11, int update)
816 {
817     struct vdagentd_guest_xorg_resolution *res = NULL;
818     int i, width = 0, height = 0, screen_count = 0;
819
820     if (x11->has_xrandr) {
821         VDAgentMonitorsConfig *curr;
822
823         if (update)
824             update_randr_res(x11, 0);
825
826         curr = get_current_mon_config(x11);
827         if (!curr)
828             goto no_info;
829
830         screen_count = curr->num_of_monitors;
831         res = malloc(screen_count * sizeof(*res));
832         if (!res) {
833             free(curr);
834             goto no_mem;
835         }
836
837         for (i = 0; i < screen_count; i++) {
838             res[i].width  = curr->monitors[i].width;
839             res[i].height = curr->monitors[i].height;
840             res[i].x = curr->monitors[i].x;
841             res[i].y = curr->monitors[i].y;
842         }
843         free(curr);
844         width  = x11->width[0];
845         height = x11->height[0];
846     } else if (x11->has_xinerama) {
847         XineramaScreenInfo *screen_info = NULL;
848
849         screen_info = XineramaQueryScreens(x11->display, &screen_count);
850         if (!screen_info)
851             goto no_info;
852         res = malloc(screen_count * sizeof(*res));
853         if (!res) {
854             XFree(screen_info);
855             goto no_mem;
856         }
857         for (i = 0; i < screen_count; i++) {
858             if (screen_info[i].screen_number >= screen_count) {
859                 syslog(LOG_ERR, "Invalid screen number in xinerama screen info (%d >= %d)",
860                        screen_info[i].screen_number, screen_count);
861                 XFree(screen_info);
862                 free(res);
863                 return;
864             }
865             res[screen_info[i].screen_number].width = screen_info[i].width;
866             res[screen_info[i].screen_number].height = screen_info[i].height;
867             res[screen_info[i].screen_number].x = screen_info[i].x_org;
868             res[screen_info[i].screen_number].y = screen_info[i].y_org;
869         }
870         XFree(screen_info);
871         width  = x11->width[0];
872         height = x11->height[0];
873     } else {
874 no_info:
875         screen_count = x11->screen_count;
876         res = malloc(screen_count * sizeof(*res));
877         if (!res)
878             goto no_mem;
879         for (i = 0; i < screen_count; i++) {
880             res[i].width  = x11->width[i];
881             res[i].height = x11->height[i];
882             /* No way to get screen coordinates, assume rtl order */
883             res[i].x = width;
884             res[i].y = 0;
885             width += x11->width[i];
886             if (x11->height[i] > height)
887                 height = x11->height[i];
888         }
889     }
890
891     if (x11->debug) {
892         for (i = 0; i < screen_count; i++)
893             syslog(LOG_DEBUG, "Screen %d %dx%d%+d%+d", i, res[i].width,
894                    res[i].height, res[i].x, res[i].y);
895     }
896
897     udscs_write(x11->vdagentd, VDAGENTD_GUEST_XORG_RESOLUTION, width, height,
898                 (uint8_t *)res, screen_count * sizeof(*res));
899     free(res);
900     return;
901 no_mem:
902     syslog(LOG_ERR, "out of memory while trying to send resolutions, not sending resolutions.");
903 }