Merge lp:~cjcurran/indicator-sound/voip-input-item into lp:~indicator-applet-developers/indicator-sound/trunk_3

Proposed by Conor Curran
Status: Merged
Merged at revision: 202
Proposed branch: lp:~cjcurran/indicator-sound/voip-input-item
Merge into: lp:~indicator-applet-developers/indicator-sound/trunk_3
Diff against target: 1537 lines (+1044/-44)
18 files modified
src/Makefile.am (+5/-1)
src/active-sink.c (+61/-5)
src/active-sink.h (+19/-6)
src/common-defs.h (+5/-1)
src/indicator-sound.c (+62/-4)
src/music-player-bridge.vala (+5/-3)
src/mute-menu-item.c (+1/-0)
src/player-item.vala (+1/-2)
src/pulseaudio-mgr.c (+190/-19)
src/pulseaudio-mgr.h (+2/-0)
src/settings-manager.vala (+2/-0)
src/sound-service-dbus.c (+6/-2)
src/sound-service-dbus.h (+2/-1)
src/sound-service.c (+2/-0)
src/voip-input-menu-item.c (+277/-0)
src/voip-input-menu-item.h (+70/-0)
src/voip-input-widget.c (+279/-0)
src/voip-input-widget.h (+55/-0)
To merge this branch: bzr merge lp:~cjcurran/indicator-sound/voip-input-item
Reviewer Review Type Date Requested Status
Ted Gould (community) Approve
Review via email: mp+50364@code.launchpad.net

Description of the change

- big Voip input menu item feature added
- little keyboard shortcut feature removal

To post a comment you must log in.
Revision history for this message
Ted Gould (ted) wrote :

