| |
1 /* |
| |
2 * Pidgin - Internet Messenger |
| |
3 * Copyright (C) Pidgin Developers <devel@pidgin.im> |
| |
4 * |
| |
5 * Pidgin is the legal property of its developers, whose names are too numerous |
| |
6 * to list here. Please refer to the COPYRIGHT file distributed with this |
| |
7 * source distribution. |
| |
8 * |
| |
9 * This program is free software; you can redistribute it and/or modify |
| |
10 * it under the terms of the GNU General Public License as published by |
| |
11 * the Free Software Foundation; either version 2 of the License, or |
| |
12 * (at your option) any later version. |
| |
13 * |
| |
14 * This program is distributed in the hope that it will be useful, |
| |
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
17 * GNU General Public License for more details. |
| |
18 * |
| |
19 * You should have received a copy of the GNU General Public License |
| |
20 * along with this program; if not, see <https://www.gnu.org/licenses/>. |
| |
21 */ |
| |
22 |
| |
23 #ifdef HAVE_CONFIG_H |
| |
24 #include <config.h> |
| |
25 #endif |
| |
26 |
| |
27 #include <math.h> |
| |
28 |
| |
29 #include <glib/gi18n-lib.h> |
| |
30 |
| |
31 #include <purple.h> |
| |
32 |
| |
33 #include <handy.h> |
| |
34 |
| |
35 #include "pidginvvprefs.h" |
| |
36 #include "pidgincore.h" |
| |
37 #include "pidginprefsinternal.h" |
| |
38 |
| |
39 struct _PidginVVPrefs { |
| |
40 HdyPreferencesPage parent; |
| |
41 |
| |
42 struct { |
| |
43 PidginPrefCombo input; |
| |
44 PidginPrefCombo output; |
| |
45 GtkWidget *level; |
| |
46 GtkWidget *threshold_label; |
| |
47 GtkWidget *threshold; |
| |
48 GtkWidget *volume; |
| |
49 GtkWidget *test; |
| |
50 GstElement *pipeline; |
| |
51 } voice; |
| |
52 |
| |
53 struct { |
| |
54 PidginPrefCombo input; |
| |
55 PidginPrefCombo output; |
| |
56 GtkWidget *frame; |
| |
57 GtkWidget *sink_widget; |
| |
58 GtkWidget *test; |
| |
59 GstElement *pipeline; |
| |
60 } video; |
| |
61 }; |
| |
62 |
| |
63 G_DEFINE_TYPE(PidginVVPrefs, pidgin_vv_prefs, HDY_TYPE_PREFERENCES_PAGE) |
| |
64 |
| |
65 /****************************************************************************** |
| |
66 * Helpers |
| |
67 *****************************************************************************/ |
| |
68 static void |
| |
69 populate_vv_device_menuitems(PurpleMediaElementType type, GtkListStore *store) |
| |
70 { |
| |
71 PurpleMediaManager *manager = NULL; |
| |
72 GList *devices; |
| |
73 |
| |
74 gtk_list_store_clear(store); |
| |
75 |
| |
76 manager = purple_media_manager_get(); |
| |
77 devices = purple_media_manager_enumerate_elements(manager, type); |
| |
78 for (; devices; devices = g_list_delete_link(devices, devices)) { |
| |
79 PurpleMediaElementInfo *info = devices->data; |
| |
80 GtkTreeIter iter; |
| |
81 const gchar *name, *id; |
| |
82 |
| |
83 name = purple_media_element_info_get_name(info); |
| |
84 id = purple_media_element_info_get_id(info); |
| |
85 |
| |
86 gtk_list_store_append(store, &iter); |
| |
87 gtk_list_store_set(store, &iter, PIDGIN_PREF_COMBO_TEXT, name, |
| |
88 PIDGIN_PREF_COMBO_VALUE, id, -1); |
| |
89 |
| |
90 g_object_unref(info); |
| |
91 } |
| |
92 } |
| |
93 |
| |
94 static GstElement * |
| |
95 create_test_element(PurpleMediaElementType type) |
| |
96 { |
| |
97 PurpleMediaElementInfo *element_info; |
| |
98 |
| |
99 element_info = purple_media_manager_get_active_element(purple_media_manager_get(), type); |
| |
100 |
| |
101 g_return_val_if_fail(element_info, NULL); |
| |
102 |
| |
103 return purple_media_element_info_call_create(element_info, |
| |
104 NULL, NULL, NULL); |
| |
105 } |
| |
106 |
| |
107 static GstElement * |
| |
108 create_voice_pipeline(void) |
| |
109 { |
| |
110 GstElement *pipeline; |
| |
111 GstElement *src, *sink; |
| |
112 GstElement *volume; |
| |
113 GstElement *level; |
| |
114 GstElement *valve; |
| |
115 |
| |
116 pipeline = gst_pipeline_new("voicetest"); |
| |
117 |
| |
118 src = create_test_element(PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SRC); |
| |
119 sink = create_test_element(PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SINK); |
| |
120 volume = gst_element_factory_make("volume", "volume"); |
| |
121 level = gst_element_factory_make("level", "level"); |
| |
122 valve = gst_element_factory_make("valve", "valve"); |
| |
123 |
| |
124 gst_bin_add_many(GST_BIN(pipeline), src, volume, level, valve, sink, NULL); |
| |
125 gst_element_link_many(src, volume, level, valve, sink, NULL); |
| |
126 |
| |
127 purple_debug_info("gtkprefs", "create_voice_pipeline: setting pipeline " |
| |
128 "state to GST_STATE_PLAYING - it may hang here on win32\n"); |
| |
129 gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING); |
| |
130 purple_debug_info("gtkprefs", "create_voice_pipeline: state is set\n"); |
| |
131 |
| |
132 return pipeline; |
| |
133 } |
| |
134 |
| |
135 static void |
| |
136 on_volume_change_cb(GtkWidget *w, gdouble value, gpointer data) |
| |
137 { |
| |
138 PidginVVPrefs *prefs = PIDGIN_VV_PREFS(data); |
| |
139 GstElement *volume; |
| |
140 |
| |
141 if (!prefs->voice.pipeline) { |
| |
142 return; |
| |
143 } |
| |
144 |
| |
145 volume = gst_bin_get_by_name(GST_BIN(prefs->voice.pipeline), "volume"); |
| |
146 g_object_set(volume, "volume", |
| |
147 gtk_scale_button_get_value(GTK_SCALE_BUTTON(w)) / 100.0, NULL); |
| |
148 } |
| |
149 |
| |
150 static gdouble |
| |
151 gst_msg_db_to_percent(GstMessage *msg, gchar *value_name) |
| |
152 { |
| |
153 const GValue *list; |
| |
154 const GValue *value; |
| |
155 gdouble value_db; |
| |
156 gdouble percent; |
| |
157 |
| |
158 list = gst_structure_get_value(gst_message_get_structure(msg), value_name); |
| |
159 G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
| |
160 value = g_value_array_get_nth(g_value_get_boxed(list), 0); |
| |
161 G_GNUC_END_IGNORE_DEPRECATIONS |
| |
162 value_db = g_value_get_double(value); |
| |
163 percent = pow(10, value_db / 20); |
| |
164 return (percent > 1.0) ? 1.0 : percent; |
| |
165 } |
| |
166 |
| |
167 static gboolean |
| |
168 gst_bus_cb(GstBus *bus, GstMessage *msg, gpointer data) |
| |
169 { |
| |
170 PidginVVPrefs *prefs = PIDGIN_VV_PREFS(data); |
| |
171 |
| |
172 if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ELEMENT && |
| |
173 gst_structure_has_name(gst_message_get_structure(msg), "level")) { |
| |
174 |
| |
175 GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg)); |
| |
176 gchar *name = gst_element_get_name(src); |
| |
177 |
| |
178 if (purple_strequal(name, "level")) { |
| |
179 gdouble percent; |
| |
180 gdouble threshold; |
| |
181 GstElement *valve; |
| |
182 |
| |
183 percent = gst_msg_db_to_percent(msg, "rms"); |
| |
184 gtk_progress_bar_set_fraction( |
| |
185 GTK_PROGRESS_BAR(prefs->voice.level), percent); |
| |
186 |
| |
187 percent = gst_msg_db_to_percent(msg, "decay"); |
| |
188 threshold = gtk_range_get_value(GTK_RANGE( |
| |
189 prefs->voice.threshold)) / |
| |
190 100.0; |
| |
191 valve = gst_bin_get_by_name(GST_BIN(GST_ELEMENT_PARENT(src)), "valve"); |
| |
192 g_object_set(valve, "drop", (percent < threshold), NULL); |
| |
193 g_object_set(prefs->voice.level, "text", |
| |
194 (percent < threshold) ? _("DROP") : " ", |
| |
195 NULL); |
| |
196 } |
| |
197 |
| |
198 g_free(name); |
| |
199 } |
| |
200 |
| |
201 return TRUE; |
| |
202 } |
| |
203 |
| |
204 static void |
| |
205 voice_test_destroy_cb(GtkWidget *w, gpointer data) |
| |
206 { |
| |
207 PidginVVPrefs *prefs = PIDGIN_VV_PREFS(data); |
| |
208 |
| |
209 if (!prefs->voice.pipeline) { |
| |
210 return; |
| |
211 } |
| |
212 |
| |
213 gst_element_set_state(prefs->voice.pipeline, GST_STATE_NULL); |
| |
214 g_clear_pointer(&prefs->voice.pipeline, gst_object_unref); |
| |
215 } |
| |
216 |
| |
217 static void |
| |
218 enable_voice_test(PidginVVPrefs *prefs) |
| |
219 { |
| |
220 GstBus *bus; |
| |
221 |
| |
222 prefs->voice.pipeline = create_voice_pipeline(); |
| |
223 bus = gst_pipeline_get_bus(GST_PIPELINE(prefs->voice.pipeline)); |
| |
224 gst_bus_add_signal_watch(bus); |
| |
225 g_signal_connect(bus, "message", G_CALLBACK(gst_bus_cb), prefs); |
| |
226 gst_object_unref(bus); |
| |
227 } |
| |
228 |
| |
229 static void |
| |
230 toggle_voice_test_cb(GtkToggleButton *test, gpointer data) |
| |
231 { |
| |
232 PidginVVPrefs *prefs = PIDGIN_VV_PREFS(data); |
| |
233 |
| |
234 if (gtk_toggle_button_get_active(test)) { |
| |
235 gtk_widget_set_sensitive(prefs->voice.level, TRUE); |
| |
236 enable_voice_test(prefs); |
| |
237 |
| |
238 g_signal_connect(prefs->voice.volume, "value-changed", |
| |
239 G_CALLBACK(on_volume_change_cb), prefs); |
| |
240 g_signal_connect(test, "destroy", |
| |
241 G_CALLBACK(voice_test_destroy_cb), prefs); |
| |
242 } else { |
| |
243 gtk_progress_bar_set_fraction( |
| |
244 GTK_PROGRESS_BAR(prefs->voice.level), 0.0); |
| |
245 gtk_widget_set_sensitive(prefs->voice.level, FALSE); |
| |
246 g_object_disconnect(prefs->voice.volume, |
| |
247 "any-signal::value-changed", |
| |
248 G_CALLBACK(on_volume_change_cb), prefs, NULL); |
| |
249 g_object_disconnect(test, "any-signal::destroy", |
| |
250 G_CALLBACK(voice_test_destroy_cb), prefs, |
| |
251 NULL); |
| |
252 voice_test_destroy_cb(NULL, prefs); |
| |
253 } |
| |
254 } |
| |
255 |
| |
256 static void |
| |
257 volume_changed_cb(GtkScaleButton *button, gdouble value, gpointer data) |
| |
258 { |
| |
259 purple_prefs_set_int("/purple/media/audio/volume/input", value * 100); |
| |
260 } |
| |
261 |
| |
262 static void |
| |
263 threshold_value_changed_cb(GtkScale *scale, gpointer data) |
| |
264 { |
| |
265 PidginVVPrefs *prefs = data; |
| |
266 int value; |
| |
267 char *tmp; |
| |
268 |
| |
269 value = (int)gtk_range_get_value(GTK_RANGE(scale)); |
| |
270 tmp = g_strdup_printf(_("Silence threshold: %d%%"), value); |
| |
271 gtk_label_set_label(GTK_LABEL(prefs->voice.threshold_label), tmp); |
| |
272 g_free(tmp); |
| |
273 |
| |
274 purple_prefs_set_int("/purple/media/audio/silence_threshold", value); |
| |
275 } |
| |
276 |
| |
277 static void |
| |
278 bind_voice_test(PidginVVPrefs *prefs) |
| |
279 { |
| |
280 char *tmp; |
| |
281 |
| |
282 gtk_scale_button_set_value(GTK_SCALE_BUTTON(prefs->voice.volume), |
| |
283 purple_prefs_get_int("/purple/media/audio/volume/input") / 100.0); |
| |
284 |
| |
285 tmp = g_strdup_printf(_("Silence threshold: %d%%"), |
| |
286 purple_prefs_get_int("/purple/media/audio/silence_threshold")); |
| |
287 gtk_label_set_text(GTK_LABEL(prefs->voice.threshold_label), tmp); |
| |
288 g_free(tmp); |
| |
289 |
| |
290 gtk_range_set_value(GTK_RANGE(prefs->voice.threshold), |
| |
291 purple_prefs_get_int("/purple/media/audio/silence_threshold")); |
| |
292 } |
| |
293 |
| |
294 static GstElement * |
| |
295 create_video_pipeline(void) |
| |
296 { |
| |
297 GstElement *pipeline; |
| |
298 GstElement *src, *sink; |
| |
299 GstElement *videoconvert; |
| |
300 GstElement *videoscale; |
| |
301 |
| |
302 pipeline = gst_pipeline_new("videotest"); |
| |
303 src = create_test_element(PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SRC); |
| |
304 sink = create_test_element(PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SINK); |
| |
305 videoconvert = gst_element_factory_make("videoconvert", NULL); |
| |
306 videoscale = gst_element_factory_make("videoscale", NULL); |
| |
307 |
| |
308 g_object_set_data(G_OBJECT(pipeline), "sink", sink); |
| |
309 |
| |
310 gst_bin_add_many(GST_BIN(pipeline), src, videoconvert, videoscale, sink, |
| |
311 NULL); |
| |
312 gst_element_link_many(src, videoconvert, videoscale, sink, NULL); |
| |
313 |
| |
314 return pipeline; |
| |
315 } |
| |
316 |
| |
317 static void |
| |
318 video_test_destroy_cb(GtkWidget *w, gpointer data) |
| |
319 { |
| |
320 PidginVVPrefs *prefs = PIDGIN_VV_PREFS(data); |
| |
321 |
| |
322 if (!prefs->video.pipeline) { |
| |
323 return; |
| |
324 } |
| |
325 |
| |
326 gst_element_set_state(prefs->video.pipeline, GST_STATE_NULL); |
| |
327 g_clear_pointer(&prefs->video.pipeline, gst_object_unref); |
| |
328 } |
| |
329 |
| |
330 static void |
| |
331 enable_video_test(PidginVVPrefs *prefs) |
| |
332 { |
| |
333 GtkWidget *video = NULL; |
| |
334 GstElement *sink = NULL; |
| |
335 |
| |
336 prefs->video.pipeline = create_video_pipeline(); |
| |
337 |
| |
338 sink = g_object_get_data(G_OBJECT(prefs->video.pipeline), "sink"); |
| |
339 g_object_get(sink, "widget", &video, NULL); |
| |
340 gtk_widget_show(video); |
| |
341 |
| |
342 g_clear_pointer(&prefs->video.sink_widget, gtk_widget_destroy); |
| |
343 gtk_widget_set_size_request(prefs->video.frame, 400, 300); |
| |
344 gtk_container_add(GTK_CONTAINER(prefs->video.frame), video); |
| |
345 prefs->video.sink_widget = video; |
| |
346 |
| |
347 gst_element_set_state(GST_ELEMENT(prefs->video.pipeline), |
| |
348 GST_STATE_PLAYING); |
| |
349 } |
| |
350 |
| |
351 static void |
| |
352 toggle_video_test_cb(GtkToggleButton *test, gpointer data) |
| |
353 { |
| |
354 PidginVVPrefs *prefs = PIDGIN_VV_PREFS(data); |
| |
355 |
| |
356 if (gtk_toggle_button_get_active(test)) { |
| |
357 enable_video_test(prefs); |
| |
358 g_signal_connect(test, "destroy", |
| |
359 G_CALLBACK(video_test_destroy_cb), prefs); |
| |
360 } else { |
| |
361 g_object_disconnect(test, "any-signal::destroy", |
| |
362 G_CALLBACK(video_test_destroy_cb), prefs, |
| |
363 NULL); |
| |
364 video_test_destroy_cb(NULL, prefs); |
| |
365 } |
| |
366 } |
| |
367 |
| |
368 static void |
| |
369 vv_device_changed_cb(const gchar *name, PurplePrefType type, |
| |
370 gconstpointer value, gpointer data) |
| |
371 { |
| |
372 PidginVVPrefs *prefs = PIDGIN_VV_PREFS(data); |
| |
373 |
| |
374 PurpleMediaManager *manager; |
| |
375 PurpleMediaElementInfo *info; |
| |
376 |
| |
377 manager = purple_media_manager_get(); |
| |
378 info = purple_media_manager_get_element_info(manager, value); |
| |
379 purple_media_manager_set_active_element(manager, info); |
| |
380 |
| |
381 /* Refresh test viewers */ |
| |
382 if (strstr(name, "audio") && prefs->voice.pipeline) { |
| |
383 voice_test_destroy_cb(NULL, prefs); |
| |
384 enable_voice_test(prefs); |
| |
385 } else if (strstr(name, "video") && prefs->video.pipeline) { |
| |
386 video_test_destroy_cb(NULL, prefs); |
| |
387 enable_video_test(prefs); |
| |
388 } |
| |
389 } |
| |
390 |
| |
391 static const char * |
| |
392 purple_media_type_to_preference_key(PurpleMediaElementType type) |
| |
393 { |
| |
394 if (type & PURPLE_MEDIA_ELEMENT_AUDIO) { |
| |
395 if (type & PURPLE_MEDIA_ELEMENT_SRC) { |
| |
396 return PIDGIN_PREFS_ROOT "/vvconfig/audio/src/device"; |
| |
397 } else if (type & PURPLE_MEDIA_ELEMENT_SINK) { |
| |
398 return PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/device"; |
| |
399 } |
| |
400 } else if (type & PURPLE_MEDIA_ELEMENT_VIDEO) { |
| |
401 if (type & PURPLE_MEDIA_ELEMENT_SRC) { |
| |
402 return PIDGIN_PREFS_ROOT "/vvconfig/video/src/device"; |
| |
403 } else if (type & PURPLE_MEDIA_ELEMENT_SINK) { |
| |
404 return PIDGIN_PREFS_ROOT "/vvconfig/video/sink/device"; |
| |
405 } |
| |
406 } |
| |
407 |
| |
408 return NULL; |
| |
409 } |
| |
410 |
| |
411 static void |
| |
412 bind_vv_dropdown(PidginPrefCombo *combo, PurpleMediaElementType element_type) |
| |
413 { |
| |
414 const gchar *preference_key; |
| |
415 GtkTreeModel *model; |
| |
416 |
| |
417 preference_key = purple_media_type_to_preference_key(element_type); |
| |
418 model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo->combo)); |
| |
419 populate_vv_device_menuitems(element_type, GTK_LIST_STORE(model)); |
| |
420 |
| |
421 combo->type = PURPLE_PREF_STRING; |
| |
422 combo->key = preference_key; |
| |
423 pidgin_prefs_bind_dropdown(combo); |
| |
424 } |
| |
425 |
| |
426 static void |
| |
427 bind_vv_frame(PidginVVPrefs *prefs, PidginPrefCombo *combo, |
| |
428 PurpleMediaElementType type) |
| |
429 { |
| |
430 bind_vv_dropdown(combo, type); |
| |
431 |
| |
432 purple_prefs_connect_callback(combo->combo, |
| |
433 purple_media_type_to_preference_key(type), |
| |
434 vv_device_changed_cb, prefs); |
| |
435 g_signal_connect_swapped(combo->combo, "destroy", |
| |
436 G_CALLBACK(purple_prefs_disconnect_by_handle), |
| |
437 combo->combo); |
| |
438 |
| |
439 g_object_set_data(G_OBJECT(combo->combo), "vv_media_type", |
| |
440 (gpointer)type); |
| |
441 g_object_set_data(G_OBJECT(combo->combo), "vv_combo", combo); |
| |
442 } |
| |
443 |
| |
444 static void |
| |
445 device_list_changed_cb(PurpleMediaManager *manager, GtkWidget *widget) |
| |
446 { |
| |
447 PidginPrefCombo *combo; |
| |
448 PurpleMediaElementType media_type; |
| |
449 const gchar *preference_key; |
| |
450 guint signal_id; |
| |
451 GtkTreeModel *model; |
| |
452 |
| |
453 combo = g_object_get_data(G_OBJECT(widget), "vv_combo"); |
| |
454 media_type = (PurpleMediaElementType)GPOINTER_TO_INT(g_object_get_data( |
| |
455 G_OBJECT(widget), |
| |
456 "vv_media_type")); |
| |
457 preference_key = purple_media_type_to_preference_key(media_type); |
| |
458 |
| |
459 /* Block signals so pref doesn't get re-saved while changing UI. */ |
| |
460 signal_id = g_signal_lookup("changed", GTK_TYPE_COMBO_BOX); |
| |
461 g_signal_handlers_block_matched(combo->combo, G_SIGNAL_MATCH_ID, signal_id, |
| |
462 0, NULL, NULL, NULL); |
| |
463 |
| |
464 model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo->combo)); |
| |
465 populate_vv_device_menuitems(media_type, GTK_LIST_STORE(model)); |
| |
466 gtk_combo_box_set_active_id(GTK_COMBO_BOX(combo->combo), |
| |
467 purple_prefs_get_string(preference_key)); |
| |
468 |
| |
469 g_signal_handlers_unblock_matched(combo->combo, G_SIGNAL_MATCH_ID, |
| |
470 signal_id, 0, NULL, NULL, NULL); |
| |
471 } |
| |
472 |
| |
473 /****************************************************************************** |
| |
474 * GObject Implementation |
| |
475 *****************************************************************************/ |
| |
476 static void |
| |
477 pidgin_vv_prefs_class_init(PidginVVPrefsClass *klass) |
| |
478 { |
| |
479 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); |
| |
480 |
| |
481 gtk_widget_class_set_template_from_resource( |
| |
482 widget_class, |
| |
483 "/im/pidgin/Pidgin3/Prefs/vv.ui" |
| |
484 ); |
| |
485 |
| |
486 gtk_widget_class_bind_template_child(widget_class, PidginVVPrefs, |
| |
487 voice.input.combo); |
| |
488 gtk_widget_class_bind_template_child(widget_class, PidginVVPrefs, |
| |
489 voice.output.combo); |
| |
490 gtk_widget_class_bind_template_child(widget_class, PidginVVPrefs, |
| |
491 voice.volume); |
| |
492 gtk_widget_class_bind_template_child(widget_class, PidginVVPrefs, |
| |
493 voice.threshold_label); |
| |
494 gtk_widget_class_bind_template_child(widget_class, PidginVVPrefs, |
| |
495 voice.threshold); |
| |
496 gtk_widget_class_bind_template_child(widget_class, PidginVVPrefs, |
| |
497 voice.level); |
| |
498 gtk_widget_class_bind_template_child(widget_class, PidginVVPrefs, |
| |
499 voice.test); |
| |
500 gtk_widget_class_bind_template_callback(widget_class, volume_changed_cb); |
| |
501 gtk_widget_class_bind_template_callback(widget_class, |
| |
502 threshold_value_changed_cb); |
| |
503 gtk_widget_class_bind_template_callback(widget_class, |
| |
504 toggle_voice_test_cb); |
| |
505 |
| |
506 gtk_widget_class_bind_template_child(widget_class, PidginVVPrefs, |
| |
507 video.input.combo); |
| |
508 gtk_widget_class_bind_template_child(widget_class, PidginVVPrefs, |
| |
509 video.output.combo); |
| |
510 gtk_widget_class_bind_template_child(widget_class, PidginVVPrefs, |
| |
511 video.frame); |
| |
512 gtk_widget_class_bind_template_child(widget_class, PidginVVPrefs, |
| |
513 video.test); |
| |
514 gtk_widget_class_bind_template_callback(widget_class, |
| |
515 toggle_video_test_cb); |
| |
516 } |
| |
517 |
| |
518 static void |
| |
519 pidgin_vv_prefs_init(PidginVVPrefs *prefs) |
| |
520 { |
| |
521 PurpleMediaManager *manager = NULL; |
| |
522 |
| |
523 gtk_widget_init_template(GTK_WIDGET(prefs)); |
| |
524 |
| |
525 manager = purple_media_manager_get(); |
| |
526 |
| |
527 bind_vv_frame(prefs, &prefs->voice.input, |
| |
528 PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SRC); |
| |
529 g_signal_connect_object(manager, "elements-changed::audiosrc", |
| |
530 G_CALLBACK(device_list_changed_cb), |
| |
531 prefs->voice.input.combo, 0); |
| |
532 |
| |
533 bind_vv_frame(prefs, &prefs->voice.output, |
| |
534 PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SINK); |
| |
535 g_signal_connect_object(manager, "elements-changed::audiosink", |
| |
536 G_CALLBACK(device_list_changed_cb), |
| |
537 prefs->voice.output.combo, 0); |
| |
538 |
| |
539 bind_voice_test(prefs); |
| |
540 |
| |
541 bind_vv_frame(prefs, &prefs->video.input, |
| |
542 PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SRC); |
| |
543 g_signal_connect_object(manager, "elements-changed::videosrc", |
| |
544 G_CALLBACK(device_list_changed_cb), |
| |
545 prefs->video.input.combo, 0); |
| |
546 |
| |
547 bind_vv_frame(prefs, &prefs->video.output, |
| |
548 PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SINK); |
| |
549 g_signal_connect_object(manager, "elements-changed::videosink", |
| |
550 G_CALLBACK(device_list_changed_cb), |
| |
551 prefs->video.output.combo, 0); |
| |
552 } |
| |
553 |
| |
554 /****************************************************************************** |
| |
555 * API |
| |
556 *****************************************************************************/ |
| |
557 GtkWidget * |
| |
558 pidgin_vv_prefs_new(void) { |
| |
559 return GTK_WIDGET(g_object_new(PIDGIN_TYPE_VV_PREFS, NULL)); |
| |
560 } |
| |
561 |
| |
562 void |
| |
563 pidgin_vv_prefs_disable_test_pipelines(PidginVVPrefs *prefs) { |
| |
564 g_return_if_fail(PIDGIN_IS_VV_PREFS(prefs)); |
| |
565 |
| |
566 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(prefs->voice.test), FALSE); |
| |
567 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(prefs->video.test), FALSE); |
| |
568 } |