videoconf.c 29.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*
 *  Copyright (C) 2004, 2005, 2006, 2009, 2008, 2009, 2010, 2011 Savoir-Faire Linux Inc.
 *  Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  Additional permission under GNU GPL version 3 section 7:
 *
 *  If you modify this program, or any covered work, by linking or
 *  combining it with the OpenSSL project's OpenSSL library (or a
 *  modified version of that library), containing parts covered by the
 *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
 *  grants you additional permission to convey the resulting work.
 *  Corresponding Source for a non-source form of such a combination
 *  shall include the source code for the parts of OpenSSL used as well
 *  as that of the covered work.
 */

Tristan Matthews's avatar
Tristan Matthews committed
31
#include <glib/gi18n.h>
32
#include <string.h>
33
#include "videoconf.h"
34
#include "logger.h"
35
#include "utils.h"
36
#include "unused.h"
37
#include "eel-gconf-extensions.h"
38
#include "dbus.h"
39
#include "codeclist.h"
40

41
static GtkWidget *v4l2Device;
42
static GtkWidget *v4l2Channel;
43
44
45
46
static GtkWidget *v4l2Size;
static GtkWidget *v4l2Rate;

static GtkListStore *v4l2DeviceList;
47
static GtkListStore *v4l2ChannelList;
48
49
50
static GtkListStore *v4l2SizeList;
static GtkListStore *v4l2RateList;

51
52
53
static GtkWidget *v4l2_hbox;
static GtkWidget *v4l2_nodev;

54
static GtkWidget *preview_button;
55

56
static GtkWidget *codecTreeView; // View used instead of store to get access to selection
57
58
static GtkWidget *codecMoveUpButton;
static GtkWidget *codecMoveDownButton;
59
60
61
62
63
64
65
66
67
68
69
70
71

// Codec properties ID
enum {
    COLUMN_CODEC_ACTIVE,
    COLUMN_CODEC_NAME,
    COLUMN_CODEC_BITRATE,
    CODEC_COLUMN_COUNT
};

/**
 * Toggle move buttons on if a codec is selected, off elsewise
 */
static void
72
select_codec(GtkTreeSelection *selection, GtkTreeModel *model)
73
74
{
    GtkTreeIter iter;
75
76
77
    if (!gtk_tree_selection_get_selected(selection, &model, &iter)) {
        gtk_widget_set_sensitive(GTK_WIDGET(codecMoveUpButton), FALSE);
        gtk_widget_set_sensitive(GTK_WIDGET(codecMoveDownButton), FALSE);
78
    } else {
79
80
        gtk_widget_set_sensitive(GTK_WIDGET(codecMoveUpButton), TRUE);
        gtk_widget_set_sensitive(GTK_WIDGET(codecMoveDownButton), TRUE);
81
82
    }
}
83

84
void active_is_always_recording()
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
{
    gboolean enabled = FALSE;

    enabled = dbus_get_is_always_recording();

    if(enabled) {
        enabled = FALSE;
    }
    else {
        enabled = TRUE;
    }

    dbus_set_is_always_recording(enabled);
}

100
101
102
static const gchar *const PREVIEW_START_STR = "_Start";
static const gchar *const PREVIEW_STOP_STR = "_Stop";

103
static void
104
preview_button_toggled(GtkButton *button, gpointer data UNUSED)
105
{
106
    preview_button = GTK_WIDGET(button);
107
    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
108
        dbus_start_video_preview();
109
    else
110
        dbus_stop_video_preview();
111

112
    update_preview_button_label();
113
114
}

115
116
117
118
119
120
121
122
123
void
set_preview_button_sensitivity(gboolean sensitive)
{
    if (!preview_button || !GTK_IS_WIDGET(preview_button))
        return;
    DEBUG("%ssetting preview button", sensitive ? "" : "Un");
    gtk_widget_set_sensitive(GTK_WIDGET(preview_button), sensitive);
}

124
void
125
update_preview_button_label()
126
{
127
128
129
    if (!preview_button || !GTK_IS_WIDGET(preview_button))
        return;

130
    GtkToggleButton *button = GTK_TOGGLE_BUTTON(preview_button);
131
132
    if (dbus_has_video_preview_started()) {
        /* We call g_object_set to avoid triggering the "toggled" signal */
133
        gtk_button_set_label(GTK_BUTTON(button), _(PREVIEW_STOP_STR));
134
135
        g_object_set(button, "active", TRUE, NULL);
    } else {
136
        gtk_button_set_label(GTK_BUTTON(button), _(PREVIEW_START_STR));
137
138
        g_object_set(button, "active", FALSE, NULL);
    }
139
140
}

