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 int check_error_handler(struct vdagent_x11
*x11
)
54 XSync(x11
->display
, False
);
55 XSetErrorHandler(old_error_handler
);
62 static XRRModeInfo
*mode_from_id(struct vdagent_x11
*x11
, int id
)
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
];
74 static XRRCrtcInfo
*crtc_from_id(struct vdagent_x11
*x11
, int id
)
78 for (i
= 0 ; i
< x11
->randr
.res
->ncrtc
; ++i
) {
79 if (id
== x11
->randr
.res
->crtcs
[i
]) {
80 return x11
->randr
.crtcs
[i
];
86 static void free_randr_resources(struct vdagent_x11
*x11
)
90 if (!x11
->randr
.res
) {
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
;
111 static void update_randr_res(struct vdagent_x11
*x11
)
115 free_randr_resources(x11
);
116 /* Note: we don't need XRRGetScreenResourcesCurrent since we cache the changes */
117 x11
->randr
.res
= XRRGetScreenResources(x11
->display
, x11
->root_window
);
118 x11
->randr
.outputs
= malloc(x11
->randr
.res
->noutput
* sizeof(*x11
->randr
.outputs
));
119 x11
->randr
.crtcs
= malloc(x11
->randr
.res
->ncrtc
* sizeof(*x11
->randr
.crtcs
));
120 for (i
= 0 ; i
< x11
->randr
.res
->noutput
; ++i
) {
121 x11
->randr
.outputs
[i
] = XRRGetOutputInfo(x11
->display
, x11
->randr
.res
,
122 x11
->randr
.res
->outputs
[i
]);
124 for (i
= 0 ; i
< x11
->randr
.res
->ncrtc
; ++i
) {
125 x11
->randr
.crtcs
[i
] = XRRGetCrtcInfo(x11
->display
, x11
->randr
.res
,
126 x11
->randr
.res
->crtcs
[i
]);
128 /* XXX is this dynamic? should it be cached? */
129 if (XRRGetScreenSizeRange(x11
->display
, x11
->root_window
,
130 &x11
->randr
.min_width
,
131 &x11
->randr
.min_height
,
132 &x11
->randr
.max_width
,
133 &x11
->randr
.max_height
) != 1) {
134 syslog(LOG_ERR
, "update_randr_res: RRGetScreenSizeRange failed");
138 void vdagent_x11_randr_init(struct vdagent_x11
*x11
)
142 if (XRRQueryExtension(x11
->display
, &i
, &i
)) {
144 update_randr_res(x11
);
145 XRRQueryVersion(x11
->display
, &x11
->xrandr_major
, &x11
->xrandr_minor
);
147 x11
->randr
.res
= NULL
;
150 if (XineramaQueryExtension(x11
->display
, &i
, &i
))
151 x11
->has_xinerama
= 1;
153 switch (x11
->has_xrandr
<< 4 | x11
->has_xinerama
) {
155 syslog(LOG_ERR
, "Neither Xrandr nor Xinerama found, assuming single monitor setup");
159 syslog(LOG_DEBUG
, "Found Xinerama extension without Xrandr, assuming Xinerama multi monitor setup");
162 syslog(LOG_ERR
, "Found Xrandr but no Xinerama, weird!");
165 /* Standard xrandr setup, nothing to see here */
171 find_mode_by_name (struct vdagent_x11
*x11
, char *name
)
174 XRRModeInfo
*ret
= NULL
;
176 for (m
= 0; m
< x11
->randr
.res
->nmode
; m
++) {
177 XRRModeInfo
*mode
= &x11
->randr
.res
->modes
[m
];
178 if (!strcmp (name
, mode
->name
)) {
187 find_mode_by_size (struct vdagent_x11
*x11
, int width
, int height
)
190 XRRModeInfo
*ret
= NULL
;
192 for (m
= 0; m
< x11
->randr
.res
->nmode
; m
++) {
193 XRRModeInfo
*mode
= &x11
->randr
.res
->modes
[m
];
194 if (mode
->width
== width
&& mode
->height
== height
) {
202 static void delete_mode(struct vdagent_x11
*x11
, int output_index
, const char* name
)
206 XRROutputInfo
*output_info
;
207 XRRCrtcInfo
*crtc_info
;
209 int current_mode
= -1;
211 output_info
= x11
->randr
.outputs
[output_index
];
212 if (output_info
->ncrtc
!= 1) {
213 syslog(LOG_ERR
, "output has %d crtcs, expected exactly 1, "
214 "failed to delete mode", output_info
->ncrtc
);
217 crtc_info
= crtc_from_id(x11
, output_info
->crtcs
[0]);
218 current_mode
= crtc_info
->mode
;
219 crtc
= output_info
->crtc
;
220 for (m
= 0 ; m
< x11
->randr
.res
->nmode
; ++m
) {
221 mode
= &x11
->randr
.res
->modes
[m
];
222 if (strcmp(mode
->name
, name
) == 0)
225 if (m
< x11
->randr
.res
->nmode
) {
226 arm_error_handler(x11
);
227 XRRDeleteOutputMode (x11
->display
, x11
->randr
.res
->outputs
[output_index
],
229 XRRDestroyMode (x11
->display
, mode
->id
);
230 // ignore race error, if mode is created by others
231 check_error_handler(x11
);
234 /* silly to update everytime for more then one monitor */
235 update_randr_res(x11
);
238 static void set_reduced_cvt_mode(XRRModeInfo
*mode
, int width
, int height
)
240 /* Code taken from hw/xfree86/modes/xf86cvt.c
241 * See that file for lineage. Originated in public domain code
242 * Would be nice if xorg exported this in a library */
244 /* 1) top/bottom margin size (% of height) - default: 1.8 */
245 #define CVT_MARGIN_PERCENTAGE 1.8
247 /* 2) character cell horizontal granularity (pixels) - default 8 */
248 #define CVT_H_GRANULARITY 8
250 /* 4) Minimum vertical porch (lines) - default 3 */
251 #define CVT_MIN_V_PORCH 3
253 /* 4) Minimum number of vertical back porch lines - default 6 */
254 #define CVT_MIN_V_BPORCH 6
256 /* Pixel Clock step (kHz) */
257 #define CVT_CLOCK_STEP 250
259 /* Minimum vertical blanking interval time (µs) - default 460 */
260 #define CVT_RB_MIN_VBLANK 460.0
262 /* Fixed number of clocks for horizontal sync */
263 #define CVT_RB_H_SYNC 32.0
265 /* Fixed number of clocks for horizontal blanking */
266 #define CVT_RB_H_BLANK 160.0
268 /* Fixed number of lines for vertical front porch - default 3 */
269 #define CVT_RB_VFPORCH 3
272 float VFieldRate
= 60.0;
276 /* 2. Horizontal pixels */
277 width
= width
- (width
% CVT_H_GRANULARITY
);
280 mode
->height
= height
;
283 /* 8. Estimate Horizontal period. */
284 HPeriod
= ((float) (1000000.0 / VFieldRate
- CVT_RB_MIN_VBLANK
)) / height
;
286 /* 9. Find number of lines in vertical blanking */
287 VBILines
= ((float) CVT_RB_MIN_VBLANK
) / HPeriod
+ 1;
289 /* 10. Check if vertical blanking is sufficient */
290 if (VBILines
< (CVT_RB_VFPORCH
+ VSync
+ CVT_MIN_V_BPORCH
))
291 VBILines
= CVT_RB_VFPORCH
+ VSync
+ CVT_MIN_V_BPORCH
;
293 /* 11. Find total number of lines in vertical field */
294 mode
->vTotal
= height
+ VBILines
;
296 /* 12. Find total number of pixels in a line */
297 mode
->hTotal
= mode
->width
+ CVT_RB_H_BLANK
;
299 /* Fill in HSync values */
300 mode
->hSyncEnd
= mode
->width
+ CVT_RB_H_BLANK
/ 2;
301 mode
->hSyncStart
= mode
->hSyncEnd
- CVT_RB_H_SYNC
;
303 /* Fill in VSync values */
304 mode
->vSyncStart
= mode
->height
+ CVT_RB_VFPORCH
;
305 mode
->vSyncEnd
= mode
->vSyncStart
+ VSync
;
307 /* 15/13. Find pixel clock frequency (kHz for xf86) */
308 mode
->dotClock
= mode
->hTotal
* 1000.0 / HPeriod
;
309 mode
->dotClock
-= mode
->dotClock
% CVT_CLOCK_STEP
;
313 static XRRModeInfo
*create_new_mode(struct vdagent_x11
*x11
, int output_index
,
314 int width
, int height
)
319 snprintf(modename
, sizeof(modename
), "%dx%d-%d", width
, height
, output_index
);
321 mode
.name
= modename
;
322 mode
.nameLength
= strlen(mode
.name
);
323 set_reduced_cvt_mode(&mode
, width
, height
);
326 arm_error_handler(x11
);
327 XRRCreateMode (x11
->display
, x11
->root_window
, &mode
);
328 // ignore race error, if mode is created by others
329 check_error_handler(x11
);
331 /* silly to update everytime for more then one monitor */
332 update_randr_res(x11
);
334 return find_mode_by_name(x11
, modename
);
337 static int xrandr_add_and_set(struct vdagent_x11
*x11
, int output
, int x
, int y
,
338 int width
, int height
)
346 if (!x11
->randr
.res
|| output
>= x11
->randr
.res
->noutput
|| output
< 0) {
347 syslog(LOG_ERR
, "%s: program error: missing RANDR or bad output",
351 if (x11
->set_crtc_config_not_functional
) {
352 /* fail, set_best_mode will find something close. */
355 xid
= x11
->randr
.res
->outputs
[output
];
356 mode
= find_mode_by_size(x11
, width
, height
);
358 mode
= create_new_mode(x11
, output
, width
, height
);
361 syslog(LOG_ERR
, "failed to add a new mode");
364 XRRAddOutputMode(x11
->display
, xid
, mode
->id
);
366 s
= XRRSetCrtcConfig(x11
->display
, x11
->randr
.res
, x11
->randr
.res
->crtcs
[output
],
367 CurrentTime
, x
, y
, mode
->id
, RR_Rotate_0
, outputs
,
370 if (s
!= RRSetConfigSuccess
) {
371 syslog(LOG_ERR
, "failed to XRRSetCrtcConfig");
372 x11
->set_crtc_config_not_functional
= 1;
376 /* clean the previous name, if any */
377 snprintf(modename
, sizeof(modename
), "%dx%d-%d", x11
->width
, x11
->height
, output
);
378 delete_mode(x11
, output
, modename
);
383 static void xrandr_disable_output(struct vdagent_x11
*x11
, int output
)
387 if (!x11
->randr
.res
|| output
>= x11
->randr
.res
->noutput
|| output
< 0) {
388 syslog(LOG_ERR
, "%s: program error: missing RANDR or bad output",
393 s
= XRRSetCrtcConfig(x11
->display
, x11
->randr
.res
,
394 x11
->randr
.res
->crtcs
[output
],
395 CurrentTime
, 0, 0, None
, RR_Rotate_0
,
398 if (s
!= RRSetConfigSuccess
)
399 syslog(LOG_ERR
, "failed to disable monitor");
402 static int set_screen_to_best_size(struct vdagent_x11
*x11
, int width
, int height
,
403 int *out_width
, int *out_height
){
404 int i
, num_sizes
= 0;
406 unsigned int closest_diff
= -1;
407 XRRScreenSize
*sizes
;
408 XRRScreenConfiguration
*config
;
411 sizes
= XRRSizes(x11
->display
, x11
->screen
, &num_sizes
);
412 if (!sizes
|| !num_sizes
) {
413 syslog(LOG_ERR
, "XRRSizes failed");
417 syslog(LOG_DEBUG
, "set_screen_to_best_size found %d modes\n", num_sizes
);
419 /* Find the closest size which will fit within the monitor */
420 for (i
= 0; i
< num_sizes
; i
++) {
421 if (sizes
[i
].width
> width
||
422 sizes
[i
].height
> height
)
423 continue; /* Too large for the monitor */
425 unsigned int wdiff
= width
- sizes
[i
].width
;
426 unsigned int hdiff
= height
- sizes
[i
].height
;
427 unsigned int diff
= wdiff
* wdiff
+ hdiff
* hdiff
;
428 if (diff
< closest_diff
) {
435 syslog(LOG_ERR
, "no suitable resolution found for monitor");
439 config
= XRRGetScreenInfo(x11
->display
, x11
->root_window
);
441 syslog(LOG_ERR
, "get screen info failed");
444 XRRConfigCurrentConfiguration(config
, &rotation
);
445 XRRSetScreenConfig(x11
->display
, config
, x11
->root_window
, best
,
446 rotation
, CurrentTime
);
447 XRRFreeScreenConfigInfo(config
);
450 syslog(LOG_DEBUG
, "set_screen_to_best_size set size to: %dx%d\n",
451 sizes
[best
].width
, sizes
[best
].height
);
452 *out_width
= sizes
[best
].width
;
453 *out_height
= sizes
[best
].height
;
457 void vdagent_x11_randr_handle_root_size_change(struct vdagent_x11
*x11
,
458 int width
, int height
)
460 if (width
== x11
->width
&& height
== x11
->height
) {
465 x11
->height
= height
;
466 if (!x11
->dont_send_guest_xorg_res
) {
467 vdagent_x11_send_daemon_guest_xorg_res(x11
);
471 static uint32_t min_int(uint32_t x
, uint32_t y
)
473 return x
> y
? y
: x
;
476 static uint32_t max_int(uint32_t x
, uint32_t y
)
478 return x
> y
? x
: y
;
481 static int constrain_to_range(uint32_t low
, uint32_t *val
, uint32_t high
)
483 if (low
<= *val
&& *val
<= high
) {
495 static void constrain_to_screen(struct vdagent_x11
*x11
, uint32_t *w
, uint32_t *h
)
497 uint32_t lx
, ly
, hx
, hy
;
498 uint32_t orig_h
= *h
;
499 uint32_t orig_w
= *w
;
501 lx
= x11
->randr
.min_width
;
502 hx
= x11
->randr
.max_width
;
503 ly
= x11
->randr
.min_height
;
504 hy
= x11
->randr
.max_height
;
505 if (constrain_to_range(lx
, w
, hx
)) {
506 syslog(LOG_ERR
, "width not in driver range: ! %d < %d < %d",
509 if (constrain_to_range(ly
, h
, hy
)) {
510 syslog(LOG_ERR
, "height not in driver range: ! %d < %d < %d",
516 * The agent config doesn't contain a primary size, just the monitors, but
517 * we need to total size as well, to make sure we have enough memory and
518 * because X needs it.
520 * At the same pass constrain any provided size to what the server accepts.
523 * x >= 0, y >= 0 for all x, y in mon_config
524 * max_width >= width >= min_width,
525 * max_height >= height >= min_height for all monitors in mon_config
527 static void zero_base_monitors(struct vdagent_x11
*x11
,
528 VDAgentMonitorsConfig
*mon_config
,
529 uint32_t *width
, uint32_t *height
)
532 uint32_t min_x
, min_y
, max_x
, max_y
;
533 uint32_t *mon_height
, *mon_width
;
535 mon_width
= &mon_config
->monitors
[i
].width
;
536 mon_height
= &mon_config
->monitors
[i
].height
;
537 constrain_to_screen(x11
, mon_width
, mon_height
);
538 min_x
= mon_config
->monitors
[0].x
;
539 min_y
= mon_config
->monitors
[0].y
;
540 max_x
= mon_config
->monitors
[0].width
+ min_x
;
541 max_y
= mon_config
->monitors
[0].height
+ min_y
;
542 for (++i
; i
< mon_config
->num_of_monitors
; ++i
) {
543 mon_width
= &mon_config
->monitors
[i
].width
;
544 mon_height
= &mon_config
->monitors
[i
].height
;
545 constrain_to_screen(x11
, mon_width
, mon_height
);
546 min_x
= min_int(mon_config
->monitors
[i
].x
, min_x
);
547 min_y
= min_int(mon_config
->monitors
[i
].y
, min_y
);
548 max_x
= max_int(mon_config
->monitors
[i
].x
+ *mon_width
, max_x
);
549 max_y
= max_int(mon_config
->monitors
[i
].y
+ *mon_height
, max_y
);
551 if (min_x
!= 0 || min_y
!= 0) {
552 syslog(LOG_ERR
, "%s: agent config %d,%d rooted, adjusting to 0,0.",
553 __FUNCTION__
, min_x
, min_y
);
554 for (i
= 0 ; i
< mon_config
->num_of_monitors
; ++i
) {
555 mon_config
->monitors
[i
].x
-= min_x
;
556 mon_config
->monitors
[i
].y
-= min_y
;
565 static int same_monitor_configs(struct vdagent_x11
*x11
, VDAgentMonitorsConfig
*mon
)
570 VDAgentMonConfig
*client_mode
;
571 XRRScreenResources
*res
= x11
->randr
.res
;
573 if (res
->noutput
> res
->ncrtc
) {
574 syslog(LOG_ERR
, "error: unexpected noutput > ncrtc in driver");
578 if (mon
->num_of_monitors
> res
->noutput
) {
579 for (i
= res
->noutput
; i
< mon
->num_of_monitors
; i
++) {
580 if (mon
->monitors
[i
].width
|| mon
->monitors
[i
].height
) {
582 "warning: unexpected client request: #mon %d > driver output %d",
583 mon
->num_of_monitors
, res
->noutput
);
587 mon
->num_of_monitors
= res
->noutput
;
590 for (i
= 0 ; i
< mon
->num_of_monitors
; ++i
) {
591 if (x11
->randr
.outputs
[i
]->ncrtc
== 0) {
594 if (x11
->randr
.outputs
[i
]->ncrtc
!= 1) {
595 syslog(LOG_ERR
, "error: unexpected driver config, ncrtc %d != 1",
596 x11
->randr
.outputs
[i
]->ncrtc
);
599 crtc
= crtc_from_id(x11
, x11
->randr
.outputs
[i
]->crtcs
[0]);
601 syslog(LOG_ERR
, "error: inconsistent or stale data from X");
604 client_mode
= &mon
->monitors
[i
];
605 mode
= mode_from_id(x11
, crtc
->mode
);
609 /* NOTE: we don't compare depth. */
610 /* NOTE 2: width set by X is a multiple of 8, so ignore lower 3 bits */
611 if ((mode
->width
& ~7) != (client_mode
->width
& ~7) ||
612 mode
->height
!= client_mode
->height
||
613 (crtc
->x
& ~7) != (client_mode
->x
& ~7) ||
614 crtc
->y
!= client_mode
->y
) {
621 static void dump_monitors_config(struct vdagent_x11
*x11
,
622 VDAgentMonitorsConfig
*mon_config
,
628 syslog(LOG_DEBUG
, "%s: %d, %x", prefix
, mon_config
->num_of_monitors
,
630 for (i
= 0 ; i
< mon_config
->num_of_monitors
; ++i
) {
631 m
= &mon_config
->monitors
[i
];
632 syslog(LOG_DEBUG
, "received monitor %d config %dx%d+%d+%d", i
,
633 m
->width
, m
->height
, m
->x
, m
->y
);
638 * Set monitor configuration according to client request.
640 * On exit send current configuration to client, regardless of error.
643 * screen size too large for driver to handle. (we set the largest/smallest possible)
644 * no randr support in X server.
645 * invalid configuration request from client.
647 void vdagent_x11_set_monitor_config(struct vdagent_x11
*x11
,
648 VDAgentMonitorsConfig
*mon_config
)
653 uint32_t primary_w
, primary_h
;
655 if (!x11
->has_xrandr
)
658 if (mon_config
->num_of_monitors
< 1) {
659 syslog(LOG_ERR
, "client sent invalid monitor config number %d",
660 mon_config
->num_of_monitors
);
664 if (same_monitor_configs(x11
, mon_config
)) {
669 dump_monitors_config(x11
, mon_config
, "from guest");
672 zero_base_monitors(x11
, mon_config
, &primary_w
, &primary_h
);
674 constrain_to_screen(x11
, &primary_w
, &primary_h
);
677 dump_monitors_config(x11
, mon_config
, "after zeroing");
680 for (i
= mon_config
->num_of_monitors
; i
< x11
->randr
.res
->noutput
; i
++)
681 xrandr_disable_output(x11
, i
);
683 for (i
= 0; i
< mon_config
->num_of_monitors
; ++i
) {
684 /* Try to create the requested resolution */
685 width
= mon_config
->monitors
[i
].width
;
686 height
= mon_config
->monitors
[i
].height
;
687 x
= mon_config
->monitors
[i
].x
;
688 y
= mon_config
->monitors
[i
].y
;
689 if (!xrandr_add_and_set(x11
, i
, x
, y
, width
, height
) &&
690 mon_config
->num_of_monitors
== 1) {
691 set_screen_to_best_size(x11
, width
, height
,
692 &primary_w
, &primary_h
);
697 if (primary_w
!= x11
->width
|| primary_h
!= x11
->height
) {
698 arm_error_handler(x11
);
699 XRRSetScreenSize(x11
->display
, x11
->root_window
, primary_w
, primary_h
,
700 DisplayWidthMM(x11
->display
, x11
->screen
),
701 DisplayHeightMM(x11
->display
, x11
->screen
));
703 x11
->set_crtc_config_not_functional
= check_error_handler(x11
);
707 update_randr_res(x11
);
708 x11
->width
= primary_w
;
709 x11
->height
= primary_h
;
711 /* Flush output buffers and consume any pending events (ConfigureNotify) */
712 x11
->dont_send_guest_xorg_res
= 1;
713 vdagent_x11_do_read(x11
);
714 x11
->dont_send_guest_xorg_res
= 0;
717 vdagent_x11_send_daemon_guest_xorg_res(x11
);
719 /* Flush output buffers and consume any pending events */
720 vdagent_x11_do_read(x11
);
723 void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11
*x11
)
725 struct vdagentd_guest_xorg_resolution
*res
= NULL
;
726 XineramaScreenInfo
*screen_info
= NULL
;
727 int i
, screen_count
= 0;
729 if (x11
->has_xinerama
) {
730 /* Xinerama reports the same information RANDR reports, so stay
731 * with Xinerama for support of Xinerama only setups */
732 screen_info
= XineramaQueryScreens(x11
->display
, &screen_count
);
735 if (screen_count
== 0)
738 res
= malloc(screen_count
* sizeof(*res
));
740 syslog(LOG_ERR
, "out of memory while trying to send resolutions, not sending resolutions.");
747 for (i
= 0; i
< screen_count
; i
++) {
748 if (screen_info
[i
].screen_number
>= screen_count
) {
749 syslog(LOG_ERR
, "Invalid screen number in xinerama screen info (%d >= %d)",
750 screen_info
[i
].screen_number
, screen_count
);
755 res
[screen_info
[i
].screen_number
].width
= screen_info
[i
].width
;
756 res
[screen_info
[i
].screen_number
].height
= screen_info
[i
].height
;
757 res
[screen_info
[i
].screen_number
].x
= screen_info
[i
].x_org
;
758 res
[screen_info
[i
].screen_number
].y
= screen_info
[i
].y_org
;
762 res
[0].width
= x11
->width
;
763 res
[0].height
= x11
->height
;
768 udscs_write(x11
->vdagentd
, VDAGENTD_GUEST_XORG_RESOLUTION
, x11
->width
,
769 x11
->height
, (uint8_t *)res
, screen_count
* sizeof(*res
));