diff options
author | Gerd Hoffmann <kraxel@redhat.com> | 2010-09-16 11:43:24 +0200 |
---|---|---|
committer | Gerd Hoffmann <kraxel@redhat.com> | 2010-09-16 11:43:24 +0200 |
commit | 03d17e3be5e9905b91a1366b7b57d50b49daafb9 (patch) | |
tree | 2a6e0195c4622ddfce4585b1fee398955ac1cca0 | |
parent | 74553154d842202533dded90b32d7a1cad94da35 (diff) |
audio bits
add forgotton file.
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | gtk/Makefile.am | 23 | ||||
-rw-r--r-- | gtk/README | 2 | ||||
-rw-r--r-- | gtk/channel-display.c | 7 | ||||
-rw-r--r-- | gtk/channel-main.c | 18 | ||||
-rw-r--r-- | gtk/channel-playback.c | 89 | ||||
-rw-r--r-- | gtk/spice-channel-cache.h | 77 | ||||
-rw-r--r-- | gtk/spice-channel-priv.h | 14 | ||||
-rw-r--r-- | gtk/spice-channel.c | 32 | ||||
-rw-r--r-- | gtk/spice-channel.h | 11 | ||||
-rw-r--r-- | gtk/spice-client.h | 5 | ||||
-rw-r--r-- | gtk/spice-events.h | 4 | ||||
-rw-r--r-- | gtk/spice-marshal.txt | 2 | ||||
-rw-r--r-- | gtk/spice-pulse.c | 187 | ||||
-rw-r--r-- | gtk/spice-pulse.h | 38 | ||||
-rw-r--r-- | gtk/spice-session-priv.h | 5 | ||||
-rw-r--r-- | gtk/spice-session.h | 3 | ||||
-rw-r--r-- | gtk/spice-types.h | 4 | ||||
-rw-r--r-- | gtk/spice-widget.c | 4 | ||||
-rw-r--r-- | gtk/spice-widget.h | 5 | ||||
-rw-r--r-- | gtk/spicy.c | 21 |
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) @@ -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; } |