141
142
143
/**
 * Fills the tree list with supported codecs
 */
144
145
static void
preferences_dialog_fill_codec_list(account_t *acc)
146
{
147
148
149
150
    if (!acc) {
        ERROR("Account is NULL");
        return;
    }
151
    // Get model of view and clear it
152
    GtkListStore *codecStore = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(codecTreeView)));
153
    gtk_list_store_clear(codecStore);
154

155
    GPtrArray *vcodecs = dbus_get_video_codecs(acc->accountID);
156
157
    if (!vcodecs)
        return;
158
159

    // Add the codecs in the list
160
161
    for (size_t i = 0; i < vcodecs->len; ++i) {
        GHashTable *c = g_ptr_array_index(vcodecs, i);
162
163

        if (c) {
164
            GtkTreeIter iter;
165
            gtk_list_store_append(codecStore, &iter);
166
167
168
            const gchar *bitrate = g_hash_table_lookup(c, "bitrate");
            const gboolean is_active = !g_strcmp0(g_hash_table_lookup(c, "enabled"), "true");
            const gchar *name = g_hash_table_lookup(c, "name");
169

170
            gtk_list_store_set(codecStore, &iter, COLUMN_CODEC_ACTIVE,
171
                               is_active, COLUMN_CODEC_NAME, name,
172
                               COLUMN_CODEC_BITRATE, bitrate, -1);
173
        }
174
    }
175
    g_ptr_array_free(vcodecs, TRUE);
176
177
178
179
180
181
}

/**
 * Toggle active value of codec on click and update changes to the deamon
 * and in configuration files
 */
182

183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
static gboolean
video_codec_has_name(GHashTable *codec, const gchar *name)
{
    return g_strcmp0(g_hash_table_lookup(codec, "name"), name) == 0;
}

static void
video_codec_set_active(GHashTable *codec, gboolean active)
{
    g_hash_table_replace(codec, g_strdup("enabled"), active ? g_strdup("true") : g_strdup("false"));
}

static void
video_codec_set_bitrate(GHashTable *codec, const gchar *bitrate)
{
    g_hash_table_replace(codec, g_strdup("bitrate"), g_strdup(bitrate));
}

static GHashTable *
video_codec_list_get_by_name(GPtrArray *vcodecs, const gchar *name)
{
    for (guint i = 0; i < vcodecs->len; ++i) {
        GHashTable *codec = g_ptr_array_index(vcodecs, i);
        if (video_codec_has_name(codec, name))
            return codec;
    }
    return NULL;
}

212
static void
213
214
codec_active_toggled(GtkCellRendererToggle *renderer UNUSED, gchar *path,
                     gpointer data)
215
{
216
    account_t *acc = (account_t*) data;
217

218
219
220
221
    if (!acc) {
        ERROR("No account selected");
        return;
    }
222

223
224
225
226
227
228
229
    // Get path of clicked codec active toggle box
    GtkTreePath *tree_path = gtk_tree_path_new_from_string(path);
    GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW (codecTreeView));
    GtkTreeIter iter;
    gtk_tree_model_get_iter(model, &iter, tree_path);
    gtk_tree_path_free(tree_path);

230
    // Get active value and name at iteration
231
232
233
234
    gboolean active = FALSE;
    gchar *name = NULL;
    gtk_tree_model_get(model, &iter, COLUMN_CODEC_ACTIVE, &active,
                       COLUMN_CODEC_NAME, &name, -1);
235

236
    DEBUG("%s", name);
237
    GPtrArray *vcodecs = dbus_get_video_codecs(acc->accountID);
238
239
240
    if (!vcodecs)
        return;

241
    DEBUG("video codecs length %i", vcodecs->len);
242
243
244
245
246

    // Toggle active value
    active = !active;

    // Store value
247
248
    gtk_list_store_set(GTK_LIST_STORE(model), &iter, COLUMN_CODEC_ACTIVE,
                       active, -1);
249

250
251
252
253
254
    GHashTable *codec = video_codec_list_get_by_name(vcodecs, name);
    if (codec) {
        video_codec_set_active(codec, active);
        dbus_set_video_codecs(acc->accountID, vcodecs);
    }
255
256
}

257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277

