/*
* Copyright © 2010 Codethink Limited
* Copyright © 2012 Red Hat , Inc .
* Copyright © 2013 Canonical Limited
*
* This library 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 licence , 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/>.
*
* Author : Ryan Lortie < desrt @ desrt . ca >
* Matthias Clasen < mclasen @ redhat . com >
*/
#include "config.h"
#include "gtkapplicationprivate.h"
#include "gtksettings.h"
#include "gtkprivate.h"
#include <glib/gi18n-lib.h>
#include "gdk/gdkconstructorprivate.h"
#include "gtktypebuiltins.h"
#ifdef G_OS_UNIX
#include <gio/gunixfdlist.h>
#endif
#include <glib/gstdio.h>
G_DEFINE_TYPE (GtkApplicationImplDBus, gtk_application_impl_dbus, GTK_TYPE_APPLICATION_IMPL)
#define DBUS_BUS_NAME "org.freedesktop.DBus"
#define DBUS_OBJECT_PATH "/org/freedesktop/DBus"
#define DBUS_BUS_INTERFACE "org.freedesktop.DBus"
static GDBusProxy*
gtk_application_get_proxy_if_service_present (GDBusConnection *connection,
GDBusProxyFlags flags,
const char *bus_name,
const char *object_path,
const char *interface,
GError **error)
{
GDBusProxy *proxy;
char *owner;
proxy = g_dbus_proxy_new_sync (connection,
flags,
NULL,
bus_name,
object_path,
interface,
NULL,
error);
if (!proxy)
return NULL;
/* is there anyone actually providing the service? */
owner = g_dbus_proxy_get_name_owner (proxy);
if (owner == NULL)
{
g_clear_object (&proxy);
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER,
"The name %s is not owned" , bus_name);
}
else
g_free (owner);
return proxy;
}
#ifdef G_HAS_CONSTRUCTORS
#ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA
#pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(unset_desktop_autostart_id)
#endif
G_DEFINE_CONSTRUCTOR(unset_desktop_autostart_id)
#endif
static void
unset_desktop_autostart_id (void )
{
/* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to
* use the same client id .
*/
g_unsetenv ("DESKTOP_AUTOSTART_ID" );
}
enum {
UNKNOWN = 0 ,
RUNNING = 1 ,
QUERY_END = 2 ,
ENDING = 3
};
static void
screensaver_signal_portal (GDBusConnection *connection,
const char *sender_name,
const char *object_path,
const char *interface_name,
const char *signal_name,
GVariant *parameters,
gpointer data)
{
GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *)data;
gboolean active;
GVariant *state;
guint32 session_state = UNKNOWN;
if (!g_str_equal (signal_name, "StateChanged" ))
return ;
g_variant_get (parameters, "(o@a{sv})" , NULL, &state);
g_variant_lookup (state, "screensaver-active" , "b" , &active);
gtk_application_set_screensaver_active (dbus->impl.application, active);
g_variant_lookup (state, "session-state" , "u" , &session_state);
g_variant_unref (state);
if (session_state != dbus->session_state)
{
dbus->session_state = session_state;
/* Note that we'll only ever get here if we get a session-state,
* in which case , the interface is new enough to have QueryEndResponse .
*/
if (session_state == ENDING)
{
g_application_quit (G_APPLICATION (dbus->impl.application));
}
else if (session_state == QUERY_END)
{
g_signal_emit_by_name (dbus->impl.application, "query-end" );
g_dbus_proxy_call (dbus->inhibit_proxy,
"QueryEndResponse" ,
g_variant_new ("(o)" , dbus->session_path),
G_DBUS_CALL_FLAGS_NONE,
G_MAXINT,
NULL,
NULL, NULL);
}
}
}
static void
create_monitor_cb (GObject *source,
GAsyncResult *result,
gpointer data)
{
GDBusProxy *proxy = G_DBUS_PROXY (source);
GError *error = NULL;
GVariant *ret = NULL;
ret = g_dbus_proxy_call_finish (proxy, result, &error);
if (ret == NULL)
{
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_warning ("Creating a portal monitor failed: %s" ,
error ? error->message : "" );
g_clear_error (&error);
return ;
}
g_variant_unref (ret);
}
static char *
get_state_file (GtkApplicationImpl *impl,
const char *instance_id_override)
{
GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
const char *app_id;
const char *instance_id;
const char *dir;
app_id = g_application_get_application_id (G_APPLICATION (impl->application));
if (!app_id)
return NULL;
if (instance_id_override)
instance_id = instance_id_override;
else if (dbus->instance_id)
instance_id = dbus->instance_id;
else
{
GTK_DEBUG (SESSION, "Session didn't report an instance_id, so we're using \" fallback\"!" );
instance_id = "fallback" ;
}
dir = g_get_user_state_dir ();
return g_strconcat (dir, G_DIR_SEPARATOR_S, app_id, "@" , instance_id, ".state" , NULL);
}
static void
debug_override_restore_reason (GtkRestoreReason *reason)
{
const char *str = g_getenv ("GTK_OVERRIDE_RESTORE_REASON" );
if (G_LIKELY (!str))
return ;
else if (strcmp (str, "pristine" ) == 0 )
*reason = GTK_RESTORE_REASON_PRISTINE;
else if (strcmp (str, "launch" ) == 0 )
*reason = GTK_RESTORE_REASON_LAUNCH;
else if (strcmp (str, "recover" ) == 0 )
*reason = GTK_RESTORE_REASON_RECOVER;
else if (strcmp (str, "restore" ) == 0 )
*reason = GTK_RESTORE_REASON_RESTORE;
else
g_warning ("Unknown GTK_OVERRIDE_RESTORE_REASON value: %s" , str);
}
static gboolean
request_restore (GtkApplicationImplDBus *dbus)
{
GtkApplicationImpl *impl = (GtkApplicationImpl*) dbus;
const char *app_id;
GError *error = NULL;
GVariant *reply;
GVariantIter *discard_iter;
gboolean discarded_any = FALSE ;
GVariantBuilder discarded;
const char *discard = NULL;
app_id = g_application_get_application_id (G_APPLICATION (dbus->impl.application));
reply = g_dbus_connection_call_sync (dbus->session,
"org.gnome.SessionManager" ,
"/org/gnome/SessionManager" ,
"org.gnome.SessionManager" ,
"RegisterRestore" ,
g_variant_new ("(ss)" , app_id, "" ),
G_VARIANT_TYPE ("(usas)" ),
G_DBUS_CALL_FLAGS_NONE,
-1 ,
NULL,
&error);
if (!reply)
{
if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD))
GTK_DEBUG (SESSION, "gnome-session does not support session saving" );
else
g_warning ("Failed to register restore: %s" , error->message);
g_clear_error (&error);
return FALSE ;
}
g_variant_get (reply, "(usas)" , &dbus->reason, &dbus->instance_id, &discard_iter);
dbus->save_restore_registered = TRUE ;
GTK_DEBUG (SESSION, "Registered restore, reason %s instance-id %s" ,
g_enum_get_value (g_type_class_get (GTK_TYPE_RESTORE_REASON), dbus->reason)->value_nick,
dbus->instance_id);
g_variant_builder_init (&discarded, G_VARIANT_TYPE ("(sas)" ));
g_variant_builder_add (&discarded, "s" , app_id);
g_variant_builder_open (&discarded, G_VARIANT_TYPE ("as" ));
while (g_variant_iter_loop (discard_iter, "s" , &discard))
{
char *path;
GTK_DEBUG (SESSION, "Cleaning up instance id: %s" , discard);
path = get_state_file (impl, discard);
if (g_remove (path) < 0 )
{
g_warning ("Failed to remove discarded state file '%s': %m" , path);
g_free (path);
continue ;
}
g_free (path);
discarded_any = TRUE ;
g_variant_builder_add (&discarded, "s" , discard);
}
g_variant_builder_close (&discarded);
g_variant_iter_free (discard_iter);
if (discarded_any)
g_dbus_connection_call_sync (dbus->session,
"org.gnome.SessionManager" ,
"/org/gnome/SessionManager" ,
"org.gnome.SessionManager" ,
"DeletedInstanceIds" ,
g_variant_builder_end (&discarded),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1 ,
NULL,
NULL);
else
g_variant_builder_clear (&discarded);
g_variant_unref (reply);
return TRUE ;
}
static void
drop_restore_request (GtkApplicationImplDBus *dbus)
{
const char *app_id;
app_id = g_application_get_application_id (G_APPLICATION (dbus->impl.application));
g_dbus_connection_call (dbus->session,
"org.gnome.SessionManager" ,
"/org/gnome/SessionManager" ,
"org.gnome.SessionManager" ,
"UnregisterRestore" ,
g_variant_new ("(ss)" , app_id, dbus->instance_id),
NULL, G_DBUS_CALL_FLAGS_NONE,
-1 , NULL, NULL, NULL);
dbus->reason = GTK_RESTORE_REASON_LAUNCH;
g_clear_pointer (&dbus->instance_id, g_free);
dbus->save_restore_registered = FALSE ;
}
static void
gtk_application_impl_dbus_startup (GtkApplicationImpl *impl,
gboolean support_save)
{
GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
GError *error = NULL;
char *token;
GVariantBuilder opt_builder;
#ifndef G_HAS_CONSTRUCTORS
unset_desktop_autostart_id ();
#endif
dbus->session = g_application_get_dbus_connection (G_APPLICATION (impl->application));
if (!dbus->session)
return ;
if (!support_save || !request_restore (dbus))
{
dbus->reason = GTK_RESTORE_REASON_LAUNCH;
dbus->instance_id = NULL;
}
debug_override_restore_reason (&dbus->reason);
dbus->application_id = g_application_get_application_id (G_APPLICATION (impl->application));
dbus->object_path = g_application_get_dbus_object_path (G_APPLICATION (impl->application));
dbus->unique_name = g_dbus_connection_get_unique_name (dbus->session);
if (!gdk_display_should_use_portal (impl->display, PORTAL_INHIBIT_INTERFACE, 0 ))
return ;
dbus->inhibit_proxy = gtk_application_get_proxy_if_service_present (dbus->session,
G_DBUS_PROXY_FLAGS_NONE,
PORTAL_BUS_NAME,
PORTAL_OBJECT_PATH,
PORTAL_INHIBIT_INTERFACE,
&error);
if (error)
{
g_debug ("Failed to get an inhibit portal proxy: %s" , error->message);
g_clear_error (&error);
return ;
}
/* Monitor screensaver state */
dbus->session_path = gtk_get_portal_session_path (dbus->session, &token);
dbus->state_changed_handler =
g_dbus_connection_signal_subscribe (dbus->session,
PORTAL_BUS_NAME,
PORTAL_INHIBIT_INTERFACE,
"StateChanged" ,
PORTAL_OBJECT_PATH,
NULL,
G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
screensaver_signal_portal,
dbus,
NULL);
g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&opt_builder, "{sv}" ,
"session_handle_token" , g_variant_new_string (token));
dbus->cancellable = g_cancellable_new ();
g_dbus_proxy_call (dbus->inhibit_proxy,
"CreateMonitor" ,
g_variant_new ("(sa{sv})" , "" , &opt_builder),
G_DBUS_CALL_FLAGS_NONE,
G_MAXINT,
dbus->cancellable,
create_monitor_cb, dbus);
g_free (token);
}
static void
gtk_application_impl_dbus_shutdown (GtkApplicationImpl *impl)
{
GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
g_cancellable_cancel (dbus->cancellable);
if (dbus->save_restore_registered)
drop_restore_request (dbus);
}
GQuark gtk_application_impl_dbus_export_id_quark (void );
G_DEFINE_QUARK (GtkApplicationImplDBus export id, gtk_application_impl_dbus_export_id)
GQuark gtk_application_impl_dbus_window_state_quark (void );
G_DEFINE_QUARK (GtkApplicationImplDBus window state, gtk_application_impl_dbus_window_state)
static void
gtk_application_impl_dbus_window_added (GtkApplicationImpl *impl,
GtkWindow *window,
GVariant *state)
{
GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
GActionGroup *actions;
char *path;
guint id;
if (!dbus->session || !GTK_IS_APPLICATION_WINDOW (window))
return ;
/* Export the action group of this window, based on its id */
actions = gtk_application_window_get_action_group (GTK_APPLICATION_WINDOW (window));
path = gtk_application_impl_dbus_get_window_path (dbus, window);
id = g_dbus_connection_export_action_group (dbus->session, path, actions, NULL);
g_free (path);
g_object_set_qdata (G_OBJECT (window), gtk_application_impl_dbus_export_id_quark (), GUINT_TO_POINTER (id));
if (state)
{
g_object_set_qdata_full (G_OBJECT (window), gtk_application_impl_dbus_window_state_quark (), g_variant_ref (state), (GDestroyNotify) g_variant_unref);
}
}
GVariant *
gtk_application_impl_dbus_get_window_state (GtkApplicationImplDBus *impl,
GtkWindow *window)
{
return (GVariant *) g_object_get_qdata (G_OBJECT (window), gtk_application_impl_dbus_window_state_quark ());
}
static void
gtk_application_impl_dbus_window_removed (GtkApplicationImpl *impl,
GtkWindow *window)
{
GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
guint id;
id = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (window), gtk_application_impl_dbus_export_id_quark ()));
if (id)
{
g_dbus_connection_unexport_action_group (dbus->session, id);
g_object_set_qdata (G_OBJECT (window), gtk_application_impl_dbus_export_id_quark (), NULL);
}
}
static void
gtk_application_impl_dbus_window_forget (GtkApplicationImpl *impl,
GtkWindow *window)
{
g_object_set_qdata (G_OBJECT (window), gtk_application_impl_dbus_window_state_quark (), NULL);
}
static void
gtk_application_impl_dbus_active_window_changed (GtkApplicationImpl *impl,
GtkWindow *window)
{
}
static void
gtk_application_impl_dbus_publish_menu (GtkApplicationImplDBus *dbus,
const char *type,
GMenuModel *model,
guint *id,
char **path)
{
int i;
if (dbus->session == NULL)
return ;
/* unexport any existing menu */
if (*id)
{
g_dbus_connection_unexport_menu_model (dbus->session, *id);
g_clear_pointer (path, g_free);
*id = 0 ;
}
/* export the new menu, if there is one */
if (model != NULL)
{
/* try getting the preferred name */
*path = g_strconcat (dbus->object_path, "/menus/" , type, NULL);
*id = g_dbus_connection_export_menu_model (dbus->session, *path, model, NULL);
/* keep trying until we get a working name... */
for (i = 0 ; *id == 0 ; i++)
{
g_free (*path);
*path = g_strdup_printf ("%s/menus/%s%d" , dbus->object_path, type, i);
*id = g_dbus_connection_export_menu_model (dbus->session, *path, model, NULL);
}
}
}
static void
gtk_application_impl_dbus_set_app_menu (GtkApplicationImpl *impl,
GMenuModel *app_menu)
{
GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
gtk_application_impl_dbus_publish_menu (dbus, "appmenu" , app_menu, &dbus->app_menu_id, &dbus->app_menu_path);
}
static void
gtk_application_impl_dbus_set_menubar (GtkApplicationImpl *impl,
GMenuModel *menubar)
{
GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
gtk_application_impl_dbus_publish_menu (dbus, "menubar" , menubar, &dbus->menubar_id, &dbus->menubar_path);
}
static int next_cookie;
typedef struct {
char *handle;
int cookie;
} InhibitHandle;
static void
inhibit_handle_free (gpointer data)
{
InhibitHandle *handle = data;
g_free (handle->handle);
g_free (handle);
}
static guint
gtk_application_impl_dbus_inhibit (GtkApplicationImpl *impl,
GtkWindow *window,
GtkApplicationInhibitFlags flags,
const char *reason)
{
GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
GVariant *res;
GError *error = NULL;
static gboolean warned = FALSE ;
if (dbus->inhibit_proxy)
{
GVariantBuilder options;
if (reason == NULL)
/* Translators: This is the 'reason' given when inhibiting
* suspend or screen locking , and the caller hasn ' t specified
* a reason .
*/
reason = _("Reason not specified" );
g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&options, "{sv}" , "reason" , g_variant_new_string (reason));
res = g_dbus_proxy_call_sync (dbus->inhibit_proxy,
"Inhibit" ,
g_variant_new ("(su@a{sv})" ,
"" , /* window */
flags,
g_variant_builder_end (&options)),
G_DBUS_CALL_FLAGS_NONE,
G_MAXINT,
NULL,
&error);
if (res)
{
InhibitHandle *handle;
handle = g_new (InhibitHandle, 1 );
handle->cookie = ++next_cookie;
g_variant_get (res, "(o)" , &handle->handle);
g_variant_unref (res);
dbus->inhibit_handles = g_slist_prepend (dbus->inhibit_handles, handle);
return handle->cookie;
}
if (error)
{
if (!warned)
{
g_warning ("Calling %s.Inhibit failed: %s" ,
g_dbus_proxy_get_interface_name (dbus->inhibit_proxy),
error->message);
warned = TRUE ;
}
g_clear_error (&error);
}
}
return 0 ;
}
static void
gtk_application_impl_dbus_uninhibit (GtkApplicationImpl *impl,
guint cookie)
{
GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
if (dbus->inhibit_proxy)
{
GSList *l;
for (l = dbus->inhibit_handles; l; l = l->next)
{
InhibitHandle *handle = l->data;
if (handle->cookie == cookie)
{
g_dbus_connection_call (dbus->session,
PORTAL_BUS_NAME,
handle->handle,
PORTAL_REQUEST_INTERFACE,
"Close" ,
g_variant_new ("()" ),
G_VARIANT_TYPE_UNIT,
G_DBUS_CALL_FLAGS_NONE,
G_MAXINT,
NULL, NULL, NULL);
dbus->inhibit_handles = g_slist_remove (dbus->inhibit_handles, handle);
inhibit_handle_free (handle);
break ;
}
}
}
}
static GtkRestoreReason
gtk_application_impl_dbus_get_restore_reason (GtkApplicationImpl *impl)
{
GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
return dbus->reason;
}
static void
gtk_application_impl_dbus_clear_restore_reason (GtkApplicationImpl *impl)
{
GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
dbus->reason = GTK_RESTORE_REASON_LAUNCH;
}
static void
gtk_application_impl_dbus_store_state (GtkApplicationImpl *impl,
GVariant *state)
{
char *file;
file = get_state_file (impl, NULL);
if (file)
{
GTK_DEBUG (SESSION, "Store state, file %s" , file);
g_file_set_contents (file,
g_variant_get_data (state),
g_variant_get_size (state),
NULL);
g_free (file);
}
}
static void
gtk_application_impl_dbus_forget_state (GtkApplicationImpl *impl)
{
GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
char *file;
file = get_state_file (impl, NULL);
g_remove (file);
g_free (file);
if (dbus->save_restore_registered)
drop_restore_request (dbus);
}
static void
gtk_application_impl_dbus_unforget_state (GtkApplicationImpl *impl)
{
GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
if (!dbus->save_restore_registered)
request_restore (dbus);
}
static GVariant *
gtk_application_impl_dbus_retrieve_state (GtkApplicationImpl *impl)
{
char *file;
char *contents;
gsize length;
GVariant *state = NULL;
file = get_state_file (impl, NULL);
if (!file)
return NULL;
if (!g_file_test (file, G_FILE_TEST_EXISTS))
{
char *old_file;
old_file = get_state_file (impl, "fallback" );
if (old_file && g_file_test (old_file, G_FILE_TEST_EXISTS))
g_rename (old_file, file);
g_free (old_file);
}
if (g_file_get_contents (file, &contents, &length, NULL))
{
GTK_DEBUG (SESSION, "Retrieve state, file %s" , file);
state = g_variant_new_from_data (G_VARIANT_TYPE ("(a{sv}a{sv}a(a{sv}a{sv}))" ), contents, length, FALSE , g_free, contents);
g_variant_ref_sink (state);
}
g_free (file);
return state;
}
static void
gtk_application_impl_dbus_init (GtkApplicationImplDBus *dbus)
{
}
static void
gtk_application_impl_dbus_finalize (GObject *object)
{
GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) object;
if (dbus->session_path)
{
g_dbus_connection_call (dbus->session,
PORTAL_BUS_NAME,
dbus->session_path,
PORTAL_SESSION_INTERFACE,
"Close" ,
NULL, NULL, 0 , -1 , NULL, NULL, NULL);
g_free (dbus->session_path);
}
if (dbus->state_changed_handler)
g_dbus_connection_signal_unsubscribe (dbus->session,
dbus->state_changed_handler);
g_clear_object (&dbus->inhibit_proxy);
g_slist_free_full (dbus->inhibit_handles, inhibit_handle_free);
g_free (dbus->app_menu_path);
g_free (dbus->menubar_path);
g_clear_object (&dbus->cancellable);
g_free (dbus->instance_id);
G_OBJECT_CLASS (gtk_application_impl_dbus_parent_class)->finalize (object);
}
static void
gtk_application_impl_dbus_class_init (GtkApplicationImplDBusClass *class )
{
GtkApplicationImplClass *impl_class = GTK_APPLICATION_IMPL_CLASS (class );
GObjectClass *gobject_class = G_OBJECT_CLASS (class );
impl_class->startup = gtk_application_impl_dbus_startup;
impl_class->shutdown = gtk_application_impl_dbus_shutdown;
impl_class->window_added = gtk_application_impl_dbus_window_added;
impl_class->window_removed = gtk_application_impl_dbus_window_removed;
impl_class->window_forget = gtk_application_impl_dbus_window_forget;
impl_class->active_window_changed = gtk_application_impl_dbus_active_window_changed;
impl_class->set_app_menu = gtk_application_impl_dbus_set_app_menu;
impl_class->set_menubar = gtk_application_impl_dbus_set_menubar;
impl_class->inhibit = gtk_application_impl_dbus_inhibit;
impl_class->uninhibit = gtk_application_impl_dbus_uninhibit;
impl_class->get_restore_reason = gtk_application_impl_dbus_get_restore_reason;
impl_class->clear_restore_reason = gtk_application_impl_dbus_clear_restore_reason;
impl_class->store_state = gtk_application_impl_dbus_store_state;
impl_class->forget_state = gtk_application_impl_dbus_forget_state;
impl_class->unforget_state = gtk_application_impl_dbus_unforget_state;
impl_class->retrieve_state = gtk_application_impl_dbus_retrieve_state;
gobject_class->finalize = gtk_application_impl_dbus_finalize;
}
char *
gtk_application_impl_dbus_get_window_path (GtkApplicationImplDBus *dbus,
GtkWindow *window)
{
if (dbus->session && GTK_IS_APPLICATION_WINDOW (window))
return g_strdup_printf ("%s/window/%d" ,
dbus->object_path,
gtk_application_window_get_id (GTK_APPLICATION_WINDOW (window)));
else
return NULL;
}
Messung V0.5 in Prozent C=98 H=94 G=95
¤ Dauer der Verarbeitung: 0.4 Sekunden
(vorverarbeitet am 2026-07-02)
¤
*© Formatika GbR, Deutschland