Skip to content

Commit

Permalink
Initial background implementation
Browse files Browse the repository at this point in the history
Add a portal backend for getting the list of active
applications, and for showing a notification about
apps that are running in the background.
  • Loading branch information
Matthias Clasen committed May 13, 2019
1 parent a1b12b6 commit d3f0171
Show file tree
Hide file tree
Showing 5 changed files with 328 additions and 1 deletion.
2 changes: 1 addition & 1 deletion data/gtk.portal
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[portal]
DBusName=org.freedesktop.impl.portal.desktop.gtk
Interfaces=org.freedesktop.impl.portal.FileChooser;org.freedesktop.impl.portal.AppChooser;org.freedesktop.impl.portal.Print;org.freedesktop.impl.portal.Screenshot;org.freedesktop.impl.portal.Notification;org.freedesktop.impl.portal.Inhibit;org.freedesktop.impl.portal.Access;org.freedesktop.impl.portal.Account;org.freedesktop.impl.portal.Email;org.freedesktop.impl.portal.ScreenCast;org.freedesktop.impl.portal.RemoteDesktop;org.freedesktop.impl.portal.Lockdown;
Interfaces=org.freedesktop.impl.portal.FileChooser;org.freedesktop.impl.portal.AppChooser;org.freedesktop.impl.portal.Print;org.freedesktop.impl.portal.Screenshot;org.freedesktop.impl.portal.Notification;org.freedesktop.impl.portal.Inhibit;org.freedesktop.impl.portal.Access;org.freedesktop.impl.portal.Account;org.freedesktop.impl.portal.Email;org.freedesktop.impl.portal.ScreenCast;org.freedesktop.impl.portal.RemoteDesktop;org.freedesktop.impl.portal.Lockdown;org.freedesktop.impl.portal.Background;
UseIn=gnome
4 changes: 4 additions & 0 deletions src/Makefile.am.inc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ $(dbus_built_sources): src/Makefile.am.inc
$(DESKTOP_PORTAL_INTERFACES_DIR)/org.freedesktop.impl.portal.ScreenCast.xml \
$(DESKTOP_PORTAL_INTERFACES_DIR)/org.freedesktop.impl.portal.RemoteDesktop.xml \
$(DESKTOP_PORTAL_INTERFACES_DIR)/org.freedesktop.impl.portal.Lockdown.xml \
$(DESKTOP_PORTAL_INTERFACES_DIR)/org.freedesktop.impl.portal.Background.xml \
$(NULL)

shell_built_sources = src/shell-dbus.c src/shell-dbus.h
Expand All @@ -35,6 +36,7 @@ $(shell_built_sources): src/Makefile.am.inc
$(top_srcdir)/data/org.gnome.Mutter.ScreenCast.xml \
$(top_srcdir)/data/org.gnome.Mutter.RemoteDesktop.xml \
$(top_srcdir)/data/org.gnome.Shell.Screenshot.xml \
$(top_srcdir)/data/org.gnome.Shell.Introspect.xml \
$(top_srcdir)/data/org.gtk.Notifications.xml \
$(top_srcdir)/data/org.gnome.SessionManager.xml \
$(top_srcdir)/data/org.gnome.ScreenSaver.xml \
Expand Down Expand Up @@ -123,6 +125,8 @@ xdg_desktop_portal_gtk_SOURCES = \
src/displaystatetracker.h \
src/lockdown.c \
src/lockdown.h \
src/background.c \
src/background.h \
$(NULL)

nodist_xdg_desktop_portal_gtk_SOURCES = \
Expand Down
291 changes: 291 additions & 0 deletions src/background.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
#define _GNU_SOURCE 1

#include "config.h"

#include <errno.h>
#include <locale.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <gtk/gtk.h>

#include <glib/gi18n.h>

#include "xdg-desktop-portal-dbus.h"
#include "shell-dbus.h"

#include "background.h"
#include "fdonotification.h"
#include "request.h"
#include "utils.h"

static OrgGnomeShellIntrospect *shell;

typedef enum { BACKGROUND, RUNNING, ACTIVE } AppState;