static GPtrArray *
swap_pointers(GPtrArray *array, guint old_pos, guint new_pos)
{
    GHashTable *src = g_ptr_array_index(array, old_pos);
    GHashTable *dst = g_ptr_array_index(array, new_pos);

    GPtrArray *new_array = g_ptr_array_new();
    for (guint i = 0; i < array->len; ++i) {
        if (i == new_pos)
            g_ptr_array_add(new_array, src);
        else if (i == old_pos)
            g_ptr_array_add(new_array, dst);
        else
            g_ptr_array_add(new_array, g_ptr_array_index(array, i));
    }

    g_ptr_array_free(array, TRUE);
    return new_array;
}

278
279
280
281
/**
 * Move codec in list depending on direction and selected codec and
 * update changes in the daemon list and the configuration files
 */
282
283
static void
codec_move(gboolean move_up, gpointer data)
284
285
{
    // Get view, model and selection of codec store
286
287
    GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(codecTreeView));
    GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(codecTreeView));
288
289

    // Find selected iteration and create a copy
290
    GtkTreeIter iter;
Tristan Matthews's avatar
Tristan Matthews committed
291
    gtk_tree_selection_get_selected(GTK_TREE_SELECTION(selection), &model, &iter);
292
    GtkTreeIter *iter_cpy = gtk_tree_iter_copy(&iter);
293
294

    // Find path of iteration
295
    gchar *path = gtk_tree_model_get_string_from_iter(GTK_TREE_MODEL(model), &iter);
296
297
    GtkTreePath *tree_path = gtk_tree_path_new_from_string(path);
    gint *indices = gtk_tree_path_get_indices(tree_path);
298
    const gint pos = indices[0];
299
300

    // Depending on button direction get new path
301
302
    if (move_up)
        gtk_tree_path_prev(tree_path);
303
    else
304
        gtk_tree_path_next(tree_path);
305

306
    gtk_tree_model_get_iter(model, &iter, tree_path);
307
308

    // Swap iterations if valid
309
310
311
312
313
314
315
316
317
    GtkListStore *list_store = GTK_LIST_STORE(model);
    if (gtk_list_store_iter_is_valid(list_store, &iter)) {
        gtk_list_store_swap(list_store, &iter, iter_cpy);

        const gint dest_pos = move_up ? pos - 1 : pos + 1;
        if (dest_pos >= 0 &&
            dest_pos < gtk_tree_model_iter_n_children(model, NULL)) {
            account_t *acc = (account_t *) data;
            GPtrArray *vcodecs = dbus_get_video_codecs(acc->accountID);
318
319
320
321
322
323
324
            if (vcodecs) {
                // Perpetuate changes in daemon
                vcodecs = swap_pointers(vcodecs, pos, dest_pos);
                // FIXME: only do this AFTER apply is clicked, not every time we move codecs!
                dbus_set_video_codecs(acc->accountID, vcodecs);
                g_ptr_array_free(vcodecs, TRUE);
            }
325
326
        }
    }
327
328

    // Scroll to new position
329
    gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(codecTreeView), tree_path, NULL, FALSE, 0, 0);
330
331

    // Free resources
332
333
    gtk_tree_path_free(tree_path);
    gtk_tree_iter_free(iter_cpy);
Tristan Matthews's avatar
Tristan Matthews committed
334
    g_free(path);
335
336
337
338
339
}

/**
 * Called from move up codec button signal
 */
340
341
static void
codec_move_up(GtkButton *button UNUSED, gpointer data)
342
{
343
    codec_move(TRUE, data);
344
345
346
347
348
}

/**
 * Called from move down codec button signal
 */
349
350
static void
codec_move_down(GtkButton *button UNUSED, gpointer data)
351
{
352
    codec_move(FALSE, data);
353
354
}

