summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGerd Hoffmann <kraxel@redhat.com>2010-09-16 11:43:24 +0200
committerGerd Hoffmann <kraxel@redhat.com>2010-09-16 11:43:24 +0200
commit03d17e3be5e9905b91a1366b7b57d50b49daafb9 (patch)
tree2a6e0195c4622ddfce4585b1fee398955ac1cca0
parent74553154d842202533dded90b32d7a1cad94da35 (diff)
audio bits
add forgotton file.
-rw-r--r--configure.ac4
-rw-r--r--gtk/Makefile.am23
-rw-r--r--gtk/README2
-rw-r--r--gtk/channel-display.c7
-rw-r--r--gtk/channel-main.c18
-rw-r--r--gtk/channel-playback.c89
-rw-r--r--gtk/spice-channel-cache.h77
-rw-r--r--gtk/spice-channel-priv.h14
-rw-r--r--gtk/spice-channel.c32
-rw-r--r--gtk/spice-channel.h11
-rw-r--r--gtk/spice-client.h5
-rw-r--r--gtk/spice-events.h4
-rw-r--r--gtk/spice-marshal.txt2
-rw-r--r--gtk/spice-pulse.c187
-rw-r--r--gtk/spice-pulse.h38
-rw-r--r--gtk/spice-session-priv.h5
-rw-r--r--gtk/spice-session.h3
-rw-r--r--gtk/spice-types.h4
-rw-r--r--gtk/spice-widget.c4
-rw-r--r--gtk/spice-widget.h5
-rw-r--r--gtk/spicy.c21
21 files changed, 535 insertions, 20 deletions
diff --git a/configure.ac b/configure.ac
index f065529..c50e59c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -220,6 +220,10 @@ PKG_CHECK_MODULES(GOBJECT2, gobject-2.0)
AC_SUBST(GOBJECT2_CFLAGS)
AC_SUBST(GOBJECT2_LIBS)
+PKG_CHECK_MODULES(PULSE, libpulse libpulse-mainloop-glib)
+AC_SUBST(PULSE_CFLAGS)
+AC_SUBST(PULSE_LIBS)
+
# Add parameter for (partial) static linkage of spice client.
# this is used to achive single binary package for all (?) distros.
AC_ARG_ENABLE(static-linkage,
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index e8727ba..fcb975f 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -6,7 +6,10 @@ CLIENT_DIR=$(top_srcdir)/client
EXTRA_DIST = spice-marshall.txt keymap-gen.pl keymaps.csv
bin_PROGRAMS = spicy snappy
-lib_LTLIBRARIES = libspice-client-gtk.la libspice-client-glib.la
+lib_LTLIBRARIES = \
+ libspice-client-gtk.la \
+ libspice-client-pulse.la \
+ libspice-client-glib.la
KEYMAP_GEN = $(srcdir)/keymap-gen.pl
@@ -31,6 +34,7 @@ INCLUDES = \
$(PROTOCOL_CFLAGS) \
$(PIXMAN_CFLAGS) \
$(CELT051_CFLAGS) \
+ $(PULSE_CFLAGS) \
$(GTK2_CFLAGS) \
$(GLIB2_CFLAGS) \
$(GOBJECT2_CFLAGS) \
@@ -55,6 +59,21 @@ libspice_client_gtk_la_SOURCES = \
$(NULL)
+libspice_client_pulse_la_LDFLAGS = \
+ -version-number 0:0:1 \
+ -no-undefined \
+ $(NULL)
+
+libspice_client_pulse_la_LIBADD = \
+ $(PULSE_LIBS) \
+ $(NULL)
+
+libspice_client_pulse_la_SOURCES = \
+ spice-pulse.c \
+ spice-pulse.h \
+ $(NULL)
+
+
libspice_client_glib_la_LDFLAGS = \
-version-number 0:0:1 \
-no-undefined \
@@ -92,6 +111,7 @@ libspice_client_glib_la_SOURCES = \
channel-display.c \
channel-cursor.c \
channel-inputs.c \
+ channel-playback.c \
\
decode.h \
decode-glz.c \
@@ -129,6 +149,7 @@ spicy_DEPENDENCIES = \
spicy_LDFLAGS = \
-lspice-client-gtk \
+ -lspice-client-pulse \
-lspice-client-glib \
$(NULL)
diff --git a/gtk/README b/gtk/README
index efecc73..83be3ee 100644
--- a/gtk/README
+++ b/gtk/README
@@ -11,7 +11,7 @@ current state
Starts becoming usable.
Some features are missing:
- - No sound support.
+ - No sound recording support.
- No client migration support.
- No library documentation.
- Probably more ...
diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index 3ed5b8b..01e3f22 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -210,8 +210,6 @@ static void display_handle_mode(SpiceChannel *channel, spice_msg_in *in)
free(surface);
}
- fprintf(stderr, "%s: %dx%d @ %d bpp\n", __FUNCTION__,
- mode->x_res, mode->y_res, mode->bits);
surface = spice_new0(display_surface, 1);
surface->format = mode->bits == 32 ?
SPICE_SURFACE_FMT_32_xRGB : SPICE_SURFACE_FMT_16_555;
@@ -373,9 +371,6 @@ static void display_handle_surface_create(SpiceChannel *channel, spice_msg_in *i
spice_channel *c = SPICE_CHANNEL_GET_PRIVATE(channel);
display_surface *surface = spice_new0(display_surface, 1);
- fprintf(stderr, "%s: id %d, %dx%d\n", __FUNCTION__,
- create->surface_id, create->width, create->height);
-
surface->surface_id = create->surface_id;
surface->format = create->format;
surface->width = create->width;
@@ -402,8 +397,6 @@ static void display_handle_surface_destroy(SpiceChannel *channel, spice_msg_in *
SpiceMsgSurfaceDestroy *destroy = spice_msg_in_parsed(in);
display_surface *surface = find_surface(channel, destroy->surface_id);
- fprintf(stderr, "%s: id %d\n", __FUNCTION__, destroy->surface_id);
-
if (surface->primary) {
g_signal_emit(channel, channel_signals[SPICE_DISPLAY_PRIMARY_DESTROY], 0);
}
diff --git a/gtk/channel-main.c b/gtk/channel-main.c
index 3b80da1..7ff7a4e 100644
--- a/gtk/channel-main.c
+++ b/gtk/channel-main.c
@@ -127,11 +127,20 @@ static void main_handle_init(SpiceChannel *channel, spice_msg_in *in)
#endif
}
+static void main_handle_mm_time(SpiceChannel *channel, spice_msg_in *in)
+{
+ fprintf(stderr, "%s: TODO\n", __FUNCTION__);
+#if 0
+ set_mm_time(init->multi_media_time);
+#endif
+}
+
static spice_channel_info *type2info[] = {
- [ SPICE_CHANNEL_MAIN ] = &main_channel_info,
- [ SPICE_CHANNEL_DISPLAY ] = &display_channel_info,
- [ SPICE_CHANNEL_CURSOR ] = &cursor_channel_info,
- [ SPICE_CHANNEL_INPUTS ] = &inputs_channel_info,
+ [ SPICE_CHANNEL_MAIN ] = &main_channel_info,
+ [ SPICE_CHANNEL_DISPLAY ] = &display_channel_info,
+ [ SPICE_CHANNEL_CURSOR ] = &cursor_channel_info,
+ [ SPICE_CHANNEL_INPUTS ] = &inputs_channel_info,
+ [ SPICE_CHANNEL_PLAYBACK ] = &playback_channel_info,
};
static void main_handle_channels_list(SpiceChannel *channel, spice_msg_in *in)
@@ -223,6 +232,7 @@ static spice_msg_handler main_handlers[] = {
[ SPICE_MSG_MAIN_INIT ] = main_handle_init,
[ SPICE_MSG_MAIN_CHANNELS_LIST ] = main_handle_channels_list,
[ SPICE_MSG_MAIN_MOUSE_MODE ] = main_handle_mouse_mode,
+ [ SPICE_MSG_MAIN_MULTI_MEDIA_TIME ] = main_handle_mm_time,
[ SPICE_MSG_MAIN_AGENT_CONNECTED ] = main_handle_agent_connected,
[ SPICE_MSG_MAIN_AGENT_DISCONNECTED ] = main_handle_agent_disconnected,
diff --git a/gtk/channel-playback.c b/gtk/channel-playback.c
new file mode 100644
index 0000000..0e3b0b8
--- /dev/null
+++ b/gtk/channel-playback.c
@@ -0,0 +1,89 @@
+#include <assert.h>
+
+#include "spice-client.h"
+#include "spice-channel-priv.h"
+
+static void playback_handle_data(SpiceChannel *channel, spice_msg_in *in)
+{
+ spice_channel *c = SPICE_CHANNEL_GET_PRIVATE(channel);
+ SpiceMsgPlaybackPacket *data = spice_msg_in_parsed(in);
+
+#if 0
+ fprintf(stderr, "%s: time %d data %p size %d\n", __FUNCTION__,
+ data->time, data->data, data->data_size);
+#endif
+
+ switch (c->playback.mode) {
+ case SPICE_AUDIO_DATA_MODE_RAW:
+ g_signal_emit(channel, channel_signals[SPICE_PLAYBACK_DATA], 0,
+ data->data, data->data_size);
+ break;
+ default:
+ fprintf(stderr, "%s: unhandled mode\n", __FUNCTION__);
+ break;
+ }
+}
+
+static void playback_handle_mode(SpiceChannel *channel, spice_msg_in *in)
+{
+ spice_channel *c = SPICE_CHANNEL_GET_PRIVATE(channel);
+ SpiceMsgPlaybackMode *mode = spice_msg_in_parsed(in);
+
+#if 0
+ fprintf(stderr, "%s: time %d mode %d data %p size %d\n", __FUNCTION__,
+ mode->time, mode->mode, mode->data, mode->data_size);
+#endif
+
+ c->playback.mode = mode->mode;
+ switch (c->playback.mode) {
+ case SPICE_AUDIO_DATA_MODE_RAW:
+ break;
+ default:
+ fprintf(stderr, "%s: unhandled mode\n", __FUNCTION__);
+ break;
+ }
+}
+
+static void playback_handle_start(SpiceChannel *channel, spice_msg_in *in)
+{
+ spice_channel *c = SPICE_CHANNEL_GET_PRIVATE(channel);
+ SpiceMsgPlaybackStart *start = spice_msg_in_parsed(in);
+
+#if 0
+ fprintf(stderr, "%s: fmt %d channels %d freq %d time %d\n", __FUNCTION__,
+ start->format, start->channels, start->frequency, start->time);
+#endif
+
+ switch (c->playback.mode) {
+ case SPICE_AUDIO_DATA_MODE_RAW:
+ g_signal_emit(channel, channel_signals[SPICE_PLAYBACK_START], 0,
+ start->format, start->channels, start->frequency);
+ break;
+ default:
+ fprintf(stderr, "%s: unhandled mode\n", __FUNCTION__);
+ break;
+ }
+}
+
+static void playback_handle_stop(SpiceChannel *channel, spice_msg_in *in)
+{
+ g_signal_emit(channel, channel_signals[SPICE_PLAYBACK_STOP], 0);
+}
+
+static spice_msg_handler playback_handlers[] = {
+ [ SPICE_MSG_SET_ACK ] = base_handle_set_ack,
+ [ SPICE_MSG_PING ] = base_handle_ping,
+ [ SPICE_MSG_NOTIFY ] = base_handle_notify,
+
+ [ SPICE_MSG_PLAYBACK_DATA ] = playback_handle_data,
+ [ SPICE_MSG_PLAYBACK_MODE ] = playback_handle_mode,
+ [ SPICE_MSG_PLAYBACK_START ] = playback_handle_start,
+ [ SPICE_MSG_PLAYBACK_STOP ] = playback_handle_stop,
+};
+
+spice_channel_info playback_channel_info = {
+ .type = SPICE_CHANNEL_PLAYBACK,
+ .name = "playback",
+ .handlers = playback_handlers,
+ .nhandlers = SPICE_N_ELEMENTS(playback_handlers),
+};
diff --git a/gtk/spice-channel-cache.h b/gtk/spice-channel-cache.h
new file mode 100644
index 0000000..c7f289c
--- /dev/null
+++ b/gtk/spice-channel-cache.h
@@ -0,0 +1,77 @@
+static inline void cache_init(display_cache *cache, const char *name)
+{
+ int i;
+
+ cache->name = name;
+ ring_init(&cache->lru);
+ for (i = 0; i < SPICE_N_ELEMENTS(cache->hash); i++) {
+ ring_init(&cache->hash[i]);
+ }
+}
+
+static inline Ring *cache_head(display_cache *cache, uint64_t id)
+{
+ return &cache->hash[id % SPICE_N_ELEMENTS(cache->hash)];
+}
+
+static inline void cache_used(display_cache *cache, display_cache_item *item)
+{
+ ring_remove(&item->lru_link);
+ ring_add(&cache->lru, &item->lru_link);
+}
+
+static inline display_cache_item *cache_get_lru(display_cache *cache)
+{
+ display_cache_item *item;
+ RingItem *ring;
+
+ ring = ring_get_tail(&cache->lru);
+ item = SPICE_CONTAINEROF(ring, display_cache_item, lru_link);
+ return item;
+}
+
+static inline display_cache_item *cache_find(display_cache *cache, uint64_t id)
+{
+ display_cache_item *item;
+ RingItem *ring;
+ Ring *head;
+
+ head = cache_head(cache, id);
+ for (ring = head; ring != NULL; ring = ring_next(head, ring)) {
+ item = SPICE_CONTAINEROF(ring, display_cache_item, hash_link);
+ if (item->id == id) {
+ return item;
+ }
+ }
+ fprintf(stderr, "%s: %s %" PRIx64 " [not found]\n", __FUNCTION__,
+ cache->name, id);
+ return NULL;
+}
+
+static inline display_cache_item *cache_add(display_cache *cache, uint64_t id)
+{
+ display_cache_item *item;
+
+ item = spice_new0(display_cache_item, 1);
+ item->id = id;
+ ring_add(cache_head(cache, item->id), &item->hash_link);
+ ring_add(&cache->lru, &item->lru_link);
+ cache->nitems++;
+#if 0
+ fprintf(stderr, "%s: %s %" PRIx64 " (%d)\n", __FUNCTION__,
+ cache->name, id, cache->nitems);
+#endif
+ return item;
+}
+
+static inline void cache_del(display_cache *cache, display_cache_item *item)
+{
+#if 0
+ fprintf(stderr, "%s: %s %" PRIx64 "\n", __FUNCTION__,
+ cache->name, item->id);
+#endif
+ ring_remove(&item->hash_link);
+ ring_remove(&item->lru_link);
+ free(item);
+ cache->nitems--;
+}
diff --git a/gtk/spice-channel-priv.h b/gtk/spice-channel-priv.h
index 3cd92a5..3839300 100644
--- a/gtk/spice-channel-priv.h
+++ b/gtk/spice-channel-priv.h
@@ -1,3 +1,6 @@
+#ifndef __SPICE_CLIENT_CHANNEL_PRIV_H__
+#define __SPICE_CLIENT_CHANNEL_PRIV_H__
+
#include <pixman.h>
#include <openssl/ssl.h>
@@ -114,6 +117,10 @@ struct inputs_channel {
int motion_count;
};
+struct playback_channel {
+ int mode;
+};
+
struct spice_channel {
SpiceSession *session;
enum spice_channel_state state;
@@ -145,6 +152,7 @@ struct spice_channel {
struct display_channel display;
struct cursor_channel cursor;
struct inputs_channel inputs;
+ struct playback_channel playback;
};
};
@@ -160,6 +168,10 @@ enum {
SPICE_CURSOR_SET,
+ SPICE_PLAYBACK_START,
+ SPICE_PLAYBACK_DATA,
+ SPICE_PLAYBACK_STOP,
+
SPICE_CHANNEL_LAST_SIGNAL,
};
@@ -183,4 +195,6 @@ extern spice_channel_info main_channel_info;
extern spice_channel_info display_channel_info;
extern spice_channel_info cursor_channel_info;
extern spice_channel_info inputs_channel_info;
+extern spice_channel_info playback_channel_info;
+#endif /* __SPICE_CLIENT_CHANNEL_PRIV_H__ */
diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index b997b9d..212b0f3 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -126,6 +126,38 @@ static void spice_channel_class_init(SpiceChannelClass *klass)
G_TYPE_INT, G_TYPE_INT,
G_TYPE_POINTER);
+ channel_signals[SPICE_PLAYBACK_START] =
+ g_signal_new("spice-playback-start",
+ G_OBJECT_CLASS_TYPE(gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(SpiceChannelClass, spice_playback_start),
+ NULL, NULL,
+ g_cclosure_user_marshal_VOID__INT_INT_INT,
+ G_TYPE_NONE,
+ 3,
+ G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
+
+ channel_signals[SPICE_PLAYBACK_DATA] =
+ g_signal_new("spice-playback-data",
+ G_OBJECT_CLASS_TYPE(gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(SpiceChannelClass, spice_playback_data),
+ NULL, NULL,
+ g_cclosure_user_marshal_VOID__POINTER_INT,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_POINTER, G_TYPE_INT);
+
+ channel_signals[SPICE_PLAYBACK_STOP] =
+ g_signal_new("spice-playback-stop",
+ G_OBJECT_CLASS_TYPE(gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(SpiceChannelClass, spice_playback_stop),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
g_type_class_add_private(klass, sizeof(spice_channel));
sw_canvas_init();
diff --git a/gtk/spice-channel.h b/gtk/spice-channel.h
index ace9c66..dca9a45 100644
--- a/gtk/spice-channel.h
+++ b/gtk/spice-channel.h
@@ -1,3 +1,6 @@
+#ifndef __SPICE_CLIENT_CHANNEL_H__
+#define __SPICE_CLIENT_CHANNEL_H__
+
G_BEGIN_DECLS
#define SPICE_TYPE_CHANNEL (spice_channel_get_type ())
@@ -54,6 +57,12 @@ struct _SpiceChannelClass
void (*spice_cursor_set)(SpiceChannel *channel, gint width, gint height,
gint hot_x, gint hot_y, gpointer rgba);
+ /* playback signals */
+ void (*spice_playback_start)(SpiceChannel *channel,
+ gint format, gint channels, gint freq);
+ void (*spice_playback_data)(SpiceChannel *channel, gpointer *data, gint size);
+ void (*spice_playback_stop)(SpiceChannel *channel);
+
#if 0
/*
* If adding fields to this struct, remove corresponding
@@ -103,3 +112,5 @@ void spice_msg_out_get(spice_msg_out *out);
void spice_msg_out_put(spice_msg_out *out);
void spice_msg_out_send(spice_msg_out *out);
void spice_msg_out_hexdump(spice_msg_out *out, unsigned char *data, int len);
+
+#endif /* __SPICE_CLIENT_CHANNEL_H__ */
diff --git a/gtk/spice-client.h b/gtk/spice-client.h
index 9e6f909..0cc7906 100644
--- a/gtk/spice-client.h
+++ b/gtk/spice-client.h
@@ -1,3 +1,6 @@
+#ifndef __SPICE_CLIENT_CLIENT_H__
+#define __SPICE_CLIENT_CLIENT_H__
+
/* system */
#include <stdio.h>
#include <stdlib.h>
@@ -41,3 +44,5 @@
__FUNCTION__, __LINE__, #x); \
abort(); } \
}
+
+#endif /* __SPICE_CLIENT_CLIENT_H__ */
diff --git a/gtk/spice-events.h b/gtk/spice-events.h
index 3720738..6803647 100644
--- a/gtk/spice-events.h
+++ b/gtk/spice-events.h
@@ -1,3 +1,5 @@
+#ifndef __SPICE_CLIENT_EVENTS_H__
+#define __SPICE_CLIENT_EVENTS_H__
typedef struct spice_watch spice_watch;
typedef struct spice_timer spice_timer;
@@ -13,3 +15,5 @@ void spice_watch_put(spice_watch *watch);
spice_timer *spice_timer_new(spice_timer_func func, void *opaque);
void spice_timer_put(spice_timer *timer);
+
+#endif /* __SPICE_CLIENT_EVENTS_H__ */
diff --git a/gtk/spice-marshal.txt b/gtk/spice-marshal.txt
index 130060b..cbb4ab1 100644
--- a/gtk/spice-marshal.txt
+++ b/gtk/spice-marshal.txt
@@ -1,3 +1,5 @@
+VOID:INT,INT,INT
VOID:INT,INT,INT,INT
VOID:INT,INT,INT,INT,POINTER
VOID:INT,INT,INT,INT,INT,POINTER
+VOID:POINTER,INT
diff --git a/gtk/spice-pulse.c b/gtk/spice-pulse.c
new file mode 100644
index 0000000..e158254
--- /dev/null
+++ b/gtk/spice-pulse.c
@@ -0,0 +1,187 @@
+#include "spice-pulse.h"
+
+#include <assert.h>
+
+#include <pulse/glib-mainloop.h>
+#include <pulse/context.h>
+#include <pulse/stream.h>
+#include <pulse/sample.h>
+
+#define SPICE_PULSE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), SPICE_TYPE_PULSE, spice_pulse))
+
+struct stream {
+ pa_sample_spec spec;
+ pa_stream *stream;
+};
+
+struct spice_pulse {
+ SpiceSession *session;
+ SpiceChannel *pchannel;
+
+ pa_glib_mainloop *mainloop;
+ pa_context *context;
+ struct stream playback;
+};
+
+G_DEFINE_TYPE(SpicePulse, spice_pulse, G_TYPE_OBJECT)
+
+static const char *stream_state_names[] = {
+ [ PA_STREAM_UNCONNECTED ] = "unconnected",
+ [ PA_STREAM_CREATING ] = "creating",
+ [ PA_STREAM_READY ] = "ready",
+ [ PA_STREAM_FAILED ] = "failed",
+ [ PA_STREAM_TERMINATED ] = "terminated",
+};
+
+static const char *context_state_names[] = {
+ [ PA_CONTEXT_UNCONNECTED ] = "unconnected",
+ [ PA_CONTEXT_CONNECTING ] = "connecting",
+ [ PA_CONTEXT_AUTHORIZING ] = "authorizing",
+ [ PA_CONTEXT_SETTING_NAME ] = "setting_name",
+ [ PA_CONTEXT_READY ] = "ready",
+ [ PA_CONTEXT_FAILED ] = "failed",
+ [ PA_CONTEXT_TERMINATED ] = "terminated",
+};
+#define STATE_NAME(array, state) \
+ ((state < G_N_ELEMENTS(array)) ? array[state] : NULL)
+
+static void spice_pulse_finalize(GObject *obj)
+{
+ G_OBJECT_CLASS(spice_pulse_parent_class)->finalize(obj);
+}
+
+static void spice_pulse_init(SpicePulse *pulse)
+{
+ spice_pulse *p;
+
+ p = pulse->priv = SPICE_PULSE_GET_PRIVATE(pulse);
+ memset(p, 0, sizeof(*p));
+}
+
+static void spice_pulse_class_init(SpicePulseClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+
+ gobject_class->finalize = spice_pulse_finalize;
+
+ g_type_class_add_private(klass, sizeof(spice_pulse));
+}
+
+/* ------------------------------------------------------------------ */
+
+static void playback_start(SpiceChannel *channel, gint format, gint channels,
+ gint frequency, gpointer data)
+{
+ SpicePulse *pulse = data;
+ spice_pulse *p = SPICE_PULSE_GET_PRIVATE(pulse);
+ pa_context_state_t state;
+
+ state = pa_context_get_state(p->context);
+ switch (state) {
+ case PA_CONTEXT_READY:
+ if (p->playback.stream &&
+ (p->playback.spec.rate != frequency ||
+ p->playback.spec.channels != channels)) {
+ pa_stream_disconnect(p->playback.stream);
+ pa_stream_unref(p->playback.stream);
+ p->playback.stream = NULL;
+ }
+ if (p->playback.stream == NULL) {
+ assert(format == SPICE_AUDIO_FMT_S16);
+ p->playback.spec.format = PA_SAMPLE_S16LE;
+ p->playback.spec.rate = frequency;
+ p->playback.spec.channels = channels;
+ p->playback.stream = pa_stream_new(p->context, "playback",
+ &p->playback.spec, NULL);
+ pa_stream_connect_playback(p->playback.stream, NULL, NULL, 0, NULL, NULL);
+ }
+ if (pa_stream_is_corked(p->playback.stream)) {
+ pa_stream_cork(p->playback.stream, 0, NULL, NULL);
+ }
+ break;
+ default:
+ fprintf(stderr, "%s: pulse context not ready (%s)\n",
+ __FUNCTION__, STATE_NAME(context_state_names, state));
+ break;
+ }
+}
+
+static void playback_data(SpiceChannel *channel, gpointer *audio, gint size,
+ gpointer data)
+{
+ SpicePulse *pulse = data;
+ spice_pulse *p = SPICE_PULSE_GET_PRIVATE(pulse);
+ pa_stream_state_t state;
+
+ if (!p->playback.stream)
+ return;
+
+ state = pa_stream_get_state(p->playback.stream);
+ switch (state) {
+ case PA_STREAM_READY:
+ pa_stream_write(p->playback.stream, audio, size, NULL, 0, PA_SEEK_RELATIVE);
+ break;
+ default:
+ fprintf(stderr, "%s: pulse playback stream not ready (%s)\n",
+ __FUNCTION__, STATE_NAME(stream_state_names, state));
+ break;
+ }
+}
+
+static void playback_stop(SpiceChannel *channel, gpointer data)
+{
+ SpicePulse *pulse = data;
+ spice_pulse *p = SPICE_PULSE_GET_PRIVATE(pulse);
+
+ if (!p->playback.stream)
+ return;
+
+ pa_stream_cork(p->playback.stream, 1, NULL, NULL);
+}
+
+static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
+{
+ SpicePulse *pulse = data;
+ int type = spice_channel_type(channel);
+
+ switch (type) {
+ case SPICE_CHANNEL_PLAYBACK:
+ g_signal_connect(channel, "spice-playback-start",
+ G_CALLBACK(playback_start), pulse);
+ g_signal_connect(channel, "spice-playback-data",
+ G_CALLBACK(playback_data), pulse);
+ g_signal_connect(channel, "spice-playback-stop",
+ G_CALLBACK(playback_stop), pulse);
+ spice_channel_connect(channel);
+ break;
+ default:
+ return;
+ }
+}
+
+SpicePulse *spice_pulse_new(SpiceSession *session, GMainLoop *mainloop,
+ const char *name)
+{
+ SpicePulse *pulse;
+ spice_pulse *p;
+ SpiceChannel *channels[16];
+ int i, n;
+
+ pulse = g_object_new(SPICE_TYPE_PULSE, NULL);
+ p = SPICE_PULSE_GET_PRIVATE(pulse);
+ p->session = session;
+
+ g_signal_connect(session, "spice-session-channel-new",
+ G_CALLBACK(channel_new), pulse);
+ n = spice_session_get_channels(session, channels, SPICE_N_ELEMENTS(channels));
+ for (i = 0; i < n; i++) {
+ channel_new(session, channels[i], (gpointer*)pulse);
+ }
+
+ p->mainloop = pa_glib_mainloop_new(g_main_loop_get_context(mainloop));
+ p->context = pa_context_new(pa_glib_mainloop_get_api(p->mainloop), name);
+ pa_context_connect(p->context, NULL, 0, NULL);
+
+ return pulse;
+}
diff --git a/gtk/spice-pulse.h b/gtk/spice-pulse.h
new file mode 100644
index 0000000..b3785e6
--- /dev/null
+++ b/gtk/spice-pulse.h
@@ -0,0 +1,38 @@
+#ifndef __SPICE_CLIENT_PULSE_H__
+#define __SPICE_CLIENT_PULSE_H__
+
+#include "spice-client.h"
+
+G_BEGIN_DECLS
+
+#define SPICE_TYPE_PULSE (spice_pulse_get_type())
+#define SPICE_PULSE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SPICE_TYPE_PULSE, SpicePulse))
+#define SPICE_PULSE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SPICE_TYPE_PULSE, SpicePulseClass))
+#define SPICE_IS_PULSE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SPICE_TYPE_PULSE))
+#define SPICE_IS_PULSE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SPICE_TYPE_PULSE))
+#define SPICE_PULSE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), SPICE_TYPE_PULSE, SpicePulseClass))
+
+
+typedef struct _SpicePulse SpicePulse;
+typedef struct _SpicePulseClass SpicePulseClass;
+typedef struct spice_pulse spice_pulse;
+
+struct _SpicePulse {
+ GObject parent;
+ spice_pulse *priv;
+ /* Do not add fields to this struct */
+};
+
+struct _SpicePulseClass {
+ GObjectClass parent_class;
+ /* Do not add fields to this struct */
+};
+
+GType spice_pulse_get_type(void);
+
+SpicePulse *spice_pulse_new(SpiceSession *session, GMainLoop *mainloop,
+ const char *name);
+
+G_END_DECLS
+
+#endif /* __SPICE_CLIENT_PULSE_H__ */
diff --git a/gtk/spice-session-priv.h b/gtk/spice-session-priv.h
index 531aaa5..48b55cb 100644
--- a/gtk/spice-session-priv.h
+++ b/gtk/spice-session-priv.h
@@ -1,5 +1,10 @@
+#ifndef __SPICE_CLIENT_SESSION_PRIV_H__
+#define __SPICE_CLIENT_SESSION_PRIV_H__
+
void spice_session_set_connection_id(SpiceSession *session, int id);
int spice_session_get_connection_id(SpiceSession *session);
int spice_session_channel_connect(SpiceSession *session, bool use_tls);
void spice_session_channel_new(SpiceSession *session, SpiceChannel *channel);
+
+#endif /* __SPICE_CLIENT_SESSION_PRIV_H__ */
diff --git a/gtk/spice-session.h b/gtk/spice-session.h
index 2c0a459..b44baf2 100644
--- a/gtk/spice-session.h
+++ b/gtk/spice-session.h
@@ -1,3 +1,5 @@
+#ifndef __SPICE_CLIENT_SESSION_H__
+#define __SPICE_CLIENT_SESSION_H__
G_BEGIN_DECLS
@@ -40,3 +42,4 @@ int spice_session_get_channels(SpiceSession *session, SpiceChannel **channels, i
G_END_DECLS
+#endif /* __SPICE_CLIENT_SESSION_H__ */
diff --git a/gtk/spice-types.h b/gtk/spice-types.h
index 967ff59..76db5a9 100644
--- a/gtk/spice-types.h
+++ b/gtk/spice-types.h
@@ -1,3 +1,6 @@
+#ifndef __SPICE_CLIENT_TYPES_H__
+#define __SPICE_CLIENT_TYPES_H__
+
/* SpiceSession */
typedef struct _SpiceSession SpiceSession;
typedef struct _SpiceSessionClass SpiceSessionClass;
@@ -8,3 +11,4 @@ typedef struct _SpiceChannel SpiceChannel;
typedef struct _SpiceChannelClass SpiceChannelClass;
typedef struct spice_channel spice_channel;
+#endif /* __SPICE_CLIENT_TYPES_H__ */
diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index 3f4db9a..7141d28 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -313,7 +313,7 @@ static void try_mouse_grab(GtkWidget *widget)
d->mouse_last_y = -1;
}
-static void check_mouse_edges(GtkWidget *widget, GdkEventMotion *motion)
+static void mouse_check_edges(GtkWidget *widget, GdkEventMotion *motion)
{
SpiceDisplay *display = SPICE_DISPLAY(widget);
spice_display *d = SPICE_DISPLAY_GET_PRIVATE(display);
@@ -728,7 +728,7 @@ static gboolean motion_event(GtkWidget *widget, GdkEventMotion *motion)
}
d->mouse_last_x = motion->x;
d->mouse_last_y = motion->y;
- check_mouse_edges(widget, motion);
+ mouse_check_edges(widget, motion);
}
break;
default:
diff --git a/gtk/spice-widget.h b/gtk/spice-widget.h
index c603531..5024f9a 100644
--- a/gtk/spice-widget.h
+++ b/gtk/spice-widget.h
@@ -1,3 +1,6 @@
+#ifndef __SPICE_CLIENT_WIDGET_H__
+#define __SPICE_CLIENT_WIDGET_H__
+
#include "spice-client.h"
#include <glib.h>
@@ -33,3 +36,5 @@ G_END_DECLS
GtkWidget* spice_display_new(SpiceSession *session, int id);
void spice_display_mouse_ungrab(GtkWidget *widget);
+
+#endif /* __SPICE_CLIENT_WIDGET_H__ */
diff --git a/gtk/spicy.c b/gtk/spicy.c
index 9860ea6..b6e4907 100644
--- a/gtk/spicy.c
+++ b/gtk/spicy.c
@@ -1,4 +1,5 @@
#include "spice-widget.h"
+#include "spice-pulse.h"
typedef struct spice_window spice_window;
struct spice_window {
@@ -21,8 +22,10 @@ static char *ca_file;
static bool fullscreen = false;
/* state */
+static GMainLoop *mainloop;
static SpiceSession *session;
static spice_window *wins[4];
+static SpicePulse *pulse;
/* ------------------------------------------------------------------ */
@@ -88,7 +91,7 @@ static void destroy_cb(GtkWidget *widget, gpointer data)
struct spice_window *win = data;
if (win->id == 0) {
- gtk_main_quit();
+ g_main_loop_quit(mainloop);
}
}
@@ -297,16 +300,16 @@ static void main_channel_event(SpiceChannel *channel, enum SpiceChannelEvent eve
break;
case SPICE_CHANNEL_CLOSED:
fprintf(stderr, "main channel: closed\n");
- gtk_main_quit();
+ g_main_loop_quit(mainloop);
break;
case SPICE_CHANNEL_ERROR_AUTH:
fprintf(stderr, "main channel: auth failure (wrong password?)\n");
- gtk_main_quit();
+ g_main_loop_quit(mainloop);
break;
default:
/* TODO: more sophisticated error handling */
fprintf(stderr, "unknown main channel event: %d\n", event);
- gtk_main_quit();
+ g_main_loop_quit(mainloop);
break;
}
}
@@ -330,6 +333,11 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer *data)
fprintf(stderr, "new display channel (#%d), creating window\n", id);
wins[id] = create_spice_window(s, id);
break;
+ case SPICE_CHANNEL_PLAYBACK:
+ if (pulse != NULL)
+ return;
+ pulse = spice_pulse_new(s, mainloop, "spice");
+ break;
}
}
@@ -394,6 +402,9 @@ int main(int argc, char *argv[])
snprintf(ca_file, size, "%s/.spicec/spice_truststore.pem", home);
}
+ g_type_init();
+ mainloop = g_main_loop_new(NULL, false);
+
session = spice_session_new();
g_signal_connect(session, "spice-session-channel-new",
G_CALLBACK(channel_new), NULL);
@@ -414,6 +425,6 @@ int main(int argc, char *argv[])
exit(1);
}
- gtk_main();
+ g_main_loop_run(mainloop);
return 0;
}