static gboolean
handle_get_app_state (XdpImplBackground *object,
GDBusMethodInvocation *invocation)
{
g_autoptr(GVariant) windows = NULL;
g_autoptr(GHashTable) app_states = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
g_autoptr(GError) error = NULL;
GVariantBuilder builder;
GHashTableIter iter;
const char *key;
gpointer value;

g_debug ("background: handle GetAppState");

if (!org_gnome_shell_introspect_call_get_windows_sync (shell, &windows, NULL, &error))
{
g_dbus_method_invocation_return_error (invocation,
XDG_DESKTOP_PORTAL_ERROR,
XDG_DESKTOP_PORTAL_ERROR_FAILED,
"Could not get window list: %s", error->message);
return TRUE;
}

if (windows)
{
g_autoptr(GVariantIter) iter = g_variant_iter_new (windows);
GVariant *dict;
while (g_variant_iter_loop (iter, "{t@a{sv}}", NULL, &dict))
{
const char *app_id = NULL;
char *app;
gboolean hidden = FALSE;
gboolean focus = FALSE;
AppState state = BACKGROUND;

g_variant_lookup (dict, "app-id", "&s", &app_id);
g_variant_lookup (dict, "is-hidden", "b", &hidden);
g_variant_lookup (dict, "has-focus", "b", &focus);

if (app_id == NULL)
continue;

app = g_strdup (app_id);
if (g_str_has_suffix (app, ".desktop"))
app[strlen (app) - strlen (".desktop")] = '\0';

state = GPOINTER_TO_INT (g_hash_table_lookup (app_states, app));
if (!hidden)
state = MAX (state, RUNNING);
if (focus)
state = MAX (state, ACTIVE);

g_hash_table_insert (app_states, app, GINT_TO_POINTER (state));
}
}

g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
g_hash_table_iter_init (&iter, app_states);
while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
{
g_variant_builder_add (&builder, "{sv}", key, g_variant_new_uint32 (GPOINTER_TO_UINT (value)));
}

xdp_impl_background_complete_get_app_state (object,
invocation,
g_variant_builder_end (&builder));

return TRUE;
}

typedef struct {
XdpImplBackground *impl;
GDBusMethodInvocation *invocation;
Request *request;
char *id;
gboolean allow;
} BackgroundHandle;

static void
background_handle_free (gpointer data)
{
BackgroundHandle *handle = data;

g_object_unref (handle->request);
g_free (handle->id);

g_free (handle);
}

static void
background_handle_close (BackgroundHandle *handle)
{
GDBusConnection *connection;

connection = g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (handle->impl));

fdo_remove_notification (connection, handle->request->app_id, handle->id);
background_handle_free (handle);
}

static void
send_response (BackgroundHandle *handle)
{
GVariantBuilder opt_builder;
int response = 0;

g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&opt_builder, "{sv}", "allow", g_variant_new_boolean (handle->allow));

if (handle->request->exported)
request_unexport (handle->request);

xdp_impl_background_complete_request_background (handle->impl,
handle->invocation,
response,
g_variant_builder_end (&opt_builder));

background_handle_close (handle);
}

static void
activate_action (GDBusConnection *connection,
const char *app_id,
const char *id,
const char *name,
GVariant *parameter,
gpointer data)
{
BackgroundHandle *handle = data;

if (g_str_equal (name, "allow"))
{
g_debug ("Allowing app %s to run in background", handle->request->app_id);
handle->allow = TRUE;
}
else if (g_str_equal (name, "forbid"))
{
g_debug ("Forbid app %s to run in background", handle->request->app_id);
handle->allow = FALSE;
}
else
{
g_debug ("Unexpected action for app %s", handle->request->app_id);
handle->allow = FALSE;
}

send_response (handle);
}

static gboolean
handle_close (XdpImplRequest *object,
GDBusMethodInvocation *invocation,
BackgroundHandle *handle)
{
GVariantBuilder opt_builder;

g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);

xdp_impl_background_complete_request_background (handle->impl,
handle->invocation,
2,
g_variant_builder_end (&opt_builder));

if (handle->request->exported)
request_unexport (handle->request);

background_handle_close (handle);

xdp_impl_request_complete_close (object, invocation);