355
356
357
358
359
360
361
362
363
364
static void
bitrate_edited_cb(GtkCellRenderer *renderer UNUSED, gchar *path, gchar *new_text, gpointer data)
{
    // Retrieve userdata
    account_t *acc = (account_t*) data;

    if (!acc) {
        ERROR("No account selected");
        return;
    }
365
    DEBUG("updating bitrate for %s", acc->accountID);
366
367
368
    // Get active value and name at iteration
    const gint base = 10;
    gchar *endptr;
369
    const long long val = strtoll(new_text, &endptr, base);
370
371
372
    /* Ignore if it's not a number */
    if (*endptr != '\0') {
        WARN("Ignoring characters %s\n", val, endptr);
373
374
    } else if (val < 0) {
        WARN("Ignoring negative bitrate value");
375
376
377
378
379
380
381
382
383
384
    } else {
        // Get path of edited codec
        GtkTreePath *tree_path = gtk_tree_path_new_from_string(path);
        GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(codecTreeView));
        GtkTreeIter iter;
        gtk_tree_model_get_iter(model, &iter, tree_path);
        gtk_tree_path_free(tree_path);
        gchar *name = NULL;
        gtk_tree_model_get(model, &iter, COLUMN_CODEC_NAME, &name, -1);

385
386
387
388
        GPtrArray *vcodecs = dbus_get_video_codecs(acc->accountID);
        if (!vcodecs)
            return;

389
390
        gchar *bitrate = g_strdup_printf("%llu", val);
        gtk_list_store_set(GTK_LIST_STORE(model), &iter, COLUMN_CODEC_BITRATE, bitrate, -1);
391

392
393
        GHashTable *codec = video_codec_list_get_by_name(vcodecs, name);
        if (codec) {
394
            DEBUG("Setting new bitrate %s for %s", bitrate, name);
395
396
            video_codec_set_bitrate(codec, bitrate);
            dbus_set_video_codecs(acc->accountID, vcodecs);
397
398
        } else {
            ERROR("Could not find codec %s", name);
399
        }
400
        g_free(bitrate);
401
        g_ptr_array_free(vcodecs, TRUE);
402
403
404
405
406
407
    }
}


GtkWidget *
videocodecs_box(account_t *acc)
408
{
409
410
    GtkWidget *ret = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
    gtk_container_set_border_width(GTK_CONTAINER(ret), 10);
411

412
413
414
    GtkWidget *scrolledWindow = gtk_scrolled_window_new(NULL, NULL);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledWindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledWindow), GTK_SHADOW_IN);
415

416
417
418
419
420
421
422
    gtk_box_pack_start(GTK_BOX(ret), scrolledWindow, TRUE, TRUE, 0);
    GtkListStore *codecStore = gtk_list_store_new(CODEC_COLUMN_COUNT,
                                                  G_TYPE_BOOLEAN, // Active
                                                  G_TYPE_STRING,  // Name
                                                  G_TYPE_STRING,  // Bit rate
                                                  G_TYPE_STRING   // Bandwith
                                                 );
423
424

    // Create codec tree view with list store
425
    codecTreeView = gtk_tree_view_new_with_model(GTK_TREE_MODEL(codecStore));
426

Tristan Matthews's avatar
Tristan Matthews committed
427
428
429
    /* The list store model will be destroyed automatically with the view */
    g_object_unref(G_OBJECT(codecStore));

430
    // Get tree selection manager
431
432
433
    GtkTreeSelection *treeSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(codecTreeView));
    g_signal_connect(G_OBJECT(treeSelection), "changed",
            G_CALLBACK(select_codec),
434
            codecStore);
435
436

    // Active column
437
438
439
    GtkCellRenderer *renderer = gtk_cell_renderer_toggle_new();
    GtkTreeViewColumn *treeViewColumn = gtk_tree_view_column_new_with_attributes("", renderer, "active", COLUMN_CODEC_ACTIVE, NULL);
    gtk_tree_view_append_column(GTK_TREE_VIEW(codecTreeView), treeViewColumn);
440
441

    // Toggle codec active property on clicked
442
    g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(codec_active_toggled), (gpointer) acc);
443
444
445

    // Name column
    renderer = gtk_cell_renderer_text_new();
446
447
    treeViewColumn = gtk_tree_view_column_new_with_attributes(_("Name"), renderer, "markup", COLUMN_CODEC_NAME, NULL);
    gtk_tree_view_append_column(GTK_TREE_VIEW(codecTreeView), treeViewColumn);
448
449
450

    // Bitrate column
    renderer = gtk_cell_renderer_text_new();
451
452
453
    g_object_set(renderer, "editable", TRUE, NULL);
    g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(bitrate_edited_cb), acc);
    treeViewColumn = gtk_tree_view_column_new_with_attributes(_("Bitrate (kbps)"), renderer, "text", COLUMN_CODEC_BITRATE, NULL);
454
    gtk_tree_view_append_column(GTK_TREE_VIEW(codecTreeView), treeViewColumn);
455

456
    gtk_container_add(GTK_CONTAINER(scrolledWindow), codecTreeView);
457
458

    // Create button box
459
460
461
    GtkWidget *buttonBox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
    gtk_container_set_border_width(GTK_CONTAINER(buttonBox), 10);
    gtk_box_pack_start(GTK_BOX(ret), buttonBox, FALSE, FALSE, 0);
462

