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/>.
25 #include <glib/gstdio.h>
31 #include <X11/extensions/Xinerama.h>
33 #include "vdagentd-proto.h"
34 #include "vdagent-x11.h"
35 #include "vdagent-x11-priv.h"
37 #define MM_PER_INCH (25.4)
39 static int error_handler(Display
*display
, XErrorEvent
*error
)
41 vdagent_x11_caught_error
= 1;
45 static XRRModeInfo
*mode_from_id(struct vdagent_x11
*x11
, int id
)
49 for (i
= 0 ; i
< x11
->randr
.res
->nmode
; ++i
) {
50 if (id
== x11
->randr
.res
->modes
[i
].id
) {
51 return &x11
->randr
.res
->modes
[i
];
57 static XRRCrtcInfo
*crtc_from_id(struct vdagent_x11
*x11
, int id
)
61 for (i
= 0 ; i
< x11
->randr
.res
->ncrtc
; ++i
) {
62 if (id
== x11
->randr
.res
->crtcs
[i
]) {
63 return x11
->randr
.crtcs
[i
];
69 static void free_randr_resources(struct vdagent_x11
*x11
)
73 if (!x11
->randr
.res
) {
76 if (x11
->randr
.outputs
!= NULL
) {
77 for (i
= 0 ; i
< x11
->randr
.res
->noutput
; ++i
) {
78 XRRFreeOutputInfo(x11
->randr
.outputs
[i
]);
80 free(x11
->randr
.outputs
);
82 if (x11
->randr
.crtcs
!= NULL
) {
83 for (i
= 0 ; i
< x11
->randr
.res
->ncrtc
; ++i
) {
84 XRRFreeCrtcInfo(x11
->randr
.crtcs
[i
]);
86 free(x11
->randr
.crtcs
);
88 XRRFreeScreenResources(x11
->randr
.res
);
89 x11
->randr
.res
= NULL
;
90 x11
->randr
.outputs
= NULL
;
91 x11
->randr
.crtcs
= NULL
;
92 x11
->randr
.num_monitors
= 0;
95 static void update_randr_res(struct vdagent_x11
*x11
, int poll
)
99 free_randr_resources(x11
);
101 x11
->randr
.res
= XRRGetScreenResources(x11
->display
, x11
->root_window
[0]);
103 x11
->randr
.res
= XRRGetScreenResourcesCurrent(x11
->display
, x11
->root_window
[0]);
104 x11
->randr
.outputs
= malloc(x11
->randr
.res
->noutput
* sizeof(*x11
->randr
.outputs
));
105 x11
->randr
.crtcs
= malloc(x11
->randr
.res
->ncrtc
* sizeof(*x11
->randr
.crtcs
));
106 for (i
= 0 ; i
< x11
->randr
.res
->noutput
; ++i
) {
107 x11
->randr
.outputs
[i
] = XRRGetOutputInfo(x11
->display
, x11
->randr
.res
,
108 x11
->randr
.res
->outputs
[i
]);
109 if (x11
->randr
.outputs
[i
]->connection
== RR_Connected
)
110 x11
->randr
.num_monitors
++;
112 for (i
= 0 ; i
< x11
->randr
.res
->ncrtc
; ++i
) {
113 x11
->randr
.crtcs
[i
] = XRRGetCrtcInfo(x11
->display
, x11
->randr
.res
,
114 x11
->randr
.res
->crtcs
[i
]);
116 /* XXX is this dynamic? should it be cached? */
117 if (XRRGetScreenSizeRange(x11
->display
, x11
->root_window
[0],
118 &x11
->randr
.min_width
,
119 &x11
->randr
.min_height
,
120 &x11
->randr
.max_width
,
121 &x11
->randr
.max_height
) != 1) {
122 syslog(LOG_ERR
, "update_randr_res: RRGetScreenSizeRange failed");
126 void vdagent_x11_randr_init(struct vdagent_x11
*x11
)
130 if (x11
->screen_count
> 1) {
131 syslog(LOG_WARNING
, "X-server has more then 1 screen, "
132 "disabling client -> guest resolution syncing");
136 if (XRRQueryExtension(x11
->display
, &x11
->xrandr_event_base
, &i
)) {
137 XRRQueryVersion(x11
->display
, &x11
->xrandr_major
, &x11
->xrandr_minor
);
138 if (x11
->xrandr_major
== 1 && x11
->xrandr_minor
>= 3)
142 XRRSelectInput(x11
->display
, x11
->root_window
[0],
143 RRScreenChangeNotifyMask
| RRCrtcChangeNotifyMask
);
145 if (x11
->has_xrandr
) {
146 update_randr_res(x11
, 0);
148 x11
->randr
.res
= NULL
;
151 if (XineramaQueryExtension(x11
->display
, &i
, &i
))
152 x11
->has_xinerama
= 1;
154 switch (x11
->has_xrandr
<< 4 | x11
->has_xinerama
) {
156 syslog(LOG_ERR
, "Neither Xrandr nor Xinerama found, assuming single monitor setup");
160 syslog(LOG_DEBUG
, "Found Xinerama extension without Xrandr, assuming Xinerama multi monitor setup");
163 syslog(LOG_ERR
, "Found Xrandr but no Xinerama, weird!");
166 /* Standard xrandr setup, nothing to see here */
172 find_mode_by_name (struct vdagent_x11
*x11
, char *name
)
175 XRRModeInfo
*ret
= NULL
;
177 for (m
= 0; m
< x11
->randr
.res
->nmode
; m
++) {
178 XRRModeInfo
*mode
= &x11
->randr
.res
->modes
[m
];
179 if (!strcmp (name
, mode
->name
)) {
188 find_mode_by_size (struct vdagent_x11
*x11
, int output
, int width
, int height
)
191 XRRModeInfo
*ret
= NULL
;
193 for (m
= 0; m
< x11
->randr
.outputs
[output
]->nmode
; m
++) {
194 XRRModeInfo
*mode
= mode_from_id(x11
,
195 x11
->randr
.outputs
[output
]->modes
[m
]);
196 if (mode
&& mode
->width
== width
&& mode
->height
== height
) {
204 static void delete_mode(struct vdagent_x11
*x11
, int output_index
,
205 int width
, int height
)
209 XRROutputInfo
*output_info
;
212 if (width
== 0 || height
== 0)
215 snprintf(name
, sizeof(name
), "%dx%d-%d", width
, height
, output_index
);
217 syslog(LOG_DEBUG
, "Deleting mode %s", name
);
219 output_info
= x11
->randr
.outputs
[output_index
];
220 if (output_info
->ncrtc
!= 1) {
221 syslog(LOG_ERR
, "output has %d crtcs, expected exactly 1, "
222 "failed to delete mode", output_info
->ncrtc
);
225 for (m
= 0 ; m
< x11
->randr
.res
->nmode
; ++m
) {
226 mode
= &x11
->randr
.res
->modes
[m
];
227 if (strcmp(mode
->name
, name
) == 0)
230 if (m
< x11
->randr
.res
->nmode
) {
231 vdagent_x11_set_error_handler(x11
, error_handler
);
232 XRRDeleteOutputMode (x11
->display
, x11
->randr
.res
->outputs
[output_index
],
234 XRRDestroyMode (x11
->display
, mode
->id
);
235 // ignore race error, if mode is created by others
236 vdagent_x11_restore_error_handler(x11
);
239 /* silly to update everytime for more then one monitor */
240 update_randr_res(x11
, 0);
243 static void set_reduced_cvt_mode(XRRModeInfo
*mode
, int width
, int height
)
245 /* Code taken from hw/xfree86/modes/xf86cvt.c
246 * See that file for lineage. Originated in public domain code
247 * Would be nice if xorg exported this in a library */
249 /* 1) top/bottom margin size (% of height) - default: 1.8 */
250 #define CVT_MARGIN_PERCENTAGE 1.8
252 /* 2) character cell horizontal granularity (pixels) - default 8 */
253 #define CVT_H_GRANULARITY 8
255 /* 4) Minimum vertical porch (lines) - default 3 */
256 #define CVT_MIN_V_PORCH 3
258 /* 4) Minimum number of vertical back porch lines - default 6 */
259 #define CVT_MIN_V_BPORCH 6
261 /* Pixel Clock step (kHz) */
262 #define CVT_CLOCK_STEP 250
264 /* Minimum vertical blanking interval time (µs) - default 460 */
265 #define CVT_RB_MIN_VBLANK 460.0
267 /* Fixed number of clocks for horizontal sync */
268 #define CVT_RB_H_SYNC 32.0
270 /* Fixed number of clocks for horizontal blanking */
271 #define CVT_RB_H_BLANK 160.0
273 /* Fixed number of lines for vertical front porch - default 3 */
274 #define CVT_RB_VFPORCH 3
277 float VFieldRate
= 60.0;
281 /* 2. Horizontal pixels */
282 width
= width
- (width
% CVT_H_GRANULARITY
);
285 mode
->height
= height
;
288 /* 8. Estimate Horizontal period. */
289 HPeriod
= ((float) (1000000.0 / VFieldRate
- CVT_RB_MIN_VBLANK
)) / height
;
291 /* 9. Find number of lines in vertical blanking */
292 VBILines
= ((float) CVT_RB_MIN_VBLANK
) / HPeriod
+ 1;
294 /* 10. Check if vertical blanking is sufficient */
295 if (VBILines
< (CVT_RB_VFPORCH
+ VSync
+ CVT_MIN_V_BPORCH
))
296 VBILines
= CVT_RB_VFPORCH
+ VSync
+ CVT_MIN_V_BPORCH
;
298 /* 11. Find total number of lines in vertical field */
299 mode
->vTotal
= height
+ VBILines
;
301 /* 12. Find total number of pixels in a line */
302 mode
->hTotal
= mode
->width
+ CVT_RB_H_BLANK
;
304 /* Fill in HSync values */
305 mode
->hSyncEnd
= mode
->width
+ CVT_RB_H_BLANK
/ 2;
306 mode
->hSyncStart
= mode
->hSyncEnd
- CVT_RB_H_SYNC
;
308 /* Fill in VSync values */
309 mode
->vSyncStart
= mode
->height
+ CVT_RB_VFPORCH
;
310 mode
->vSyncEnd
= mode
->vSyncStart
+ VSync
;
312 /* 15/13. Find pixel clock frequency (kHz for xf86) */
313 mode
->dotClock
= mode
->hTotal
* 1000.0 / HPeriod
;
314 mode
->dotClock
-= mode
->dotClock
% CVT_CLOCK_STEP
;
318 static XRRModeInfo
*create_new_mode(struct vdagent_x11
*x11
, int output_index
,
319 int width
, int height
)
324 snprintf(modename
, sizeof(modename
), "%dx%d-%d", width
, height
, output_index
);
326 mode
.name
= modename
;
327 mode
.nameLength
= strlen(mode
.name
);
328 set_reduced_cvt_mode(&mode
, width
, height
);
331 vdagent_x11_set_error_handler(x11
, error_handler
);
332 XRRCreateMode (x11
->display
, x11
->root_window
[0], &mode
);
333 // ignore race error, if mode is created by others
334 vdagent_x11_restore_error_handler(x11
);
336 /* silly to update everytime for more then one monitor */
337 update_randr_res(x11
, 0);
339 return find_mode_by_name(x11
, modename
);
342 static int xrandr_add_and_set(struct vdagent_x11
*x11
, int output
, int x
, int y
,
343 int width
, int height
)
349 int old_width
= x11
->randr
.monitor_sizes
[output
].width
;
350 int old_height
= x11
->randr
.monitor_sizes
[output
].height
;
352 if (!x11
->randr
.res
|| output
>= x11
->randr
.res
->noutput
|| output
< 0) {
353 syslog(LOG_ERR
, "%s: program error: missing RANDR or bad output",
357 if (x11
->set_crtc_config_not_functional
) {
358 /* fail, set_best_mode will find something close. */
361 xid
= x11
->randr
.res
->outputs
[output
];
362 mode
= find_mode_by_size(x11
, output
, width
, height
);
364 mode
= create_new_mode(x11
, output
, width
, height
);
367 syslog(LOG_ERR
, "failed to add a new mode");
370 XRRAddOutputMode(x11
->display
, xid
, mode
->id
);
371 x11
->randr
.monitor_sizes
[output
].width
= width
;
372 x11
->randr
.monitor_sizes
[output
].height
= height
;
374 vdagent_x11_set_error_handler(x11
, error_handler
);
375 s
= XRRSetCrtcConfig(x11
->display
, x11
->randr
.res
, x11
->randr
.res
->crtcs
[output
],
376 CurrentTime
, x
, y
, mode
->id
, RR_Rotate_0
, outputs
,
378 if (vdagent_x11_restore_error_handler(x11
) || (s
!= RRSetConfigSuccess
)) {
379 syslog(LOG_ERR
, "failed to XRRSetCrtcConfig");
380 x11
->set_crtc_config_not_functional
= 1;
384 /* clean the previous name, if any */
385 if (width
!= old_width
|| height
!= old_height
)
386 delete_mode(x11
, output
, old_width
, old_height
);
391 static void xrandr_disable_output(struct vdagent_x11
*x11
, int output
)
395 if (!x11
->randr
.res
|| output
>= x11
->randr
.res
->noutput
|| output
< 0) {
396 syslog(LOG_ERR
, "%s: program error: missing RANDR or bad output",
401 s
= XRRSetCrtcConfig(x11
->display
, x11
->randr
.res
,
402 x11
->randr
.res
->crtcs
[output
],
403 CurrentTime
, 0, 0, None
, RR_Rotate_0
,
406 if (s
!= RRSetConfigSuccess
)
407 syslog(LOG_ERR
, "failed to disable monitor");
409 delete_mode(x11
, output
, x11
->randr
.monitor_sizes
[output
].width
,
410 x11
->randr
.monitor_sizes
[output
].height
);
411 x11
->randr
.monitor_sizes
[output
].width
= 0;
412 x11
->randr
.monitor_sizes
[output
].height
= 0;
415 static int set_screen_to_best_size(struct vdagent_x11
*x11
, int width
, int height
,
416 int *out_width
, int *out_height
){
417 int i
, num_sizes
= 0;
419 unsigned int closest_diff
= -1;
420 XRRScreenSize
*sizes
;
421 XRRScreenConfiguration
*config
;
424 sizes
= XRRSizes(x11
->display
, 0, &num_sizes
);
425 if (!sizes
|| !num_sizes
) {
426 syslog(LOG_ERR
, "XRRSizes failed");
430 syslog(LOG_DEBUG
, "set_screen_to_best_size found %d modes\n", num_sizes
);
432 /* Find the closest size which will fit within the monitor */
433 for (i
= 0; i
< num_sizes
; i
++) {
434 if (sizes
[i
].width
> width
||
435 sizes
[i
].height
> height
)
436 continue; /* Too large for the monitor */
438 unsigned int wdiff
= width
- sizes
[i
].width
;
439 unsigned int hdiff
= height
- sizes
[i
].height
;
440 unsigned int diff
= wdiff
* wdiff
+ hdiff
* hdiff
;
441 if (diff
< closest_diff
) {
448 syslog(LOG_ERR
, "no suitable resolution found for monitor");
452 config
= XRRGetScreenInfo(x11
->display
, x11
->root_window
[0]);
454 syslog(LOG_ERR
, "get screen info failed");
457 XRRConfigCurrentConfiguration(config
, &rotation
);
458 XRRSetScreenConfig(x11
->display
, config
, x11
->root_window
[0], best
,
459 rotation
, CurrentTime
);
460 XRRFreeScreenConfigInfo(config
);
463 syslog(LOG_DEBUG
, "set_screen_to_best_size set size to: %dx%d\n",
464 sizes
[best
].width
, sizes
[best
].height
);
465 *out_width
= sizes
[best
].width
;
466 *out_height
= sizes
[best
].height
;
470 void vdagent_x11_randr_handle_root_size_change(struct vdagent_x11
*x11
,
471 int screen
, int width
, int height
)
473 update_randr_res(x11
, 0);
475 if (width
== x11
->width
[screen
] && height
== x11
->height
[screen
]) {
480 syslog(LOG_DEBUG
, "Root size of screen %d changed to %dx%d send %d",
481 screen
, width
, height
, !x11
->dont_send_guest_xorg_res
);
483 x11
->width
[screen
] = width
;
484 x11
->height
[screen
] = height
;
485 if (!x11
->dont_send_guest_xorg_res
) {
486 vdagent_x11_send_daemon_guest_xorg_res(x11
, 1);
490 int vdagent_x11_randr_handle_event(struct vdagent_x11
*x11
,
495 switch (event
.type
- x11
->xrandr_event_base
) {
496 case RRScreenChangeNotify
: {
497 XRRScreenChangeNotifyEvent
*sce
=
498 (XRRScreenChangeNotifyEvent
*) &event
;
499 vdagent_x11_randr_handle_root_size_change(x11
, 0,
500 sce
->width
, sce
->height
);
504 update_randr_res(x11
, 0);
505 if (!x11
->dont_send_guest_xorg_res
)
506 vdagent_x11_send_daemon_guest_xorg_res(x11
, 1);
517 static int min_int(int x
, int y
)
519 return x
> y
? y
: x
;
522 static int max_int(int x
, int y
)
524 return x
> y
? x
: y
;
527 static int constrain_to_range(int low
, int *val
, int high
)
529 if (low
<= *val
&& *val
<= high
) {
541 static void constrain_to_screen(struct vdagent_x11
*x11
, int *w
, int *h
)
547 lx
= x11
->randr
.min_width
;
548 hx
= x11
->randr
.max_width
;
549 ly
= x11
->randr
.min_height
;
550 hy
= x11
->randr
.max_height
;
551 if (constrain_to_range(lx
, w
, hx
)) {
552 syslog(LOG_ERR
, "width not in driver range: ! %d < %d < %d",
555 if (constrain_to_range(ly
, h
, hy
)) {
556 syslog(LOG_ERR
, "height not in driver range: ! %d < %d < %d",
561 static int monitor_enabled(VDAgentMonConfig
*mon
)
563 return mon
->width
!= 0 && mon
->height
!= 0;
567 * The agent config doesn't contain a primary size, just the monitors, but
568 * we need to total size as well, to make sure we have enough memory and
569 * because X needs it.
571 * At the same pass constrain any provided size to what the server accepts.
574 * x >= 0, y >= 0 for all x, y in mon_config
575 * max_width >= width >= min_width,
576 * max_height >= height >= min_height for all monitors in mon_config
578 static void zero_base_monitors(struct vdagent_x11
*x11
,
579 VDAgentMonitorsConfig
*mon_config
,
580 int *width
, int *height
)
582 int i
, min_x
= INT_MAX
, min_y
= INT_MAX
, max_x
= INT_MIN
, max_y
= INT_MIN
;
583 int *mon_height
, *mon_width
;
585 for (i
= 0; i
< mon_config
->num_of_monitors
; i
++) {
586 if (!monitor_enabled(&mon_config
->monitors
[i
]))
588 mon_config
->monitors
[i
].x
&= ~7;
589 mon_config
->monitors
[i
].width
&= ~7;
590 mon_width
= (int *)&mon_config
->monitors
[i
].width
;
591 mon_height
= (int *)&mon_config
->monitors
[i
].height
;
592 constrain_to_screen(x11
, mon_width
, mon_height
);
593 min_x
= min_int(mon_config
->monitors
[i
].x
, min_x
);
594 min_y
= min_int(mon_config
->monitors
[i
].y
, min_y
);
595 max_x
= max_int(mon_config
->monitors
[i
].x
+ *mon_width
, max_x
);
596 max_y
= max_int(mon_config
->monitors
[i
].y
+ *mon_height
, max_y
);
598 if (min_x
!= 0 || min_y
!= 0) {
599 syslog(LOG_ERR
, "%s: agent config %d,%d rooted, adjusting to 0,0.",
600 __FUNCTION__
, min_x
, min_y
);
601 for (i
= 0 ; i
< mon_config
->num_of_monitors
; ++i
) {
602 if (!monitor_enabled(&mon_config
->monitors
[i
]))
604 mon_config
->monitors
[i
].x
-= min_x
;
605 mon_config
->monitors
[i
].y
-= min_y
;
614 static int enabled_monitors(VDAgentMonitorsConfig
*mon
)
618 for (i
= 0; i
< mon
->num_of_monitors
; i
++) {
619 if (monitor_enabled(&mon
->monitors
[i
]))
625 static int same_monitor_configs(VDAgentMonitorsConfig
*conf1
,
626 VDAgentMonitorsConfig
*conf2
)
630 if (conf1
== NULL
|| conf2
== NULL
||
631 conf1
->num_of_monitors
!= conf2
->num_of_monitors
)
634 for (i
= 0; i
< conf1
->num_of_monitors
; i
++) {
635 VDAgentMonConfig
*mon1
= &conf1
->monitors
[i
];
636 VDAgentMonConfig
*mon2
= &conf2
->monitors
[i
];
637 /* NOTE: we don't compare depth. */
638 if (mon1
->x
!= mon2
->x
|| mon1
->y
!= mon2
->y
||
639 mon1
->width
!= mon2
->width
|| mon1
->height
!= mon2
->height
)
645 static int config_size(int num_of_monitors
)
647 return sizeof(VDAgentMonitorsConfig
) +
648 num_of_monitors
* sizeof(VDAgentMonConfig
);
651 static VDAgentMonitorsConfig
*get_current_mon_config(struct vdagent_x11
*x11
)
653 int i
, num_of_monitors
= 0;
656 XRRScreenResources
*res
= x11
->randr
.res
;
657 VDAgentMonitorsConfig
*mon_config
;
659 mon_config
= calloc(1, config_size(res
->noutput
));
661 syslog(LOG_ERR
, "out of memory allocating current monitor config");
665 for (i
= 0 ; i
< res
->noutput
; i
++) {
666 if (x11
->randr
.outputs
[i
]->ncrtc
== 0)
667 continue; /* Monitor disabled, already zero-ed by calloc */
668 if (x11
->randr
.outputs
[i
]->ncrtc
!= 1)
671 crtc
= crtc_from_id(x11
, x11
->randr
.outputs
[i
]->crtcs
[0]);
675 mode
= mode_from_id(x11
, crtc
->mode
);
677 continue; /* Monitor disabled, already zero-ed by calloc */
679 mon_config
->monitors
[i
].x
= crtc
->x
;
680 mon_config
->monitors
[i
].y
= crtc
->y
;
681 mon_config
->monitors
[i
].width
= mode
->width
;
682 mon_config
->monitors
[i
].height
= mode
->height
;
683 num_of_monitors
= i
+ 1;
685 mon_config
->num_of_monitors
= num_of_monitors
;
686 mon_config
->flags
= VD_AGENT_CONFIG_MONITORS_FLAG_USE_POS
;
690 syslog(LOG_ERR
, "error: inconsistent or stale data from X");
695 static void dump_monitors_config(struct vdagent_x11
*x11
,
696 VDAgentMonitorsConfig
*mon_config
,
702 syslog(LOG_DEBUG
, "%s: %d, %x", prefix
, mon_config
->num_of_monitors
,
704 for (i
= 0 ; i
< mon_config
->num_of_monitors
; ++i
) {
705 m
= &mon_config
->monitors
[i
];
706 if (!monitor_enabled(m
))
708 syslog(LOG_DEBUG
, "received monitor %d config %dx%d+%d+%d", i
,
709 m
->width
, m
->height
, m
->x
, m
->y
);
714 * Set monitor configuration according to client request.
716 * On exit send current configuration to client, regardless of error.
719 * screen size too large for driver to handle. (we set the largest/smallest possible)
720 * no randr support in X server.
721 * invalid configuration request from client.
723 void vdagent_x11_set_monitor_config(struct vdagent_x11
*x11
,
724 VDAgentMonitorsConfig
*mon_config
,
727 int primary_w
, primary_h
;
728 int i
, real_num_of_monitors
= 0;
729 VDAgentMonitorsConfig
*curr
= NULL
;
731 if (!x11
->has_xrandr
)
734 if (enabled_monitors(mon_config
) < 1) {
735 syslog(LOG_ERR
, "client sent config with all monitors disabled");
740 dump_monitors_config(x11
, mon_config
, "from guest");
743 for (i
= 0; i
< mon_config
->num_of_monitors
; i
++) {
744 if (monitor_enabled(&mon_config
->monitors
[i
]))
745 real_num_of_monitors
= i
+ 1;
747 mon_config
->num_of_monitors
= real_num_of_monitors
;
749 update_randr_res(x11
, 0);
750 if (mon_config
->num_of_monitors
> x11
->randr
.res
->noutput
) {
752 "warning unexpected client request: #mon %d > driver output %d",
753 mon_config
->num_of_monitors
, x11
->randr
.res
->noutput
);
754 mon_config
->num_of_monitors
= x11
->randr
.res
->noutput
;
757 if (mon_config
->num_of_monitors
> MONITOR_SIZE_COUNT
) {
758 syslog(LOG_WARNING
, "warning: client send %d monitors, capping at %d",
759 mon_config
->num_of_monitors
, MONITOR_SIZE_COUNT
);
760 mon_config
->num_of_monitors
= MONITOR_SIZE_COUNT
;
763 zero_base_monitors(x11
, mon_config
, &primary_w
, &primary_h
);
765 constrain_to_screen(x11
, &primary_w
, &primary_h
);
768 dump_monitors_config(x11
, mon_config
, "after zeroing");
771 curr
= get_current_mon_config(x11
);
774 if (same_monitor_configs(mon_config
, curr
) &&
775 x11
->width
[0] == primary_w
&& x11
->height
[0] == primary_h
) {
779 if (same_monitor_configs(mon_config
, x11
->randr
.failed_conf
)) {
780 syslog(LOG_WARNING
, "Ignoring previous failed client monitor config");
784 gchar
*config
= g_build_filename (g_get_user_config_dir (), "monitors.xml", NULL
);
788 for (i
= mon_config
->num_of_monitors
; i
< x11
->randr
.res
->noutput
; i
++)
789 xrandr_disable_output(x11
, i
);
791 /* First, disable disabled CRTCs... */
792 for (i
= 0; i
< mon_config
->num_of_monitors
; ++i
) {
793 if (!monitor_enabled(&mon_config
->monitors
[i
])) {
794 xrandr_disable_output(x11
, i
);
798 /* ... and disable the ones that would be bigger than
799 * the new RandR screen once it is resized. If they are enabled the
800 * XRRSetScreenSize call will fail with BadMatch. They will be
801 * reenabled after hanging the screen size.
803 for (i
= 0; i
< curr
->num_of_monitors
; ++i
) {
807 width
= curr
->monitors
[i
].width
;
808 height
= curr
->monitors
[i
].height
;
809 x
= curr
->monitors
[i
].x
;
810 y
= curr
->monitors
[i
].y
;
812 if ((x
+ width
> primary_w
) || (y
+ height
> primary_h
)) {
814 syslog(LOG_DEBUG
, "Disabling monitor %d: "
815 "%dx%d+%d+%d > (%d,%d)",
816 i
, width
, height
, x
, y
, primary_w
, primary_h
);
818 xrandr_disable_output(x11
, i
);
822 /* Then we can resize the RandR screen. */
823 if (primary_w
!= x11
->width
[0] || primary_h
!= x11
->height
[0]) {
824 const int dpi
= 96; /* FIXME: read settings from desktop or get from client dpi? */
825 int width_mm
= (MM_PER_INCH
* primary_w
) / dpi
;
826 int height_mm
= (MM_PER_INCH
* primary_h
) / dpi
;
829 syslog(LOG_DEBUG
, "Changing screen size to %dx%d",
830 primary_w
, primary_h
);
831 vdagent_x11_set_error_handler(x11
, error_handler
);
832 XRRSetScreenSize(x11
->display
, x11
->root_window
[0], primary_w
, primary_h
,
833 width_mm
, height_mm
);
834 if (vdagent_x11_restore_error_handler(x11
)) {
835 syslog(LOG_ERR
, "XRRSetScreenSize failed, not enough mem?");
837 syslog(LOG_WARNING
, "Restoring previous config");
838 vdagent_x11_set_monitor_config(x11
, curr
, 1);
840 /* Remember this config failed, if the client is maximized or
841 fullscreen it will keep sending the failing config. */
842 free(x11
->randr
.failed_conf
);
843 x11
->randr
.failed_conf
=
844 malloc(config_size(mon_config
->num_of_monitors
));
845 if (x11
->randr
.failed_conf
)
846 memcpy(x11
->randr
.failed_conf
, mon_config
,
847 config_size(mon_config
->num_of_monitors
));
853 /* Finally, we set the new resolutions on RandR CRTCs now that the
854 * RandR screen is big enough to hold these. */
855 for (i
= 0; i
< mon_config
->num_of_monitors
; ++i
) {
859 if (!monitor_enabled(&mon_config
->monitors
[i
])) {
862 /* Try to create the requested resolution */
863 width
= mon_config
->monitors
[i
].width
;
864 height
= mon_config
->monitors
[i
].height
;
865 x
= mon_config
->monitors
[i
].x
;
866 y
= mon_config
->monitors
[i
].y
;
867 if (!xrandr_add_and_set(x11
, i
, x
, y
, width
, height
) &&
868 enabled_monitors(mon_config
) == 1) {
869 set_screen_to_best_size(x11
, width
, height
,
870 &primary_w
, &primary_h
);
875 update_randr_res(x11
,
876 x11
->randr
.num_monitors
!= enabled_monitors(mon_config
));
877 x11
->width
[0] = primary_w
;
878 x11
->height
[0] = primary_h
;
880 /* Flush output buffers and consume any pending events (ConfigureNotify) */
881 x11
->dont_send_guest_xorg_res
= 1;
882 vdagent_x11_do_read(x11
);
883 x11
->dont_send_guest_xorg_res
= 0;
886 vdagent_x11_send_daemon_guest_xorg_res(x11
, 0);
888 /* Flush output buffers and consume any pending events */
889 vdagent_x11_do_read(x11
);
893 void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11
*x11
, int update
)
895 struct vdagentd_guest_xorg_resolution
*res
= NULL
;
896 int i
, width
= 0, height
= 0, screen_count
= 0;
898 if (x11
->has_xrandr
) {
899 VDAgentMonitorsConfig
*curr
;
902 update_randr_res(x11
, 0);
904 curr
= get_current_mon_config(x11
);
908 screen_count
= curr
->num_of_monitors
;
909 res
= malloc(screen_count
* sizeof(*res
));
915 for (i
= 0; i
< screen_count
; i
++) {
916 res
[i
].width
= curr
->monitors
[i
].width
;
917 res
[i
].height
= curr
->monitors
[i
].height
;
918 res
[i
].x
= curr
->monitors
[i
].x
;
919 res
[i
].y
= curr
->monitors
[i
].y
;
922 width
= x11
->width
[0];
923 height
= x11
->height
[0];
924 } else if (x11
->has_xinerama
) {
925 XineramaScreenInfo
*screen_info
= NULL
;
927 screen_info
= XineramaQueryScreens(x11
->display
, &screen_count
);
930 res
= malloc(screen_count
* sizeof(*res
));
935 for (i
= 0; i
< screen_count
; i
++) {
936 if (screen_info
[i
].screen_number
>= screen_count
) {
937 syslog(LOG_ERR
, "Invalid screen number in xinerama screen info (%d >= %d)",
938 screen_info
[i
].screen_number
, screen_count
);
943 res
[screen_info
[i
].screen_number
].width
= screen_info
[i
].width
;
944 res
[screen_info
[i
].screen_number
].height
= screen_info
[i
].height
;
945 res
[screen_info
[i
].screen_number
].x
= screen_info
[i
].x_org
;
946 res
[screen_info
[i
].screen_number
].y
= screen_info
[i
].y_org
;
949 width
= x11
->width
[0];
950 height
= x11
->height
[0];
953 screen_count
= x11
->screen_count
;
954 res
= malloc(screen_count
* sizeof(*res
));
957 for (i
= 0; i
< screen_count
; i
++) {
958 res
[i
].width
= x11
->width
[i
];
959 res
[i
].height
= x11
->height
[i
];
960 /* No way to get screen coordinates, assume rtl order */
963 width
+= x11
->width
[i
];
964 if (x11
->height
[i
] > height
)
965 height
= x11
->height
[i
];
970 for (i
= 0; i
< screen_count
; i
++)
971 syslog(LOG_DEBUG
, "Screen %d %dx%d%+d%+d", i
, res
[i
].width
,
972 res
[i
].height
, res
[i
].x
, res
[i
].y
);
975 udscs_write(x11
->vdagentd
, VDAGENTD_GUEST_XORG_RESOLUTION
, width
, height
,
976 (uint8_t *)res
, screen_count
* sizeof(*res
));
980 syslog(LOG_ERR
, "out of memory while trying to send resolutions, not sending resolutions.");