1 /* cc-input-list-box.c
2 *
3 * Copyright (C) 2010 Intel, Inc
4 * Copyright (C) 2020 System76, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Sergey Udaltsov <svu@gnome.org>
20 * Ian Douglas Scott <idscott@system76.com>
21 *
22 * SPDX-License-Identifier: GPL-2.0-or-later
23 */
24
25 #define GNOME_DESKTOP_USE_UNSTABLE_API
26 #include <libgnome-desktop/gnome-xkb-info.h>
27
28 #include "list-box-helper.h"
29 #include "cc-input-list-box.h"
30 #include "cc-input-chooser.h"
31 #include "cc-input-row.h"
32 #include "cc-input-source-ibus.h"
33 #include "cc-input-source-xkb.h"
34
35 #ifdef HAVE_IBUS
36 #include <ibus.h>
37 #endif
38
39 #define GNOME_DESKTOP_INPUT_SOURCES_DIR "org.gnome.desktop.input-sources"
40 #define KEY_INPUT_SOURCES "sources"
41
42 struct _CcInputListBox {
43 GtkListBox parent_instance;
44
45 GtkListBoxRow *add_input_row;
46 GtkSizeGroup *input_size_group;
47 GtkListBoxRow *no_inputs_row;
48
49 GCancellable *cancellable;
50
51 gboolean login;
52 gboolean login_auto_apply;
53 GPermission *permission;
54 GDBusProxy *localed;
55
56 GSettings *input_settings;
57 GnomeXkbInfo *xkb_info;
58 #ifdef HAVE_IBUS
59 IBusBus *ibus;
60 GHashTable *ibus_engines;
61 #endif
62 };
63
64 G_DEFINE_TYPE (CcInputListBox, cc_input_list_box, GTK_TYPE_LIST_BOX)
65
66 typedef struct
67 {
68 CcInputListBox *panel;
69 CcInputRow *source;
70 CcInputRow *dest;
71 } RowData;
72
73 static RowData *
row_data_new(CcInputListBox * panel,CcInputRow * source,CcInputRow * dest)74 row_data_new (CcInputListBox *panel, CcInputRow *source, CcInputRow *dest)
75 {
76 RowData *data = g_malloc0 (sizeof (RowData));
77 data->panel = panel;
78 data->source = g_object_ref (source);
79 if (dest != NULL)
80 data->dest = g_object_ref (dest);
81 return data;
82 }
83
84 static void
row_data_free(RowData * data)85 row_data_free (RowData *data)
86 {
87 g_clear_object (&data->source);
88 g_clear_object (&data->dest);
89 g_free (data);
90 }
91
92 G_DEFINE_AUTOPTR_CLEANUP_FUNC (RowData, row_data_free)
93
94 static void show_input_chooser (CcInputListBox *self);
95
96 #ifdef HAVE_IBUS
97 static void
update_ibus_active_sources(CcInputListBox * self)98 update_ibus_active_sources (CcInputListBox *self)
99 {
100 g_autoptr(GList) rows = NULL;
101 GList *l;
102
103 rows = gtk_container_get_children (GTK_CONTAINER (self));
104 for (l = rows; l; l = l->next) {
105 CcInputRow *row;
106 CcInputSourceIBus *source;
107 IBusEngineDesc *engine_desc;
108
109 if (!CC_IS_INPUT_ROW (l->data))
110 continue;
111 row = CC_INPUT_ROW (l->data);
112
113 if (!CC_IS_INPUT_SOURCE_IBUS (cc_input_row_get_source (row)))
114 continue;
115 source = CC_INPUT_SOURCE_IBUS (cc_input_row_get_source (row));
116
117 engine_desc = g_hash_table_lookup (self->ibus_engines, cc_input_source_ibus_get_engine_name (source));
118 if (engine_desc != NULL)
119 cc_input_source_ibus_set_engine_desc (source, engine_desc);
120 }
121 }
122
123 static void
fetch_ibus_engines_result(GObject * object,GAsyncResult * result,CcInputListBox * self)124 fetch_ibus_engines_result (GObject *object,
125 GAsyncResult *result,
126 CcInputListBox *self)
127 {
128 g_autoptr(GList) list = NULL;
129 GList *l;
130 g_autoptr(GError) error = NULL;
131
132 list = ibus_bus_list_engines_async_finish (IBUS_BUS (object), result, &error);
133 if (!list && error) {
134 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
135 g_warning ("Couldn't finish IBus request: %s", error->message);
136 return;
137 }
138
139 /* Maps engine ids to engine description objects */
140 self->ibus_engines = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
141
142 for (l = list; l; l = l->next) {
143 IBusEngineDesc *engine = l->data;
144 const gchar *engine_id = ibus_engine_desc_get_name (engine);
145
146 if (g_str_has_prefix (engine_id, "xkb:"))
147 g_object_unref (engine);
148 else
149 g_hash_table_replace (self->ibus_engines, (gpointer)engine_id, engine);
150 }
151
152 update_ibus_active_sources (self);
153 }
154
155 static void
fetch_ibus_engines(CcInputListBox * self)156 fetch_ibus_engines (CcInputListBox *self)
157 {
158 ibus_bus_list_engines_async (self->ibus,
159 -1,
160 self->cancellable,
161 (GAsyncReadyCallback)fetch_ibus_engines_result,
162 self);
163
164 /* We've got everything we needed, don't want to be called again. */
165 g_signal_handlers_disconnect_by_func (self->ibus, fetch_ibus_engines, self);
166 }
167
168 static void
maybe_start_ibus(void)169 maybe_start_ibus (void)
170 {
171 /* IBus doesn't export API in the session bus. The only thing
172 * we have there is a well known name which we can use as a
173 * sure-fire way to activate it.
174 */
175 g_bus_unwatch_name (g_bus_watch_name (G_BUS_TYPE_SESSION,
176 IBUS_SERVICE_IBUS,
177 G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
178 NULL,
179 NULL,
180 NULL,
181 NULL));
182 }
183
184 #endif
185
186 static void
row_settings_cb(CcInputListBox * self,CcInputRow * row)187 row_settings_cb (CcInputListBox *self,
188 CcInputRow *row)
189 {
190 CcInputSourceIBus *source;
191 g_autoptr(GdkAppLaunchContext) ctx = NULL;
192 GDesktopAppInfo *app_info;
193 g_autoptr(GError) error = NULL;
194
195 g_return_if_fail (CC_IS_INPUT_SOURCE_IBUS (cc_input_row_get_source (row)));
196 source = CC_INPUT_SOURCE_IBUS (cc_input_row_get_source (row));
197
198 app_info = cc_input_source_ibus_get_app_info (source);
199 if (app_info == NULL)
200 return;
201
202 ctx = gdk_display_get_app_launch_context (gdk_display_get_default ());
203 gdk_app_launch_context_set_timestamp (ctx, gtk_get_current_event_time ());
204
205 g_app_launch_context_setenv (G_APP_LAUNCH_CONTEXT (ctx),
206 "IBUS_ENGINE_NAME", cc_input_source_ibus_get_engine_name (source));
207
208 if (!g_app_info_launch (G_APP_INFO (app_info), NULL, G_APP_LAUNCH_CONTEXT (ctx), &error))
209 g_warning ("Failed to launch input source setup: %s", error->message);
210 }
211
212 static void
row_layout_cb(CcInputListBox * self,CcInputRow * row)213 row_layout_cb (CcInputListBox *self,
214 CcInputRow *row)
215 {
216 CcInputSource *source;
217 const gchar *layout, *layout_variant;
218 g_autofree gchar *commandline = NULL;
219
220 source = cc_input_row_get_source (row);
221
222 layout = cc_input_source_get_layout (source);
223 layout_variant = cc_input_source_get_layout_variant (source);
224
225 if (layout_variant && layout_variant[0])
226 commandline = g_strdup_printf ("gkbd-keyboard-display -l \"%s\t%s\"",
227 layout, layout_variant);
228 else
229 commandline = g_strdup_printf ("gkbd-keyboard-display -l %s",
230 layout);
231
232 g_spawn_command_line_async (commandline, NULL);
233 }
234
235 static void move_input (CcInputListBox *self, CcInputRow *source, CcInputRow *dest);
236
237 static void
row_moved_cb(CcInputListBox * self,CcInputRow * dest_row,CcInputRow * row)238 row_moved_cb (CcInputListBox *self,
239 CcInputRow *dest_row,
240 CcInputRow *row)
241 {
242 move_input (self, row, dest_row);
243 }
244
245 static void remove_input (CcInputListBox *self, CcInputRow *row);
246
247 static void
row_removed_cb(CcInputListBox * self,CcInputRow * row)248 row_removed_cb (CcInputListBox *self,
249 CcInputRow *row)
250 {
251 remove_input (self, row);
252 }
253
254 static void
update_input_rows(CcInputListBox * self)255 update_input_rows (CcInputListBox *self)
256 {
257 g_autoptr(GList) rows = NULL;
258 GList *l;
259 guint n_input_rows = 0;
260
261 rows = gtk_container_get_children (GTK_CONTAINER (self));
262 for (l = rows; l; l = l->next)
263 if (CC_IS_INPUT_ROW (l->data))
264 n_input_rows++;
265 for (l = rows; l; l = l->next) {
266 CcInputRow *row;
267
268 if (!CC_IS_INPUT_ROW (l->data))
269 continue;
270 row = CC_INPUT_ROW (l->data);
271
272 cc_input_row_set_removable (row, n_input_rows > 1);
273 cc_input_row_set_draggable (row, n_input_rows > 1);
274 }
275 }
276
277 static void
add_input_row(CcInputListBox * self,CcInputSource * source)278 add_input_row (CcInputListBox *self, CcInputSource *source)
279 {
280 CcInputRow *row;
281
282 gtk_widget_set_visible (GTK_WIDGET (self->no_inputs_row), FALSE);
283
284 row = cc_input_row_new (source);
285 gtk_widget_show (GTK_WIDGET (row));
286 gtk_size_group_add_widget (self->input_size_group, GTK_WIDGET (row));
287 g_signal_connect_object (row, "show-settings", G_CALLBACK (row_settings_cb), self, G_CONNECT_SWAPPED);
288 g_signal_connect_object (row, "show-layout", G_CALLBACK (row_layout_cb), self, G_CONNECT_SWAPPED);
289 g_signal_connect_object (row, "move-row", G_CALLBACK (row_moved_cb), self, G_CONNECT_SWAPPED);
290 g_signal_connect_object (row, "remove-row", G_CALLBACK (row_removed_cb), self, G_CONNECT_SWAPPED);
291 gtk_list_box_insert (GTK_LIST_BOX (self), GTK_WIDGET (row), gtk_list_box_row_get_index (self->add_input_row));
292 update_input_rows (self);
293 }
294
295 static void
add_input_sources(CcInputListBox * self,GVariant * sources)296 add_input_sources (CcInputListBox *self,
297 GVariant *sources)
298 {
299 GVariantIter iter;
300 const gchar *type, *id;
301
302 if (g_variant_n_children (sources) < 1) {
303 gtk_widget_set_visible (GTK_WIDGET (self->no_inputs_row), TRUE);
304 return;
305 }
306
307 g_variant_iter_init (&iter, sources);
308 while (g_variant_iter_next (&iter, "(&s&s)", &type, &id)) {
309 g_autoptr(CcInputSource) source = NULL;
310
311 if (g_str_equal (type, "xkb")) {
312 source = CC_INPUT_SOURCE (cc_input_source_xkb_new_from_id (self->xkb_info, id));
313 } else if (g_str_equal (type, "ibus")) {
314 source = CC_INPUT_SOURCE (cc_input_source_ibus_new (id));
315 #ifdef HAVE_IBUS
316 if (self->ibus_engines) {
317 IBusEngineDesc *engine_desc = g_hash_table_lookup (self->ibus_engines, id);
318 if (engine_desc != NULL)
319 cc_input_source_ibus_set_engine_desc (CC_INPUT_SOURCE_IBUS (source), engine_desc);
320 }
321 #endif
322 } else {
323 g_warning ("Unhandled input source type '%s'", type);
324 continue;
325 }
326
327 add_input_row (self, source);
328 }
329 }
330
331 static void
add_input_sources_from_settings(CcInputListBox * self)332 add_input_sources_from_settings (CcInputListBox *self)
333 {
334 g_autoptr(GVariant) sources = NULL;
335 sources = g_settings_get_value (self->input_settings, "sources");
336 add_input_sources (self, sources);
337 }
338
339 static void
clear_input_sources(CcInputListBox * self)340 clear_input_sources (CcInputListBox *self)
341 {
342 g_autoptr(GList) list = NULL;
343 GList *l;
344
345 list = gtk_container_get_children (GTK_CONTAINER (self));
346 for (l = list; l; l = l->next) {
347 if (CC_IS_INPUT_ROW (l->data))
348 gtk_container_remove (GTK_CONTAINER (self), GTK_WIDGET (l->data));
349 }
350
351 cc_list_box_adjust_scrolling (GTK_LIST_BOX (self));
352 }
353
354 static CcInputRow *
get_row_by_source(CcInputListBox * self,CcInputSource * source)355 get_row_by_source (CcInputListBox *self, CcInputSource *source)
356 {
357 g_autoptr(GList) list = NULL;
358 GList *l;
359
360 list = gtk_container_get_children (GTK_CONTAINER (self));
361 for (l = list; l; l = l->next) {
362 CcInputRow *row;
363
364 if (!CC_IS_INPUT_ROW (l->data))
365 continue;
366 row = CC_INPUT_ROW (l->data);
367
368 if (cc_input_source_matches (source, cc_input_row_get_source (row)))
369 return row;
370 }
371
372 return NULL;
373 }
374
375 static void
input_sources_changed(CcInputListBox * self,const gchar * key)376 input_sources_changed (CcInputListBox *self,
377 const gchar *key)
378 {
379 CcInputRow *selected;
380 g_autoptr(CcInputSource) source = NULL;
381
382 selected = CC_INPUT_ROW (gtk_list_box_get_selected_row (GTK_LIST_BOX (self)));
383 if (selected)
384 source = g_object_ref (cc_input_row_get_source (selected));
385 clear_input_sources (self);
386 add_input_sources_from_settings (self);
387 if (source != NULL) {
388 CcInputRow *row = get_row_by_source (self, source);
389 if (row != NULL)
390 gtk_list_box_select_row (GTK_LIST_BOX (self), GTK_LIST_BOX_ROW (row));
391 }
392 }
393
394 static void
set_input_settings(CcInputListBox * self)395 set_input_settings (CcInputListBox *self)
396 {
397 GVariantBuilder builder;
398 g_autoptr(GList) list = NULL;
399 GList *l;
400
401 g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ss)"));
402 list = gtk_container_get_children (GTK_CONTAINER (self));
403 for (l = list; l; l = l->next) {
404 CcInputRow *row;
405 CcInputSource *source;
406
407 if (!CC_IS_INPUT_ROW (l->data))
408 continue;
409 row = CC_INPUT_ROW (l->data);
410 source = cc_input_row_get_source (row);
411
412 if (CC_IS_INPUT_SOURCE_XKB (source)) {
413 g_autofree gchar *id = cc_input_source_xkb_get_id (CC_INPUT_SOURCE_XKB (source));
414 g_variant_builder_add (&builder, "(ss)", "xkb", id);
415 } else if (CC_IS_INPUT_SOURCE_IBUS (source)) {
416 g_variant_builder_add (&builder, "(ss)", "ibus",
417 cc_input_source_ibus_get_engine_name (CC_INPUT_SOURCE_IBUS (source)));
418 }
419 }
420
421 g_settings_set_value (self->input_settings, KEY_INPUT_SOURCES, g_variant_builder_end (&builder));
422 }
423
424 static void set_localed_input (CcInputListBox *self);
425
426 static void
update_input(CcInputListBox * self)427 update_input (CcInputListBox *self)
428 {
429 if (self->login) {
430 set_localed_input (self);
431 } else {
432 set_input_settings (self);
433 if (self->login_auto_apply)
434 set_localed_input (self);
435 }
436 }
437
438 static void
show_input_chooser(CcInputListBox * self)439 show_input_chooser (CcInputListBox *self)
440 {
441 CcInputChooser *chooser;
442
443 chooser = cc_input_chooser_new (self->login,
444 self->xkb_info,
445 #ifdef HAVE_IBUS
446 self->ibus_engines
447 #else
448 NULL
449 #endif
450 );
451 gtk_window_set_transient_for (GTK_WINDOW (chooser), GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))));
452
453 if (gtk_dialog_run (GTK_DIALOG (chooser)) == GTK_RESPONSE_OK) {
454 CcInputSource *source;
455
456 source = cc_input_chooser_get_source (chooser);
457 if (source != NULL && get_row_by_source (self, source) == NULL) {
458 add_input_row (self, source);
459 update_input (self);
460 }
461 }
462 gtk_widget_destroy (GTK_WIDGET (chooser));
463 }
464
465 // Duplicated from cc-region-panel.c
466 static gboolean
permission_acquired(GPermission * permission,GAsyncResult * res,const gchar * action)467 permission_acquired (GPermission *permission, GAsyncResult *res, const gchar *action)
468 {
469 g_autoptr(GError) error = NULL;
470
471 if (!g_permission_acquire_finish (permission, res, &error)) {
472 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
473 g_warning ("Failed to acquire permission to %s: %s\n", error->message, action);
474 return FALSE;
475 }
476
477 return FALSE;
478 }
479
480 static void
add_input_permission_cb(GObject * source,GAsyncResult * res,gpointer user_data)481 add_input_permission_cb (GObject *source, GAsyncResult *res, gpointer user_data)
482 {
483 CcInputListBox *self = user_data;
484 if (permission_acquired (G_PERMISSION (source), res, "add input"))
485 show_input_chooser (self);
486 }
487
488 static void
add_input(CcInputListBox * self)489 add_input (CcInputListBox *self)
490 {
491 if (!self->login) {
492 show_input_chooser (self);
493 } else if (g_permission_get_allowed (self->permission)) {
494 show_input_chooser (self);
495 } else if (g_permission_get_can_acquire (self->permission)) {
496 g_permission_acquire_async (self->permission,
497 self->cancellable,
498 add_input_permission_cb,
499 self);
500 }
501 }
502
503 static GtkWidget *
find_sibling(GtkContainer * container,GtkWidget * child)504 find_sibling (GtkContainer *container, GtkWidget *child)
505 {
506 g_autoptr(GList) list = NULL;
507 GList *c, *l;
508 GtkWidget *sibling;
509
510 list = gtk_container_get_children (container);
511 c = g_list_find (list, child);
512
513 for (l = c->next; l; l = l->next) {
514 sibling = l->data;
515 if (gtk_widget_get_visible (sibling) && gtk_widget_get_child_visible (sibling))
516 return sibling;
517 }
518
519 for (l = c->prev; l; l = l->prev) {
520 sibling = l->data;
521 if (gtk_widget_get_visible (sibling) && gtk_widget_get_child_visible (sibling))
522 return sibling;
523 }
524
525 return NULL;
526 }
527
528 static void
do_remove_input(CcInputListBox * self,CcInputRow * row)529 do_remove_input (CcInputListBox *self, CcInputRow *row)
530 {
531 GtkWidget *sibling;
532
533 sibling = find_sibling (GTK_CONTAINER (self), GTK_WIDGET (row));
534 gtk_container_remove (GTK_CONTAINER (self), GTK_WIDGET (row));
535 gtk_list_box_select_row (GTK_LIST_BOX (self), GTK_LIST_BOX_ROW (sibling));
536
537 cc_list_box_adjust_scrolling (GTK_LIST_BOX(self));
538
539 update_input (self);
540 update_input_rows (self);
541 }
542
543 static void
remove_input_permission_cb(GObject * source,GAsyncResult * res,gpointer user_data)544 remove_input_permission_cb (GObject *source, GAsyncResult *res, gpointer user_data)
545 {
546 RowData *data = user_data;
547 if (permission_acquired (G_PERMISSION (source), res, "remove input"))
548 do_remove_input (data->panel, data->source);
549 }
550
551 static void
remove_input(CcInputListBox * self,CcInputRow * row)552 remove_input (CcInputListBox *self, CcInputRow *row)
553 {
554 if (!self->login) {
555 do_remove_input (self, row);
556 } else if (g_permission_get_allowed (self->permission)) {
557 do_remove_input (self, row);
558 } else if (g_permission_get_can_acquire (self->permission)) {
559 g_permission_acquire_async (self->permission,
560 self->cancellable,
561 remove_input_permission_cb,
562 row_data_new (self, row, NULL));
563 }
564 }
565
566 static void
do_move_input(CcInputListBox * self,CcInputRow * source,CcInputRow * dest)567 do_move_input (CcInputListBox *self, CcInputRow *source, CcInputRow *dest)
568 {
569 gint dest_index;
570
571 dest_index = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (dest));
572
573 g_object_ref (source);
574 gtk_container_remove (GTK_CONTAINER (self), GTK_WIDGET (source));
575 gtk_list_box_insert (GTK_LIST_BOX (self), GTK_WIDGET (source), dest_index);
576 g_object_unref (source);
577
578 cc_list_box_adjust_scrolling (GTK_LIST_BOX (self));
579
580 update_input (self);
581 }
582
583 static void
move_input_permission_cb(GObject * source,GAsyncResult * res,gpointer user_data)584 move_input_permission_cb (GObject *source, GAsyncResult *res, gpointer user_data)
585 {
586 RowData *data = user_data;
587 if (permission_acquired (G_PERMISSION (source), res, "move input"))
588 do_move_input (data->panel, data->source, data->dest);
589 }
590
591 static void
move_input(CcInputListBox * self,CcInputRow * source,CcInputRow * dest)592 move_input (CcInputListBox *self,
593 CcInputRow *source,
594 CcInputRow *dest)
595 {
596 if (!self->login) {
597 do_move_input (self, source, dest);
598 } else if (g_permission_get_allowed (self->permission)) {
599 do_move_input (self, source, dest);
600 } else if (g_permission_get_can_acquire (self->permission)) {
601 g_permission_acquire_async (self->permission,
602 self->cancellable,
603 move_input_permission_cb,
604 row_data_new (self, source, dest));
605 }
606 }
607
608 static void
input_row_activated_cb(CcInputListBox * self,GtkListBoxRow * row)609 input_row_activated_cb (CcInputListBox *self, GtkListBoxRow *row)
610 {
611 if (row == self->add_input_row) {
612 add_input (self);
613 }
614 }
615
616 static void
add_input_sources_from_localed(CcInputListBox * self)617 add_input_sources_from_localed (CcInputListBox *self)
618 {
619 g_autoptr(GVariant) layout_property = NULL;
620 g_autoptr(GVariant) variant_property = NULL;
621 const gchar *s;
622 g_auto(GStrv) layouts = NULL;
623 g_auto(GStrv) variants = NULL;
624 gint i, n;
625
626 if (!self->localed)
627 return;
628
629 layout_property = g_dbus_proxy_get_cached_property (self->localed, "X11Layout");
630 if (layout_property) {
631 s = g_variant_get_string (layout_property, NULL);
632 layouts = g_strsplit (s, ",", -1);
633 }
634
635 variant_property = g_dbus_proxy_get_cached_property (self->localed, "X11Variant");
636 if (variant_property) {
637 s = g_variant_get_string (variant_property, NULL);
638 if (s && *s)
639 variants = g_strsplit (s, ",", -1);
640 }
641
642 if (variants && variants[0])
643 n = MIN (g_strv_length (layouts), g_strv_length (variants));
644 else if (layouts && layouts[0])
645 n = g_strv_length (layouts);
646 else
647 n = 0;
648
649 for (i = 0; i < n && layouts[i][0]; i++) {
650 const char *variant = variants ? variants[i] : NULL;
651 g_autoptr(CcInputSourceXkb) source = cc_input_source_xkb_new (self->xkb_info, layouts[i], variant);
652 add_input_row (self, CC_INPUT_SOURCE (source));
653 }
654 gtk_widget_set_visible (GTK_WIDGET (self->no_inputs_row), n == 0);
655 }
656
657 static void
set_localed_input(CcInputListBox * self)658 set_localed_input (CcInputListBox *self)
659 {
660 g_autoptr(GString) layouts = NULL;
661 g_autoptr(GString) variants = NULL;
662 g_autoptr(GList) list = NULL;
663 GList *li;
664
665 layouts = g_string_new ("");
666 variants = g_string_new ("");
667
668 list = gtk_container_get_children (GTK_CONTAINER (self));
669 for (li = list; li; li = li->next) {
670 CcInputRow *row;
671 CcInputSourceXkb *source;
672 g_autofree gchar *id = NULL;
673 const gchar *l, *v;
674
675 if (!CC_IS_INPUT_ROW (li->data))
676 continue;
677 row = CC_INPUT_ROW (li->data);
678
679 if (!CC_IS_INPUT_SOURCE_XKB (cc_input_row_get_source (row)))
680 continue;
681 source = CC_INPUT_SOURCE_XKB (cc_input_row_get_source (row));
682
683 id = cc_input_source_xkb_get_id (source);
684 if (gnome_xkb_info_get_layout_info (self->xkb_info, id, NULL, NULL, &l, &v)) {
685 if (layouts->str[0]) {
686 g_string_append_c (layouts, ',');
687 g_string_append_c (variants, ',');
688 }
689 g_string_append (layouts, l);
690 g_string_append (variants, v);
691 }
692 }
693
694 g_dbus_proxy_call (self->localed,
695 "SetX11Keyboard",
696 g_variant_new ("(ssssbb)", layouts->str, "", variants->str, "", TRUE, TRUE),
697 G_DBUS_CALL_FLAGS_NONE,
698 -1, NULL, NULL, NULL);
699 }
700
701 static void
cc_input_list_box_finalize(GObject * object)702 cc_input_list_box_finalize (GObject *object)
703 {
704 CcInputListBox *self = CC_INPUT_LIST_BOX (object);
705
706 g_cancellable_cancel (self->cancellable);
707
708 g_clear_object (&self->input_settings);
709 g_clear_object (&self->xkb_info);
710 #ifdef HAVE_IBUS
711 g_clear_object (&self->ibus);
712 g_clear_pointer (&self->ibus_engines, g_hash_table_destroy);
713 #endif
714
715 G_OBJECT_CLASS (cc_input_list_box_parent_class)->finalize (object);
716 }
717
718 static void
cc_input_list_box_class_init(CcInputListBoxClass * klass)719 cc_input_list_box_class_init (CcInputListBoxClass *klass)
720 {
721 GObjectClass *object_class = G_OBJECT_CLASS (klass);
722 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
723
724 object_class->finalize = cc_input_list_box_finalize;
725
726 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/keyboard/cc-input-list-box.ui");
727
728 gtk_widget_class_bind_template_child (widget_class, CcInputListBox, add_input_row);
729 gtk_widget_class_bind_template_child (widget_class, CcInputListBox, input_size_group);
730 gtk_widget_class_bind_template_child (widget_class, CcInputListBox, no_inputs_row);
731
732 gtk_widget_class_bind_template_callback (widget_class, input_row_activated_cb);
733 }
734
735 static void
cc_input_list_box_init(CcInputListBox * self)736 cc_input_list_box_init (CcInputListBox *self)
737 {
738 gtk_widget_init_template (GTK_WIDGET (self));
739
740 self->login = FALSE;
741 self->login_auto_apply = FALSE;
742 self->localed = NULL;
743 self->permission = NULL;
744
745 self->cancellable = g_cancellable_new();
746
747 self->input_settings = g_settings_new (GNOME_DESKTOP_INPUT_SOURCES_DIR);
748
749 self->xkb_info = gnome_xkb_info_new ();
750
751 #ifdef HAVE_IBUS
752 ibus_init ();
753 if (!self->ibus) {
754 self->ibus = ibus_bus_new_async ();
755 if (ibus_bus_is_connected (self->ibus))
756 fetch_ibus_engines (self);
757 else
758 g_signal_connect_object (self->ibus, "connected",
759 G_CALLBACK (fetch_ibus_engines), self,
760 G_CONNECT_SWAPPED);
761 }
762 maybe_start_ibus ();
763 #endif
764
765 g_signal_connect_object (self->input_settings, "changed::" KEY_INPUT_SOURCES,
766 G_CALLBACK (input_sources_changed), self, G_CONNECT_SWAPPED);
767
768 add_input_sources_from_settings (self);
769 }
770
771 void
cc_input_list_box_set_login(CcInputListBox * self,gboolean login)772 cc_input_list_box_set_login (CcInputListBox *self, gboolean login)
773 {
774 self->login = login;
775 clear_input_sources (self);
776 if (login)
777 add_input_sources_from_localed (self);
778 else
779 add_input_sources_from_settings (self);
780 }
781
782 void
cc_input_list_box_set_login_auto_apply(CcInputListBox * self,gboolean login_auto_apply)783 cc_input_list_box_set_login_auto_apply (CcInputListBox *self, gboolean login_auto_apply)
784 {
785 self->login_auto_apply = login_auto_apply;
786 }
787
788 void
cc_input_list_box_set_localed(CcInputListBox * self,GDBusProxy * localed)789 cc_input_list_box_set_localed (CcInputListBox *self, GDBusProxy *localed)
790 {
791 self->localed = localed;
792 }
793
794 void
cc_input_list_box_set_permission(CcInputListBox * self,GPermission * permission)795 cc_input_list_box_set_permission (CcInputListBox *self, GPermission *permission)
796 {
797 self->permission = permission;
798 }
799