463
464
465
    codecMoveUpButton = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
    gtk_widget_set_sensitive(GTK_WIDGET(codecMoveUpButton), FALSE);
    gtk_box_pack_start(GTK_BOX(buttonBox), codecMoveUpButton, FALSE, FALSE, 0);
466
    g_signal_connect(G_OBJECT(codecMoveUpButton), "clicked", G_CALLBACK(codec_move_up), acc);
467

468
469
470
    codecMoveDownButton = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
    gtk_widget_set_sensitive(GTK_WIDGET(codecMoveDownButton), FALSE);
    gtk_box_pack_start(GTK_BOX(buttonBox), codecMoveDownButton, FALSE, FALSE, 0);
471
    g_signal_connect(G_OBJECT(codecMoveDownButton), "clicked", G_CALLBACK(codec_move_down), acc);
472

473
    preferences_dialog_fill_codec_list(acc);
474
475
476

    return ret;
}
477

478
479
/* Gets a newly allocated string with the active text, the caller must
 * free this string */
480
481
static gchar *
get_active_text(GtkComboBox *box)
482
{
483
    gchar *text = NULL;
484
    int comboBoxIndex = gtk_combo_box_get_active(box);
485
486
    if (comboBoxIndex >= 0) {
        GtkTreeIter iter;
487
488
        gtk_combo_box_get_active_iter(box, &iter);
        gtk_tree_model_get(gtk_combo_box_get_model(box), &iter, 0, &text, -1);
489
490
491
492
493
    }
    return text;
}

/* Return 0 if string was found in the combo box, != 0 if the string was not found */
494
495
static int
set_combo_index_from_str(GtkComboBox *box, const gchar *str, size_t max)
496
{
497
    g_assert(str);
498

499
    GtkTreeModel *model = gtk_combo_box_get_model(box);
500
501
502
503
    GtkTreeIter iter;
    unsigned idx = 0;
    gtk_tree_model_get_iter_first(model, &iter);
    do {
504
        gchar *boxstr = 0;
505
        gtk_tree_model_get(model, &iter, 0, &boxstr, -1);
506
        if (boxstr && !g_strcmp0(boxstr, str))
507
508
509
510
511
512
513
514
515
516
517
            break;
    } while (idx++ < max && gtk_tree_model_iter_next(model, &iter));

    if (idx >= max)
        return 1;

    gtk_combo_box_set_active(box, idx);
    return 0;
}


518
519
520
521
522
523
524
/**
 * Fill video input device rate store
 */
static void
preferences_dialog_fill_video_input_device_rate_list()
{
    GtkTreeIter iter;
525
    gchar** list = NULL;
526

527
528
    if (v4l2RateList)
        gtk_list_store_clear(v4l2RateList);
529

530
531
532
533
    gchar *dev  = get_active_text(GTK_COMBO_BOX(v4l2Device));
    gchar *chan = get_active_text(GTK_COMBO_BOX(v4l2Channel));
    gchar *size = get_active_text(GTK_COMBO_BOX(v4l2Size));

534
    // Call dbus to retreive list
535
    if (dev && chan && size) {
536
537
538
539
        list = dbus_get_video_device_rate_list(dev, chan, size);
        g_free(size);
        g_free(chan);
        g_free(dev);
540
    }
541
542

    // For each device name included in list
543
    if (list && *list) {
544
        gint c = 0;
545
        for (gchar **tmp = list; *tmp; c++, tmp++) {
546
            gtk_list_store_append(v4l2RateList, &iter);
547
            gtk_list_store_set(v4l2RateList, &iter, 0, *tmp, 1, c, -1);
548
        }
549
        g_strfreev(list);
550

551
        gchar *rate = dbus_get_active_video_device_rate();
552
553
554
        if (!rate || !*rate || set_combo_index_from_str(GTK_COMBO_BOX(v4l2Rate), rate, c)) {
            // if setting is invalid, choose first entry
            gtk_combo_box_set_active(GTK_COMBO_BOX(v4l2Rate), 0);
555
            dbus_set_active_video_device_rate(get_active_text(GTK_COMBO_BOX(v4l2Rate)));
556
        }
557
558
        g_free(rate);
    } else
559
        ERROR("No video rate list found for device");
560
561
}

562

563
/**
Rafaël Carré's avatar
Rafaël Carré committed
564
 * Set the video input device rate on the server
565
566
 */
static void
567
select_video_input_device_rate_cb(GtkComboBox* comboBox, gpointer data UNUSED)
568
{
569
    gchar *str = get_active_text(comboBox);
570
    if (str)
571
        dbus_set_active_video_device_rate(str);
572
    g_free(str);
573
574
575
576
577
578
579
580
}

/**
 * Fill video input device size store
 */
static void
preferences_dialog_fill_video_input_device_size_list()
{
581
582
    if (v4l2SizeList)
        gtk_list_store_clear(v4l2SizeList);
583

584
585
586
    gchar *dev  = get_active_text(GTK_COMBO_BOX(v4l2Device));
    gchar *chan = get_active_text(GTK_COMBO_BOX(v4l2Channel));

587
    gchar** list = NULL;
588
    // Call dbus to retrieve list
589
    if (dev && chan) {
590
        list = dbus_get_video_device_size_list(dev, chan);
591
592
593
        g_free(chan);
        g_free(dev);
    }
594

595
    if (list && *list) {
596
        // For each device name included in list
597
        gint c = 0;
598
        for (gchar **tmp = list; *tmp; c++, tmp++) {
599
            GtkTreeIter iter;
600
            gtk_list_store_append(v4l2SizeList, &iter);
601
            gtk_list_store_set(v4l2SizeList, &iter, 0, *tmp, 1, c, -1);
602
        }
603
        g_strfreev(list);
604
        gchar *size = dbus_get_active_video_device_size();
605
606
607
        if (!size || !*size || set_combo_index_from_str(GTK_COMBO_BOX(v4l2Size), size, c)) {
            // if setting is invalid, choose first entry
            gtk_combo_box_set_active(GTK_COMBO_BOX(v4l2Size), 0);
608
            dbus_set_active_video_device_size(get_active_text(GTK_COMBO_BOX(v4l2Size)));
609
        }
610
611
        g_free(size);
    } else
612
        ERROR("No device size list found");
613
614
615
}

/**
Rafaël Carré's avatar
Rafaël Carré committed
616
 * Set the video input device size on the server
617
618
 */
static void
619
select_video_input_device_size_cb(GtkComboBox* comboBox, gpointer data UNUSED)
620
{
621
    gchar *str = get_active_text(comboBox);
622
    if (str) {
623
        dbus_set_active_video_device_size(str);
624
        preferences_dialog_fill_video_input_device_rate_list();
625
        g_free(str);
626
627
628
629
630
631
632
    }
}

/**
 * Fill video input device input store
 */
static void
633
preferences_dialog_fill_video_input_device_channel_list()
634
{
635
636
    if (v4l2ChannelList)
        gtk_list_store_clear(v4l2ChannelList);
637
638

    gchar *dev = get_active_text(GTK_COMBO_BOX(v4l2Device));
639

640
641
642
    gchar **list = NULL;
    // Call dbus to retrieve list
    if (dev) {
643
        list = dbus_get_video_device_channel_list(dev);
644
645
        g_free(dev);
    }
646

647
    if (list && *list) {
648
        // For each device name included in list
649
        int c = 0;
650
651
        for (gchar **tmp = list; *tmp; c++, tmp++) {
            GtkTreeIter iter;
652
            gtk_list_store_append(v4l2ChannelList, &iter);
653
            gtk_list_store_set(v4l2ChannelList, &iter, 0, *tmp, 1, c, -1);
654
        }
655
        g_strfreev(list);
656
        gchar *channel = dbus_get_active_video_device_channel();
657
658
659
        if (!channel || !*channel || set_combo_index_from_str(GTK_COMBO_BOX(v4l2Channel), channel, c)) {
            // if setting is invalid, choose first entry
            gtk_combo_box_set_active(GTK_COMBO_BOX(v4l2Channel), 0);
660
            dbus_set_active_video_device_channel(get_active_text(GTK_COMBO_BOX(v4l2Channel)));
661
        }
662
663
        g_free(channel);
    } else
664
        ERROR("No channel list found");
665
666
667
}

/**
Rafaël Carré's avatar
Rafaël Carré committed
668
 * Set the video input device input on the server
669
670
 */
static void
671
select_video_input_device_channel_cb(GtkComboBox* comboBox, gpointer data UNUSED)
672
{
673
    gchar *str = get_active_text(comboBox);
674
    if (str) {
675
        dbus_set_active_video_device_channel(str);
676
        preferences_dialog_fill_video_input_device_size_list();
677
        g_free(str);
678
679
680
681
682
683
    }
}

/**
 * Fill video input device store
 */
