Check if the current display server is active or not and stream only if active. This will allow to support multiple display servers running. When multiple display servers are running only one have the GPU associated and is writing to the screen. Stop capturing and release resources when the display server is not active and vice versa. Signed-off-by: Frediano Ziglio <fziglio@xxxxxxxxxx> --- src/Makefile.am | 2 ++ src/eventfd.cpp | 38 ++++++++++++++++++++ src/eventfd.hpp | 34 ++++++++++++++++++ src/spice-streaming-agent.cpp | 65 +++++++++++++++++++++++++++++++---- 4 files changed, 132 insertions(+), 7 deletions(-) create mode 100644 src/eventfd.cpp create mode 100644 src/eventfd.hpp diff --git a/src/Makefile.am b/src/Makefile.am index 276478f..ffc52b2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -62,4 +62,6 @@ spice_streaming_agent_SOURCES = \ jpeg.hpp \ stream-port.cpp \ stream-port.hpp \ + eventfd.cpp \ + eventfd.hpp \ $(NULL) diff --git a/src/eventfd.cpp b/src/eventfd.cpp new file mode 100644 index 0000000..e3c1e72 --- /dev/null +++ b/src/eventfd.cpp @@ -0,0 +1,38 @@ +/* Linux event file descriptors + * + * \copyright + * Copyright 2018 Red Hat Inc. All rights reserved. + */ +#include <config.h> +#include "eventfd.hpp" + +#include <stdexcept> +#include <sys/eventfd.h> + +template<typename T> +inline void ignore_result(T /* unused result */) {} + +namespace spice { +namespace streaming_agent { + +EventFD::EventFD(): + fd(eventfd(0, EFD_CLOEXEC|EFD_NONBLOCK)) +{ + if (fd < 0) { + throw std::runtime_error("Failed to create eventfd"); + } +} + +void EventFD::signal() +{ + uint64_t n = 1; + ignore_result(write(fd, &n, sizeof(n))); +} + +void EventFD::ack() +{ + uint64_t n; + ignore_result(read(fd, &n, sizeof(n))); +} + +}} // namespace spice::streaming_agent diff --git a/src/eventfd.hpp b/src/eventfd.hpp new file mode 100644 index 0000000..48a175c --- /dev/null +++ b/src/eventfd.hpp @@ -0,0 +1,34 @@ +/* Linux event file descriptors + * + * \copyright + * Copyright 2018 Red Hat Inc. All rights reserved. + */ +#ifndef SPICE_STREAMING_AGENT_EVENTFD_HPP +#define SPICE_STREAMING_AGENT_EVENTFD_HPP + +#include <unistd.h> + +namespace spice { +namespace streaming_agent { + +class EventFD { +public: + EventFD(); + ~EventFD() { close(fd); } + void signal(); + void ack(); + /** + * Returns underlying file descriptor. + * Do not close, should just be used for polling. + */ + int raw_fd() const { return fd; } +private: + EventFD(const EventFD&) = delete; + void operator=(const EventFD&) = delete; + + int fd; +}; + +}} // namespace spice::streaming_agent + +#endif // SPICE_STREAMING_AGENT_EVENTFD_HPP diff --git a/src/spice-streaming-agent.cpp b/src/spice-streaming-agent.cpp index 6388bb2..240b9c7 100644 --- a/src/spice-streaming-agent.cpp +++ b/src/spice-streaming-agent.cpp @@ -10,6 +10,7 @@ #include "stream-port.hpp" #include "error.hpp" #include "xorg-utils.hpp" +#include "eventfd.hpp" #include <spice/stream-device.h> #include <spice/enums.h> @@ -36,6 +37,7 @@ #include <thread> #include <vector> #include <string> +#include <atomic> #include <functional> #include <X11/Xlib.h> #include <X11/extensions/Xfixes.h> @@ -61,12 +63,21 @@ static bool quit_requested = false; static bool log_binary = false; static bool log_frames = false; static std::set<SpiceVideoCodecType> client_codecs; +static EventFD update_fd; +static const char vt_property_name[] = "XFree86_has_VT"; +static const char *stream_port_name = "/dev/virtio-ports/org.spice-space.stream.0"; +static Atom vt_property = None; +static std::atomic_bool vt_active; -static bool have_something_to_read(StreamPort &stream_port, bool blocking) +static bool have_something_to_read(StreamPort &stream_port, bool blocking, int &fd) { - struct pollfd pollfd = {stream_port.fd, POLLIN, 0}; + struct pollfd pollfds[2] = { + { stream_port.fd, POLLIN, 0 }, + { update_fd.raw_fd(), POLLIN, 0 }, + }; - if (poll(&pollfd, 1, blocking ? -1 : 0) < 0) { + fd = -1; + if (poll(pollfds, 2, blocking ? -1 : 0) < 0) { if (errno == EINTR) { // report nothing to read, next iteration of the enclosing loop will retry return false; @@ -75,7 +86,13 @@ static bool have_something_to_read(StreamPort &stream_port, bool blocking) throw IOError("poll failed on the device", errno); } - if (pollfd.revents & POLLIN) { + if (pollfds[1].revents & POLLIN) { + fd = update_fd.raw_fd(); + return true; + } + + if (pollfds[0].revents & POLLIN) { + fd = stream_port.fd; return true; } @@ -173,8 +190,18 @@ static void read_command_from_device(StreamPort &stream_port) static void read_command(StreamPort &stream_port, bool blocking) { while (!quit_requested) { - if (have_something_to_read(stream_port, blocking)) { - read_command_from_device(stream_port); + int fd; + if (have_something_to_read(stream_port, blocking, fd)) { + if (fd == stream_port.fd) { + read_command_from_device(stream_port); + } else if (fd == update_fd.raw_fd()) { + update_fd.ack(); + bool vt_active = ::vt_active.load(std::memory_order_relaxed); + if (!vt_active) { + throw std::runtime_error("VT disabled"); + } + continue; + } break; } @@ -305,10 +332,22 @@ send_cursor(StreamPort &stream_port, unsigned width, unsigned height, int hotspo static void cursor_changes(StreamPort *stream_port, Display *display, int event_base) { unsigned long last_serial = 0; + Window rootwindow = DefaultRootWindow(display); while (1) { XEvent event; XNextEvent(display, &event); + + if (event.type == PropertyNotify) { + if (vt_property == None || event.xproperty.atom != vt_property) + continue; + // update vt property, activate screen read if needed + vt_active.store(get_win_prop_int(display, rootwindow, vt_property) != 0, std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + update_fd.signal(); + continue; + } + if (event.type != event_base + 1) { continue; } @@ -416,7 +455,6 @@ do_capture(StreamPort &stream_port, FILE *f_log) int main(int argc, char* argv[]) { - const char *stream_port_name = "/dev/virtio-ports/org.spice-space.stream.0"; int opt; const char *log_filename = NULL; int logmask = LOG_UPTO(LOG_WARNING); @@ -522,6 +560,19 @@ int main(int argc, char* argv[]) Window rootwindow = DefaultRootWindow(display); XFixesSelectCursorInput(display, rootwindow, XFixesDisplayCursorNotifyMask); + vt_property = XInternAtom(display, vt_property_name, True); + if (vt_property == None) { + syslog(LOG_ERR, "VT property not found\n"); + return EXIT_FAILURE; + } + XSelectInput(display, rootwindow, PropertyChangeMask); + + vt_active.store(get_win_prop_int(display, rootwindow, vt_property) != 0, std::memory_order_relaxed); + if (!::vt_active.load(std::memory_order_relaxed)) { + syslog(LOG_ERR, "VT is disabled"); + return EXIT_FAILURE; + } + int ret = EXIT_SUCCESS; try { -- 2.17.0 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel