* 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, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#include "gactiongroupexporter.h"
#include "gdbusmethodinvocation.h"
+#include "gremoteactiongroup.h"
#include "gdbusintrospection.h"
#include "gdbusconnection.h"
#include "gactiongroup.h"
-#include "gapplication.h"
#include "gdbuserror.h"
/**
* SECTION:gactiongroupexporter
* @title: GActionGroup exporter
+ * @include: gio/gio.h
* @short_description: Export GActionGroups on D-Bus
* @see_also: #GActionGroup, #GDBusActionGroup
*
* detail.
*
* To access an exported #GActionGroup remotely, use
- * g_dbus_action_group_new() to obtain a #GDBusActionGroup.
+ * g_dbus_action_group_get() to obtain a #GDBusActionGroup.
*/
-G_GNUC_INTERNAL GVariant *
+static GVariant *
g_action_group_describe_action (GActionGroup *action_group,
const gchar *name)
{
* actually use the exporter -- not in every single app using GIO.
*
* It's also a lot easier to read. :)
+ *
+ * For documentation of this interface, see
+ * https://wiki.gnome.org/Projects/GLib/GApplication/DBusAPI
*/
const char org_gtk_Actions_xml[] =
"<node>"
"</node>";
static GDBusInterfaceInfo *org_gtk_Actions;
-static GHashTable *exported_groups;
typedef struct
{
GActionGroup *action_group;
GDBusConnection *connection;
+ GMainContext *context;
gchar *object_path;
- guint registration_id;
GHashTable *pending_changes;
- guint pending_id;
- gulong signal_ids[4];
+ GSource *pending_source;
} GActionGroupExporter;
#define ACTION_ADDED_EVENT (1u<<0)
&state_changes, &adds),
NULL);
- exporter->pending_id = 0;
+ exporter->pending_source = NULL;
return FALSE;
}
g_hash_table_remove (exporter->pending_changes, name);
have_events = g_hash_table_size (exporter->pending_changes) > 0;
- is_queued = exporter->pending_id > 0;
+ is_queued = exporter->pending_source != NULL;
if (have_events && !is_queued)
- exporter->pending_id = g_idle_add (g_action_group_exporter_dispatch_events, exporter);
+ {
+ GSource *source;
+
+ source = g_idle_source_new ();
+ exporter->pending_source = source;
+ g_source_set_callback (source, g_action_group_exporter_dispatch_events, exporter, NULL);
+ g_source_set_name (source, "[gio] g_action_group_exporter_dispatch_events");
+ g_source_attach (source, exporter->context);
+ g_source_unref (source);
+ }
if (!have_events && is_queued)
{
- g_source_remove (exporter->pending_id);
- exporter->pending_id = 0;
+ g_source_destroy (exporter->pending_source);
+ exporter->pending_source = NULL;
}
}
}
static void
-g_action_group_exporter_pre_emit (GActionGroupExporter *exporter,
- GVariant *platform_data)
-{
- if (G_IS_APPLICATION (exporter->action_group))
- G_APPLICATION_GET_CLASS (exporter->action_group)
- ->before_emit (G_APPLICATION (exporter->action_group), platform_data);
-}
-
-static void
-g_action_group_exporter_post_emit (GActionGroupExporter *exporter,
- GVariant *platform_data)
-{
- if (G_IS_APPLICATION (exporter->action_group))
- G_APPLICATION_GET_CLASS (exporter->action_group)
- ->after_emit (G_APPLICATION (exporter->action_group), platform_data);
-}
-
-static void
org_gtk_Actions_method_call (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
GVariant *desc;
g_variant_get (parameters, "(&s)", &name);
+
+ if (!g_action_group_has_action (exporter->action_group, name))
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+ "The named action ('%s') does not exist.", name);
+ return;
+ }
+
desc = g_action_group_describe_action (exporter->action_group, name);
result = g_variant_new ("(@(bgav))", desc);
}
g_variant_iter_next (iter, "v", ¶meter);
g_variant_iter_free (iter);
- g_action_group_exporter_pre_emit (exporter, platform_data);
- g_action_group_activate_action (exporter->action_group, name, parameter);
- g_action_group_exporter_post_emit (exporter, platform_data);
+ if (G_IS_REMOTE_ACTION_GROUP (exporter->action_group))
+ g_remote_action_group_activate_action_full (G_REMOTE_ACTION_GROUP (exporter->action_group),
+ name, parameter, platform_data);
+ else
+ g_action_group_activate_action (exporter->action_group, name, parameter);
if (parameter)
g_variant_unref (parameter);
GVariant *state;
g_variant_get (parameters, "(&sv@a{sv})", &name, &state, &platform_data);
- g_action_group_exporter_pre_emit (exporter, platform_data);
- g_action_group_change_action_state (exporter->action_group, name, state);
- g_action_group_exporter_post_emit (exporter, platform_data);
+
+ if (G_IS_REMOTE_ACTION_GROUP (exporter->action_group))
+ g_remote_action_group_change_action_state_full (G_REMOTE_ACTION_GROUP (exporter->action_group),
+ name, state, platform_data);
+ else
+ g_action_group_change_action_state (exporter->action_group, name, state);
+
g_variant_unref (platform_data);
g_variant_unref (state);
}
g_dbus_method_invocation_return_value (invocation, result);
}
+static void
+g_action_group_exporter_free (gpointer user_data)
+{
+ GActionGroupExporter *exporter = user_data;
+
+ g_signal_handlers_disconnect_by_func (exporter->action_group,
+ g_action_group_exporter_action_added, exporter);
+ g_signal_handlers_disconnect_by_func (exporter->action_group,
+ g_action_group_exporter_action_enabled_changed, exporter);
+ g_signal_handlers_disconnect_by_func (exporter->action_group,
+ g_action_group_exporter_action_state_changed, exporter);
+ g_signal_handlers_disconnect_by_func (exporter->action_group,
+ g_action_group_exporter_action_removed, exporter);
+
+ g_hash_table_unref (exporter->pending_changes);
+ if (exporter->pending_source)
+ g_source_destroy (exporter->pending_source);
+
+ g_main_context_unref (exporter->context);
+ g_object_unref (exporter->connection);
+ g_object_unref (exporter->action_group);
+ g_free (exporter->object_path);
+
+ g_slice_free (GActionGroupExporter, exporter);
+}
+
/**
- * g_action_group_exporter_export:
+ * g_dbus_connection_export_action_group:
* @connection: a #GDBusConnection
* @object_path: a D-Bus object path
* @action_group: a #GActionGroup
* The implemented D-Bus API should be considered private. It is
* subject to change in the future.
*
- * A given action group can only be exported on one object path and an
- * object_path can only have one action group exported on it. If either
- * constraint is violated, the export will fail and %FALSE will be
+ * A given object path can only have one action group exported on it.
+ * If this constraint is violated, the export will fail and 0 will be
* returned (with @error set accordingly).
*
- * Use g_action_group_exporter_stop() to stop exporting @action_group,
- * or g_action_group_exporter_query() to find out if and where a given
- * action group is exported.
+ * You can unexport the action group using
+ * g_dbus_connection_unexport_action_group() with the return value of
+ * this function.
+ *
+ * The thread default main context is taken at the time of this call.
+ * All incoming action activations and state change requests are
+ * reported from this context. Any changes on the action group that
+ * cause it to emit signals must also come from this same context.
+ * Since incoming action activations and state change requests are
+ * rather likely to cause changes on the action group, this effectively
+ * limits a given action group to being exported from only one main
+ * context.
+ *
+ * Returns: the ID of the export (never zero), or 0 in case of failure
*
- * Returns: %TRUE if the export is successful, or %FALSE (with @error
- * set) in the event of a failure.
+ * Since: 2.32
**/
-gboolean
-g_action_group_exporter_export (GDBusConnection *connection,
- const gchar *object_path,
- GActionGroup *action_group,
- GError **error)
+guint
+g_dbus_connection_export_action_group (GDBusConnection *connection,
+ const gchar *object_path,
+ GActionGroup *action_group,
+ GError **error)
{
const GDBusInterfaceVTable vtable = {
org_gtk_Actions_method_call
};
GActionGroupExporter *exporter;
-
- if G_UNLIKELY (exported_groups == NULL)
- exported_groups = g_hash_table_new (NULL, NULL);
+ guint id;
if G_UNLIKELY (org_gtk_Actions == NULL)
{
g_dbus_node_info_unref (info);
}
- if G_UNLIKELY (g_hash_table_lookup (exported_groups, action_group))
- {
- g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FILE_EXISTS,
- "The given GActionGroup has already been exported");
- return FALSE;
- }
-
exporter = g_slice_new (GActionGroupExporter);
- exporter->registration_id = g_dbus_connection_register_object (connection, object_path, org_gtk_Actions,
- &vtable, exporter, NULL, error);
+ id = g_dbus_connection_register_object (connection, object_path, org_gtk_Actions, &vtable,
+ exporter, g_action_group_exporter_free, error);
- if (exporter->registration_id == 0)
+ if (id == 0)
{
g_slice_free (GActionGroupExporter, exporter);
- return FALSE;
+ return 0;
}
- g_hash_table_insert (exported_groups, action_group, exporter);
+ exporter->context = g_main_context_ref_thread_default ();
exporter->pending_changes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
- exporter->pending_id = 0;
+ exporter->pending_source = NULL;
exporter->action_group = g_object_ref (action_group);
exporter->connection = g_object_ref (connection);
exporter->object_path = g_strdup (object_path);
- exporter->signal_ids[0] =
- g_signal_connect (action_group, "action-added",
- G_CALLBACK (g_action_group_exporter_action_added), exporter);
- exporter->signal_ids[1] =
- g_signal_connect (action_group, "action-removed",
- G_CALLBACK (g_action_group_exporter_action_removed), exporter);
- exporter->signal_ids[2] =
- g_signal_connect (action_group, "action-state-changed",
- G_CALLBACK (g_action_group_exporter_action_state_changed), exporter);
- exporter->signal_ids[3] =
- g_signal_connect (action_group, "action-enabled-changed",
- G_CALLBACK (g_action_group_exporter_action_enabled_changed), exporter);
-
- return TRUE;
-}
-
-/**
- * g_action_group_exporter_stop:
- * @action_group: a #GActionGroup
- *
- * Stops the export of @action_group.
- *
- * This reverses the effect of a previous call to
- * g_action_group_exporter_export() for @action_group.
- *
- * Returns: %TRUE if an export was stopped or %FALSE if @action_group
- * was not exported in the first place
- **/
-gboolean
-g_action_group_exporter_stop (GActionGroup *action_group)
-{
- GActionGroupExporter *exporter;
- gint i;
-
- if G_UNLIKELY (exported_groups == NULL)
- return FALSE;
-
- exporter = g_hash_table_lookup (exported_groups, action_group);
- if G_UNLIKELY (exporter == NULL)
- return FALSE;
+ g_signal_connect (action_group, "action-added",
+ G_CALLBACK (g_action_group_exporter_action_added), exporter);
+ g_signal_connect (action_group, "action-removed",
+ G_CALLBACK (g_action_group_exporter_action_removed), exporter);
+ g_signal_connect (action_group, "action-state-changed",
+ G_CALLBACK (g_action_group_exporter_action_state_changed), exporter);
+ g_signal_connect (action_group, "action-enabled-changed",
+ G_CALLBACK (g_action_group_exporter_action_enabled_changed), exporter);
- g_dbus_connection_unregister_object (exporter->connection, exporter->registration_id);
- for (i = 0; i < G_N_ELEMENTS (exporter->signal_ids); i++)
- g_signal_handler_disconnect (exporter->action_group, exporter->signal_ids[i]);
- g_object_unref (exporter->connection);
- g_object_unref (exporter->action_group);
- g_free (exporter->object_path);
-
- g_slice_free (GActionGroupExporter, exporter);
-
- return TRUE;
+ return id;
}
/**
- * g_action_group_exporter_query:
- * @action_group: a #GActionGroup
- * @connection: (out): the #GDBusConnection used for exporting
- * @object_path: (out): the object path used for exporting
- *
- * Queries if and where @action_group is exported.
+ * g_dbus_connection_unexport_action_group:
+ * @connection: a #GDBusConnection
+ * @export_id: the ID from g_dbus_connection_export_action_group()
*
- * If @action_group is exported, %TRUE is returned. If @connection is
- * non-%NULL then it is set to the #GDBusConnection used for the export.
- * If @object_path is non-%NULL then it is set to the object path.
+ * Reverses the effect of a previous call to
+ * g_dbus_connection_export_action_group().
*
- * If the @action_group is not exported, %FALSE is returned and
- * @connection and @object_path remain unmodified.
+ * It is an error to call this function with an ID that wasn't returned
+ * from g_dbus_connection_export_action_group() or to call it with the
+ * same ID more than once.
*
- * Returns: %TRUE if @action_group was exported, else %FALSE
+ * Since: 2.32
**/
-gboolean
-g_action_group_exporter_query (GActionGroup *action_group,
- GDBusConnection **connection,
- const gchar **object_path)
+void
+g_dbus_connection_unexport_action_group (GDBusConnection *connection,
+ guint export_id)
{
- GActionGroupExporter *exporter;
-
- if (exported_groups == NULL)
- return FALSE;
-
- exporter = g_hash_table_lookup (exported_groups, action_group);
- if (exporter == NULL)
- return FALSE;
-
- if (connection)
- *connection = exporter->connection;
-
- if (object_path)
- *object_path = exporter->object_path;
-
- return TRUE;
+ g_dbus_connection_unregister_object (connection, export_id);
}