684
static gboolean
685
686
preferences_dialog_fill_video_input_device_list()
{
687
    gtk_list_store_clear(v4l2DeviceList);
688

689
    // Call dbus to retrieve list
690
    gchar **list = dbus_get_video_device_list();
691
692
693
694
    if (!list || !*list) {
        ERROR("No device list found");
        return FALSE;
    } else {
695
        // For each device name included in list
696
        gint c = 0;
697
        for (gchar **tmp = list; *tmp; c++, tmp++) {
698
            GtkTreeIter iter;
699
            gtk_list_store_append(v4l2DeviceList, &iter);
700
            gtk_list_store_set(v4l2DeviceList, &iter, 0, *tmp, 1, c, -1);
701
        }
702
        g_strfreev(list);
703
        gchar *dev = dbus_get_active_video_device();
704
705
706
        if (!dev || !*dev || set_combo_index_from_str(GTK_COMBO_BOX(v4l2Device), dev, c)) {
            // if setting is invalid, choose first entry
            gtk_combo_box_set_active(GTK_COMBO_BOX(v4l2Device), 0);
707
            dbus_set_active_video_device(get_active_text(GTK_COMBO_BOX(v4l2Device)));
708
        }
709
710
        g_free(dev);
        return TRUE;
711
    }
712
713
714
}

/**
Rafaël Carré's avatar
Rafaël Carré committed
715
 * Set the video input device on the server
716
717
 */
static void
718
select_video_input_device_cb(GtkComboBox* comboBox, gpointer data UNUSED)
719
{
720
    gchar *str = get_active_text(comboBox);
721
    if (str) {
722
        DEBUG("Setting video input device to %s", str);
723
        dbus_set_active_video_device(str);
724
        preferences_dialog_fill_video_input_device_channel_list();
725
        g_free(str);
726
727
728
    }
}

729
730
static void
fill_devices()
731
{
732
    if (preferences_dialog_fill_video_input_device_list()) {
733
734
        gtk_widget_show_all(v4l2_hbox);
        gtk_widget_hide(v4l2_nodev);
735
        gtk_widget_set_sensitive(preview_button, TRUE);
Tristan Matthews's avatar
Tristan Matthews committed
736
737
738
739
    } else if (GTK_IS_WIDGET(v4l2_hbox)) {
        gtk_widget_hide(v4l2_hbox);
        gtk_widget_show(v4l2_nodev);
        gtk_widget_set_sensitive(preview_button, FALSE);
740
741
742
    }
}

743
744
void
video_device_event_cb(DBusGProxy *proxy UNUSED, void * foo UNUSED)
745
746
747
748
{
    fill_devices();
}

749

750
751
static GtkWidget *
v4l2_box()
752
{
753
    GtkWidget *ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
754

755
756
    v4l2_nodev = gtk_label_new(_("No devices found"));
    v4l2_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
757

758
759
    gtk_box_pack_start(GTK_BOX(ret), v4l2_hbox , TRUE , TRUE , 0);
    gtk_box_pack_start(GTK_BOX(ret), v4l2_nodev, TRUE , TRUE , 0);
760

761
    GtkWidget *table = gtk_table_new(6, 3, FALSE);
762
763
    gtk_table_set_col_spacing(GTK_TABLE(table), 0, 40);
    gtk_box_pack_start(GTK_BOX(v4l2_hbox) , table , TRUE , TRUE , 1);
764
765

    // Set choices of input devices
766
    GtkWidget *item = gtk_label_new(_("Device"));
767
768
769
    v4l2DeviceList = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
    v4l2Device = gtk_combo_box_new_with_model(GTK_TREE_MODEL(v4l2DeviceList));
    gtk_label_set_mnemonic_widget(GTK_LABEL(item), v4l2Device);
770

771
    g_signal_connect(G_OBJECT(v4l2Device), "changed", G_CALLBACK(select_video_input_device_cb), NULL);
772
    gtk_table_attach(GTK_TABLE(table), item, 0, 1, 0, 1, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0);
773
774

    // Set rendering
775
    GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
776
777
778
    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(v4l2Device), renderer, TRUE);
    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(v4l2Device), renderer, "text", 0, NULL);
    gtk_table_attach(GTK_TABLE(table), v4l2Device, 1, 2, 0, 1, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0);
779
780

    // Set choices of input
781
782
783
784
    item = gtk_label_new(_("Channel"));
    v4l2ChannelList = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
    v4l2Channel = gtk_combo_box_new_with_model(GTK_TREE_MODEL(v4l2ChannelList));
    gtk_label_set_mnemonic_widget(GTK_LABEL(item), v4l2Channel);