Don't see anything odd. Looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/Makefile.am'
2--- src/Makefile.am 2011-02-11 15:22:10 +0000
3+++ src/Makefile.am 2011-02-18 17:27:12 +0000
4@@ -20,6 +20,8 @@
5 title-widget.h \
6 volume-widget.c \
7 volume-widget.h \
8+ voip-input-widget.c \
9+ voip-input-widget.h \
10 gen-sound-service.xml.h \
11 gen-sound-service.xml.c \
12 dbus-shared-names.h
13@@ -85,7 +87,7 @@
14 # Sound Service C
15 ###############################
16 indicator_sound_service_SOURCES = \
17- common-defs.h \
18+ common-defs.h \
19 sound-service.h \
20 sound-service.c \
21 pulseaudio-mgr.h \
22@@ -96,6 +98,8 @@
23 sound-service-dbus.c \
24 slider-menu-item.h \
25 slider-menu-item.c \
26+ voip-input-menu-item.h \
27+ voip-input-menu-item.c \
28 mute-menu-item.h \
29 mute-menu-item.c \
30 gen-sound-service.xml.h \
31
32=== modified file 'src/active-sink.c'
33--- src/active-sink.c 2011-02-07 12:53:45 +0000
34+++ src/active-sink.c 2011-02-18 17:27:12 +0000
35@@ -21,7 +21,7 @@
36 #include "active-sink.h"
37 #include "slider-menu-item.h"
38 #include "mute-menu-item.h"
39-
40+#include "voip-input-menu-item.h"
41 #include "pulseaudio-mgr.h"
42
43 typedef struct _ActiveSinkPrivate ActiveSinkPrivate;
44@@ -30,7 +30,8 @@
45 {
46 SliderMenuItem* volume_slider_menuitem;
47 MuteMenuItem* mute_menuitem;
48- SoundState current_sound_state;
49+ VoipInputMenuItem* voip_input_menu_item;
50+ SoundState current_sound_state;
51 SoundServiceDbus* service;
52 gint index;
53 gchar* name;
54@@ -52,7 +53,6 @@
55 static void active_sink_volume_update (ActiveSink* self, gdouble percent);
56 static void active_sink_mute_update (ActiveSink* self, gboolean muted);
57
58-
59 G_DEFINE_TYPE (ActiveSink, active_sink, G_TYPE_OBJECT);
60
61 static void
62@@ -72,6 +72,7 @@
63 ActiveSinkPrivate* priv = ACTIVE_SINK_GET_PRIVATE (self);
64 priv->mute_menuitem = NULL;
65 priv->volume_slider_menuitem = NULL;
66+ priv->voip_input_menu_item = NULL;
67 priv->current_sound_state = UNAVAILABLE;
68 priv->index = -1;
69 priv->name = NULL;
70@@ -79,6 +80,7 @@
71
72 // Init our menu items.
73 priv->mute_menuitem = g_object_new (MUTE_MENU_ITEM_TYPE, NULL);
74+ priv->voip_input_menu_item = g_object_new (VOIP_INPUT_MENU_ITEM_TYPE, NULL);;
75 priv->volume_slider_menuitem = slider_menu_item_new (self);
76 mute_menu_item_enable (priv->mute_menuitem, FALSE);
77 slider_menu_item_enable (priv->volume_slider_menuitem, FALSE);
78@@ -121,6 +123,32 @@
79 }
80
81 void
82+active_sink_activate_voip_item (ActiveSink* self, gint sink_input_index, gint client_index)
83+{
84+ ActiveSinkPrivate* priv = ACTIVE_SINK_GET_PRIVATE (self);
85+ if (voip_input_menu_item_is_interested (priv->voip_input_menu_item,
86+ sink_input_index,
87+ client_index)){
88+ voip_input_menu_item_enable (priv->voip_input_menu_item, TRUE);
89+ }
90+}
91+
92+void
93+active_sink_deactivate_voip_source (ActiveSink* self, gboolean visible)
94+{
95+ ActiveSinkPrivate* priv = ACTIVE_SINK_GET_PRIVATE (self);
96+ visible &= voip_input_menu_item_is_active (priv->voip_input_menu_item);
97+ voip_input_menu_item_deactivate_source (priv->voip_input_menu_item, visible);
98+}
99+
100+void
101+active_sink_deactivate_voip_client (ActiveSink* self)
102+{
103+ ActiveSinkPrivate* priv = ACTIVE_SINK_GET_PRIVATE (self);
104+ voip_input_menu_item_deactivate_voip_client (priv->voip_input_menu_item);
105+}
106+
107+void
108 active_sink_update (ActiveSink* sink,
109 const pa_sink_info* update)
110 {
111@@ -168,6 +196,13 @@
112 }
113
114
115+gint
116+active_sink_get_current_sink_input_index (ActiveSink* sink)
117+{
118+ ActiveSinkPrivate* priv = ACTIVE_SINK_GET_PRIVATE (sink);
119+ return voip_input_menu_item_get_sink_input_index (priv->voip_input_menu_item);
120+}
121+
122 static void
123 active_sink_mute_update (ActiveSink* self, gboolean muted)
124 {
125@@ -219,7 +254,7 @@
126 return state;
127 }
128
129-static pa_cvolume
130+pa_cvolume
131 active_sink_construct_mono_volume (const pa_cvolume* vol)
132 {
133 pa_cvolume new_volume;
134@@ -279,6 +314,26 @@
135 return priv->current_sound_state;
136 }
137
138+void
139+active_sink_update_voip_input_source (ActiveSink* self, const pa_source_info* update)
140+{
141+ ActiveSinkPrivate* priv = ACTIVE_SINK_GET_PRIVATE (self);
142+ voip_input_menu_item_update (priv->voip_input_menu_item, update);
143+}
144+
145+gboolean
146+active_sink_is_voip_source_populated (ActiveSink* self)
147+{
148+ ActiveSinkPrivate* priv = ACTIVE_SINK_GET_PRIVATE (self);
149+ return voip_input_menu_item_is_populated (priv->voip_input_menu_item);
150+}
151+
152+gint active_sink_get_source_index (ActiveSink* self)
153+{
154+ ActiveSinkPrivate* priv = ACTIVE_SINK_GET_PRIVATE (self);
155+ return voip_input_menu_item_get_index (priv->voip_input_menu_item);
156+}
157+
158 ActiveSink*
159 active_sink_new (SoundServiceDbus* service)
160 {
161@@ -287,7 +342,8 @@
162 priv->service = service;
163 sound_service_dbus_build_sound_menu (service,
164 mute_menu_item_get_button (priv->mute_menuitem),
165- DBUSMENU_MENUITEM (priv->volume_slider_menuitem));
166+ DBUSMENU_MENUITEM (priv->volume_slider_menuitem),
167+ DBUSMENU_MENUITEM (priv->voip_input_menu_item));
168 pm_establish_pulse_connection (sink);
169 return sink;
170 }
171
172=== modified file 'src/active-sink.h'
173--- src/active-sink.h 2011-02-07 11:45:06 +0000
174+++ src/active-sink.h 2011-02-18 17:27:12 +0000
175@@ -50,21 +50,34 @@
176
177 GType active_sink_get_type (void) G_GNUC_CONST;
178
179+/**
180+ * TODO
181+ * Refactor this to become a device manager obj basically acting as wrapper for
182+ * the communication between pulseaudio-mgr and the individual items.
183+ * First steps collapse slider/volume related stuff into slider-menu-item.
184+ */
185+
186+// Sink related
187 void active_sink_populate (ActiveSink* sink, const pa_sink_info* update);
188 void active_sink_update (ActiveSink* sink, const pa_sink_info* update);
189-
190 gboolean active_sink_is_populated (ActiveSink* sink);
191-void active_sink_determine_blocking_state (ActiveSink* self);
192-
193 gint active_sink_get_index (ActiveSink* self);
194-SoundState active_sink_get_state (ActiveSink* self);
195-
196 void active_sink_deactivate (ActiveSink* self);
197-
198 void active_sink_update_mute (ActiveSink* self, gboolean mute_update);
199 void active_sink_update_volume (ActiveSink* self, gdouble percent);
200 void active_sink_ensure_sink_is_unmuted (ActiveSink* self);
201
202+// source and sinkinput/client related for VOIP functionality
203+void active_sink_update_voip_input_source (ActiveSink* sink, const pa_source_info* update);
204+void active_sink_activate_voip_item (ActiveSink* sink, gint sink_input_index, gint client_index);
205+gint active_sink_get_current_sink_input_index (ActiveSink* sink);
206+gboolean active_sink_is_voip_source_populated (ActiveSink* sink);
207+gint active_sink_get_source_index (ActiveSink* self);
208+void active_sink_determine_blocking_state (ActiveSink* self);
209+void active_sink_deactivate_voip_source (ActiveSink* self, gboolean visible);
210+void active_sink_deactivate_voip_client (ActiveSink* self);
211+SoundState active_sink_get_state (ActiveSink* self);
212+
213 ActiveSink* active_sink_new (SoundServiceDbus* service);
214
215 G_END_DECLS
216
217=== modified file 'src/common-defs.h'
218--- src/common-defs.h 2011-02-08 18:58:10 +0000
219+++ src/common-defs.h 2011-02-18 17:27:12 +0000
220@@ -31,13 +31,17 @@
221 AVAILABLE
222 }SoundState;
223
224-
225+#define NOT_ACTIVE -1
226 #define DBUSMENU_PROPERTY_EMPTY -1
227
228 /* DBUS Custom Items */
229 #define DBUSMENU_VOLUME_MENUITEM_TYPE "x-canonical-ido-volume-type"
230 #define DBUSMENU_VOLUME_MENUITEM_LEVEL "x-canonical-ido-volume-level"
231
232+#define DBUSMENU_VOIP_INPUT_MENUITEM_TYPE "x-canonical-ido-voip-input-type"
233+#define DBUSMENU_VOIP_INPUT_MENUITEM_LEVEL "x-canonical-ido-voip-input-level"
234+#define DBUSMENU_VOIP_INPUT_MENUITEM_MUTE "x-canonical-ido-voip-input-mute"
235+
236 #define DBUSMENU_MUTE_MENUITEM_TYPE "x-canonical-sound-menu-mute-type"
237 #define DBUSMENU_MUTE_MENUITEM_VALUE "x-canonical-sound-menu-mute-value"
238
239
240=== modified file 'src/indicator-sound.c'
241--- src/indicator-sound.c 2011-02-16 11:20:21 +0000
242+++ src/indicator-sound.c 2011-02-18 17:27:12 +0000
243@@ -32,12 +32,12 @@
244 #include "metadata-widget.h"
245 #include "title-widget.h"
246 #include "volume-widget.h"
247-
248+#include "voip-input-widget.h"
249 #include "dbus-shared-names.h"
250+#include "sound-state-manager.h"
251
252 #include "gen-sound-service.xml.h"
253 #include "common-defs.h"
254-#include "sound-state-manager.h"
255
256 typedef struct _IndicatorSoundPrivate IndicatorSoundPrivate;
257
258@@ -79,6 +79,10 @@
259 DbusmenuMenuitem * parent,
260 DbusmenuClient * client,
261 gpointer user_data);
262+static gboolean new_voip_slider_widget (DbusmenuMenuitem * newitem,
263+ DbusmenuMenuitem * parent,
264+ DbusmenuClient * client,
265+ gpointer user_data);
266 static gboolean new_transport_widget (DbusmenuMenuitem * newitem,
267 DbusmenuMenuitem * parent,
268 DbusmenuClient * client,
269@@ -191,6 +195,9 @@
270 DBUSMENU_VOLUME_MENUITEM_TYPE,
271 new_volume_slider_widget);
272 dbusmenu_client_add_type_handler (DBUSMENU_CLIENT(client),
273+ DBUSMENU_VOIP_INPUT_MENUITEM_TYPE,
274+ new_voip_slider_widget);
275+ dbusmenu_client_add_type_handler (DBUSMENU_CLIENT(client),
276 DBUSMENU_TRANSPORT_MENUITEM_TYPE,
277 new_transport_widget);
278 dbusmenu_client_add_type_handler (DBUSMENU_CLIENT(client),
279@@ -403,6 +410,53 @@
280 parent);
281 return TRUE;
282 }
283+/**
284+ * new_voip_slider_widget
285+ * Create the voip menu item widget, must of the time this widget will be hidden.
286+ * @param newitem
287+ * @param parent
288+ * @param client
289+ * @param user_data
290+ * @return
291+ */
292+static gboolean
293+new_voip_slider_widget (DbusmenuMenuitem * newitem,
294+ DbusmenuMenuitem * parent,
295+ DbusmenuClient * client,
296+ gpointer user_data)
297+{
298+ g_debug("indicator-sound: new_voip_slider_widget");
299+ GtkWidget* voip_widget = NULL;
300+ //IndicatorObject *io = NULL;
301+
302+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE);
303+ g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE);
304+
305+ voip_widget = voip_input_widget_new (newitem);
306+/*
307+ / io = g_object_get_data (G_OBJECT (client), "indicator");
308+*/
309+ //IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(INDICATOR_SOUND (io));
310+ //priv->volume_widget = volume_widget;
311+
312+ GtkWidget* ido_slider_widget = voip_input_widget_get_ido_slider(VOIP_INPUT_WIDGET(voip_widget));
313+
314+ gtk_widget_show_all(ido_slider_widget);
315+ // register the style callback on this widget with state manager's style change
316+ // handler (needs to remake the blocking animation for each style).
317+/*
318+ g_signal_connect (ido_slider_widget, "style-set",
319+ G_CALLBACK(sound_state_manager_style_changed_cb),
320+ priv->state_manager);
321+*/
322+
323+ GtkMenuItem *menu_volume_item = GTK_MENU_ITEM(ido_slider_widget);
324+ dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client),
325+ newitem,
326+ menu_volume_item,
327+ parent);
328+ return TRUE;
329+}
330
331 /*******************************************************************/
332 //UI callbacks
333@@ -439,19 +493,23 @@
334 switch (event->keyval) {
335 case GDK_Right:
336 digested = TRUE;
337+/*
338 if (event->state & GDK_CONTROL_MASK) {
339 new_value = 100;
340 } else {
341+*/
342 new_value = current_value + five_percent;
343- }
344+ //}
345 break;
346 case GDK_Left:
347 digested = TRUE;
348+/*
349 if (event->state & GDK_CONTROL_MASK) {
350 new_value = 0;
351 } else {
352+*/
353 new_value = current_value - five_percent;
354- }
355+ //}
356 break;
357 case GDK_plus:
358 digested = TRUE;
359
360=== modified file 'src/music-player-bridge.vala'
361--- src/music-player-bridge.vala 2011-01-31 19:14:47 +0000
362+++ src/music-player-bridge.vala 2011-02-18 17:27:12 +0000
363@@ -23,11 +23,13 @@
364
365 public class MusicPlayerBridge : GLib.Object
366 {
367+ const int DEVICE_ITEMS_COUNT = 3;
368+
369 private SettingsManager settings_manager;
370 private Dbusmenu.Menuitem root_menu;
371 private HashMap<string, PlayerController> registered_clients;
372 private Mpris2Watcher watcher;
373-
374+
375 public MusicPlayerBridge()
376 {
377 }
378@@ -79,10 +81,10 @@
379 private int calculate_menu_position()
380 {
381 if(this.registered_clients.size == 0){
382- return 2;
383+ return DEVICE_ITEMS_COUNT;
384 }
385 else{
386- return (2 + (this.registered_clients.size * PlayerController.WIDGET_QUANTITY));
387+ return (DEVICE_ITEMS_COUNT + (this.registered_clients.size * PlayerController.WIDGET_QUANTITY));
388 }
389 }
390
391
392=== modified file 'src/mute-menu-item.c'
393--- src/mute-menu-item.c 2011-02-07 18:08:19 +0000
394+++ src/mute-menu-item.c 2011-02-18 17:27:12 +0000
395@@ -61,6 +61,7 @@
396 {
397 g_debug("Building new Mute Menu Item");
398 MuteMenuItemPrivate* priv = MUTE_MENU_ITEM_GET_PRIVATE(self);
399+ priv->button = NULL;
400 priv->button = dbusmenu_menuitem_new();
401 dbusmenu_menuitem_property_set_bool (priv->button,
402 DBUSMENU_MENUITEM_PROP_VISIBLE,
403
404=== modified file 'src/player-item.vala'
405--- src/player-item.vala 2011-02-09 10:56:49 +0000
406+++ src/player-item.vala 2011-02-18 17:27:12 +0000
407@@ -94,9 +94,8 @@
408 {
409 foreach(string prop in attrs){
410 //debug("populated ? - prop: %s", prop);
411- int value_int = property_get_int(prop);
412 if(property_get_int(prop) != EMPTY){
413- //debug("populated - prop %s and value %i", prop, value_int);
414+ //debug("populated - prop %s and value %i", prop, property_get_int(prop));
415 return true;
416 }
417 }
418
419=== modified file 'src/pulseaudio-mgr.c'
420--- src/pulseaudio-mgr.c 2011-02-10 14:34:51 +0000
421+++ src/pulseaudio-mgr.c 2011-02-18 17:27:12 +0000
422@@ -19,9 +19,9 @@
423
424 /**Notes
425 *
426- * Approach now is to set up the communication channels then query the server
427- * fetch its default sink. If this fails then fetch the list of sinks and take
428- * the first one which is not the auto-null sink.
429+ * Approach now is to set up the communication channels, query the server
430+ * fetch its default sink/source. If this fails then fetch the list of sinks/sources
431+ * and take the first one which is not the auto-null sink.
432 * TODO: need to handle the situation where one chink in this linear chain breaks
433 * i.e. start off the process again and count the attempts (note different to
434 reconnect attempts)
435@@ -47,10 +47,22 @@
436 const pa_sink_info *info,
437 int eol,
438 void *userdata);
439+static void pm_default_source_info_callback (pa_context *c,
440+ const pa_source_info *info,
441+ int eol,
442+ void *userdata);
443 static void pm_sink_info_callback (pa_context *c,
444 const pa_sink_info *sink,
445 int eol,
446 void *userdata);
447+static void pm_source_info_callback (pa_context *c,
448+ const pa_source_info *info,
449+ int eol,
450+ void *userdata);
451+static void pm_update_source_info_callback (pa_context *c,
452+ const pa_source_info *info,
453+ int eol,
454+ void *userdata);
455 static void pm_sink_input_info_callback (pa_context *c,
456 const pa_sink_input_info *info,
457 int eol,
458@@ -64,6 +76,7 @@
459 int eol,
460 void* userdata);
461
462+
463 static gboolean reconnect_to_pulse (gpointer user_data);
464
465 static gint connection_attempts = 0;
466@@ -152,6 +165,25 @@
467 GINT_TO_POINTER (update)));
468 }
469
470+void
471+pm_update_mic_gain (gint source_index, pa_cvolume new_gain)
472+{
473+ pa_operation_unref (pa_context_set_source_volume_by_index (pulse_context,
474+ source_index,
475+ &new_gain,
476+ NULL,
477+ NULL) );
478+}
479+
480+void
481+pm_update_mic_mute (gint source_index, gint mute_update)
482+{
483+ pa_operation_unref (pa_context_set_source_mute_by_index (pulse_context,
484+ source_index,
485+ mute_update,
486+ NULL,
487+ NULL));
488+}
489 /**********************************************************************************************************************/
490 // Pulse-Audio asychronous call-backs
491 /**********************************************************************************************************************/
492@@ -163,19 +195,21 @@
493 uint32_t index,
494 void* userdata)
495 {
496+ if (IS_ACTIVE_SINK (userdata) == FALSE){
497+ g_critical ("subscribed events callback - our userdata is not what we think it should be");
498+ return;
499+ }
500+ ActiveSink* sink = ACTIVE_SINK (userdata);
501+
502 switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
503 case PA_SUBSCRIPTION_EVENT_SINK:
504- if (IS_ACTIVE_SINK (userdata) == FALSE){
505- g_warning ("subscribed events callback - our userdata is not what we think it should be");
506- return;
507- }
508- ActiveSink* sink = ACTIVE_SINK (userdata);
509+
510 // We don't care about any other sink other than the active one.
511 if (index != active_sink_get_index (sink))
512- return;
513+ return;
514
515 if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
516- active_sink_deactivate (ACTIVE_SINK (userdata));
517+ active_sink_deactivate (sink);
518
519 }
520 else{
521@@ -185,9 +219,36 @@
522 userdata) );
523 }
524 break;
525+ case PA_SUBSCRIPTION_EVENT_SOURCE:
526+ g_debug ("Looks like source event of some description");
527+ // We don't care about any other sink other than the active one.
528+ if (index != active_sink_get_source_index (sink))
529+ return;
530+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
531+ active_sink_deactivate_voip_source (sink, FALSE);
532+ }
533+ else{
534+ pa_operation_unref (pa_context_get_source_info_by_index (c,
535+ index,
536+ pm_update_source_info_callback,
537+ userdata) );
538+ }
539+ break;
540 case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
541 // We don't care about sink input removals.
542- if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_REMOVE) {
543+ g_debug ("sink input event");
544+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
545+ gint cached_index = active_sink_get_current_sink_input_index (sink);
546+
547+ g_debug ("Just saw a sink input removal event - index = %i and cached index = %i", index, cached_index);
548+
549+ if (index == cached_index){
550+ active_sink_deactivate_voip_client (sink);
551+ }
552+ }
553+ else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
554+ g_debug ("some new sink input event ? - index = %i", index);
555+ // Determine if its a VOIP app or a maybe blocking state.
556 pa_operation_unref (pa_context_get_sink_input_info (c,
557 index,
558 pm_sink_input_info_callback, userdata));
559@@ -206,6 +267,7 @@
560 }
561
562
563+
564 static void
565 pm_context_state_callback (pa_context *c, void *userdata)
566 {
567@@ -244,6 +306,7 @@
568
569 if (!(o = pa_context_subscribe (c, (pa_subscription_mask_t)
570 (PA_SUBSCRIPTION_MASK_SINK|
571+ PA_SUBSCRIPTION_MASK_SOURCE|
572 PA_SUBSCRIPTION_MASK_SINK_INPUT|
573 PA_SUBSCRIPTION_MASK_SERVER), NULL, NULL))) {
574 g_warning("pa_context_subscribe() failed");
575@@ -261,7 +324,8 @@
576
577 /**
578 After startup we go straight for the server info to see if it has details of
579- the default sink. If so it makes things much easier.
580+ the default sink and source. Normally these are valid, if there is none set
581+ fetch the list of each and try to determine the sink.
582 **/
583 static void
584 pm_server_info_callback (pa_context *c,
585@@ -276,24 +340,46 @@
586 active_sink_deactivate (ACTIVE_SINK (userdata));
587 return;
588 }
589+ // Go for the default sink
590 if (info->default_sink_name != NULL) {
591 g_debug ("default sink name from the server ain't null'");
592 if (!(operation = pa_context_get_sink_info_by_name (c,
593 info->default_sink_name,
594 pm_default_sink_info_callback,
595 userdata) )) {
596- }
597- else{
598+ g_warning("pa_context_get_sink_info_by_namet() failed");
599+ active_sink_deactivate (ACTIVE_SINK (userdata));
600 pa_operation_unref(operation);
601 return;
602 }
603- }
604+ } // If there is no default sink, try to determine a sink from the list of sinks
605 else if (!(operation = pa_context_get_sink_info_list(c,
606 pm_sink_info_callback,
607- NULL))) {
608+ userdata))) {
609 g_warning("pa_context_get_sink_info_list() failed");
610+ active_sink_deactivate (ACTIVE_SINK (userdata));
611+ pa_operation_unref(operation);
612 return;
613 }
614+ // And the source
615+ if (info->default_source_name != NULL) {
616+ g_debug ("default source name from the server is not null'");
617+ if (!(operation = pa_context_get_source_info_by_name (c,
618+ info->default_source_name,
619+ pm_default_source_info_callback,
620+ userdata) )) {
621+ g_warning("pa_context_get_default_source_info() failed");
622+ // TODO: call some input deactivate method on active sink
623+ pa_operation_unref(operation);
624+ return;
625+ }
626+ }
627+ else if (!(operation = pa_context_get_source_info_list(c,
628+ pm_source_info_callback,
629+ userdata))) {
630+ g_warning("pa_context_get_sink_info_list() failed");
631+ // TODO: call some input deactivate method for the source
632+ }
633 pa_operation_unref(operation);
634 }
635
636@@ -335,8 +421,12 @@
637 if (IS_ACTIVE_SINK (userdata) == FALSE){
638 g_warning ("Default sink info callback - our user data is not what we think it should be");
639 return;
640- }
641- g_debug ("server has handed us a default sink");
642+ }
643+ // Only repopulate if there is a change with regards the index
644+ if (active_sink_get_index (ACTIVE_SINK (userdata)) == info->index)
645+ return;
646+
647+ g_debug ("Pulse Server has handed us a new default sink");
648 active_sink_populate (ACTIVE_SINK (userdata), info);
649 }
650 }
651@@ -356,12 +446,30 @@
652 g_warning("\n Sink input info callback : SINK INPUT INFO IS NULL BUT EOL was not POSITIVE!!!");
653 return;
654 }
655+
656 if (IS_ACTIVE_SINK (userdata) == FALSE){
657 g_warning ("sink input info callback - our user data is not what we think it should be");
658 return;
659 }
660-
661+ // Check if this is Voip sink input
662+ gint result = pa_proplist_contains (info->proplist, PA_PROP_MEDIA_ROLE);
663 ActiveSink* a_sink = ACTIVE_SINK (userdata);
664+
665+ if (result == 1){
666+ g_debug ("Sink input info has media role property");
667+ const char* value = pa_proplist_gets (info->proplist, PA_PROP_MEDIA_ROLE);
668+ g_debug ("prop role = %s", value);
669+ if (g_strcmp0 (value, "phone") == 0) {
670+ g_debug ("And yes its a VOIP app ... sink input index = %i", info->index);
671+ active_sink_activate_voip_item (a_sink, (gint)info->index, (gint)info->client);
672+ // TODO to start with we will assume our source is the same as what this 'client'
673+ // is pointing at. This should probably be more intelligent :
674+ // query for the list of source output info's and going on the name of the client
675+ // from the sink input ensure our voip item is using the right source.
676+ }
677+ }
678+
679+ // And finally check for the mute blocking state
680 if (active_sink_get_index (a_sink) == info->sink){
681 active_sink_determine_blocking_state (a_sink);
682 }
683@@ -404,3 +512,66 @@
684 }
685 }
686
687+// Source info related callbacks
688+static void
689+pm_default_source_info_callback (pa_context *c,
690+ const pa_source_info *info,
691+ int eol,
692+ void *userdata)
693+{
694+ if (eol > 0) {
695+ return;
696+ }
697+ else {
698+ if (IS_ACTIVE_SINK (userdata) == FALSE){
699+ g_warning ("Default sink info callback - our user data is not what we think it should be");
700+ return;
701+ }
702+ // If there is an index change we need to change our cached source
703+ if (active_sink_get_source_index (ACTIVE_SINK (userdata)) == info->index)
704+ return;
705+ g_debug ("Pulse Server has handed us a new default source");
706+ active_sink_deactivate_voip_source (ACTIVE_SINK (userdata), TRUE);
707+ active_sink_update_voip_input_source (ACTIVE_SINK (userdata), info);
708+ }
709+}
710+
711+static void
712+pm_source_info_callback (pa_context *c,
713+ const pa_source_info *info,
714+ int eol,
715+ void *userdata)
716+{
717+ if (eol > 0) {
718+ return;
719+ }
720+ else {
721+ if (IS_ACTIVE_SINK (userdata) == FALSE){
722+ g_warning ("Default sink info callback - our user data is not what we think it should be");
723+ return;
724+ }
725+ // For now we will take the first available
726+ if (active_sink_is_voip_source_populated (ACTIVE_SINK (userdata)) == FALSE){
727+ active_sink_update_voip_input_source (ACTIVE_SINK (userdata), info);
728+ }
729+ }
730+}
731+
732+static void
733+pm_update_source_info_callback (pa_context *c,
734+ const pa_source_info *info,
735+ int eol,
736+ void *userdata)
737+{
738+ if (eol > 0) {
739+ return;
740+ }
741+ else {
742+ if (IS_ACTIVE_SINK (userdata) == FALSE){
743+ g_warning ("Default sink info callback - our user data is not what we think it should be");
744+ return;
745+ }
746+ g_debug ("Got a source update for %s , index %i", info->name, info->index);
747+ active_sink_update_voip_input_source (ACTIVE_SINK (userdata), info);
748+ }
749+}
750\ No newline at end of file
751
752=== modified file 'src/pulseaudio-mgr.h'
753--- src/pulseaudio-mgr.h 2011-02-07 11:19:17 +0000
754+++ src/pulseaudio-mgr.h 2011-02-18 17:27:12 +0000
755@@ -22,6 +22,8 @@
756 void pm_establish_pulse_connection (ActiveSink* active_sink);
757 void close_pulse_activites();
758 void pm_update_volume (gint sink_index, pa_cvolume new_volume);
759+void pm_update_mic_gain (gint source_index, pa_cvolume new_gain);
760+void pm_update_mic_mute (gint source_index, int mute_update);
761 void pm_update_mute (gboolean update);
762
763
764
765=== modified file 'src/settings-manager.vala'
766--- src/settings-manager.vala 2011-02-01 01:33:46 +0000
767+++ src/settings-manager.vala 2011-02-18 17:27:12 +0000
768@@ -71,6 +71,7 @@
769
770 // Convenient debug method inorder to provide visability over
771 // the contents of both interested and blacklisted containers in its gsettings
772+/**
773 private void reveal_contents()
774 {
775 var already_interested = this.settings.get_strv ("interested-media-players");
776@@ -87,4 +88,5 @@
777 debug ("interested array size = %i", already_interested.length);
778 debug ("blacklisted array size = %i", blacklisted.length);
779 }
780+**/
781 }
782
783=== modified file 'src/sound-service-dbus.c'
784--- src/sound-service-dbus.c 2011-02-04 18:25:54 +0000
785+++ src/sound-service-dbus.c 2011-02-18 17:27:12 +0000
786@@ -157,14 +157,18 @@
787 void
788 sound_service_dbus_build_sound_menu ( SoundServiceDbus* self,
789 DbusmenuMenuitem* mute_item,
790- DbusmenuMenuitem* slider_item)
791+ DbusmenuMenuitem* slider_item,
792+ DbusmenuMenuitem* voip_input_menu_item)
793 {
794 SoundServiceDbusPrivate * priv = SOUND_SERVICE_DBUS_GET_PRIVATE(self);
795
796 // Mute button
797+ // TODO this additions should be fixed position, i.e. add via position and not just append
798+ // be explicit as it is fixed.
799 dbusmenu_menuitem_child_append (priv->root_menuitem, mute_item);
800+ dbusmenu_menuitem_child_append (priv->root_menuitem, slider_item);
801 g_debug ("just about to add the slider %i", DBUSMENU_IS_MENUITEM(slider_item));
802- dbusmenu_menuitem_child_append (priv->root_menuitem, slider_item);
803+ dbusmenu_menuitem_child_append (priv->root_menuitem, voip_input_menu_item);
804
805 // Separator
806 DbusmenuMenuitem* separator = dbusmenu_menuitem_new();
807
808=== modified file 'src/sound-service-dbus.h'
809--- src/sound-service-dbus.h 2011-02-04 16:17:31 +0000
810+++ src/sound-service-dbus.h 2011-02-18 17:27:12 +0000
811@@ -57,7 +57,8 @@
812 void sound_service_dbus_update_sound_state (SoundServiceDbus* self, SoundState new_state);
813 void sound_service_dbus_build_sound_menu ( SoundServiceDbus* self,
814 DbusmenuMenuitem* mute_item,
815- DbusmenuMenuitem* slider_item);
816+ DbusmenuMenuitem* slider_item,
817+ DbusmenuMenuitem* voip_input_menu_item);
818
819
820 G_END_DECLS
821
822=== modified file 'src/sound-service.c'
823--- src/sound-service.c 2011-02-09 10:57:13 +0000
824+++ src/sound-service.c 2011-02-18 17:27:12 +0000
825@@ -39,8 +39,10 @@
826 {
827 if (mainloop != NULL) {
828 g_debug("Service shutdown !");
829+
830 close_pulse_activites();
831 g_main_loop_quit(mainloop);
832+
833 }
834 return;
835 }
836
837=== added file 'src/voip-input-menu-item.c'
838--- src/voip-input-menu-item.c 1970-01-01 00:00:00 +0000
839+++ src/voip-input-menu-item.c 2011-02-18 17:27:12 +0000
840@@ -0,0 +1,277 @@
841+/*
842+Copyright 2011 Canonical Ltd.
843+
844+Authors:
845+ Conor Curran <conor.curran@canonical.com>
846+
847+This program is free software: you can redistribute it and/or modify it
848+under the terms of the GNU General Public License version 3, as published
849+by the Free Software Foundation.
850+
851+This program is distributed in the hope that it will be useful, but
852+WITHOUT ANY WARRANTY; without even the implied warranties of
853+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
854+PURPOSE. See the GNU General Public License for more details.
855+
856+You should have received a copy of the GNU General Public License along
857+with this program. If not, see <http://www.gnu.org/licenses/>.
858+*/
859+#ifdef HAVE_CONFIG_H
860+#include "config.h"
861+#endif
862+
863+#include <glib/gi18n.h>
864+#include "voip-input-menu-item.h"
865+#include "common-defs.h"
866+#include "pulseaudio-mgr.h"
867+
868+typedef struct _VoipInputMenuItemPrivate VoipInputMenuItemPrivate;
869+
870+struct _VoipInputMenuItemPrivate {
871+ ActiveSink* a_sink;
872+ pa_cvolume volume;
873+ gint mute;
874+ guint32 volume_steps;
875+ pa_channel_map channel_map;
876+ pa_volume_t base_volume;
877+ gint source_index;
878+ gint sink_input_index;
879+ gint client_index;
880+};
881+
882+#define VOIP_INPUT_MENU_ITEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), VOIP_INPUT_MENU_ITEM_TYPE, VoipInputMenuItemPrivate))
883+
884+/* Prototypes */
885+static void voip_input_menu_item_class_init (VoipInputMenuItemClass *klass);
886+static void voip_input_menu_item_init (VoipInputMenuItem *self);
887+static void voip_input_menu_item_dispose (GObject *object);
888+static void voip_input_menu_item_finalize (GObject *object);
889+static void handle_event (DbusmenuMenuitem * mi, const gchar * name,
890+ GVariant * value, guint timestamp);
891+// TODO:
892+// This method should really be shared between this and the volume slider obj
893+// perfectly static - wait until the device mgr wrapper is properly sorted and
894+// then consolidate
895+static pa_cvolume voip_input_menu_item_construct_mono_volume (const pa_cvolume* vol);
896+
897+G_DEFINE_TYPE (VoipInputMenuItem, voip_input_menu_item, DBUSMENU_TYPE_MENUITEM);
898+
899+static void
900+voip_input_menu_item_class_init (VoipInputMenuItemClass *klass)
901+{
902+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
903+
904+ g_type_class_add_private (klass, sizeof (VoipInputMenuItemPrivate));
905+
906+ object_class->dispose = voip_input_menu_item_dispose;
907+ object_class->finalize = voip_input_menu_item_finalize;
908+
909+ DbusmenuMenuitemClass * mclass = DBUSMENU_MENUITEM_CLASS(klass);
910+ mclass->handle_event = handle_event;
911+}
912+
913+static void
914+voip_input_menu_item_init (VoipInputMenuItem *self)
915+{
916+ g_debug("Building new Slider Menu Item");
917+ dbusmenu_menuitem_property_set( DBUSMENU_MENUITEM(self),
918+ DBUSMENU_MENUITEM_PROP_TYPE,
919+ DBUSMENU_VOIP_INPUT_MENUITEM_TYPE );
920+ VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (self);
921+ dbusmenu_menuitem_property_set_bool( DBUSMENU_MENUITEM(self),
922+ DBUSMENU_MENUITEM_PROP_VISIBLE,
923+ FALSE );
924+
925+ priv->source_index = NOT_ACTIVE;
926+ priv->sink_input_index = NOT_ACTIVE;
927+ priv->client_index = NOT_ACTIVE;
928+ priv->mute = NOT_ACTIVE;
929+}
930+
931+static void
932+voip_input_menu_item_dispose (GObject *object)
933+{
934+ G_OBJECT_CLASS (voip_input_menu_item_parent_class)->dispose (object);
935+ return;
936+}
937+
938+static void
939+voip_input_menu_item_finalize (GObject *object)
940+{
941+ G_OBJECT_CLASS (voip_input_menu_item_parent_class)->finalize (object);
942+}
943+
944+static void
945+handle_event (DbusmenuMenuitem * mi,
946+ const gchar * name,
947+ GVariant * value,
948+ guint timestamp)
949+{
950+ GVariant* input = NULL;
951+ input = value;
952+ if (g_variant_is_of_type(value, G_VARIANT_TYPE_VARIANT) == TRUE) {
953+ input = g_variant_get_variant(value);
954+ }
955+
956+ gdouble percent = g_variant_get_double(input);
957+ if (value != NULL){
958+ if (IS_VOIP_INPUT_MENU_ITEM (mi)) {
959+ VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (VOIP_INPUT_MENU_ITEM (mi));
960+ g_debug ("Handle event in the voip input level backend instance - %f", percent);
961+ pa_cvolume new_volume;
962+ pa_cvolume_init(&new_volume);
963+ new_volume.channels = 1;
964+ pa_volume_t new_volume_value = (pa_volume_t) ((percent * PA_VOLUME_NORM) / 100);
965+ pa_cvolume_set(&new_volume, 1, new_volume_value);
966+
967+ pm_update_mic_gain (priv->source_index, new_volume);
968+ // finally unmute if needed
969+ if (priv->mute == 1) {
970+ pm_update_mic_mute (priv->source_index, 0);
971+ }
972+ //active_sink_update_volume (priv->a_sink, volume_input);
973+ //active_sink_ensure_sink_is_unmuted (priv->a_sink);
974+ }
975+ }
976+}
977+
978+static pa_cvolume
979+voip_input_menu_item_construct_mono_volume (const pa_cvolume* vol)
980+{
981+ pa_cvolume new_volume;
982+ pa_cvolume_init(&new_volume);
983+ new_volume.channels = 1;
984+ pa_volume_t max_vol = pa_cvolume_max(vol);
985+ pa_cvolume_set(&new_volume, 1, max_vol);
986+ return new_volume;
987+}
988+
989+void
990+voip_input_menu_item_update (VoipInputMenuItem* item,
991+ const pa_source_info* source)
992+{
993+ VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (item);
994+ // only overwrite the constants of each source if the device has changed
995+ if (priv->source_index == NOT_ACTIVE){
996+ priv->base_volume = source->base_volume;
997+ priv->volume_steps = source->n_volume_steps;
998+ priv->channel_map = source->channel_map;
999+ priv->source_index = source->index;
1000+ }
1001+ priv->volume = voip_input_menu_item_construct_mono_volume (&source->volume);
1002+ pa_volume_t vol = pa_cvolume_max (&source->volume);
1003+ gdouble update = ((gdouble) vol * 100) / PA_VOLUME_NORM;
1004+
1005+ GVariant* new_volume = g_variant_new_double(update);
1006+ dbusmenu_menuitem_property_set_variant(DBUSMENU_MENUITEM(item),
1007+ DBUSMENU_VOIP_INPUT_MENUITEM_LEVEL,
1008+ new_volume);
1009+ // Only send over the mute updates if the state has changed.
1010+ // in this order - volume first mute last!!
1011+ if (priv->mute != source->mute){
1012+ g_debug ("voip menu item - update - mute = %i", priv->mute);
1013+ GVariant* new_mute_update = g_variant_new_int32 (source->mute);
1014+ dbusmenu_menuitem_property_set_variant (DBUSMENU_MENUITEM(item),
1015+ DBUSMENU_VOIP_INPUT_MENUITEM_MUTE,
1016+ new_mute_update);
1017+ }
1018+
1019+ priv->mute = source->mute;
1020+
1021+}
1022+
1023+gboolean
1024+voip_input_menu_item_is_interested (VoipInputMenuItem* item,
1025+ gint sink_input_index,
1026+ gint client_index)
1027+{
1028+ VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (item);
1029+ // Check to make sure we are not handling another voip beforehand and that we
1030+ // have an active sink (might need to match up at start up)
1031+ if (priv->sink_input_index != NOT_ACTIVE &&
1032+ priv->source_index != NOT_ACTIVE){
1033+ return FALSE;
1034+ }
1035+
1036+ priv->sink_input_index = sink_input_index;
1037+ priv->client_index = client_index;
1038+
1039+ return TRUE;
1040+}
1041+
1042+gboolean
1043+voip_input_menu_item_is_active (VoipInputMenuItem* item)
1044+{
1045+ VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (item);
1046+ return (priv->sink_input_index != NOT_ACTIVE && priv->client_index != NOT_ACTIVE);
1047+}
1048+
1049+
1050+gboolean
1051+voip_input_menu_item_is_populated (VoipInputMenuItem* item)
1052+{
1053+ VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (item);
1054+ return priv->source_index != NOT_ACTIVE;
1055+}
1056+
1057+gint
1058+voip_input_menu_item_get_index (VoipInputMenuItem* item)
1059+{
1060+ VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (item);
1061+ return priv->source_index;
1062+}
1063+
1064+gint
1065+voip_input_menu_item_get_sink_input_index (VoipInputMenuItem* item)
1066+{
1067+ VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (item);
1068+
1069+ return priv->sink_input_index;
1070+}
1071+
1072+/**
1073+ * If the pulse server informs of a default source change
1074+ * or the source in question is removed.
1075+ * @param item
1076+ */
1077+void
1078+voip_input_menu_item_deactivate_source (VoipInputMenuItem* item, gboolean visible)
1079+{
1080+ VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (item);
1081+ priv->source_index = NOT_ACTIVE;
1082+ dbusmenu_menuitem_property_set_bool( DBUSMENU_MENUITEM(item),
1083+ DBUSMENU_MENUITEM_PROP_VISIBLE,
1084+ visible );
1085+}
1086+
1087+void
1088+voip_input_menu_item_deactivate_voip_client (VoipInputMenuItem* item)
1089+{
1090+ VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (item);
1091+ priv->client_index = NOT_ACTIVE;
1092+ priv->sink_input_index = NOT_ACTIVE;
1093+ voip_input_menu_item_enable (item, FALSE);
1094+}
1095+
1096+void
1097+voip_input_menu_item_enable (VoipInputMenuItem* item,
1098+ gboolean active)
1099+{
1100+ VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (item);
1101+ if (priv->source_index == NOT_ACTIVE && active == TRUE) {
1102+ g_warning ("Tried to enable the VOIP menuitem but we don't have an active source ??");
1103+ active = FALSE;
1104+ }
1105+ dbusmenu_menuitem_property_set_bool( DBUSMENU_MENUITEM(item),
1106+ DBUSMENU_MENUITEM_PROP_VISIBLE,
1107+ active );
1108+}
1109+
1110+VoipInputMenuItem*
1111+voip_input_menu_item_new (ActiveSink* sink)
1112+{
1113+ VoipInputMenuItem *self = g_object_new(VOIP_INPUT_MENU_ITEM_TYPE, NULL);
1114+ VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (self);
1115+ priv->a_sink = sink;
1116+ return self;
1117+}
1118\ No newline at end of file
1119
1120=== added file 'src/voip-input-menu-item.h'
1121--- src/voip-input-menu-item.h 1970-01-01 00:00:00 +0000
1122+++ src/voip-input-menu-item.h 2011-02-18 17:27:12 +0000
1123@@ -0,0 +1,70 @@
1124+/*
1125+Copyright 2011 Canonical Ltd.
1126+
1127+Authors:
1128+ Conor Curran <conor.curran@canonical.com>
1129+
1130+This program is free software: you can redistribute it and/or modify it
1131+under the terms of the GNU General Public License version 3, as published
1132+by the Free Software Foundation.
1133+
1134+This program is distributed in the hope that it will be useful, but
1135+WITHOUT ANY WARRANTY; without even the implied warranties of
1136+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1137+PURPOSE. See the GNU General Public License for more details.
1138+
1139+You should have received a copy of the GNU General Public License along
1140+with this program. If not, see <http://www.gnu.org/licenses/>.
1141+*/
1142+#ifndef __VOIP_INPUT_MENU_ITEM_H__
1143+#define __VOIP_INPUT_MENU_ITEM_H__
1144+
1145+#include <glib.h>
1146+#include <pulse/pulseaudio.h>
1147+#include <libdbusmenu-glib/menuitem.h>
1148+#include "active-sink.h"
1149+
1150+G_BEGIN_DECLS
1151+
1152+#define VOIP_INPUT_MENU_ITEM_TYPE (voip_input_menu_item_get_type ())
1153+#define VOIP_INPUT_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VOIP_INPUT_MENU_ITEM_TYPE, VoipInputMenuItem))
1154+#define VOIP_INPUT_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VOIP_INPUT_MENU_ITEM_TYPE, VoipInputMenuItemClass))
1155+#define IS_VOIP_INPUT_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VOIP_INPUT_MENU_ITEM_TYPE))
1156+#define IS_VOIP_INPUT_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VOIP_INPUT_MENU_ITEM_TYPE))
1157+#define VOIP_INPUT_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), VOIP_INPUT_MENU_ITEM_TYPE, VoipInputMenuItemClass))
1158+
1159+typedef struct _VoipInputMenuItem VoipInputMenuItem;
1160+typedef struct _VoipInputMenuItemClass VoipInputMenuItemClass;
1161+
1162+struct _VoipInputMenuItemClass {
1163+ DbusmenuMenuitemClass parent_class;
1164+};
1165+
1166+struct _VoipInputMenuItem {
1167+ DbusmenuMenuitem parent;
1168+};
1169+
1170+GType voip_input_menu_item_get_type (void);
1171+
1172+void voip_input_menu_item_update (VoipInputMenuItem* item,
1173+ const pa_source_info* source);
1174+void voip_input_menu_item_enable (VoipInputMenuItem* item, gboolean active);
1175+gboolean voip_input_menu_item_is_interested (VoipInputMenuItem* item,
1176+ gint sink_input_index,
1177+ gint client_index);
1178+gboolean voip_input_menu_item_is_active (VoipInputMenuItem* item);
1179+gboolean voip_input_menu_item_is_populated (VoipInputMenuItem* item);
1180+// TODO rename get source index
1181+gint voip_input_menu_item_get_index (VoipInputMenuItem* item);
1182+
1183+gint voip_input_menu_item_get_sink_input_index (VoipInputMenuItem* item);
1184+
1185+void voip_input_menu_item_deactivate_source (VoipInputMenuItem* item, gboolean visible);
1186+void voip_input_menu_item_deactivate_voip_client (VoipInputMenuItem* item);
1187+
1188+VoipInputMenuItem* voip_input_menu_item_new (ActiveSink* sink);
1189+
1190+G_END_DECLS
1191+
1192+#endif
1193+
1194
1195=== added file 'src/voip-input-widget.c'
1196--- src/voip-input-widget.c 1970-01-01 00:00:00 +0000
1197+++ src/voip-input-widget.c 2011-02-18 17:27:12 +0000
1198@@ -0,0 +1,279 @@
1199+
1200+/*
1201+Copyright 2011 Canonical Ltd.
1202+
1203+Authors:
1204+ Conor Curran <conor.curran@canonical.com>
1205+
1206+This program is free software: you can redistribute it and/or modify it
1207+under the terms of the GNU General Public License version 3, as published
1208+by the Free Software Foundation.
1209+
1210+This program is distributed in the hope that it will be useful, but
1211+WITHOUT ANY WARRANTY; without even the implied warranties of
1212+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1213+PURPOSE. See the GNU General Public License for more details.
1214+
1215+You should have received a copy of the GNU General Public License along
1216+with this program. If not, see <http://www.gnu.org/licenses/>.
1217+*/
1218+
1219+#ifdef HAVE_CONFIG_H
1220+#include "config.h"
1221+#endif
1222+
1223+#include <glib/gi18n.h>
1224+#include <math.h>
1225+#include <glib.h>
1226+#include "voip-input-widget.h"
1227+#include "common-defs.h"
1228+#include <libido/idoscalemenuitem.h>
1229+
1230+typedef struct _VoipInputWidgetPrivate VoipInputWidgetPrivate;
1231+
1232+struct _VoipInputWidgetPrivate
1233+{
1234+ DbusmenuMenuitem* twin_item;
1235+ GtkWidget* ido_voip_input_slider;
1236+ gboolean grabbed;
1237+};
1238+
1239+#define VOIP_INPUT_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), VOIP_INPUT_WIDGET_TYPE, VoipInputWidgetPrivate))
1240+
1241+/* Prototypes */
1242+static void voip_input_widget_class_init (VoipInputWidgetClass *klass);
1243+static void voip_input_widget_init (VoipInputWidget *self);
1244+static void voip_input_widget_dispose (GObject *object);
1245+static void voip_input_widget_finalize (GObject *object);
1246+static void voip_input_widget_set_twin_item( VoipInputWidget* self,
1247+ DbusmenuMenuitem* twin_item);
1248+static void voip_input_widget_property_update( DbusmenuMenuitem* item, gchar* property,
1249+ GVariant* value, gpointer userdata );
1250+
1251+static gboolean voip_input_widget_change_value_cb (GtkRange *range,
1252+ GtkScrollType scroll,
1253+ gdouble value,
1254+ gpointer user_data);
1255+static gboolean voip_input_widget_value_changed_cb(GtkRange *range, gpointer user_data);
1256+static void voip_input_widget_slider_grabbed(GtkWidget *widget, gpointer user_data);
1257+static void voip_input_widget_slider_released(GtkWidget *widget, gpointer user_data);
1258+static void voip_input_widget_parent_changed (GtkWidget *widget, gpointer user_data);
1259+
1260+G_DEFINE_TYPE (VoipInputWidget, voip_input_widget, G_TYPE_OBJECT);
1261+
1262+
1263+static void
1264+voip_input_widget_class_init (VoipInputWidgetClass *klass)
1265+{
1266+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1267+
1268+ g_type_class_add_private (klass, sizeof (VoipInputWidgetPrivate));
1269+
1270+ gobject_class->dispose = voip_input_widget_dispose;
1271+ gobject_class->finalize = voip_input_widget_finalize;
1272+}
1273+
1274+static void
1275+voip_input_widget_init (VoipInputWidget *self)
1276+{
1277+ VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(self);
1278+
1279+ priv->ido_voip_input_slider = ido_scale_menu_item_new_with_range ("VOLUME", IDO_RANGE_STYLE_DEFAULT, 0, 0, 100, 1);
1280+ g_object_ref (priv->ido_voip_input_slider);
1281+ ido_scale_menu_item_set_style (IDO_SCALE_MENU_ITEM (priv->ido_voip_input_slider), IDO_SCALE_MENU_ITEM_STYLE_IMAGE);
1282+ g_object_set(priv->ido_voip_input_slider, "reverse-scroll-events", TRUE, NULL);
1283+
1284+ g_signal_connect (priv->ido_voip_input_slider,
1285+ "notify::parent", G_CALLBACK (voip_input_widget_parent_changed),
1286+ NULL);
1287+
1288+ GtkWidget* voip_input_widget = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)priv->ido_voip_input_slider);
1289+
1290+ g_signal_connect(voip_input_widget, "change-value", G_CALLBACK(voip_input_widget_change_value_cb), self);
1291+ g_signal_connect(voip_input_widget, "value-changed", G_CALLBACK(voip_input_widget_value_changed_cb), self);
1292+ g_signal_connect(priv->ido_voip_input_slider, "slider-grabbed", G_CALLBACK(voip_input_widget_slider_grabbed), self);
1293+ g_signal_connect(priv->ido_voip_input_slider, "slider-released", G_CALLBACK(voip_input_widget_slider_released), self);
1294+
1295+ GtkWidget* primary_image = ido_scale_menu_item_get_primary_image((IdoScaleMenuItem*)priv->ido_voip_input_slider);
1296+ GIcon * primary_gicon = g_themed_icon_new_with_default_fallbacks("audio-input-microphone");
1297+ gtk_image_set_from_gicon(GTK_IMAGE(primary_image), primary_gicon, GTK_ICON_SIZE_MENU);
1298+ g_object_unref(primary_gicon);
1299+
1300+ GtkWidget* secondary_image = ido_scale_menu_item_get_secondary_image((IdoScaleMenuItem*)priv->ido_voip_input_slider);
1301+ GIcon * secondary_gicon = g_themed_icon_new_with_default_fallbacks("audio-input-microphone-high");
1302+ gtk_image_set_from_gicon(GTK_IMAGE(secondary_image), secondary_gicon, GTK_ICON_SIZE_MENU);
1303+ g_object_unref(secondary_gicon);
1304+
1305+ GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (voip_input_widget));
1306+ gtk_adjustment_set_step_increment(adj, 4);
1307+}
1308+
1309+static void
1310+voip_input_widget_dispose (GObject *object)
1311+{
1312+ G_OBJECT_CLASS (voip_input_widget_parent_class)->dispose (object);
1313+}
1314+
1315+static void
1316+voip_input_widget_finalize (GObject *object)
1317+{
1318+ G_OBJECT_CLASS (voip_input_widget_parent_class)->finalize (object);
1319+}
1320+
1321+static void
1322+voip_input_widget_property_update (DbusmenuMenuitem* item, gchar* property,
1323+ GVariant* value, gpointer userdata)
1324+{
1325+ g_return_if_fail (IS_VOIP_INPUT_WIDGET (userdata));
1326+ VoipInputWidget* mitem = VOIP_INPUT_WIDGET(userdata);
1327+ VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(mitem);
1328+ //g_debug("scrub-widget::property_update for prop %s", property);
1329+ if(g_ascii_strcasecmp(DBUSMENU_VOIP_INPUT_MENUITEM_LEVEL, property) == 0){
1330+ if(priv->grabbed == FALSE){
1331+ GtkWidget *slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)priv->ido_voip_input_slider);
1332+ GtkRange *range = (GtkRange*)slider;
1333+ gdouble update = g_variant_get_double (value);
1334+ //g_debug("volume-widget - update level with value %f", update);
1335+ gtk_range_set_value(range, update);
1336+ }
1337+ }
1338+ if(g_ascii_strcasecmp(DBUSMENU_VOIP_INPUT_MENUITEM_MUTE, property) == 0){
1339+ if(priv->grabbed == FALSE){
1340+ GtkWidget *slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)priv->ido_voip_input_slider);
1341+ GtkRange *range = (GtkRange*)slider;
1342+ gint update = g_variant_get_int32 (value);
1343+ gdouble level;
1344+ if (update == 1){
1345+ level = 0;
1346+ }
1347+ else{
1348+ level = g_variant_get_double (dbusmenu_menuitem_property_get_variant (priv->twin_item,
1349+ DBUSMENU_VOIP_INPUT_MENUITEM_LEVEL));
1350+ }
1351+ gtk_range_set_value(range, level);
1352+
1353+ g_debug ("voip-item-widget - update mute with value %i", update);
1354+ }
1355+ }
1356+}
1357+
1358+static void
1359+voip_input_widget_set_twin_item (VoipInputWidget* self,
1360+ DbusmenuMenuitem* twin_item)
1361+{
1362+ VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(self);
1363+ priv->twin_item = twin_item;
1364+ g_object_ref(priv->twin_item);
1365+ g_signal_connect(G_OBJECT(twin_item), "property-changed",
1366+ G_CALLBACK(voip_input_widget_property_update), self);
1367+ gdouble initial_level = g_variant_get_double (dbusmenu_menuitem_property_get_variant(twin_item,
1368+ DBUSMENU_VOIP_INPUT_MENUITEM_LEVEL));
1369+ //g_debug("voip_input_widget_set_twin_item initial level = %f", initial_level);
1370+ GtkWidget *slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)priv->ido_voip_input_slider);
1371+ GtkRange *range = (GtkRange*)slider;
1372+ gtk_range_set_value(range, initial_level);
1373+
1374+ gint mute = g_variant_get_int32 (dbusmenu_menuitem_property_get_variant (priv->twin_item,
1375+ DBUSMENU_VOIP_INPUT_MENUITEM_MUTE));
1376+ if (mute == 1){
1377+ gtk_range_set_value (range, 0.0);
1378+ }
1379+}
1380+
1381+static gboolean
1382+voip_input_widget_change_value_cb (GtkRange *range,
1383+ GtkScrollType scroll,
1384+ gdouble new_value,
1385+ gpointer user_data)
1386+{
1387+ g_return_val_if_fail (IS_VOIP_INPUT_WIDGET (user_data), FALSE);
1388+ VoipInputWidget* mitem = VOIP_INPUT_WIDGET(user_data);
1389+ voip_input_widget_update(mitem, new_value);
1390+ return FALSE;
1391+}
1392+
1393+
1394+/**
1395+ * We only want this callback to catch mouse icon press events which set the
1396+ * slider to 0 or 100. Ignore all other events including the new Mute behaviour
1397+ * (slider to go to 0 on mute without setting the level to 0 and return to
1398+ * previous level on unmute)
1399+ **/
1400+static gboolean
1401+voip_input_widget_value_changed_cb(GtkRange *range, gpointer user_data)
1402+{
1403+ g_return_val_if_fail (IS_VOIP_INPUT_WIDGET (user_data), FALSE);
1404+ VoipInputWidget* mitem = VOIP_INPUT_WIDGET(user_data);
1405+ VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(mitem);
1406+ GtkWidget *slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)priv->ido_voip_input_slider);
1407+ gdouble current_value = CLAMP(gtk_range_get_value(GTK_RANGE(slider)), 0, 100);
1408+
1409+ gint mute = g_variant_get_int32 (dbusmenu_menuitem_property_get_variant (priv->twin_item,
1410+ DBUSMENU_VOIP_INPUT_MENUITEM_MUTE));
1411+ if ((current_value == 0 && mute != 1) || current_value == 100 ){
1412+ voip_input_widget_update(mitem, current_value);
1413+ }
1414+ return FALSE;
1415+}
1416+
1417+void
1418+voip_input_widget_update(VoipInputWidget* self, gdouble update)
1419+{
1420+ VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(self);
1421+ gdouble clamped = CLAMP(update, 0, 100);
1422+ GVariant* new_volume = g_variant_new_double(clamped);
1423+ dbusmenu_menuitem_handle_event (priv->twin_item, "update", new_volume, 0);
1424+}
1425+
1426+GtkWidget*
1427+voip_input_widget_get_ido_slider(VoipInputWidget* self)
1428+{
1429+ VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(self);
1430+ return priv->ido_voip_input_slider;
1431+}
1432+
1433+static void
1434+voip_input_widget_parent_changed (GtkWidget *widget,
1435+ gpointer user_data)
1436+{
1437+ gtk_widget_set_size_request (widget, 200, -1);
1438+ //g_debug("voip_input_widget_parent_changed");
1439+}
1440+
1441+static void
1442+voip_input_widget_slider_grabbed(GtkWidget *widget, gpointer user_data)
1443+{
1444+ VoipInputWidget* mitem = VOIP_INPUT_WIDGET(user_data);
1445+ VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(mitem);
1446+ priv->grabbed = TRUE;
1447+}
1448+
1449+static void
1450+voip_input_widget_slider_released(GtkWidget *widget, gpointer user_data)
1451+{
1452+ VoipInputWidget* mitem = VOIP_INPUT_WIDGET(user_data);
1453+ VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(mitem);
1454+ priv->grabbed = FALSE;
1455+}
1456+
1457+void
1458+voip_input_widget_tidy_up (GtkWidget *widget)
1459+{
1460+ VoipInputWidget* mitem = VOIP_INPUT_WIDGET(widget);
1461+ VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(mitem);
1462+ gtk_widget_destroy (priv->ido_voip_input_slider);
1463+}
1464+
1465+/**
1466+ * voip_input_widget_new:
1467+ * @returns: a new #VoipInputWidget.
1468+ **/
1469+GtkWidget*
1470+voip_input_widget_new(DbusmenuMenuitem *item)
1471+{
1472+ GtkWidget* widget = g_object_new(VOIP_INPUT_WIDGET_TYPE, NULL);
1473+ voip_input_widget_set_twin_item((VoipInputWidget*)widget, item);
1474+ return widget;
1475+}
1476+
1477+
1478
1479=== added file 'src/voip-input-widget.h'
1480--- src/voip-input-widget.h 1970-01-01 00:00:00 +0000
1481+++ src/voip-input-widget.h 2011-02-18 17:27:12 +0000
1482@@ -0,0 +1,55 @@
1483+/*
1484+Copyright 2011 Canonical Ltd.
1485+
1486+Authors:
1487+ Conor Curran <conor.curran@canonical.com>
1488+
1489+This program is free software: you can redistribute it and/or modify it
1490+under the terms of the GNU General Public License version 3, as published
1491+by the Free Software Foundation.
1492+
1493+This program is distributed in the hope that it will be useful, but
1494+WITHOUT ANY WARRANTY; without even the implied warranties of
1495+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1496+PURPOSE. See the GNU General Public License for more details.
1497+
1498+You should have received a copy of the GNU General Public License along
1499+with this program. If not, see <http://www.gnu.org/licenses/>.
1500+*/
1501+#ifndef __VOIP_INPUT_WIDGET_H__
1502+#define __VOIP_INPUT_WIDGET_H__
1503+
1504+#include <glib.h>
1505+#include <glib-object.h>
1506+#include <libdbusmenu-gtk/menuitem.h>
1507+
1508+G_BEGIN_DECLS
1509+
1510+#define VOIP_INPUT_WIDGET_TYPE (voip_input_widget_get_type ())
1511+#define VOIP_INPUT_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VOIP_INPUT_WIDGET_TYPE, VoipInputWidget))
1512+#define VOIP_INPUT_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VOIP_INPUT_WIDGET_TYPE, VoipInputWidgetClass))
1513+#define IS_VOIP_INPUT_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VOIP_INPUT_WIDGET_TYPE))
1514+#define IS_VOIP_INPUT_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VOIP_INPUT_WIDGET_TYPE))
1515+#define VOIP_INPUT_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), VOIP_INPUT_WIDGET_TYPE, VoipInputWidgetClass))
1516+
1517+typedef struct _VoipInputWidget VoipInputWidget;
1518+typedef struct _VoipInputWidgetClass VoipInputWidgetClass;
1519+
1520+struct _VoipInputWidgetClass {
1521+ GObjectClass parent_class;
1522+};
1523+
1524+struct _VoipInputWidget {
1525+ GObject parent;
1526+};
1527+
1528+GType voip_input_widget_get_type (void) G_GNUC_CONST;
1529+GtkWidget* voip_input_widget_new(DbusmenuMenuitem* twin_item);
1530+GtkWidget* voip_input_widget_get_ido_slider(VoipInputWidget* self);
1531+void voip_input_widget_update(VoipInputWidget* self, gdouble update);
1532+void voip_input_widget_tidy_up (GtkWidget *widget);
1533+
1534+G_END_DECLS
1535+
1536+#endif
1537+

Subscribers

People subscribed via source and target branches