1 /* vdagent-x11-randr.c vdagent Xrandr integration code
3 Copyright 2012 Red Hat, Inc.
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/>.
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
)
43 static void arm_error_handler(struct vdagent_x11
*x11
)
46 XSync(x11
->display
, False
);
47 old_error_handler
= XSetErrorHandler(error_handler
);
50 static void check_error_handler(struct vdagent_x11
*x11
)
52 XSync(x11
->display
, False
);
53 XSetErrorHandler(old_error_handler
);
55 x11
->set_crtc_config_not_functional
= 1;
59 static XRRModeInfo
*mode_from_id(struct vdagent_x11
*x11
, int id
)
63 for (i
= 0 ; i
< x11
->randr
.res
->nmode
; ++i
) {
64 if (id
== x11
->randr
.res
->modes
[i
].id
) {
65 return &x11
->randr
.res
->modes
[i
];
71 static XRRCrtcInfo
*crtc_from_id(struct vdagent_x11
*x11
, int id
)
75 for (i
= 0 ; i
< x11
->randr
.res
->ncrtc
; ++i
) {
76 if (id
== x11
->randr
.res
->crtcs
[i
]) {
77 return x11
->randr
.crtcs
[i
];
83 static void free_randr_resources(struct vdagent_x11
*x11
)
87 if (!x11
->randr
.res
) {
90 if (x11
->randr
.outputs
!= NULL
) {
91 for (i
= 0 ; i
< x11
->randr
.res
->noutput
; ++i
) {
92 XRRFreeOutputInfo(x11
->randr
.outputs
[i
]);
94 free(x11
->randr
.outputs
);
96 if (x11
->randr
.crtcs
!= NULL
) {
97 for (i
= 0 ; i
< x11
->randr
.res
->ncrtc
; ++i
) {
98 XRRFreeCrtcInfo(x11
->randr
.crtcs
[i
]);
100 free(x11
->randr
.crtcs
);
102 XRRFreeScreenResources(x11
->randr
.res
);
103 x11
->randr
.res
= NULL
;
104 x11
->randr
.outputs
= NULL
;
105 x11
->randr
.crtcs
= NULL
;
108 static void update_randr_res(struct vdagent_x11
*x11
)
112 free_randr_resources(x11
);
113 /* Note: we don't need XRRGetScreenResourcesCurrent since we cache the changes */
114 x11
->randr
.res
= XRRGetScreenResources(x11
->display
, x11
->root_window
);
115 x11
->randr
.outputs
= malloc(x11
->randr
.res
->noutput
* sizeof(*x11
->randr
.outputs
));
116 x11
->randr
.crtcs
= malloc(x11
->randr
.res
->ncrtc
* sizeof(*x11
->randr
.crtcs
));
117 for (i
= 0 ; i
< x11
->randr
.res
->noutput
; ++i
) {
118 x11
->randr
.outputs
[i
] = XRRGetOutputInfo(x11
->display
, x11
->randr
.res
,
119 x11
->randr
.res
->outputs
[i
]);
121 for (i
= 0 ; i
< x11
->randr
.res
->ncrtc
; ++i
) {
122 x11
->randr
.crtcs
[i
] = XRRGetCrtcInfo(x11
->display
, x11
->randr
.res
,
123 x11
->randr
.res
->crtcs
[i
]);
125 /* XXX is this dynamic? should it be cached? */
126 if (XRRGetScreenSizeRange(x11
->display
, x11
->root_window
,
127 &x11
->randr
.min_width
,
128 &x11
->randr
.min_height
,
129 &x11
->randr
.max_width
,
130 &x11
->randr
.max_height
) != 1) {
131 syslog(LOG_ERR
, "update_randr_res: RRGetScreenSizeRange failed");
135 void vdagent_x11_randr_init(struct vdagent_x11
*x11
)
139 if (XRRQueryExtension(x11
->display
, &i
, &i
)) {
141 update_randr_res(x11
);
142 XRRQueryVersion(x11
->display
, &x11
->xrandr_major
, &x11
->xrandr_minor
);
144 x11
->randr
.res
= NULL
;
147 if (XineramaQueryExtension(x11
->display
, &i
, &i
))
148 x11
->has_xinerama
= 1;
150 switch (x11
->has_xrandr
<< 4 | x11
->has_xinerama
) {
152 syslog(LOG_ERR
, "Neither Xrandr nor Xinerama found, assuming single monitor setup");
156 syslog(LOG_DEBUG
, "Found Xinerama extension without Xrandr, assuming Xinerama multi monitor setup");
159 syslog(LOG_ERR
, "Found Xrandr but no Xinerama, weird!");
162 /* Standard xrandr setup, nothing to see here */
168 find_mode_by_name (struct vdagent_x11
*x11
, char *name
)
171 XRRModeInfo
*ret
= NULL
;
173 for (m
= 0; m
< x11
->randr
.res
->nmode
; m
++) {
174 XRRModeInfo
*mode
= &x11
->randr
.res
->modes
[m
];
175 if (!strcmp (name
, mode
->name
)) {
184 find_mode_by_size (struct vdagent_x11
*x11
, int width
, int height
)
187 XRRModeInfo
*ret
= NULL
;
189 for (m
= 0; m
< x11
->randr
.res
->nmode
; m
++) {
190 XRRModeInfo
*mode
= &x11
->randr
.res
->modes
[m
];
191 if (mode
->width
== width
&& mode
->height
== height
) {
199 static void delete_mode(struct vdagent_x11
*x11
, int output_index
)
203 XRROutputInfo
*output_info
;
204 XRRCrtcInfo
*crtc_info
;
206 int current_mode
= -1;
209 output_info
= x11
->randr
.outputs
[output_index
];
210 if (output_info
->ncrtc
!= 1) {
211 syslog(LOG_ERR
, "output has %d crtcs, expected exactly 1, "
212 "failed to delete mode", output_info
->ncrtc
);
215 crtc_info
= crtc_from_id(x11
, output_info
->crtcs
[0]);
216 current_mode
= crtc_info
->mode
;
217 crtc
= output_info
->crtc
;
218 snprintf(name
, sizeof(name
), "vdagent-%d", output_index
);
219 for (m
= 0 ; m
< x11
->randr
.res
->nmode
; ++m
) {
220 mode
= &x11
->randr
.res
->modes
[m
];
221 if (strcmp(mode
->name
, name
) == 0)
224 if (m
< x11
->randr
.res
->nmode
) {
225 if (crtc
&& mode
->id
== current_mode
) {
227 "delete_mode of in use mode, setting crtc to NULL mode");
228 XRRSetCrtcConfig(x11
->display
, x11
->randr
.res
, crtc
,
229 CurrentTime
, 0, 0, None
, RR_Rotate_0
, NULL
, 0);
231 XRRDeleteOutputMode (x11
->display
, x11
->randr
.res
->outputs
[output_index
],
233 XRRDestroyMode (x11
->display
, mode
->id
);
237 static void set_reduced_cvt_mode(XRRModeInfo
*mode
, int width
, int height
)
239 /* Code taken from hw/xfree86/modes/xf86cvt.c
240 * See that file for lineage. Originated in public domain code
241 * Would be nice if xorg exported this in a library */
243 /* 1) top/bottom margin size (% of height) - default: 1.8 */
244 #define CVT_MARGIN_PERCENTAGE 1.8
246 /* 2) character cell horizontal granularity (pixels) - default 8 */
247 #define CVT_H_GRANULARITY 8
249 /* 4) Minimum vertical porch (lines) - default 3 */
250 #define CVT_MIN_V_PORCH 3
252 /* 4) Minimum number of vertical back porch lines - default 6 */
253 #define CVT_MIN_V_BPORCH 6
255 /* Pixel Clock step (kHz) */
256 #define CVT_CLOCK_STEP 250
258 /* Minimum vertical blanking interval time (µs) - default 460 */
259 #define CVT_RB_MIN_VBLANK 460.0
261 /* Fixed number of clocks for horizontal sync */
262 #define CVT_RB_H_SYNC 32.0
264 /* Fixed number of clocks for horizontal blanking */
265 #define CVT_RB_H_BLANK 160.0
267 /* Fixed number of lines for vertical front porch - default 3 */
268 #define CVT_RB_VFPORCH 3
271 float VFieldRate
= 60.0;
275 /* 2. Horizontal pixels */
276 width
= width
- (width
% CVT_H_GRANULARITY
);
279 mode
->height
= height
;
282 /* 8. Estimate Horizontal period. */
283 HPeriod
= ((float) (1000000.0 / VFieldRate
- CVT_RB_MIN_VBLANK
)) / height
;
285 /* 9. Find number of lines in vertical blanking */
286 VBILines
= ((float) CVT_RB_MIN_VBLANK
) / HPeriod
+ 1;
288 /* 10. Check if vertical blanking is sufficient */
289 if (VBILines
< (CVT_RB_VFPORCH
+ VSync
+ CVT_MIN_V_BPORCH
))
290 VBILines
= CVT_RB_VFPORCH
+ VSync
+ CVT_MIN_V_BPORCH
;
292 /* 11. Find total number of lines in vertical field */
293 mode
->vTotal
= height
+ VBILines
;
295 /* 12. Find total number of pixels in a line */
296 mode
->hTotal
= mode
->width
+ CVT_RB_H_BLANK
;
298 /* Fill in HSync values */
299 mode
->hSyncEnd
= mode
->width
+ CVT_RB_H_BLANK
/ 2;
300 mode
->hSyncStart
= mode
->hSyncEnd
- CVT_RB_H_SYNC
;
302 /* Fill in VSync values */
303 mode
->vSyncStart
= mode
->height
+ CVT_RB_VFPORCH
;
304 mode
->vSyncEnd
= mode
->vSyncStart
+ VSync
;
306 /* 15/13. Find pixel clock frequency (kHz for xf86) */
307 mode
->dotClock
= mode
->hTotal
* 1000.0 / HPeriod
;
308 mode
->dotClock
-= mode
->dotClock
% CVT_CLOCK_STEP
;
312 static XRRModeInfo
*create_new_mode(struct vdagent_x11
*x11
, int output_index
,
313 int width
, int height
)
318 /* we may be using this mode from an old invocation - check first */
319 snprintf(modename
, sizeof(modename
), "vdagent-%d", output_index
);
320 arm_error_handler(x11
);
321 delete_mode(x11
, output_index
);
322 check_error_handler(x11
);
323 if (x11
->set_crtc_config_not_functional
) {
327 mode
.name
= modename
;
328 mode
.nameLength
= strlen(mode
.name
);
329 set_reduced_cvt_mode(&mode
, width
, height
);
333 XRRCreateMode (x11
->display
, x11
->root_window
, &mode
);
334 /* silly to update everytime for more then one monitor */
335 update_randr_res(x11
);
336 return find_mode_by_name(x11
, modename
);
339 static int xrandr_add_and_set(struct vdagent_x11
*x11
, int output
, int x
, int y
,
340 int width
, int height
)
347 if (!x11
->randr
.res
|| output
>= x11
->randr
.res
->noutput
|| output
< 0) {
348 syslog(LOG_ERR
, "%s: program error: missing RANDR or bad output",
352 if (x11
->set_crtc_config_not_functional
) {
353 /* fail, set_best_mode will find something close. */
356 xid
= x11
->randr
.res
->outputs
[output
];
357 mode
= find_mode_by_size(x11
, width
, height
);
359 mode
= create_new_mode(x11
, output
, width
, height
);
362 syslog(LOG_ERR
, "failed to add a new mode");
365 XRRAddOutputMode(x11
->display
, xid
, mode
->id
);
367 s
= XRRSetCrtcConfig(x11
->display
, x11
->randr
.res
, x11
->randr
.res
->crtcs
[output
],
368 CurrentTime
, x
, y
, mode
->id
, RR_Rotate_0
, outputs
,
370 if (s
!= RRSetConfigSuccess
) {
371 syslog(LOG_ERR
, "failed to XRRSetCrtcConfig");
372 delete_mode(x11
, output
);
373 x11
->set_crtc_config_not_functional
= 1;
379 static void xrandr_disable_output(struct vdagent_x11
*x11
, int output
)
383 if (!x11
->randr
.res
|| output
>= x11
->randr
.res
->noutput
|| output
< 0) {
384 syslog(LOG_ERR
, "%s: program error: missing RANDR or bad output",
389 s
= XRRSetCrtcConfig(x11
->display
, x11
->randr
.res
,
390 x11
->randr
.res
->crtcs
[output
],
391 CurrentTime
, 0, 0, None
, RR_Rotate_0
,
394 if (s
!= RRSetConfigSuccess
)
395 syslog(LOG_ERR
, "failed to disable monitor");
398 static int set_screen_to_best_size(struct vdagent_x11
*x11
, int width
, int height
,
399 int *out_width
, int *out_height
){
400 int i
, num_sizes
= 0;
402 unsigned int closest_diff
= -1;
403 XRRScreenSize
*sizes
;
404 XRRScreenConfiguration
*config
;
407 sizes
= XRRSizes(x11
->display
, x11
->screen
, &num_sizes
);
408 if (!sizes
|| !num_sizes
) {
409 syslog(LOG_ERR
, "XRRSizes failed");
413 syslog(LOG_DEBUG
, "set_screen_to_best_size found %d modes\n", num_sizes
);
415 /* Find the closest size which will fit within the monitor */
416 for (i
= 0; i
< num_sizes
; i
++) {
417 if (sizes
[i
].width
> width
||
418 sizes
[i
].height
> height
)
419 continue; /* Too large for the monitor */
421 unsigned int wdiff
= width
- sizes
[i
].width
;
422 unsigned int hdiff
= height
- sizes
[i
].height
;
423 unsigned int diff
= wdiff
* wdiff
+ hdiff
* hdiff
;
424 if (diff
< closest_diff
) {
431 syslog(LOG_ERR
, "no suitable resolution found for monitor");
435 config
= XRRGetScreenInfo(x11
->display
, x11
->root_window
);
437 syslog(LOG_ERR
, "get screen info failed");
440 XRRConfigCurrentConfiguration(config
, &rotation
);
441 XRRSetScreenConfig(x11
->display
, config
, x11
->root_window
, best
,
442 rotation
, CurrentTime
);
443 XRRFreeScreenConfigInfo(config
);
446 syslog(LOG_DEBUG
, "set_screen_to_best_size set size to: %dx%d\n",
447 sizes
[best
].width
, sizes
[best
].height
);
448 *out_width
= sizes
[best
].width
;
449 *out_height
= sizes
[best
].height
;
453 void vdagent_x11_randr_handle_root_size_change(struct vdagent_x11
*x11
,
454 int width
, int height
)
456 if (width
== x11
->width
&& height
== x11
->height
) {
461 x11
->height
= height
;
462 if (!x11
->dont_send_guest_xorg_res
) {
463 vdagent_x11_send_daemon_guest_xorg_res(x11
);
467 static uint32_t min_int(uint32_t x
, uint32_t y
)
469 return x
> y
? y
: x
;
472 static uint32_t max_int(uint32_t x
, uint32_t y
)
474 return x
> y
? x
: y
;
477 static int constrain_to_range(uint32_t low
, uint32_t *val
, uint32_t high
)
479 if (low
<= *val
&& *val
<= high
) {
491 static void constrain_to_screen(struct vdagent_x11
*x11
, uint32_t *w
, uint32_t *h
)
493 uint32_t lx
, ly
, hx
, hy
;
494 uint32_t orig_h
= *h
;
495 uint32_t orig_w
= *w
;
497 lx
= x11
->randr
.min_width
;
498 hx
= x11
->randr
.max_width
;
499 ly
= x11
->randr
.min_height
;
500 hy
= x11
->randr
.max_height
;
501 if (constrain_to_range(lx
, w
, hx
)) {
502 syslog(LOG_ERR
, "width not in driver range: ! %d < %d < %d",
505 if (constrain_to_range(ly
, h
, hy
)) {
506 syslog(LOG_ERR
, "height not in driver range: ! %d < %d < %d",
512 * The agent config doesn't contain a primary size, just the monitors, but
513 * we need to total size as well, to make sure we have enough memory and
514 * because X needs it.
516 * At the same pass constrain any provided size to what the server accepts.
519 * x >= 0, y >= 0 for all x, y in mon_config
520 * max_width >= width >= min_width,
521 * max_height >= height >= min_height for all monitors in mon_config
523 static void zero_base_monitors(struct vdagent_x11
*x11
,
524 VDAgentMonitorsConfig
*mon_config
,
525 uint32_t *width
, uint32_t *height
)
528 uint32_t min_x
, min_y
, max_x
, max_y
;
529 uint32_t *mon_height
, *mon_width
;
531 mon_width
= &mon_config
->monitors
[i
].width
;
532 mon_height
= &mon_config
->monitors
[i
].height
;
533 constrain_to_screen(x11
, mon_width
, mon_height
);
534 min_x
= mon_config
->monitors
[0].x
;
535 min_y
= mon_config
->monitors
[0].y
;
536 max_x
= mon_config
->monitors
[0].width
+ min_x
;
537 max_y
= mon_config
->monitors
[0].height
+ min_y
;
538 for (++i
; i
< mon_config
->num_of_monitors
; ++i
) {
539 mon_width
= &mon_config
->monitors
[i
].width
;
540 mon_height
= &mon_config
->monitors
[i
].height
;
541 constrain_to_screen(x11
, mon_width
, mon_height
);
542 min_x
= min_int(mon_config
->monitors
[i
].x
, min_x
);
543 min_y
= min_int(mon_config
->monitors
[i
].y
, min_y
);
544 max_x
= max_int(mon_config
->monitors
[i
].x
+ *mon_width
, max_x
);
545 max_y
= max_int(mon_config
->monitors
[i
].y
+ *mon_height
, max_y
);
547 if (min_x
!= 0 || min_y
!= 0) {
548 syslog(LOG_ERR
, "%s: agent config %d,%d rooted, adjusting to 0,0.",
549 __FUNCTION__
, min_x
, min_y
);
550 for (i
= 0 ; i
< mon_config
->num_of_monitors
; ++i
) {
551 mon_config
->monitors
[i
].x
-= min_x
;
552 mon_config
->monitors
[i
].y
-= min_y
;
561 static int same_monitor_configs(struct vdagent_x11
*x11
, VDAgentMonitorsConfig
*mon
)
566 VDAgentMonConfig
*client_mode
;
567 XRRScreenResources
*res
= x11
->randr
.res
;
569 if (res
->noutput
> res
->ncrtc
) {
570 syslog(LOG_ERR
, "error: unexpected noutput > ncrtc in driver");
574 if (mon
->num_of_monitors
> res
->noutput
) {
575 for (i
= res
->noutput
; i
< mon
->num_of_monitors
; i
++) {
576 if (mon
->monitors
[i
].width
|| mon
->monitors
[i
].height
) {
578 "warning: unexpected client request: #mon %d > driver output %d",
579 mon
->num_of_monitors
, res
->noutput
);
583 mon
->num_of_monitors
= res
->noutput
;
586 for (i
= 0 ; i
< mon
->num_of_monitors
; ++i
) {
587 if (x11
->randr
.outputs
[i
]->ncrtc
== 0) {
590 if (x11
->randr
.outputs
[i
]->ncrtc
!= 1) {
591 syslog(LOG_ERR
, "error: unexpected driver config, ncrtc %d != 1",
592 x11
->randr
.outputs
[i
]->ncrtc
);
595 crtc
= crtc_from_id(x11
, x11
->randr
.outputs
[i
]->crtcs
[0]);
597 syslog(LOG_ERR
, "error: inconsistent or stale data from X");
600 client_mode
= &mon
->monitors
[i
];
601 mode
= mode_from_id(x11
, crtc
->mode
);
605 /* NOTE: we don't compare depth. */
606 /* NOTE 2: width set by X is a multiple of 8, so ignore lower 3 bits */
607 if ((mode
->width
& ~7) != (client_mode
->width
& ~7) ||
608 mode
->height
!= client_mode
->height
||
609 (crtc
->x
& ~7) != (client_mode
->x
& ~7) ||
610 crtc
->y
!= client_mode
->y
) {
617 static void dump_monitors_config(struct vdagent_x11
*x11
,
618 VDAgentMonitorsConfig
*mon_config
,
624 syslog(LOG_DEBUG
, "%s: %d, %x", prefix
, mon_config
->num_of_monitors
,
626 for (i
= 0 ; i
< mon_config
->num_of_monitors
; ++i
) {
627 m
= &mon_config
->monitors
[i
];
628 syslog(LOG_DEBUG
, "received monitor %d config %dx%d+%d+%d", i
,
629 m
->width
, m
->height
, m
->x
, m
->y
);
634 * Set monitor configuration according to client request.
636 * On exit send current configuration to client, regardless of error.
639 * screen size too large for driver to handle. (we set the largest/smallest possible)
640 * no randr support in X server.
641 * invalid configuration request from client.
643 void vdagent_x11_set_monitor_config(struct vdagent_x11
*x11
,
644 VDAgentMonitorsConfig
*mon_config
)
649 uint32_t primary_w
, primary_h
;
651 if (!x11
->has_xrandr
)
654 if (mon_config
->num_of_monitors
< 1) {
655 syslog(LOG_ERR
, "client sent invalid monitor config number %d",
656 mon_config
->num_of_monitors
);
660 if (same_monitor_configs(x11
, mon_config
)) {
665 dump_monitors_config(x11
, mon_config
, "from guest");
668 zero_base_monitors(x11
, mon_config
, &primary_w
, &primary_h
);
670 constrain_to_screen(x11
, &primary_w
, &primary_h
);
673 dump_monitors_config(x11
, mon_config
, "after zeroing");
676 for (i
= mon_config
->num_of_monitors
; i
< x11
->randr
.res
->noutput
; i
++)
677 xrandr_disable_output(x11
, i
);
679 for (i
= 0; i
< mon_config
->num_of_monitors
; ++i
) {
680 /* Try to create the requested resolution */
681 width
= mon_config
->monitors
[i
].width
;
682 height
= mon_config
->monitors
[i
].height
;
683 x
= mon_config
->monitors
[i
].x
;
684 y
= mon_config
->monitors
[i
].y
;
685 if (!xrandr_add_and_set(x11
, i
, x
, y
, width
, height
) &&
686 mon_config
->num_of_monitors
== 1) {
687 set_screen_to_best_size(x11
, width
, height
,
688 &primary_w
, &primary_h
);
693 if (primary_w
!= x11
->width
|| primary_h
!= x11
->height
) {
694 arm_error_handler(x11
);
695 XRRSetScreenSize(x11
->display
, x11
->root_window
, primary_w
, primary_h
,
696 DisplayWidthMM(x11
->display
, x11
->screen
),
697 DisplayHeightMM(x11
->display
, x11
->screen
));
698 check_error_handler(x11
);
702 update_randr_res(x11
);
703 x11
->width
= primary_w
;
704 x11
->height
= primary_h
;
706 /* Flush output buffers and consume any pending events (ConfigureNotify) */
707 x11
->dont_send_guest_xorg_res
= 1;
708 vdagent_x11_do_read(x11
);
709 x11
->dont_send_guest_xorg_res
= 0;
712 vdagent_x11_send_daemon_guest_xorg_res(x11
);
714 /* Flush output buffers and consume any pending events */
715 vdagent_x11_do_read(x11
);
718 void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11
*x11
)
720 struct vdagentd_guest_xorg_resolution
*res
= NULL
;
721 XineramaScreenInfo
*screen_info
= NULL
;
722 int i
, screen_count
= 0;
724 if (x11
->has_xinerama
) {
725 /* Xinerama reports the same information RANDR reports, so stay
726 * with Xinerama for support of Xinerama only setups */
727 screen_info
= XineramaQueryScreens(x11
->display
, &screen_count
);
730 if (screen_count
== 0)
733 res
= malloc(screen_count
* sizeof(*res
));
735 syslog(LOG_ERR
, "out of memory while trying to send resolutions, not sending resolutions.");
742 for (i
= 0; i
< screen_count
; i
++) {
743 if (screen_info
[i
].screen_number
>= screen_count
) {
744 syslog(LOG_ERR
, "Invalid screen number in xinerama screen info (%d >= %d)",
745 screen_info
[i
].screen_number
, screen_count
);
750 res
[screen_info
[i
].screen_number
].width
= screen_info
[i
].width
;
751 res
[screen_info
[i
].screen_number
].height
= screen_info
[i
].height
;
752 res
[screen_info
[i
].screen_number
].x
= screen_info
[i
].x_org
;
753 res
[screen_info
[i
].screen_number
].y
= screen_info
[i
].y_org
;
757 res
[0].width
= x11
->width
;
758 res
[0].height
= x11
->height
;
763 udscs_write(x11
->vdagentd
, VDAGENTD_GUEST_XORG_RESOLUTION
, x11
->width
,
764 x11
->height
, (uint8_t *)res
, screen_count
* sizeof(*res
));