785
    g_signal_connect(G_OBJECT(v4l2Channel), "changed", G_CALLBACK(select_video_input_device_channel_cb), NULL);
786
    gtk_table_attach(GTK_TABLE(table), item, 0, 1, 1, 2, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0);
787
788
789

    // Set rendering
    renderer = gtk_cell_renderer_text_new();
790
791
792
    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(v4l2Channel), renderer, TRUE);
    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(v4l2Channel), renderer, "text", 0, NULL);
    gtk_table_attach(GTK_TABLE(table), v4l2Channel, 1, 2, 1, 2, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0);
793
794

    // Set choices of sizes
795
796
797
798
    item = gtk_label_new(_("Size"));
    v4l2SizeList = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
    v4l2Size = gtk_combo_box_new_with_model(GTK_TREE_MODEL(v4l2SizeList));
    gtk_label_set_mnemonic_widget(GTK_LABEL(item), v4l2Size);
799
    g_signal_connect(G_OBJECT(v4l2Size), "changed", G_CALLBACK(select_video_input_device_size_cb), NULL);
800
    gtk_table_attach(GTK_TABLE(table), item, 0, 1, 2, 3, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0);
801
802
803

    // Set rendering
    renderer = gtk_cell_renderer_text_new();
804
805
806
    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(v4l2Size), renderer, TRUE);
    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(v4l2Size), renderer, "text", 0, NULL);
    gtk_table_attach(GTK_TABLE(table), v4l2Size, 1, 2, 2, 3, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0);
807
808

    // Set choices of rates
809
810
811
812
    item = gtk_label_new(_("Rate"));
    v4l2RateList = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
    v4l2Rate = gtk_combo_box_new_with_model(GTK_TREE_MODEL(v4l2RateList));
    gtk_label_set_mnemonic_widget(GTK_LABEL(item), v4l2Rate);
813
    g_signal_connect(G_OBJECT(v4l2Rate), "changed", G_CALLBACK(select_video_input_device_rate_cb), NULL);
814
    gtk_table_attach(GTK_TABLE(table), item, 0, 1, 3, 4, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0);
815
816
817

    // Set rendering
    renderer = gtk_cell_renderer_text_new();
818
819
820
    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(v4l2Rate), renderer, TRUE);
    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(v4l2Rate), renderer, "text", 0, NULL);
    gtk_table_attach(GTK_TABLE(table), v4l2Rate, 1, 2, 3, 4, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0);
821
822

    return ret;
823
824
}

825

826
827
GtkWidget *
create_video_configuration()
828
829
{
    // Main widget
830
    GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
831
    gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
832

833
834
835
836
    // Sub boxes
    GtkWidget *frame, *table;
    gnome_main_section_new_with_table(_("Video Manager"), &frame, &table, 1, 5);
    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
837

838
839
    gnome_main_section_new_with_table(_("Video4Linux2"), &frame, &table, 1, 4);
    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
840
841

    GtkWidget *v4l2box = v4l2_box();
842
843
    gtk_table_attach(GTK_TABLE(table), v4l2box, 0, 1, 1, 2,
                     GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 6);
844

845
    gnome_main_section_new_with_table(_("Preview"), &frame, &table, 1, 2);
846
    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
847

848
849
850
    const gboolean started = dbus_has_video_preview_started();

    preview_button = gtk_toggle_button_new_with_mnemonic(started ? _(PREVIEW_STOP_STR) : _(PREVIEW_START_STR));
851
    gtk_widget_set_size_request(preview_button, 80, 30);
852
    gtk_table_attach(GTK_TABLE(table), preview_button, 0, 1, 0, 1, 0, 0, 0, 6);
853
854
855
    gtk_widget_show(GTK_WIDGET(preview_button));
    if (started)
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(preview_button), TRUE);
856
857
    g_signal_connect(G_OBJECT(preview_button), "toggled",
                     G_CALLBACK(preview_button_toggled), NULL);
858

859
    gchar **list = dbus_get_call_list();
860
861
862
863
    gboolean active_call;
    if (list && *list) {
        active_call = TRUE;
        g_strfreev(list);
864
865
866
867
868
    }

    if (active_call)
        gtk_widget_set_sensitive(GTK_WIDGET(preview_button), FALSE);

869
    gtk_widget_show_all(vbox);
870
871
872
873
874

    // get devices list from daemon *after* showing all widgets
    // that way we can show either the list, either the "no devices found" label
    fill_devices();