return TRUE;
}

static int count;

static gboolean
handle_request_background (XdpImplBackground *object,
GDBusMethodInvocation *invocation,
const char *arg_handle,
const char *arg_app_id,
const char *arg_name)
{
g_autofree char *body = NULL;
GVariantBuilder builder;
GVariantBuilder bbuilder;
GVariantBuilder button;
GDBusConnection *connection;
const char *sender;
g_autoptr (Request) request = NULL;
BackgroundHandle *handle;

g_debug ("background: handle RequestBackground");

sender = g_dbus_method_invocation_get_sender (invocation);
request = request_new (sender, arg_app_id, arg_handle);

g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&builder, "{sv}", "title", g_variant_new_string (_("Background activity")));

body = g_strdup_printf (_("%s is running in the background."), arg_name);
g_variant_builder_add (&builder, "{sv}", "body", g_variant_new_string (body));

g_variant_builder_init (&bbuilder, G_VARIANT_TYPE ("aa{sv}"));
g_variant_builder_init (&button, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&button, "{sv}", "label", g_variant_new_string (_("Allow")));
g_variant_builder_add (&button, "{sv}", "action", g_variant_new_string ("allow"));
g_variant_builder_add (&button, "{sv}", "target", g_variant_new_string (arg_app_id));

g_variant_builder_add (&bbuilder, "@a{sv}", g_variant_builder_end (&button));

g_variant_builder_init (&button, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&button, "{sv}", "label", g_variant_new_string (_("Forbid")));
g_variant_builder_add (&button, "{sv}", "action", g_variant_new_string ("forbid"));
g_variant_builder_add (&button, "{sv}", "target", g_variant_new_string (arg_app_id));

g_variant_builder_add (&bbuilder, "@a{sv}", g_variant_builder_end (&button));

g_variant_builder_add (&builder, "{sv}", "buttons", g_variant_builder_end (&bbuilder));

handle = g_new0 (BackgroundHandle, 1);
handle->impl = object;
handle->invocation = invocation;
handle->request = g_object_ref (request);
handle->id = g_strdup_printf ("request_background_%d", count++);

g_signal_connect (request, "handle-close", G_CALLBACK (handle_close), handle);

connection = g_dbus_method_invocation_get_connection (invocation);

fdo_add_notification (connection, "", handle->id, g_variant_builder_end (&builder), activate_action, handle);

request_export (request, connection);

return TRUE;
}

gboolean
background_init (GDBusConnection *bus,
GError **error)
{
GDBusInterfaceSkeleton *helper;

shell = org_gnome_shell_introspect_proxy_new_sync (bus,
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
"org.gnome.Shell",
"/org/gnome/Shell/Introspect",
NULL,
NULL);

helper = G_DBUS_INTERFACE_SKELETON (xdp_impl_background_skeleton_new ());

g_signal_connect (helper, "handle-get-app-state", G_CALLBACK (handle_get_app_state), NULL);
g_signal_connect (helper, "handle-request-background", G_CALLBACK (handle_request_background), NULL);

if (!g_dbus_interface_skeleton_export (helper,
bus,
DESKTOP_PORTAL_OBJECT_PATH,
error))
return FALSE;

g_debug ("providing %s", g_dbus_interface_skeleton_get_info (helper)->name);

return TRUE;
}
25 changes: 25 additions & 0 deletions src/background.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright © 2019 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Matthias Clasen <[email protected]>
*/

#pragma once

#include <gio/gio.h>

gboolean background_init (GDBusConnection *bus, GError **error);
7 changes: 7 additions & 0 deletions src/xdg-desktop-portal-gtk.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
#include "screencast.h"
#include "remotedesktop.h"
#include "lockdown.h"
#include "background.h"


static GMainLoop *loop = NULL;
Expand Down Expand Up @@ -174,6 +175,12 @@ on_bus_acquired (GDBusConnection *connection,
g_warning ("error: %s\n", error->message);
g_clear_error (&error);
}

if (!background_init (connection, &error))
{
g_warning ("error: %s\n", error->message);
g_clear_error (&error);
}
}

static void
Expand Down

0 comments on commit d3f0171

Please sign in to comment.