[Spice-devel] [RFC 27/33] xspice: init spice server, add main loop

Alon Levy alevy at redhat.com
Wed Apr 27 08:56:16 PDT 2011


Initialize a SpiceServer instance, and implement SpiceCoreInterface,
that is fd read, write notification and watchs (timers).

The fd implementation is off because Xserver doesn't allow us to be
notified on write unblock, only read. Workaround is to poll.
---
 src/Makefile.am          |    1 +
 src/qxl.h                |    4 +
 src/qxl_driver.c         |   26 ++++
 src/spiceqxl_main_loop.c |  317 ++++++++++++++++++++++++++++++++++++++++++++++
 src/spiceqxl_main_loop.h |   10 ++
 5 files changed, 358 insertions(+), 0 deletions(-)
 create mode 100644 src/spiceqxl_main_loop.c
 create mode 100644 src/spiceqxl_main_loop.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 6ecb17d..eaa0746 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -61,6 +61,7 @@ spiceqxl_drv_la_SOURCES =				\
 	qxl.h					\
 	spiceqxl_io_port.c			\
 	spiceqxl_driver.c			\
+	spiceqxl_main_loop.c			\
 	qxl_driver.c				\
 	qxl_image.c				\
 	qxl_surface.c				\
diff --git a/src/qxl.h b/src/qxl.h
index 2dc4cf9..0b291c9 100644
--- a/src/qxl.h
+++ b/src/qxl.h
@@ -28,6 +28,9 @@
 #include <stdint.h>
 
 #include <spice/qxl_dev.h>
+#ifdef XSPICE
+#include <spice.h>
+#endif
 
 #include "compiler.h"
 #include "xf86.h"
@@ -186,6 +189,7 @@ struct _qxl_screen_t
 #ifdef XSPICE
     /* XSpice specific */
     struct QXLRom		shadow_rom;    /* Parameter RAM */
+    SpiceServer *       spice_server;
 #endif /* XSPICE */
 };
 
diff --git a/src/qxl_driver.c b/src/qxl_driver.c
index e6fbb93..938727e 100644
--- a/src/qxl_driver.c
+++ b/src/qxl_driver.c
@@ -39,6 +39,7 @@
 
 #ifdef XSPICE
 #include "spiceqxl_driver.h"
+#include "spiceqxl_main_loop.h"
 #endif /* XSPICE */
 
 #if 0
@@ -883,6 +884,28 @@ setup_uxa (qxl_screen_t *qxl, ScreenPtr screen)
     return TRUE;
 }
 
