From 571948ffb78eddd3015c456d084e0ca941f3e45d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 26 Sep 2011 13:01:56 +0200 Subject: vdagentd: Autogenerate a Xinerama xorg.conf for multi monitor setups The autogenerated file will get written as /etc/X11/xorg.conf.spice Signed-off-by: Hans de Goede --- Makefile.am | 6 +- configure.ac | 12 +++- src/vdagentd-xorg-conf.c | 171 +++++++++++++++++++++++++++++++++++++++++++++++ src/vdagentd.c | 12 ++++ 4 files changed, 198 insertions(+), 3 deletions(-) create mode 100644 src/vdagentd-xorg-conf.c diff --git a/Makefile.am b/Makefile.am index c3881ef..1102c02 100644 --- a/Makefile.am +++ b/Makefile.am @@ -7,10 +7,11 @@ src_spice_vdagent_CFLAGS = $(X_CFLAGS) $(SPICE_CFLAGS) src_spice_vdagent_LDADD = $(X_LIBS) $(SPICE_LIBS) src_spice_vdagent_SOURCES = src/vdagent.c src/vdagent-x11.c src/udscs.c -src_spice_vdagentd_CFLAGS = $(DBUS_CFLAGS) $(SPICE_CFLAGS) -src_spice_vdagentd_LDADD = $(DBUS_LIBS) $(SPICE_LIBS) +src_spice_vdagentd_CFLAGS = $(DBUS_CFLAGS) $(PCIACCESS_CFLAGS) $(SPICE_CFLAGS) +src_spice_vdagentd_LDADD = $(DBUS_LIBS) $(PCIACCESS_LIBS) $(SPICE_LIBS) src_spice_vdagentd_SOURCES = src/vdagentd.c \ src/vdagentd-uinput.c \ + src/vdagentd-xorg-conf.c \ src/vdagent-virtio-port.c \ src/udscs.c if HAVE_CONSOLE_KIT @@ -22,6 +23,7 @@ noinst_HEADERS = src/console-kit.h \ src/vdagent-x11.h \ src/udscs.h \ src/vdagentd-uinput.h \ + src/vdagentd-xorg-conf.h \ src/vdagentd-proto.h \ src/vdagent-virtio-port.h diff --git a/configure.ac b/configure.ac index 6c9c156..c4659f8 100644 --- a/configure.ac +++ b/configure.ac @@ -18,14 +18,24 @@ AC_ARG_ENABLE([console-kit], [enable_console_kit="$enableval"], [enable_console_kit="yes"]) +AC_ARG_ENABLE([pciaccess], + [AS_HELP_STRING([--enable-pciaccess], [Enable libpciaccess use for auto generation of Xinerama xorg.conf (default: yes)])], + [enable_pciaccess="$enableval"], + [enable_pciaccess="yes"]) + PKG_PROG_PKG_CONFIG PKG_CHECK_MODULES(X, [xfixes xrandr xinerama x11]) +PKG_CHECK_MODULES(SPICE, [spice-protocol >= 0.8.0]) if test x"$enable_console_kit" = "xyes" ; then PKG_CHECK_MODULES(DBUS, [dbus-1]) AC_DEFINE([HAVE_CONSOLE_KIT], [1], [If defined, vdagentd will be compiled with ConsoleKit support] ) fi AM_CONDITIONAL(HAVE_CONSOLE_KIT, test x"$enable_console_kit" = "xyes") -PKG_CHECK_MODULES(SPICE, [spice-protocol >= 0.8.0]) +if test x"$enable_pciaccess" = "xyes" ; then + PKG_CHECK_MODULES(PCIACCESS, [pciaccess >= 0.10]) + AC_DEFINE([HAVE_PCIACCESS], [1], [If defined, vdagentd will be compiled with pciaccess support] ) +fi +AM_CONDITIONAL(HAVE_PCIACCESS, test x"$enable_pciaccess" = "xyes") AC_CONFIG_FILES([ Makefile diff --git a/src/vdagentd-xorg-conf.c b/src/vdagentd-xorg-conf.c new file mode 100644 index 0000000..5610837 --- /dev/null +++ b/src/vdagentd-xorg-conf.c @@ -0,0 +1,171 @@ +/* vdagentd.c vdagentd xorg.conf writing code + + Copyright 2011 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_PCIACCESS +#include +#endif + +#include +#include +#include +#include "vdagentd-xorg-conf.h" + +#define FPRINTF(...) \ + do { \ + r = fprintf(f, __VA_ARGS__); \ + if (r < 0) { \ + fprintf(logfile, "Error writing to %s: %s\n", \ + xorg_conf, strerror(errno)); \ + fclose(f); \ + pci_system_cleanup(); \ + return; \ + } \ + } while(0) + +void vdagentd_write_xorg_conf(VDAgentMonitorsConfig *monitor_conf, + FILE *logfile) +{ +#ifdef HAVE_PCIACCESS + int i, r, count, min_x = INT_MAX, min_y = INT_MAX; + FILE *f; + struct pci_device_iterator *it; + struct pci_device *dev; + const struct pci_id_match qxl_id_match = { + .vendor_id = 0x1b36, + .device_id = 0x0100, + .subvendor_id = PCI_MATCH_ANY, + .subdevice_id = PCI_MATCH_ANY, + }; + const char *xorg_conf = "/var/run/spice-vdagentd/xorg.conf.spice"; + const char *xorg_conf_old = "/var/run/spice-vdagentd/xorg.conf.spice.old"; + + r = rename(xorg_conf, xorg_conf_old); + if (r && errno != ENOENT) { + fprintf(logfile, + "Error renaming %s to %s: %s, not generating xorg.conf\n", + xorg_conf, xorg_conf_old, strerror(errno)); + return; + } + + r = pci_system_init(); + if (r) { + fprintf(logfile, "Error initializing libpciaccess: %d, not generating xorg.conf\n", r); + return; + } + + it = pci_id_match_iterator_create(&qxl_id_match); + if (!it) { + fprintf(logfile, "Error could not create pci id iterator for QXL devices, not generating xorg.conf\n"); + pci_system_cleanup(); + return; + } + + dev = pci_device_next(it); + if (!dev) { + fprintf(logfile, "No QXL devices found, not generating xorg.conf\n"); + pci_system_cleanup(); + return; + } + + f = fopen(xorg_conf, "w"); + if (!f) { + fprintf(logfile, "Error opening %s for writing: %s\n", + xorg_conf, strerror(errno)); + pci_system_cleanup(); + return; + } + + FPRINTF("# xorg.conf generated by spice-vdagentd\n"); + FPRINTF("# generated from monitor info provided by the client\n\n"); + + if (monitor_conf->num_of_monitors == 1) { + FPRINTF("# Client has only 1 monitor\n"); + FPRINTF("# This works best with no xorg.conf, leaving xorg.conf empty\n"); + fclose(f); + pci_system_cleanup(); + return; + } + + FPRINTF("Section \"ServerFlags\"\n"); + FPRINTF("\tOption\t\t\"Xinerama\"\t\"true\"\n"); + FPRINTF("EndSection\n\n"); + + i = 0; + do { + FPRINTF("Section \"Device\"\n"); + FPRINTF("\tIdentifier\t\"qxl%d\"\n", i++); + FPRINTF("\tDriver\t\t\"qxl\"\n"); + FPRINTF("\tBusID\t\t\"PCI:%02d:%02d:%d\"\n", + dev->bus, dev->dev, dev->func); + FPRINTF("EndSection\n\n"); + } while ((dev = pci_device_next(it))); + + if (i < monitor_conf->num_of_monitors) { + fprintf(logfile, "Client has %d monitors, but only %d qxl devices found\n", + monitor_conf->num_of_monitors, i); + FPRINTF("# Client has %d monitors, but only %d qxl devices found\n", + monitor_conf->num_of_monitors, i); + FPRINTF("# Only generation %d \"Screen\" sections\n\n", i); + count = i; + } else { + count = monitor_conf->num_of_monitors; + } + + for (i = 0; i < count; i++) { + FPRINTF("Section \"Screen\"\n"); + FPRINTF("\tIdentifier\t\"Screen%d\"\n", i); + FPRINTF("\tDevice\t\t\"qxl%d\"\n", i); + FPRINTF("\tDefaultDepth\t24\n"); + FPRINTF("\tSubSection \"Display\"\n"); + FPRINTF("\t\tViewport\t0 0\n"); + FPRINTF("\t\tDepth\t\t24\n"); + FPRINTF("\t\tModes\t\t\"%dx%d\"\n", monitor_conf->monitors[i].width, + monitor_conf->monitors[i].height); + FPRINTF("\tEndSubSection\n"); + FPRINTF("EndSection\n\n"); + } + + /* monitor_conf may contain negative values, convert these to 0 - # */ + for (i = 0; i < count; i++) { + if (monitor_conf->monitors[i].x < min_x) { + min_x = monitor_conf->monitors[i].x; + } + if (monitor_conf->monitors[i].y < min_y) { + min_y = monitor_conf->monitors[i].y; + } + } + + FPRINTF("Section \"ServerLayout\"\n"); + FPRINTF("\tIdentifier\t\"layout\"\n"); + for (i = 0; i < count; i++) { + FPRINTF("\tScreen\t\t\"Screen%d\" %d %d\n", i, + monitor_conf->monitors[i].x - min_x, + monitor_conf->monitors[i].y - min_y); + } + FPRINTF("EndSection\n"); + + fclose(f); + pci_system_cleanup(); +#endif +} diff --git a/src/vdagentd.c b/src/vdagentd.c index cee779b..939d36a 100644 --- a/src/vdagentd.c +++ b/src/vdagentd.c @@ -38,6 +38,7 @@ #include "vdagentd-proto.h" #include "vdagentd-proto-strings.h" #include "vdagentd-uinput.h" +#include "vdagentd-xorg-conf.h" #include "vdagent-virtio-port.h" #include "console-kit.h" @@ -118,6 +119,16 @@ static void do_client_monitors(struct vdagent_virtio_port *vport, int port_nr, return; } + vdagentd_write_xorg_conf(new_monitors, logfile); + + if (new_monitors->num_of_monitors != 1) { + /* No use in sending this to the session agent it cannot handle it + anyways */ + free(mon_config); + mon_config = NULL; + goto ack; + } + if (!mon_config || mon_config->num_of_monitors != new_monitors->num_of_monitors) { free(mon_config); @@ -133,6 +144,7 @@ static void do_client_monitors(struct vdagent_virtio_port *vport, int port_nr, udscs_server_write_all(server, VDAGENTD_MONITORS_CONFIG, 0, 0, (uint8_t *)mon_config, size); +ack: /* Acknowledge reception of monitors config to spice server / client */ reply.type = VD_AGENT_MONITORS_CONFIG; reply.error = VD_AGENT_SUCCESS; -- cgit v1.2.3