GCC Code Coverage Report


Directory: ./
File: panels/applications/cc-applications-panel.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 878 0.0%
Functions: 0 87 0.0%
Branches: 0 425 0.0%

Line Branch Exec Source
1 /* cc-applications-panel.c
2 *
3 * Copyright 2018 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: GPL-3.0-or-later
19 */
20
21 #undef G_LOG_DOMAIN
22 #define G_LOG_DOMAIN "cc-applications-panel"
23
24 #include <config.h>
25 #include <glib/gi18n.h>
26 #ifdef HAVE_MALCONTENT
27 #include <libmalcontent/malcontent.h>
28 #endif
29
30 #include <gio/gdesktopappinfo.h>
31
32 #include "cc-applications-panel.h"
33 #include "cc-applications-row.h"
34 #include "cc-info-row.h"
35 #include "cc-list-row.h"
36 #include "cc-default-apps-page.h"
37 #include "cc-removable-media-settings.h"
38 #include "cc-applications-resources.h"
39 #ifdef HAVE_SNAP
40 #include "cc-snapd-client.h"
41 #include "cc-snap-row.h"
42 #endif
43 #include "cc-util.h"
44 #include "globs.h"
45 #include "search.h"
46 #include "utils.h"
47
48 #define MASTER_SCHEMA "org.gnome.desktop.notifications"
49 #define APP_SCHEMA MASTER_SCHEMA ".application"
50 #define APP_PREFIX "/org/gnome/desktop/notifications/application/"
51
52 #define PORTAL_SNAP_PREFIX "snap."
53
54 struct _CcApplicationsPanel
55 {
56 CcPanel parent;
57
58 CcDefaultAppsPage *default_apps_page;
59 AdwSwitchRow *autorun_never_row;
60 CcRemovableMediaSettings *removable_media_settings;
61
62 AdwNavigationView *navigation_view;
63 AdwNavigationPage *app_settings_page;
64 GtkListBox *app_listbox;
65 GtkEntry *app_search_entry;
66 GtkWidget *no_apps_page;
67 GtkStack *app_listbox_stack;
68 GAppInfoMonitor *monitor;
69 gulong monitor_id;
70 GListModel *app_model;
71 GListModel *filter_model;
72 GtkFilter *filter;
73 #ifdef HAVE_MALCONTENT
74 GCancellable *cancellable;
75
76 MctAppFilter *app_filter;
77 MctManager *manager;
78 guint app_filter_id;
79 #endif
80
81 gchar *current_app_id;
82 GAppInfo *current_app_info;
83 gchar *current_portal_app_id;
84
85 GHashTable *globs;
86 GHashTable *search_providers;
87
88 GtkImage *app_icon_image;
89 GtkLabel *app_name_label;
90 GtkButton *launch_button;
91 GtkButton *view_details_button;
92 AdwBanner *sandbox_banner;
93 GtkWidget *sandbox_info_button;
94
95 GDBusProxy *perm_store;
96 GSettings *media_handling_settings;
97 GtkListBoxRow *perm_store_pending_row;
98 GSettings *notification_settings;
99 GSettings *location_settings;
100 GSettings *privacy_settings;
101 GSettings *search_settings;
102
103 GtkButton *install_button;
104
105 AdwPreferencesGroup *integration_section;
106 AdwSwitchRow *notification;
107 AdwSwitchRow *background;
108 AdwSwitchRow *wallpaper;
109 AdwSwitchRow *screenshot;
110 AdwSwitchRow *sound;
111 CcInfoRow *no_sound;
112 AdwSwitchRow *search;
113 CcInfoRow *no_search;
114 AdwSwitchRow *camera;
115 CcInfoRow *no_camera;
116 AdwSwitchRow *location;
117 CcInfoRow *no_location;
118 AdwSwitchRow *shortcuts;
119 AdwSwitchRow *microphone;
120 CcInfoRow *no_microphone;
121 AdwPreferencesGroup *other_permissions_section;
122 CcListRow *builtin;
123 AdwDialog *builtin_dialog;
124 AdwPreferencesPage *builtin_page;
125 GtkListBox *builtin_list;
126 GList *snap_permission_rows;
127
128 GtkButton *handler_reset;
129 AdwDialog *handler_dialog;
130 AdwPreferencesPage *handler_page;
131 CcListRow *handler_row;
132 AdwPreferencesGroup *handler_file_group;
133 AdwPreferencesGroup *handler_link_group;
134 GList *file_handler_rows;
135 GList *link_handler_rows;
136
137 GtkWidget *usage_section;
138 CcListRow *storage;
139 AdwDialog *storage_dialog;
140 CcInfoRow *app;
141 CcInfoRow *data;
142 CcInfoRow *cache;
143 CcInfoRow *total;
144 GtkButton *clear_cache_button;
145
146 guint64 app_size;
147 guint64 cache_size;
148 guint64 data_size;
149 };
150
151 static void select_app (CcApplicationsPanel *self,
152 const gchar *app_id,
153 gboolean emit_activate);
154
155 static void update_handler_dialog (CcApplicationsPanel *self, GAppInfo *info);
156
157 G_DEFINE_TYPE (CcApplicationsPanel, cc_applications_panel, CC_TYPE_PANEL)
158
159 enum
160 {
161 PROP_0,
162 PROP_PARAMETERS
163 };
164
165 static gboolean
166 gnome_software_is_installed (void)
167 {
168 g_autofree gchar *path = g_find_program_in_path ("gnome-software");
169 return path != NULL;
170 }
171
172 /* Callbacks */
173
174 static gboolean
175 privacy_link_cb (CcApplicationsPanel *self)
176 {
177 CcShell *shell = cc_panel_get_shell (CC_PANEL (self));
178 g_autoptr(GError) error = NULL;
179
180 if (!cc_shell_set_active_panel_from_id (shell, "location", NULL, &error))
181 g_warning ("Failed to switch to privacy panel: %s", error->message);
182
183 return TRUE;
184 }
185
186 static void
187 open_software_cb (CcApplicationsPanel *self)
188 {
189 const gchar *argv[] = { "gnome-software", "--details", "appid", NULL };
190
191 if (self->current_app_id == NULL)
192 argv[1] = NULL;
193 else
194 argv[2] = self->current_app_id;
195
196 g_spawn_async (NULL, (char **)argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
197 }
198
199 /* --- portal permissions and utilities --- */
200
201 static gchar **
202 get_portal_permissions (CcApplicationsPanel *self,
203 const gchar *table,
204 const gchar *id,
205 const gchar *app_id)
206 {
207 g_autoptr(GVariant) ret = NULL;
208 g_autoptr(GVariantIter) iter = NULL;
209 const gchar *key = NULL;
210 GStrv val;
211 GStrv result = NULL;
212
213 ret = g_dbus_proxy_call_sync (self->perm_store,
214 "Lookup",
215 g_variant_new ("(ss)", table, id),
216 0, G_MAXINT, NULL, NULL);
217 if (ret == NULL)
218 return NULL;
219
220 g_variant_get (ret, "(a{sas}v)", &iter, NULL);
221
222 while (g_variant_iter_loop (iter, "{&s^a&s}", &key, &val))
223 {
224 if (strcmp (key, app_id) == 0 && result == NULL)
225 result = g_strdupv (val);
226 }
227
228 return result;
229 }
230
231 static void
232 set_portal_permissions (CcApplicationsPanel *self,
233 const gchar *table,
234 const gchar *id,
235 const gchar *app_id,
236 const gchar * const *permissions)
237 {
238 g_autoptr(GError) error = NULL;
239
240 g_dbus_proxy_call_sync (self->perm_store,
241 "SetPermission",
242 g_variant_new ("(sbss^as)", table, TRUE, id, app_id, permissions),
243 0,
244 G_MAXINT,
245 NULL,
246 &error);
247 if (error)
248 g_warning ("Error setting portal permissions: %s", error->message);
249 }
250
251 static gchar *
252 get_portal_app_id (GAppInfo *info)
253 {
254 if (G_IS_DESKTOP_APP_INFO (info))
255 {
256 g_autofree gchar *snap_name = NULL;
257 gchar *flatpak_id;
258
259 flatpak_id = g_desktop_app_info_get_string (G_DESKTOP_APP_INFO (info), "X-Flatpak");
260 if (flatpak_id != NULL)
261 return flatpak_id;
262
263 snap_name = g_desktop_app_info_get_string (G_DESKTOP_APP_INFO (info), "X-SnapInstanceName");
264 if (snap_name != NULL)
265 return g_strdup_printf ("%s%s", PORTAL_SNAP_PREFIX, snap_name);
266 }
267
268 return NULL;
269 }
270
271 static GFile *
272 get_flatpak_app_dir (const gchar *app_id,
273 const gchar *subdir)
274 {
275 g_autofree gchar *path = NULL;
276 g_autoptr(GFile) appdir = NULL;
277
278 path = g_build_filename (g_get_home_dir (), ".var", "app", app_id, NULL);
279 appdir = g_file_new_for_path (path);
280
281 return g_file_get_child (appdir, subdir);
282 }
283
284 /* --- search settings --- */
285
286 static void
287 set_search_enabled (CcApplicationsPanel *self,
288 const gchar *app_id,
289 gboolean enabled)
290 {
291 g_autoptr(GPtrArray) new_apps = NULL;
292 g_autofree gchar *desktop_id = NULL;
293 g_auto(GStrv) apps = NULL;
294 gpointer key, value;
295 gboolean default_disabled;
296 gint i;
297
298 desktop_id = g_strconcat (app_id, ".desktop", NULL);
299
300 if (!g_hash_table_lookup_extended (self->search_providers, app_id, &key, &value))
301 {
302 g_warning ("Trying to configure search for a provider-less app - this shouldn't happen");
303 return;
304 }
305
306 default_disabled = GPOINTER_TO_INT (value);
307
308 new_apps = g_ptr_array_new_with_free_func (g_free);
309 if (default_disabled)
310 {
311 apps = g_settings_get_strv (self->search_settings, "enabled");
312 for (i = 0; apps[i]; i++)
313 {
314 if (strcmp (apps[i], desktop_id) != 0)
315 g_ptr_array_add (new_apps, g_strdup (apps[i]));
316 }
317 if (enabled)
318 g_ptr_array_add (new_apps, g_strdup (desktop_id));
319 g_ptr_array_add (new_apps, NULL);
320 g_settings_set_strv (self->search_settings, "enabled", (const gchar * const *)new_apps->pdata);
321 }
322 else
323 {
324 apps = g_settings_get_strv (self->search_settings, "disabled");
325 for (i = 0; apps[i]; i++)
326 {
327 if (strcmp (apps[i], desktop_id) != 0)
328 g_ptr_array_add (new_apps, g_strdup (apps[i]));
329 }
330 if (!enabled)
331 g_ptr_array_add (new_apps, g_strdup (desktop_id));
332 g_ptr_array_add (new_apps, NULL);
333 g_settings_set_strv (self->search_settings, "disabled", (const gchar * const *)new_apps->pdata);
334 }
335 }
336
337 static gboolean
338 search_contains_string_for_app (CcApplicationsPanel *self,
339 const gchar *app_id,
340 const gchar *setting)
341 {
342 g_autofree gchar *desktop_id = NULL;
343 g_auto(GStrv) apps = NULL;
344
345 desktop_id = g_strconcat (app_id, ".desktop", NULL);
346 apps = g_settings_get_strv (self->search_settings, setting);
347
348 return g_strv_contains ((const gchar * const *)apps, desktop_id);
349 }
350
351 static gboolean
352 search_enabled_for_app (CcApplicationsPanel *self,
353 const gchar *app_id)
354 {
355 return search_contains_string_for_app (self, app_id, "enabled");
356 }
357
358 static gboolean
359 search_disabled_for_app (CcApplicationsPanel *self,
360 const gchar *app_id)
361 {
362 return search_contains_string_for_app (self, app_id, "disabled");
363 }
364
365 static void
366 get_search_enabled (CcApplicationsPanel *self,
367 const gchar *app_id,
368 gboolean *set,
369 gboolean *enabled)
370 {
371 gpointer key, value;
372
373 *enabled = FALSE;
374 *set = g_hash_table_lookup_extended (self->search_providers, app_id, &key, &value);
375 if (!*set)
376 return;
377
378 if (search_enabled_for_app (self, app_id))
379 *enabled = TRUE;
380 else if (search_disabled_for_app (self, app_id))
381 *enabled = FALSE;
382 else
383 *enabled = !GPOINTER_TO_INT (value);
384 }
385
386 static void
387 search_cb (CcApplicationsPanel *self)
388 {
389 if (self->current_app_id)
390 set_search_enabled (self,
391 self->current_app_id,
392 adw_switch_row_get_active (self->search));
393 }
394
395 /* --- notification permissions (flatpaks and non-flatpak) --- */
396
397 static void
398 get_notification_allowed (CcApplicationsPanel *self,
399 const gchar *app_id,
400 gboolean *set,
401 gboolean *allowed)
402 {
403 if (self->notification_settings)
404 {
405 /* FIXME */
406 *set = TRUE;
407 *allowed = g_settings_get_boolean (self->notification_settings, "enable");
408 }
409 else
410 {
411 g_auto(GStrv) perms = get_portal_permissions (self, "notifications", "notification", app_id);
412 *set = perms != NULL;
413 /* FIXME: needs unreleased xdg-desktop-portals to write permissions on use */
414 *set = TRUE;
415 *allowed = perms == NULL || strcmp (perms[0], "no") != 0;
416 }
417 }
418
419 static void
420 set_notification_allowed (CcApplicationsPanel *self,
421 gboolean allowed)
422 {
423 if (self->notification_settings)
424 {
425 g_settings_set_boolean (self->notification_settings, "enable", allowed);
426 }
427 else
428 {
429 const gchar *perms[2] = { NULL, NULL };
430
431 perms[0] = allowed ? "yes" : "no";
432 set_portal_permissions (self, "notifications", "notification", self->current_portal_app_id, perms);
433 }
434 }
435
436 static void
437 notification_cb (CcApplicationsPanel *self)
438 {
439 if (self->current_app_id)
440 set_notification_allowed (self, adw_switch_row_get_active (self->notification));
441 }
442
443 static gchar *
444 munge_app_id (const gchar *app_id)
445 {
446 gchar *id = g_strdup (app_id);
447 gint i;
448
449 g_strcanon (id,
450 "0123456789"
451 "abcdefghijklmnopqrstuvwxyz"
452 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
453 "-",
454 '-');
455 for (i = 0; id[i] != '\0'; i++)
456 id[i] = g_ascii_tolower (id[i]);
457
458 return id;
459 }
460
461 static GSettings *
462 get_notification_settings (const gchar *app_id)
463 {
464 g_autofree gchar *munged_app_id = munge_app_id (app_id);
465 g_autofree gchar *path = g_strconcat (APP_PREFIX, munged_app_id, "/", NULL);
466 return g_settings_new_with_path (APP_SCHEMA, path);
467 }
468
469
470 /* --- background --- */
471
472 static void
473 get_background_allowed (CcApplicationsPanel *self,
474 const gchar *app_id,
475 gboolean *set,
476 gboolean *allowed)
477 {
478 g_auto(GStrv) perms = get_portal_permissions (self, "background", "background", app_id);
479 *set = TRUE;
480 *allowed = perms == NULL || strcmp (perms[0], "no") != 0;
481 }
482
483 static void
484 set_background_allowed (CcApplicationsPanel *self,
485 gboolean allowed)
486 {
487 const gchar *perms[2] = { NULL, NULL };
488
489 perms[0] = allowed ? "yes" : "no";
490 set_portal_permissions (self, "background", "background", self->current_portal_app_id, perms);
491 }
492
493 static void
494 background_cb (CcApplicationsPanel *self)
495 {
496 if (self->current_app_id)
497 set_background_allowed (self, adw_switch_row_get_active (self->background));
498 }
499
500 /* --- wallpaper --- */
501
502 static void
503 get_wallpaper_allowed (CcApplicationsPanel *self,
504 const gchar *app_id,
505 gboolean *set,
506 gboolean *allowed)
507 {
508 g_auto(GStrv) perms = get_portal_permissions (self, "wallpaper", "wallpaper", app_id);
509
510 *set = perms != NULL;
511 *allowed = perms == NULL || strcmp (perms[0], "no") != 0;
512 }
513
514 static void
515 set_wallpaper_allowed (CcApplicationsPanel *self,
516 gboolean allowed)
517 {
518 const gchar *perms[2] = { NULL, NULL };
519
520 perms[0] = allowed ? "yes" : "no";
521 set_portal_permissions (self, "wallpaper", "wallpaper", self->current_app_id, perms);
522 }
523
524 static void
525 wallpaper_cb (CcApplicationsPanel *self)
526 {
527 if (self->current_app_id)
528 set_wallpaper_allowed (self, adw_switch_row_get_active (self->wallpaper));
529 }
530
531 /* --- screenshot --- */
532
533 static void
534 get_screenshot_allowed (CcApplicationsPanel *self,
535 const gchar *app_id,
536 gboolean *set,
537 gboolean *allowed)
538 {
539 g_auto(GStrv) perms = get_portal_permissions (self, "screenshot", "screenshot", app_id);
540
541 *set = perms != NULL;
542 *allowed = perms == NULL || strcmp (perms[0], "no") != 0;
543 }
544
545 static void
546 set_screenshot_allowed (CcApplicationsPanel *self,
547 gboolean allowed)
548 {
549 const gchar *perms[2] = { NULL, NULL };
550
551 perms[0] = allowed ? "yes" : "no";
552 set_portal_permissions (self, "screenshot", "screenshot", self->current_app_id, perms);
553 }
554
555 static void
556 screenshot_cb (CcApplicationsPanel *self)
557 {
558 if (self->current_app_id)
559 set_screenshot_allowed (self, adw_switch_row_get_active (self->screenshot));
560 }
561
562 /* --- shortcuts permissions (flatpak) --- */
563
564 static void
565 get_shortcuts_allowed (CcApplicationsPanel *self,
566 const gchar *app_id,
567 gboolean *set,
568 gboolean *granted)
569 {
570 g_auto(GStrv) perms = NULL;
571
572 perms = get_portal_permissions (self, "gnome", "shortcuts-inhibitor", app_id);
573
574 /* GNOME Shell's "inhibit shortcut dialog" sets the permission to "GRANTED" if
575 * the user allowed for the keyboard shortcuts to be inhibited, check for that
576 * string value here.
577 */
578 *set = perms != NULL;
579 *granted = (perms != NULL) && g_ascii_strcasecmp (perms[0], "GRANTED") == 0;
580 }
581
582 static void
583 set_shortcuts_allowed (CcApplicationsPanel *self,
584 gboolean granted)
585 {
586 const gchar *perms[2];
587 g_autofree gchar *desktop_id = g_strconcat (self->current_app_id, ".desktop", NULL);
588
589 /* "GRANTED" and "DENIED" here match the values set by the "inhibit shortcut
590 * dialog" is GNOME Shell:
591 * https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/inhibitShortcutsDialog.js
592 */
593 perms[0] = granted ? "GRANTED" : "DENIED";
594 perms[1] = NULL;
595
596 set_portal_permissions (self, "gnome", "shortcuts-inhibitor", desktop_id, perms);
597 }
598
599 static void
600 shortcuts_cb (CcApplicationsPanel *self)
601 {
602 if (self->current_app_id)
603 set_shortcuts_allowed (self, adw_switch_row_get_active (self->shortcuts));
604 }
605
606 /* --- device (microphone, camera, speaker) permissions (flatpak) --- */
607
608 static void
609 get_device_allowed (CcApplicationsPanel *self,
610 const gchar *device,
611 const gchar *app_id,
612 gboolean *set,
613 gboolean *allowed)
614 {
615 g_auto(GStrv) perms = NULL;
616
617 perms = get_portal_permissions (self, "devices", device, app_id);
618
619 *set = perms != NULL;
620 *allowed = perms == NULL || strcmp (perms[0], "no") != 0;
621 }
622
623 static void
624 set_device_allowed (CcApplicationsPanel *self,
625 const gchar *device,
626 gboolean allowed)
627 {
628 const gchar *perms[2];
629
630 perms[0] = allowed ? "yes" : "no";
631 perms[1] = NULL;
632
633 set_portal_permissions (self, "devices", device, self->current_portal_app_id, perms);
634 }
635
636 static void
637 microphone_cb (CcApplicationsPanel *self)
638 {
639 if (self->current_portal_app_id)
640 set_device_allowed (self, "microphone", adw_switch_row_get_active (self->microphone));
641 }
642
643 static void
644 sound_cb (CcApplicationsPanel *self)
645 {
646 if (self->current_portal_app_id)
647 set_device_allowed (self, "speakers", adw_switch_row_get_active (self->sound));
648 }
649
650 static void
651 camera_cb (CcApplicationsPanel *self)
652 {
653 if (self->current_portal_app_id)
654 set_device_allowed (self, "camera", adw_switch_row_get_active (self->camera));
655 }
656
657 /* --- location permissions (flatpak) --- */
658
659 static void
660 get_location_allowed (CcApplicationsPanel *self,
661 const gchar *app_id,
662 gboolean *set,
663 gboolean *allowed)
664 {
665 g_auto(GStrv) perms = NULL;
666
667 perms = get_portal_permissions (self, "location", "location", app_id);
668
669 *set = perms != NULL;
670 *allowed = perms == NULL || strcmp (perms[0], "NONE") != 0;
671 }
672
673 static void
674 set_location_allowed (CcApplicationsPanel *self,
675 gboolean allowed)
676 {
677 const gchar *perms[3];
678
679 /* FIXME allow setting accuracy */
680 perms[0] = allowed ? "EXACT" : "NONE";
681 perms[1] = "0";
682 perms[2] = NULL;
683
684 set_portal_permissions (self, "location", "location", self->current_portal_app_id, perms);
685 }
686
687 static void
688 location_cb (CcApplicationsPanel *self)
689 {
690 if (self->current_portal_app_id)
691 set_location_allowed (self, adw_switch_row_get_active (self->location));
692 }
693
694 /* --- permissions section --- */
695
696 #ifdef HAVE_SNAP
697 static void
698 remove_snap_permissions (CcApplicationsPanel *self)
699 {
700 GList *l;
701
702 for (l = self->snap_permission_rows; l; l = l->next)
703 adw_preferences_group_remove (self->integration_section, l->data);
704 g_clear_pointer (&self->snap_permission_rows, g_list_free);
705 }
706
707 static gboolean
708 add_snap_permissions (CcApplicationsPanel *self,
709 GAppInfo *info,
710 const gchar *app_id)
711 {
712 const gchar *snap_name;
713 g_autoptr(CcSnapdClient) client = NULL;
714 g_autoptr(JsonArray) plugs = NULL;
715 g_autoptr(JsonArray) slots = NULL;
716 gint added = 0;
717 g_autoptr(GError) error = NULL;
718 g_autoptr(GError) interfaces_error = NULL;
719
720 if (!g_str_has_prefix (app_id, PORTAL_SNAP_PREFIX))
721 return FALSE;
722 snap_name = app_id + strlen (PORTAL_SNAP_PREFIX);
723
724 client = cc_snapd_client_new ();
725
726 if (!cc_snapd_client_get_all_connections_sync (client, &plugs, &slots, cc_panel_get_cancellable (CC_PANEL (self)), &error))
727 {
728 g_warning ("Failed to get snap connections: %s", error->message);
729 return FALSE;
730 }
731
732 for (guint i = 0; i < json_array_get_length (plugs); i++)
733 {
734 JsonObject *plug = json_array_get_object_element (plugs, i);
735 const gchar *plug_interface;
736 CcSnapRow *row;
737 g_autoptr(JsonArray) available_slots = NULL;
738 const gchar * const hidden_interfaces[] = { "content",
739 "desktop", "desktop-legacy",
740 "mir",
741 "unity7", "unity8",
742 "wayland",
743 "x11",
744 NULL };
745
746 /* Skip if not relating to this snap */
747 if (g_strcmp0 (json_object_get_string_member (plug, "snap"), snap_name) != 0)
748 continue;
749
750 /* Ignore interfaces that are too low level to make sense to show or disable */
751 plug_interface = json_object_get_string_member (plug, "interface");
752 if (g_strv_contains (hidden_interfaces, plug_interface))
753 continue;
754
755 available_slots = json_array_new ();
756 for (guint j = 0; j < json_array_get_length (slots); j++)
757 {
758 JsonObject *slot = json_array_get_object_element (slots, j);
759 if (g_strcmp0 (plug_interface, json_object_get_string_member (slot, "interface")) != 0)
760 continue;
761
762 json_array_add_object_element (available_slots, slot);
763 }
764
765 row = cc_snap_row_new (cc_panel_get_cancellable (CC_PANEL (self)), plug, available_slots);
766 adw_preferences_group_add (self->integration_section, GTK_WIDGET (row));
767 self->snap_permission_rows = g_list_prepend (self->snap_permission_rows, row);
768 added++;
769 }
770
771 return added > 0;
772 }
773 #endif
774
775 static void
776 update_sandbox_banner (CcApplicationsPanel *self,
777 gboolean is_sandboxed)
778 {
779
780 gtk_widget_set_visible (GTK_WIDGET (self->sandbox_banner), !is_sandboxed);
781 if (is_sandboxed)
782 return;
783
784 adw_banner_set_title (self->sandbox_banner, _("App is not sandboxed"));
785 }
786
787 static gint
788 add_static_permission_row (CcApplicationsPanel *self,
789 const gchar *title,
790 const gchar *subtitle)
791 {
792 GtkWidget *row;
793
794 row = adw_action_row_new ();
795 adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), title);
796 adw_action_row_set_subtitle (ADW_ACTION_ROW (row), subtitle);
797 gtk_widget_add_css_class (row, "property");
798 gtk_list_box_append (self->builtin_list, row);
799
800 return 1;
801 }
802
803 static gboolean
804 add_static_permissions (CcApplicationsPanel *self,
805 GAppInfo *info,
806 const gchar *app_id)
807 {
808 g_autoptr(GKeyFile) keyfile = NULL;
809 g_auto(GStrv) sockets = NULL;
810 g_auto(GStrv) devices = NULL;
811 g_auto(GStrv) shared = NULL;
812 g_auto(GStrv) filesystems = NULL;
813 g_autofree gchar *str = NULL;
814 gint added = 0;
815 g_autofree gchar *text = NULL;
816 g_autofree gchar *static_permissions_number = NULL;
817 gboolean is_sandboxed, is_snap = FALSE;
818
819 is_snap = app_id && g_str_has_prefix (app_id, PORTAL_SNAP_PREFIX);
820 if (app_id && !is_snap)
821 keyfile = get_flatpak_metadata (app_id);
822
823 is_sandboxed = (keyfile != NULL) || is_snap;
824 update_sandbox_banner (self, is_sandboxed);
825 if (keyfile == NULL)
826 return FALSE;
827
828 sockets = g_key_file_get_string_list (keyfile, "Context", "sockets", NULL, NULL);
829 if (sockets && g_strv_contains ((const gchar * const*)sockets, "system-bus"))
830 added += add_static_permission_row (self, _("System Bus"), _("Full access"));
831 if (sockets && g_strv_contains ((const gchar * const*)sockets, "session-bus"))
832 added += add_static_permission_row (self, _("Session Bus"), _("Full access"));
833
834 devices = g_key_file_get_string_list (keyfile, "Context", "devices", NULL, NULL);
835 if (devices && g_strv_contains ((const gchar * const*)devices, "all"))
836 added += add_static_permission_row (self, _("Devices"), _("Full access to /dev"));
837
838 shared = g_key_file_get_string_list (keyfile, "Context", "shared", NULL, NULL);
839 if (shared && g_strv_contains ((const gchar * const*)shared, "network"))
840 added += add_static_permission_row (self, _("Network"), _("Has network access"));
841
842 filesystems = g_key_file_get_string_list (keyfile, "Context", "filesystems", NULL, NULL);
843 if (filesystems && (g_strv_contains ((const gchar * const *)filesystems, "home") ||
844 g_strv_contains ((const gchar * const *)filesystems, "home:rw")))
845 added += add_static_permission_row (self, _("Home"), _("Full access"));
846 else if (filesystems && g_strv_contains ((const gchar * const *)filesystems, "home:ro"))
847 added += add_static_permission_row (self, _("Home"), _("Read-only"));
848 if (filesystems && (g_strv_contains ((const gchar * const *)filesystems, "host") ||
849 g_strv_contains ((const gchar * const *)filesystems, "host:rw")))
850 added += add_static_permission_row (self, _("File System"), _("Full access"));
851 else if (filesystems && g_strv_contains ((const gchar * const *)filesystems, "host:ro"))
852 added += add_static_permission_row (self, _("File System"), _("Read-only"));
853
854 str = g_key_file_get_string (keyfile, "Session Bus Policy", "ca.desrt.dconf", NULL);
855 if (str && g_str_equal (str, "talk"))
856 added += add_static_permission_row (self, _("Settings"), _("Can change settings"));
857
858 text = g_strdup_printf (_("<b>%s</b> requires access to the following system resources. To stop this access, the app must be removed."), g_app_info_get_display_name (info));
859 adw_preferences_page_set_description (self->builtin_page, text);
860
861 static_permissions_number = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE,
862 "%u permission",
863 "%u permissions",
864 added),
865 added);
866 cc_list_row_set_secondary_label (self->builtin, static_permissions_number);
867
868 return added > 0;
869 }
870
871 static void
872 remove_static_permissions (CcApplicationsPanel *self)
873 {
874 gtk_list_box_remove_all (self->builtin_list);
875 }
876
877 /* --- header section --- */
878
879 static void
880 update_header_section (CcApplicationsPanel *self,
881 GAppInfo *info)
882 {
883 GIcon *icon;
884
885 icon = g_app_info_get_icon (info);
886 gtk_image_set_from_gicon (self->app_icon_image, icon);
887
888 gtk_label_set_label (self->app_name_label,
889 g_app_info_get_display_name (info));
890 }
891
892
893 /* --- gintegration section --- */
894
895 static void
896 update_integration_section (CcApplicationsPanel *self,
897 GAppInfo *info)
898 {
899 g_autofree gchar *app_id = get_app_id (info);
900 g_autofree gchar *portal_app_id = get_portal_app_id (info);
901 gboolean set, allowed, disabled;
902 gboolean has_any = FALSE;
903
904 disabled = g_settings_get_boolean (self->search_settings, "disable-external");
905 get_search_enabled (self, app_id, &set, &allowed);
906 adw_switch_row_set_active (self->search, allowed);
907 gtk_widget_set_visible (GTK_WIDGET (self->search), set && !disabled);
908 gtk_widget_set_visible (GTK_WIDGET (self->no_search), set && disabled);
909
910 if (app_id != NULL)
911 {
912 g_autofree gchar *desktop_id = g_strconcat (app_id, ".desktop", NULL);
913 get_shortcuts_allowed (self, desktop_id, &set, &allowed);
914 gtk_widget_set_visible (GTK_WIDGET (self->shortcuts), set);
915 adw_switch_row_set_active (self->shortcuts, allowed);
916 }
917 else
918 {
919 gtk_widget_set_visible (GTK_WIDGET (self->shortcuts), FALSE);
920 }
921
922 #ifdef HAVE_SNAP
923 remove_snap_permissions (self);
924 #endif
925
926 if (portal_app_id != NULL)
927 {
928 g_clear_object (&self->notification_settings);
929 get_notification_allowed (self, portal_app_id, &set, &allowed);
930 adw_switch_row_set_active (self->notification, allowed);
931 gtk_widget_set_visible (GTK_WIDGET (self->notification), set);
932 has_any |= set;
933
934 get_background_allowed (self, portal_app_id, &set, &allowed);
935 adw_switch_row_set_active (self->background, allowed);
936 gtk_widget_set_visible (GTK_WIDGET (self->background), set);
937 has_any |= set;
938
939 get_wallpaper_allowed (self, portal_app_id, &set, &allowed);
940 adw_switch_row_set_active (self->wallpaper, allowed);
941 gtk_widget_set_visible (GTK_WIDGET (self->wallpaper), set);
942 has_any |= set;
943
944 get_screenshot_allowed (self, portal_app_id, &set, &allowed);
945 adw_switch_row_set_active (self->screenshot, allowed);
946 gtk_widget_set_visible (GTK_WIDGET (self->screenshot), set);
947 has_any |= set;
948
949 disabled = g_settings_get_boolean (self->privacy_settings, "disable-sound-output");
950 get_device_allowed (self, "speakers", portal_app_id, &set, &allowed);
951 adw_switch_row_set_active (self->sound, allowed);
952 gtk_widget_set_visible (GTK_WIDGET (self->sound), set && !disabled);
953 gtk_widget_set_visible (GTK_WIDGET (self->no_sound), set && disabled);
954
955 disabled = g_settings_get_boolean (self->privacy_settings, "disable-camera");
956 get_device_allowed (self, "camera", portal_app_id, &set, &allowed);
957 adw_switch_row_set_active (self->camera, allowed);
958 gtk_widget_set_visible (GTK_WIDGET (self->camera), set && !disabled);
959 gtk_widget_set_visible (GTK_WIDGET (self->no_camera), set && disabled);
960 has_any |= set;
961
962 disabled = g_settings_get_boolean (self->privacy_settings, "disable-microphone");
963 get_device_allowed (self, "microphone", portal_app_id, &set, &allowed);
964 adw_switch_row_set_active (self->microphone, allowed);
965 gtk_widget_set_visible (GTK_WIDGET (self->microphone), set && !disabled);
966 gtk_widget_set_visible (GTK_WIDGET (self->no_microphone), set && disabled);
967 has_any |= set;
968
969 disabled = !g_settings_get_boolean (self->location_settings, "enabled");
970 get_location_allowed (self, portal_app_id, &set, &allowed);
971 adw_switch_row_set_active (self->location, allowed);
972 gtk_widget_set_visible (GTK_WIDGET (self->location), set && !disabled);
973 gtk_widget_set_visible (GTK_WIDGET (self->no_location), set && disabled);
974 has_any |= set;
975
976 #ifdef HAVE_SNAP
977 has_any |= add_snap_permissions (self, info, portal_app_id);
978 #endif
979 }
980 else
981 {
982 g_set_object (&self->notification_settings, get_notification_settings (app_id));
983 get_notification_allowed (self, app_id, &set, &allowed);
984 adw_switch_row_set_active (self->notification, allowed);
985 gtk_widget_set_visible (GTK_WIDGET (self->notification), set);
986 has_any |= set;
987
988 gtk_widget_set_visible (GTK_WIDGET (self->background), FALSE);
989 gtk_widget_set_visible (GTK_WIDGET (self->wallpaper), FALSE);
990 gtk_widget_set_visible (GTK_WIDGET (self->screenshot), FALSE);
991 gtk_widget_set_visible (GTK_WIDGET (self->sound), FALSE);
992 gtk_widget_set_visible (GTK_WIDGET (self->no_sound), FALSE);
993 gtk_widget_set_visible (GTK_WIDGET (self->camera), FALSE);
994 gtk_widget_set_visible (GTK_WIDGET (self->no_camera), FALSE);
995 gtk_widget_set_visible (GTK_WIDGET (self->microphone), FALSE);
996 gtk_widget_set_visible (GTK_WIDGET (self->no_microphone), FALSE);
997 gtk_widget_set_visible (GTK_WIDGET (self->location), FALSE);
998 gtk_widget_set_visible (GTK_WIDGET (self->no_location), FALSE);
999 }
1000
1001 gtk_widget_set_visible (GTK_WIDGET (self->integration_section), has_any);
1002 }
1003
1004 /* --- handler section --- */
1005
1006 static void
1007 unset_cb (CcApplicationsPanel *self,
1008 GtkButton *button)
1009 {
1010 const gchar *type;
1011
1012 type = (const gchar *)g_object_get_data (G_OBJECT (button), "type");
1013
1014 g_app_info_remove_supports_type (self->current_app_info, type, NULL);
1015 update_handler_dialog (self, self->current_app_info);
1016 }
1017
1018 static void
1019 add_scheme (CcApplicationsPanel *self,
1020 const gchar *type)
1021 {
1022 g_autofree gchar *title = NULL;
1023 GtkWidget *button;
1024 GtkWidget *row;
1025 gchar *scheme;
1026
1027 scheme = strrchr (type, '/') + 1;
1028 title = g_strdup_printf ("%s://", scheme);
1029
1030 row = adw_action_row_new ();
1031 adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), title);
1032
1033 button = gtk_button_new_from_icon_name ("edit-delete-symbolic");
1034 gtk_widget_set_tooltip_text (button, _("Remove Link Type"));
1035 gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
1036 gtk_widget_set_halign (button, GTK_ALIGN_END);
1037 gtk_widget_add_css_class (button, "flat");
1038 gtk_widget_add_css_class (button, "circular");
1039 adw_action_row_add_suffix (ADW_ACTION_ROW (row), button);
1040 g_object_set_data_full (G_OBJECT (button), "type", g_strdup (type), g_free);
1041 g_signal_connect_object (button, "clicked", G_CALLBACK (unset_cb), self, G_CONNECT_SWAPPED);
1042
1043 gtk_widget_set_visible (GTK_WIDGET (self->handler_link_group), TRUE);
1044 adw_preferences_group_add (self->handler_link_group, GTK_WIDGET (row));
1045
1046 self->link_handler_rows = g_list_prepend (self->link_handler_rows, row);
1047 }
1048
1049 static void
1050 add_file_type (CcApplicationsPanel *self,
1051 const gchar *type)
1052 {
1053 g_autofree gchar *desc = NULL;
1054 const gchar *glob;
1055 GtkWidget *button;
1056 GtkWidget *row;
1057
1058 glob = g_hash_table_lookup (self->globs, type);
1059
1060 desc = g_content_type_get_description (type);
1061 row = adw_action_row_new ();
1062 adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), desc);
1063 adw_action_row_set_subtitle (ADW_ACTION_ROW (row), glob ? glob : "");
1064
1065 button = gtk_button_new_from_icon_name ("edit-delete-symbolic");
1066 gtk_widget_set_tooltip_text (button, _("Remove File Type"));
1067 gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
1068 gtk_widget_set_halign (button, GTK_ALIGN_END);
1069 gtk_widget_add_css_class (button, "flat");
1070 gtk_widget_add_css_class (button, "circular");
1071 adw_action_row_add_suffix (ADW_ACTION_ROW (row), button);
1072 g_object_set_data_full (G_OBJECT (button), "type", g_strdup (type), g_free);
1073 g_signal_connect_object (button, "clicked", G_CALLBACK (unset_cb), self, G_CONNECT_SWAPPED);
1074
1075 gtk_widget_set_visible (GTK_WIDGET (self->handler_file_group), TRUE);
1076 adw_preferences_group_add (self->handler_file_group, GTK_WIDGET (row));
1077
1078 self->file_handler_rows = g_list_prepend (self->file_handler_rows, row);
1079 }
1080
1081 static void
1082 add_handler_row (CcApplicationsPanel *self,
1083 const gchar *type)
1084 {
1085 gtk_widget_set_visible (GTK_WIDGET (self->handler_row), TRUE);
1086
1087 if (g_content_type_is_a (type, "x-scheme-handler/*"))
1088 add_scheme (self, type);
1089 else
1090 add_file_type (self, type);
1091 }
1092
1093 static gboolean
1094 app_info_recommended_for (GAppInfo *info,
1095 const gchar *type)
1096 {
1097 /* this is horribly inefficient. I blame the mime system */
1098 g_autolist(GObject) list = NULL;
1099 GList *l;
1100 gboolean ret = FALSE;
1101
1102 list = g_app_info_get_recommended_for_type (type);
1103 for (l = list; l; l = l->next)
1104 {
1105 GAppInfo *ri = l->data;
1106
1107 if (g_app_info_equal (info, ri))
1108 {
1109 ret = TRUE;
1110 break;
1111 }
1112 }
1113
1114 return ret;
1115 }
1116
1117 static void
1118 handler_reset_cb (CcApplicationsPanel *self)
1119 {
1120 const gchar **types;
1121 gint i;
1122
1123 types = g_app_info_get_supported_types (self->current_app_info);
1124 if (types == NULL || types[0] == NULL)
1125 return;
1126
1127 g_signal_handler_block (self->monitor, self->monitor_id);
1128 for (i = 0; types[i]; i++)
1129 {
1130 gchar *ctype = g_content_type_from_mime_type (types[i]);
1131 g_app_info_add_supports_type (self->current_app_info, ctype, NULL);
1132 }
1133 g_signal_handler_unblock (self->monitor, self->monitor_id);
1134 g_signal_emit_by_name (self->monitor, "changed");
1135 update_handler_dialog(self, self->current_app_info);
1136 }
1137
1138 static void
1139 remove_all_handler_rows (CcApplicationsPanel *self)
1140 {
1141 GList *l;
1142
1143 for (l = self->file_handler_rows; l; l = l->next)
1144 adw_preferences_group_remove (self->handler_file_group, l->data);
1145 g_clear_pointer (&self->file_handler_rows, g_list_free);
1146
1147 for (l = self->link_handler_rows; l; l = l->next)
1148 adw_preferences_group_remove (self->handler_link_group, l->data);
1149 g_clear_pointer (&self->link_handler_rows, g_list_free);
1150 }
1151
1152 static void
1153 update_handler_dialog (CcApplicationsPanel *self,
1154 GAppInfo *info)
1155 {
1156 g_autofree gchar *header_title = NULL;
1157 g_autoptr(GHashTable) hash = NULL;
1158 const gchar **types;
1159 guint n_associations = 0;
1160 gint i;
1161
1162 remove_all_handler_rows (self);
1163
1164 gtk_widget_set_visible (GTK_WIDGET (self->handler_row), FALSE);
1165 gtk_widget_set_visible (GTK_WIDGET (self->handler_file_group), FALSE);
1166 gtk_widget_set_visible (GTK_WIDGET (self->handler_link_group), FALSE);
1167
1168 types = g_app_info_get_supported_types (info);
1169 if (types == NULL || types[0] == NULL)
1170 return;
1171
1172 hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
1173
1174 gtk_widget_set_sensitive (GTK_WIDGET (self->handler_reset), FALSE);
1175 for (i = 0; types[i]; i++)
1176 {
1177 g_autofree gchar *ctype = g_content_type_from_mime_type (types[i]);
1178
1179 if (g_hash_table_contains (hash, ctype))
1180 continue;
1181
1182 if (!app_info_recommended_for (info, ctype))
1183 {
1184 gtk_widget_set_sensitive (GTK_WIDGET (self->handler_reset), TRUE);
1185 continue;
1186 }
1187
1188 add_handler_row (self, ctype);
1189 g_hash_table_add (hash, g_steal_pointer (&ctype));
1190
1191 n_associations++;
1192 }
1193
1194 if (n_associations > 0)
1195 {
1196 g_autofree gchar *types_number = NULL;
1197
1198 types_number = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE,
1199 "%u type",
1200 "%u types",
1201 n_associations),
1202 n_associations);
1203 cc_list_row_set_secondary_label (self->handler_row, types_number);
1204 }
1205
1206 header_title = g_strdup_printf (_("<b>%s</b> is used to open the following types of files and links."),
1207 g_app_info_get_display_name (info));
1208 adw_preferences_page_set_description (self->handler_page, header_title);
1209 }
1210
1211 /* --- usage section --- */
1212
1213 static void
1214 on_items_changed_cb (GListModel *list,
1215 guint position,
1216 guint removed,
1217 guint added,
1218 gpointer data)
1219 {
1220 CcApplicationsPanel *self = data;
1221
1222 if (g_list_model_get_n_items (list) == 0)
1223 gtk_stack_set_visible_child (self->app_listbox_stack,
1224 self->no_apps_page);
1225 else
1226 gtk_stack_set_visible_child (self->app_listbox_stack,
1227 GTK_WIDGET (self->app_listbox));
1228 }
1229
1230 static void
1231 update_total_size (CcApplicationsPanel *self)
1232 {
1233 g_autofree gchar *formatted_size = NULL;
1234 guint64 total;
1235
1236 total = self->app_size + self->data_size + self->cache_size;
1237 formatted_size = g_format_size (total);
1238 g_object_set (self->total, "info", formatted_size, NULL);
1239
1240 cc_list_row_set_secondary_label (self->storage, formatted_size);
1241 }
1242
1243 static void
1244 set_cache_size (GObject *source,
1245 GAsyncResult *res,
1246 gpointer data)
1247 {
1248 CcApplicationsPanel *self = data;
1249 g_autofree gchar *formatted_size = NULL;
1250 guint64 size;
1251 g_autoptr(GError) error = NULL;
1252
1253 if (!file_size_finish (G_FILE (source), res, &size, &error))
1254 {
1255 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1256 g_warning ("Failed to get flatpak cache size: %s", error->message);
1257 return;
1258 }
1259 self->cache_size = size;
1260
1261 formatted_size = g_format_size (self->cache_size);
1262 g_object_set (self->cache, "info", formatted_size, NULL);
1263
1264 gtk_widget_set_sensitive (GTK_WIDGET (self->clear_cache_button), self->cache_size > 0);
1265
1266 update_total_size (self);
1267 }
1268
1269 static void
1270 update_cache_row (CcApplicationsPanel *self,
1271 const gchar *app_id)
1272 {
1273 g_autoptr(GFile) dir = get_flatpak_app_dir (app_id, "cache");
1274 g_object_set (self->cache, "info", "...", NULL);
1275 file_size_async (dir, cc_panel_get_cancellable (CC_PANEL (self)), set_cache_size, self);
1276 }
1277
1278 static void
1279 set_data_size (GObject *source,
1280 GAsyncResult *res,
1281 gpointer data)
1282 {
1283 CcApplicationsPanel *self = data;
1284 g_autofree gchar *formatted_size = NULL;
1285 guint64 size;
1286 g_autoptr(GError) error = NULL;
1287
1288 if (!file_size_finish (G_FILE (source), res, &size, &error))
1289 {
1290 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1291 g_warning ("Failed to get flatpak data size: %s", error->message);
1292 return;
1293 }
1294 self->data_size = size;
1295
1296 formatted_size = g_format_size (self->data_size);
1297 g_object_set (self->data, "info", formatted_size, NULL);
1298
1299 update_total_size (self);
1300 }
1301
1302 static void
1303 update_data_row (CcApplicationsPanel *self,
1304 const gchar *app_id)
1305 {
1306 g_autoptr(GFile) dir = get_flatpak_app_dir (app_id, "data");
1307
1308 g_object_set (self->data, "info", "...", NULL);
1309 file_size_async (dir, cc_panel_get_cancellable (CC_PANEL (self)), set_data_size, self);
1310 }
1311
1312 static void
1313 cache_cleared (GObject *source,
1314 GAsyncResult *res,
1315 gpointer data)
1316 {
1317 CcApplicationsPanel *self = data;
1318 g_autoptr(GError) error = NULL;
1319
1320 if (!file_remove_finish (G_FILE (source), res, &error))
1321 {
1322 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1323 g_warning ("Failed to remove cache: %s", error->message);
1324 return;
1325 }
1326
1327 update_cache_row (self, self->current_app_id);
1328 }
1329
1330 static void
1331 clear_cache_cb (CcApplicationsPanel *self)
1332 {
1333 g_autoptr(GFile) dir = NULL;
1334
1335 if (self->current_app_id == NULL)
1336 return;
1337
1338 dir = get_flatpak_app_dir (self->current_app_id, "cache");
1339 file_remove_async (dir, cc_panel_get_cancellable (CC_PANEL (self)), cache_cleared, self);
1340 }
1341
1342 static void
1343 update_app_row (CcApplicationsPanel *self,
1344 const gchar *app_id)
1345 {
1346 g_autofree gchar *formatted_size = NULL;
1347
1348 if (g_str_has_prefix (app_id, PORTAL_SNAP_PREFIX))
1349 self->app_size = get_snap_app_size (app_id + strlen (PORTAL_SNAP_PREFIX));
1350 else
1351 self->app_size = get_flatpak_app_size (app_id);
1352 formatted_size = g_format_size (self->app_size);
1353 g_object_set (self->app, "info", formatted_size, NULL);
1354 update_total_size (self);
1355 }
1356
1357 static void
1358 update_app_sizes (CcApplicationsPanel *self,
1359 const gchar *app_id)
1360 {
1361 gtk_widget_set_sensitive (GTK_WIDGET (self->clear_cache_button), FALSE);
1362
1363 self->app_size = self->data_size = self->cache_size = 0;
1364
1365 update_app_row (self, app_id);
1366 update_cache_row (self, app_id);
1367 update_data_row (self, app_id);
1368 }
1369
1370 static void
1371 update_usage_section (CcApplicationsPanel *self,
1372 GAppInfo *info)
1373 {
1374 g_autofree gchar *portal_app_id = get_portal_app_id (info);
1375 gboolean has_builtin = FALSE;
1376
1377 if (portal_app_id != NULL)
1378 update_app_sizes (self, portal_app_id);
1379
1380 remove_static_permissions (self);
1381 has_builtin = add_static_permissions (self, info, portal_app_id);
1382 gtk_widget_set_visible (GTK_WIDGET (self->other_permissions_section), has_builtin);
1383
1384 gtk_widget_set_visible (GTK_WIDGET (self->usage_section), portal_app_id || has_builtin);
1385 }
1386
1387 /* --- panel setup --- */
1388
1389 static void
1390 update_panel (CcApplicationsPanel *self,
1391 GtkListBoxRow *row)
1392 {
1393 GAppInfo *info;
1394
1395 if (self->perm_store == NULL)
1396 {
1397 /* Async permission store not initialized, row will be re-activated in the callback */
1398 self->perm_store_pending_row = row;
1399 return;
1400 }
1401
1402 if (row == NULL)
1403 {
1404 g_message ("No app selected, try again");
1405 return;
1406 }
1407
1408 info = cc_applications_row_get_info (CC_APPLICATIONS_ROW (row));
1409
1410 adw_navigation_page_set_title (self->app_settings_page,
1411 g_app_info_get_display_name (info));
1412 adw_navigation_view_push_by_tag(self->navigation_view, "settings-box");
1413 gtk_widget_set_visible (GTK_WIDGET (self->view_details_button), gnome_software_is_installed ());
1414
1415 g_clear_pointer (&self->current_app_id, g_free);
1416 g_clear_pointer (&self->current_portal_app_id, g_free);
1417
1418 update_header_section (self, info);
1419 update_integration_section (self, info);
1420 update_handler_dialog (self, info);
1421 update_usage_section (self, info);
1422
1423 g_set_object (&self->current_app_info, info);
1424 self->current_app_id = get_app_id (info);
1425 self->current_portal_app_id = get_portal_app_id (info);
1426 }
1427
1428
1429 static gint
1430 compare_rows (gconstpointer a,
1431 gconstpointer b,
1432 gpointer data)
1433 {
1434 GAppInfo *item1 = (GAppInfo *) a;
1435 GAppInfo *item2 = (GAppInfo *) b;
1436
1437 g_autofree gchar *key1 = NULL;
1438 g_autofree gchar *key2 = NULL;
1439
1440 key1 = g_utf8_casefold (g_app_info_get_display_name (item1), -1);
1441 key2 = g_utf8_casefold (g_app_info_get_display_name (item2), -1);
1442
1443 const gchar *sort_key1 = g_utf8_collate_key (key1, -1);
1444 const gchar *sort_key2 = g_utf8_collate_key (key2, -1);
1445
1446 return strcmp (sort_key1, sort_key2);
1447 }
1448
1449 static void
1450 populate_applications (CcApplicationsPanel *self)
1451 {
1452 g_autolist(GObject) infos = NULL;
1453 GList *l;
1454
1455 g_list_store_remove_all (G_LIST_STORE (self->app_model));
1456 #ifdef HAVE_MALCONTENT
1457 g_signal_handler_block (self->manager, self->app_filter_id);
1458 #endif
1459
1460 infos = g_app_info_get_all ();
1461
1462 if (!infos)
1463 gtk_widget_set_visible (GTK_WIDGET (self->app_search_entry), 0);
1464 else
1465 gtk_widget_set_visible (GTK_WIDGET (self->app_search_entry), 1);
1466
1467 for (l = infos; l; l = l->next)
1468 {
1469 GAppInfo *info = l->data;
1470 GtkWidget *row;
1471 g_autofree gchar *id = NULL;
1472
1473 if (!g_app_info_should_show (info))
1474 continue;
1475
1476 #ifdef HAVE_MALCONTENT
1477 if (!mct_app_filter_is_appinfo_allowed (self->app_filter, info))
1478 continue;
1479 #endif
1480
1481 row = GTK_WIDGET (cc_applications_row_new (info));
1482 g_list_store_insert_sorted (G_LIST_STORE (self->app_model), info, compare_rows, NULL);
1483
1484 id = get_app_id (info);
1485 if (g_strcmp0 (id, self->current_app_id) == 0)
1486 gtk_list_box_select_row (self->app_listbox, GTK_LIST_BOX_ROW (row));
1487 }
1488 #ifdef HAVE_MALCONTENT
1489 g_signal_handler_unblock (self->manager, self->app_filter_id);
1490 #endif
1491 }
1492
1493 static gboolean
1494 filter_app_rows (GObject *item,
1495 gpointer data)
1496 {
1497 CcApplicationsPanel *self = CC_APPLICATIONS_PANEL (data);
1498 g_autofree gchar *app_name = NULL;
1499 g_autofree gchar *search_text = NULL;
1500 const gchar *text;
1501 GAppInfo *info = G_APP_INFO (item);
1502
1503 text = gtk_editable_get_text (GTK_EDITABLE (self->app_search_entry));
1504
1505 /* Only filter after the second character */
1506 if (g_utf8_strlen (text, -1) < 2)
1507 return TRUE;
1508
1509 app_name = cc_util_normalize_casefold_and_unaccent (g_app_info_get_name (info));
1510 search_text = cc_util_normalize_casefold_and_unaccent (text);
1511
1512 return g_strstr_len (app_name, -1, search_text) != NULL;
1513 }
1514
1515 #ifdef HAVE_MALCONTENT
1516 static void
1517 app_filter_changed_cb (MctAppFilter *app_filter,
1518 uid_t uid,
1519 CcApplicationsPanel *self)
1520 {
1521 populate_applications (self);
1522 }
1523 #endif
1524
1525 static void
1526 apps_changed (CcApplicationsPanel *self)
1527 {
1528 populate_applications (self);
1529 }
1530
1531 static void
1532 row_activated_cb (CcApplicationsPanel *self,
1533 GtkListBoxRow *row)
1534 {
1535 update_panel (self, row);
1536 }
1537
1538 static void
1539 on_perm_store_ready (GObject *source_object,
1540 GAsyncResult *res,
1541 gpointer data)
1542 {
1543 CcApplicationsPanel *self = data;
1544 GDBusProxy *proxy;
1545 g_autoptr(GError) error = NULL;
1546
1547 proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
1548 if (proxy == NULL)
1549 {
1550 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1551 g_warning ("Failed to connect to portal permission store: %s",
1552 error->message);
1553 return;
1554 }
1555
1556 self->perm_store = proxy;
1557
1558 if (self->perm_store_pending_row)
1559 g_signal_emit_by_name (self->perm_store_pending_row, "activate");
1560
1561 self->perm_store_pending_row = NULL;
1562 }
1563
1564 static void
1565 select_app (CcApplicationsPanel *self,
1566 const gchar *app_id,
1567 gboolean emit_activate)
1568 {
1569 GtkWidget *child;
1570
1571 for (child = gtk_widget_get_first_child (GTK_WIDGET (self->app_listbox));
1572 child;
1573 child = gtk_widget_get_next_sibling (child))
1574 {
1575 CcApplicationsRow *row = CC_APPLICATIONS_ROW (child);
1576 GAppInfo *info = cc_applications_row_get_info (row);
1577 if (g_str_has_prefix (g_app_info_get_id (info), app_id))
1578 {
1579 gtk_list_box_select_row (self->app_listbox, GTK_LIST_BOX_ROW (row));
1580 if (emit_activate)
1581 g_signal_emit_by_name (row, "activate");
1582 break;
1583 }
1584 }
1585 }
1586
1587 static void
1588 on_launch_button_clicked_cb (CcApplicationsPanel *self)
1589 {
1590 g_autoptr(GdkAppLaunchContext) context = NULL;
1591 g_autoptr(GError) error = NULL;
1592 GdkDisplay *display;
1593
1594 if (!self->current_app_info)
1595 return;
1596
1597 display = gtk_widget_get_display (GTK_WIDGET (self));
1598 context = gdk_display_get_app_launch_context (display);
1599
1600 g_app_info_launch (self->current_app_info,
1601 NULL,
1602 G_APP_LAUNCH_CONTEXT (context),
1603 &error);
1604
1605 if (error)
1606 g_warning ("Error launching app: %s", error->message);
1607 }
1608
1609 static void
1610 on_app_search_entry_activated_cb (CcApplicationsPanel *self)
1611 {
1612 GtkListBoxRow *row;
1613
1614 row = gtk_list_box_get_row_at_y (self->app_listbox, 0);
1615
1616 if (!row)
1617 return;
1618
1619 /* Show the app */
1620 gtk_list_box_select_row (self->app_listbox, row);
1621 g_signal_emit_by_name (row, "activate");
1622
1623 /* Cleanup the entry */
1624 gtk_editable_set_text (GTK_EDITABLE (self->app_search_entry), "");
1625 gtk_widget_grab_focus (GTK_WIDGET (self->app_search_entry));
1626 }
1627
1628 static void
1629 on_app_search_entry_search_changed_cb (CcApplicationsPanel *self)
1630 {
1631 gtk_filter_changed (self->filter, GTK_FILTER_CHANGE_DIFFERENT);
1632 }
1633
1634 static void
1635 on_app_search_entry_search_stopped_cb (CcApplicationsPanel *self)
1636 {
1637 gtk_editable_set_text (GTK_EDITABLE (self->app_search_entry), "");
1638 }
1639
1640 static void
1641 cc_applications_panel_dispose (GObject *object)
1642 {
1643 CcApplicationsPanel *self = CC_APPLICATIONS_PANEL (object);
1644
1645 g_clear_pointer (&self->sandbox_info_button, gtk_widget_unparent);
1646
1647 remove_all_handler_rows (self);
1648 #ifdef HAVE_SNAP
1649 remove_snap_permissions (self);
1650 #endif
1651 g_clear_object (&self->monitor);
1652 g_clear_object (&self->perm_store);
1653
1654 G_OBJECT_CLASS (cc_applications_panel_parent_class)->dispose (object);
1655 }
1656
1657 static void
1658 cc_applications_panel_finalize (GObject *object)
1659 {
1660 CcApplicationsPanel *self = CC_APPLICATIONS_PANEL (object);
1661 #ifdef HAVE_MALCONTENT
1662 if (self->app_filter != NULL && self->app_filter_id != 0)
1663 {
1664 g_signal_handler_disconnect (self->manager, self->app_filter_id);
1665 self->app_filter_id = 0;
1666 }
1667 g_clear_pointer (&self->app_filter, mct_app_filter_unref);
1668
1669 g_clear_object (&self->manager);
1670 #endif
1671 g_clear_object (&self->media_handling_settings);
1672 g_clear_object (&self->notification_settings);
1673 g_clear_object (&self->location_settings);
1674 g_clear_object (&self->privacy_settings);
1675 g_clear_object (&self->search_settings);
1676
1677 g_clear_object (&self->current_app_info);
1678 g_clear_pointer (&self->current_app_id, g_free);
1679 g_clear_pointer (&self->current_portal_app_id, g_free);
1680 g_clear_pointer (&self->globs, g_hash_table_unref);
1681 g_clear_pointer (&self->search_providers, g_hash_table_unref);
1682
1683 G_OBJECT_CLASS (cc_applications_panel_parent_class)->finalize (object);
1684 }
1685
1686 static void
1687 cc_applications_panel_set_property (GObject *object,
1688 guint property_id,
1689 const GValue *value,
1690 GParamSpec *pspec)
1691 {
1692 switch (property_id)
1693 {
1694 case PROP_PARAMETERS:
1695 {
1696 GVariant *parameters, *v;
1697 const gchar *first_arg = NULL;
1698
1699 parameters = g_value_get_variant (value);
1700 if (parameters == NULL)
1701 return;
1702
1703 if (g_variant_n_children (parameters) > 0)
1704 {
1705 g_variant_get_child (parameters, 0, "v", &v);
1706 if (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING))
1707 first_arg = g_variant_get_string (v, NULL);
1708 else
1709 g_warning ("Wrong type for the second argument GVariant, expected 's' but got '%s'",
1710 (gchar *)g_variant_get_type (v));
1711 g_variant_unref (v);
1712
1713 select_app (CC_APPLICATIONS_PANEL (object), first_arg, TRUE);
1714 }
1715
1716 return;
1717 }
1718 }
1719
1720 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1721 }
1722
1723 static void
1724 cc_applications_panel_constructed (GObject *object)
1725 {
1726 G_OBJECT_CLASS (cc_applications_panel_parent_class)->constructed (object);
1727 }
1728
1729 static void
1730 cc_applications_panel_class_init (CcApplicationsPanelClass *klass)
1731 {
1732 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1733 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1734
1735 g_type_ensure (CC_TYPE_DEFAULT_APPS_PAGE);
1736 g_type_ensure (CC_TYPE_REMOVABLE_MEDIA_SETTINGS);
1737
1738 object_class->dispose = cc_applications_panel_dispose;
1739 object_class->finalize = cc_applications_panel_finalize;
1740 object_class->constructed = cc_applications_panel_constructed;
1741 object_class->set_property = cc_applications_panel_set_property;
1742
1743 g_object_class_override_property (object_class, PROP_PARAMETERS, "parameters");
1744
1745 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/applications/cc-applications-panel.ui");
1746
1747 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, app);
1748 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, app_icon_image);
1749 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, app_listbox);
1750 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, app_name_label);
1751 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, app_search_entry);
1752 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, app_settings_page);
1753 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, other_permissions_section);
1754 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, autorun_never_row);
1755 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, builtin);
1756 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, builtin_dialog);
1757 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, builtin_page);
1758 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, builtin_list);
1759 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, cache);
1760 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, camera);
1761 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, clear_cache_button);
1762 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, data);
1763 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, default_apps_page);
1764 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, no_apps_page);
1765 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, app_listbox_stack);
1766 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, handler_dialog);
1767 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, handler_page);
1768 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, handler_file_group);
1769 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, handler_link_group);
1770 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, handler_row);
1771 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, handler_reset);
1772 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, install_button);
1773 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, integration_section);
1774 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, launch_button);
1775 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, location);
1776 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, microphone);
1777 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, navigation_view);
1778 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, no_camera);
1779 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, no_location);
1780 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, no_microphone);
1781 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, no_search);
1782 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, no_sound);
1783 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, notification);
1784 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, background);
1785 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, wallpaper);
1786 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, removable_media_settings);
1787 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, sandbox_banner);
1788 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, sandbox_info_button);
1789 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, screenshot);
1790 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, shortcuts);
1791 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, search);
1792 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, sound);
1793 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, storage);
1794 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, storage_dialog);
1795 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, total);
1796 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, usage_section);
1797 gtk_widget_class_bind_template_child (widget_class, CcApplicationsPanel, view_details_button);
1798
1799 gtk_widget_class_bind_template_callback (widget_class, camera_cb);
1800 gtk_widget_class_bind_template_callback (widget_class, location_cb);
1801 gtk_widget_class_bind_template_callback (widget_class, microphone_cb);
1802 gtk_widget_class_bind_template_callback (widget_class, search_cb);
1803 gtk_widget_class_bind_template_callback (widget_class, notification_cb);
1804 gtk_widget_class_bind_template_callback (widget_class, background_cb);
1805 gtk_widget_class_bind_template_callback (widget_class, wallpaper_cb);
1806 gtk_widget_class_bind_template_callback (widget_class, screenshot_cb);
1807 gtk_widget_class_bind_template_callback (widget_class, shortcuts_cb);
1808 gtk_widget_class_bind_template_callback (widget_class, privacy_link_cb);
1809 gtk_widget_class_bind_template_callback (widget_class, sound_cb);
1810 gtk_widget_class_bind_template_callback (widget_class, clear_cache_cb);
1811 gtk_widget_class_bind_template_callback (widget_class, open_software_cb);
1812 gtk_widget_class_bind_template_callback (widget_class, handler_reset_cb);
1813 gtk_widget_class_bind_template_callback (widget_class, on_launch_button_clicked_cb);
1814 gtk_widget_class_bind_template_callback (widget_class, on_app_search_entry_activated_cb);
1815 gtk_widget_class_bind_template_callback (widget_class, on_app_search_entry_search_changed_cb);
1816 gtk_widget_class_bind_template_callback (widget_class, on_app_search_entry_search_stopped_cb);
1817 }
1818
1819 static GtkWidget *
1820 app_row_new (gpointer item,
1821 gpointer user_data)
1822 {
1823 GAppInfo *info = item;
1824
1825 return GTK_WIDGET (cc_applications_row_new (info));
1826 }
1827
1828 static void
1829 cc_applications_panel_init (CcApplicationsPanel *self)
1830 {
1831 #ifdef HAVE_MALCONTENT
1832 g_autoptr(GDBusConnection) system_bus = NULL;
1833 g_autoptr(GError) error = NULL;
1834 #endif
1835
1836 g_resources_register (cc_applications_get_resource ());
1837
1838 g_type_ensure (CC_TYPE_INFO_ROW);
1839 g_type_ensure (CC_TYPE_LIST_ROW);
1840
1841 gtk_widget_init_template (GTK_WIDGET (self));
1842
1843 gtk_widget_set_visible (GTK_WIDGET (self->install_button), gnome_software_is_installed ());
1844
1845 g_signal_connect_object (self->app_listbox, "row-activated",
1846 G_CALLBACK (row_activated_cb), self, G_CONNECT_SWAPPED);
1847
1848 g_signal_connect_object (self->view_details_button,
1849 "clicked",
1850 G_CALLBACK (open_software_cb),
1851 self,
1852 G_CONNECT_SWAPPED);
1853
1854 self->filter = GTK_FILTER (gtk_custom_filter_new ((GtkCustomFilterFunc) filter_app_rows,
1855 self, NULL));
1856
1857 self->app_model = G_LIST_MODEL (g_list_store_new (G_TYPE_APP_INFO));
1858 self->filter_model = G_LIST_MODEL (gtk_filter_list_model_new (self->app_model,
1859 GTK_FILTER (self->filter)));
1860 g_signal_connect (self->filter_model, "items-changed",
1861 G_CALLBACK (on_items_changed_cb), self);
1862
1863 gtk_list_box_bind_model (self->app_listbox,
1864 self->filter_model,
1865 app_row_new,
1866 NULL,
1867 NULL);
1868
1869 self->location_settings = g_settings_new ("org.gnome.system.location");
1870 self->privacy_settings = g_settings_new ("org.gnome.desktop.privacy");
1871 self->search_settings = g_settings_new ("org.gnome.desktop.search-providers");
1872 self->media_handling_settings = g_settings_new ("org.gnome.desktop.media-handling");
1873
1874 g_settings_bind (self->media_handling_settings,
1875 "autorun-never",
1876 self->autorun_never_row,
1877 "active",
1878 G_SETTINGS_BIND_INVERT_BOOLEAN);
1879
1880 #ifdef HAVE_MALCONTENT
1881 /* FIXME: should become asynchronous */
1882 system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, self->cancellable, &error);
1883 if (system_bus == NULL)
1884 {
1885 g_warning ("Error getting system bus while setting up app permissions: %s", error->message);
1886 return;
1887 }
1888
1889 /* Load the user’s parental controls settings too, so we can filter the list. */
1890 self->manager = mct_manager_new (system_bus);
1891 self->app_filter = mct_manager_get_app_filter (self->manager,
1892 getuid (),
1893 MCT_MANAGER_GET_VALUE_FLAGS_NONE,
1894 self->cancellable,
1895 &error);
1896 if (error)
1897 {
1898 g_warning ("Error retrieving app filter: %s", error->message);
1899 return;
1900 }
1901
1902 self->app_filter_id = g_signal_connect (self->manager, "app-filter-changed",
1903 G_CALLBACK (app_filter_changed_cb), self);
1904 #endif
1905 populate_applications (self);
1906
1907 self->monitor = g_app_info_monitor_get ();
1908 self->monitor_id = g_signal_connect_object (self->monitor, "changed", G_CALLBACK (apps_changed), self, G_CONNECT_SWAPPED);
1909
1910 g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
1911 G_DBUS_PROXY_FLAGS_NONE,
1912 NULL,
1913 "org.freedesktop.impl.portal.PermissionStore",
1914 "/org/freedesktop/impl/portal/PermissionStore",
1915 "org.freedesktop.impl.portal.PermissionStore",
1916 cc_panel_get_cancellable (CC_PANEL (self)),
1917 on_perm_store_ready,
1918 self);
1919
1920 self->globs = parse_globs ();
1921 self->search_providers = parse_search_providers ();
1922 }
1923