+#ifdef XSPICE
+
+static void
+spiceqxl_screen_init(int scrnIndex, ScrnInfoPtr pScrn, qxl_screen_t *qxl)
+{
+    SpiceCoreInterface *core;
+
+    // Init spice
+    if (!qxl->spice_server) {
+        qxl->spice_server = spice_server_new();
+        // some common initialization for all display tests
+        spice_server_set_port(qxl->spice_server, qxl->options[OPTION_SPICE_PORT].value.num);
+        spice_server_set_noauth(qxl->spice_server); // TODO - take this from config
+        // TODO - parse rest of parameters (streaming, compression, jpeg, etc.) from config
+        core = basic_event_loop_init();
+        spice_server_init(qxl->spice_server, core);
+    }
+    qxl->spice_server = qxl->spice_server;
+}
+
+#endif
+
 static Bool
 qxl_screen_init(int scrnIndex, ScreenPtr pScreen, int argc, char **argv)
 {
@@ -898,6 +921,9 @@ qxl_screen_init(int scrnIndex, ScreenPtr pScreen, int argc, char **argv)
     if (!qxl_map_memory(qxl, scrnIndex))
 	return FALSE;
 
+#ifdef XSPICE
+    spiceqxl_screen_init(scrnIndex, pScrn, qxl);
+#endif
     ram_header = (void *)((unsigned long)qxl->ram + (unsigned long)qxl->rom->ram_header_offset);
     
     printf ("ram_header at %d\n", qxl->rom->ram_header_offset);
diff --git a/src/spiceqxl_main_loop.c b/src/spiceqxl_main_loop.c
new file mode 100644
index 0000000..7f56622
--- /dev/null
+++ b/src/spiceqxl_main_loop.c
@@ -0,0 +1,317 @@
+#include <sys/time.h>
+
+#include <spice.h>
+#include "spiceqxl_main_loop.h"
+
+static int spiceqxl_main_loop_debug = 0;
+
+#define DPRINTF(x, format, ...) { \
+    if (x <= spiceqxl_main_loop_debug) { \
+        printf("%s: " format "\n" , __FUNCTION__, ## __VA_ARGS__); \
+    } \
+}
+
+/* From ring.h */
+typedef struct Ring RingItem;
+typedef struct Ring {
+    RingItem *prev;
+    RingItem *next;
+} Ring;
+
+static inline void ring_init(Ring *ring)
+{
+    ring->next = ring->prev = ring;
+}
+
+static inline void ring_item_init(RingItem *item)
+{
+    item->next = item->prev = NULL;
+}
+
+static inline int ring_item_is_linked(RingItem *item)
+{
+    return !!item->next;
+}
+
+static inline int ring_is_empty(Ring *ring)
+{
+    assert(ring->next != NULL && ring->prev != NULL);
+    return ring == ring->next;
+}
+
+static inline void ring_add(Ring *ring, RingItem *item)
+{
+    assert(ring->next != NULL && ring->prev != NULL);
+    assert(item->next == NULL && item->prev == NULL);
+
+    item->next = ring->next;
+    item->prev = ring;
+    ring->next = item->next->prev = item;
+}
+
+static inline void __ring_remove(RingItem *item)
+{
+    item->next->prev = item->prev;
+    item->prev->next = item->next;
+    item->prev = item->next = 0;
+}
+
+static inline void ring_remove(RingItem *item)
+{
+    assert(item->next != NULL && item->prev != NULL);
+    assert(item->next != item);
+
+    __ring_remove(item);
+}
+
+static inline RingItem *ring_get_head(Ring *ring)
+{
+    RingItem *ret;
+
+    assert(ring->next != NULL && ring->prev != NULL);
+
+    if (ring_is_empty(ring)) {
+        return NULL;
+    }
+    ret = ring->next;
+    return ret;
+}
+
+static inline RingItem *ring_get_tail(Ring *ring)
+{
+    RingItem *ret;
+
+    assert(ring->next != NULL && ring->prev != NULL);
+
+    if (ring_is_empty(ring)) {
+        return NULL;
+    }
+    ret = ring->prev;
+    return ret;
+}
+
+static inline RingItem *ring_next(Ring *ring, RingItem *pos)
+{
+    RingItem *ret;
+
+    assert(ring->next != NULL && ring->prev != NULL);
+    assert(pos);
+    assert(pos->next != NULL && pos->prev != NULL);
+    ret = pos->next;
+    return (ret == ring) ? NULL : ret;
+}
+
+static inline RingItem *ring_prev(Ring *ring, RingItem *pos)
+{
+    RingItem *ret;
+
+    assert(ring->next != NULL && ring->prev != NULL);
+    assert(pos);
+    assert(pos->next != NULL && pos->prev != NULL);
+    ret = pos->prev;
+    return (ret == ring) ? NULL : ret;
+}
+
+#define RING_FOREACH_SAFE(var, next, ring)                    \
+    for ((var) = ring_get_head(ring),                         \
+         (next) = (var) ? ring_next(ring, (var)) : NULL;      \
+            (var);                                            \
+            (var) = (next),                                   \
+            (next) = (var) ? ring_next(ring, (var)) : NULL)
+
+/**/
+
+#define NOT_IMPLEMENTED printf("%s not implemented\n", __func__);
+
+static SpiceCoreInterface core;
+
+typedef struct SpiceTimer {
+    OsTimerPtr xorg_timer;
+    SpiceTimerFunc func;
+    void *opaque; // also stored in xorg_timer, but needed for timer_start
+} Timer;
+
+static CARD32 xorg_timer_callback(
+    OsTimerPtr xorg_timer,
+    CARD32 time,
+    pointer arg)
+{
+    SpiceTimer *timer = (SpiceTimer*)arg;
+
+    timer->func(timer->opaque);
+    return 0; // if non zero xorg does a TimerSet, we don't want that.
+}
+
+static SpiceTimer* timer_add(SpiceTimerFunc func, void *opaque)
+{
+    SpiceTimer *timer = calloc(sizeof(SpiceTimer), 1);
+
+    timer->xorg_timer = TimerSet(NULL, 0, 1e9 /* TODO: infinity? */, xorg_timer_callback, timer);
+    timer->func = func;
+    timer->opaque = opaque;
+    return timer;
+}
+
+static void timer_start(SpiceTimer *timer, uint32_t ms)
+{
+    TimerSet(timer->xorg_timer, 0 /* flags */, ms, xorg_timer_callback, timer);
+}
+
+static void timer_cancel(SpiceTimer *timer)
+{
+    TimerCancel(timer->xorg_timer);
+}
+
+static void timer_remove(SpiceTimer *timer)
+{
+    TimerFree(timer->xorg_timer);
+    free(timer);
+}
+
+struct SpiceWatch {
+    RingItem link;
+    int fd;
+    int event_mask;
+    SpiceWatchFunc func;
+    void *opaque;
+    int remove;
+};
+
+Ring watches;
+
+int watch_count = 0;
+
+static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque)
+{
+    SpiceWatch *watch = malloc(sizeof(SpiceWatch));
+
+    DPRINTF(0, "adding %p, fd=%d at %d", watch,
+        fd, watch_count);
+    watch->fd = fd;
+    watch->event_mask = event_mask;
+    watch->func = func;
+    watch->opaque = opaque;
+    watch->remove = FALSE;
+    ring_item_init(&watch->link);
+    ring_add(&watches, &watch->link);
+    watch_count++;
+    return watch;
+}
+
+static void watch_update_mask(SpiceWatch *watch, int event_mask)
+{
+    DPRINTF(0, "fd %d to %d", watch->fd, event_mask);
+    watch->event_mask = event_mask;
+}
+
+static void watch_remove(SpiceWatch *watch)
+{
+    DPRINTF(0, "remove %p (fd %d)", watch, watch->fd);
+    ring_remove(&watch->link);
+    watch->remove = TRUE;
+    watch_count--;
+}
+
+static void channel_event(int event, SpiceChannelEventInfo *info)
+{
+    NOT_IMPLEMENTED
+}
+
+static int set_watch_fds(fd_set *rfds, fd_set *wfds)
+{
+    SpiceWatch *watch;
+    RingItem *link;
+    RingItem *next;
+    int max_fd = -1;
+
+    RING_FOREACH_SAFE(link, next, &watches) {
+        watch = (SpiceWatch*)link;
+        if (watch->event_mask & SPICE_WATCH_EVENT_READ) {
+            FD_SET(watch->fd, rfds);
+            max_fd = watch->fd > max_fd ? watch->fd : max_fd;
+        }
+        if (watch->event_mask & SPICE_WATCH_EVENT_WRITE) {
+            FD_SET(watch->fd, wfds);
+            max_fd = watch->fd > max_fd ? watch->fd : max_fd;
+        }
+    }
+    return max_fd;
+}
+
+/*
+ * called just before the X server goes into select()
+ * readmask is just an fdset on linux, but something totally different on windows (etc).
+ * DIX has a comment about it using a special type to hide this (so we break that here)
+ */
+static void xspice_block_handler(pointer data, OSTimePtr timeout, pointer readmask)
+{
+    /* set all our fd's */
+    set_watch_fds((fd_set*)readmask, (fd_set*)readmask);
+}
+
+/*
+ * xserver only calles wakeup_handler with the read fd_set, so we
+ * must either patch it or do a polling select ourselves, this is the
+ * later approach. Since we are already doing a polling select, we
+ * already select on all (we could avoid selecting on the read since
+ * that *is* actually taken care of by the wakeup handler).
+ */
+static void select_and_check_watches(void)
+{
+    fd_set rfds, wfds;
+    int max_fd = -1;
+    SpiceWatch *watch;
+    RingItem *link;
+    RingItem *next;
+    struct timeval timeout;
+    int retval;
+
+    FD_ZERO(&rfds);
+    FD_ZERO(&wfds);
+    max_fd = set_watch_fds(&rfds, &wfds);
+    watch = (SpiceWatch*)watches.next;
+    timeout.tv_sec = timeout.tv_usec = 0;
+    retval = select(max_fd + 1, &rfds, &wfds, NULL, &timeout);
+    if (retval) {
+        RING_FOREACH_SAFE(link, next, &watches) {
+            watch = (SpiceWatch*)link;
+            if ((watch->event_mask & SPICE_WATCH_EVENT_READ)
+                 && FD_ISSET(watch->fd, &rfds)) {
+                watch->func(watch->fd, SPICE_WATCH_EVENT_READ, watch->opaque);
+            }
+            if (!watch->remove && (watch->event_mask & SPICE_WATCH_EVENT_WRITE)
+                 && FD_ISSET(watch->fd, &wfds)) {
+                watch->func(watch->fd, SPICE_WATCH_EVENT_WRITE, watch->opaque);
+            }
+            if (watch->remove) {
+                free(watch);
+            }
+        }
+    }
+}
+
+static void xspice_wakeup_handler(pointer data, int nfds, pointer readmask)
+{
+    if (!nfds) {
+        return;
+    }
+    select_and_check_watches();
+}
+
+SpiceCoreInterface *basic_event_loop_init(void)
+{
+    ring_init(&watches);
+    bzero(&core, sizeof(core));
+    core.base.major_version = SPICE_INTERFACE_CORE_MAJOR;
+    core.base.minor_version = SPICE_INTERFACE_CORE_MINOR; // anything less then 3 and channel_event isn't called
+    core.timer_add = timer_add;
+    core.timer_start = timer_start;
+    core.timer_cancel = timer_cancel;
+    core.timer_remove = timer_remove;
+    core.watch_add = watch_add;
+    core.watch_update_mask = watch_update_mask;
+    core.watch_remove = watch_remove;
+    core.channel_event = channel_event;
+    RegisterBlockAndWakeupHandlers(xspice_block_handler, xspice_wakeup_handler, 0);
+    return &core;
+}
diff --git a/src/spiceqxl_main_loop.h b/src/spiceqxl_main_loop.h
new file mode 100644
index 0000000..373756c
--- /dev/null
+++ b/src/spiceqxl_main_loop.h
@@ -0,0 +1,10 @@
+#ifndef QXL_MAIN_LOOP_H
+#define QXL_MAIN_LOOP_H
+
+#include "qxl.h"
+#include <spice.h>
+
+SpiceCoreInterface *basic_event_loop_init(void);
+void basic_event_loop_mainloop(void);
+
+#endif // QXL_MAIN_LOOP_H
-- 
1.7.4.4



More information about the Spice-devel mailing list