Commit 3e6eee80 authored by Allison Karlitskaya's avatar Allison Karlitskaya

GApplication: add remote commandline support

parent 2854c373
......@@ -139,6 +139,7 @@ application_headers = \
gsimpleactiongroup.h \
gaction.h \
gsimpleaction.h \
gapplicationcommandline.h \
gapplication.h
application_sources = \
......@@ -146,6 +147,7 @@ application_sources = \
gsimpleactiongroup.c \
gaction.c \
gsimpleaction.c \
gapplicationcommandline.c \
gapplicationimpl-dbus.c \
gapplication.c
......
......@@ -22,6 +22,7 @@
/* Prologue {{{1 */
#include "gapplication.h"
#include "gapplicationcommandline.h"
#include "gapplicationimpl.h"
#include "gioenumtypes.h"
......@@ -132,12 +133,30 @@ g_application_real_open (GApplication *application,
}
}
static int
g_application_real_command_line (GApplication *application,
GApplicationCommandLine *cmdline)
{
static gboolean warned;
if (warned)
return 1;
g_warning ("Your application claims to support custom command line "
"handling but does not implement g_application_command_line() "
"and has no handlers connected to the 'command-line' signal.");
warned = TRUE;
return 1;
}
static gboolean
g_application_real_local_command_line (GApplication *application,
GVariant **arguments,
int *exit_status)
{
if (0)
if (application->priv->flags & G_APPLICATION_HANDLES_COMMAND_LINE)
return FALSE;
else
......@@ -155,12 +174,12 @@ g_application_real_local_command_line (GApplication *application,
n_args = g_variant_n_children (*arguments);
if (application->priv->flags & G_APPLICATION_FLAGS_IS_SERVICE)
if (application->priv->flags & G_APPLICATION_IS_SERVICE)
{
if (n_args > 1)
{
g_printerr ("GApplication service mode takes no arguments.\n");
application->priv->flags &= ~G_APPLICATION_FLAGS_IS_SERVICE;
application->priv->flags &= ~G_APPLICATION_IS_SERVICE;
}
return TRUE;
......@@ -174,7 +193,7 @@ g_application_real_local_command_line (GApplication *application,
else
{
if (~application->priv->flags & G_APPLICATION_FLAGS_HANDLES_OPEN)
if (~application->priv->flags & G_APPLICATION_HANDLES_OPEN)
{
g_critical ("This application can not open files.");
*exit_status = 1;
......@@ -330,6 +349,17 @@ g_application_init (GApplication *application)
GApplicationPrivate);
}
static gboolean
first_wins_accumulator (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer data)
{
g_value_copy (handler_return, return_accu);
return FALSE;
}
static void
g_application_class_init (GApplicationClass *class)
{
......@@ -345,6 +375,7 @@ g_application_class_init (GApplicationClass *class)
class->startup = g_application_real_startup;
class->activate = g_application_real_activate;
class->open = g_application_real_open;
class->command_line = g_application_real_command_line;
class->local_command_line = g_application_real_local_command_line;
class->add_platform_data = g_application_real_add_platform_data;
class->quit_mainloop = g_application_real_quit_mainloop;
......@@ -394,6 +425,12 @@ g_application_class_init (GApplicationClass *class)
NULL, NULL, _gio_marshal_VOID__POINTER_INT_STRING,
G_TYPE_NONE, 3, G_TYPE_POINTER, G_TYPE_INT, G_TYPE_STRING);
g_application_signals[SIGNAL_COMMAND_LINE] =
g_signal_new ("command-line", G_TYPE_APPLICATION, G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GApplicationClass, command_line),
first_wins_accumulator, NULL, _gio_marshal_INT__OBJECT,
G_TYPE_INT, 1, G_TYPE_APPLICATION_COMMAND_LINE);
g_type_class_add_private (class, sizeof (GApplicationPrivate));
}
......@@ -729,9 +766,9 @@ g_application_register (GApplication *application,
gboolean try;
/* don't try to be the primary instance if
* G_APPLICATION_FLAGS_IS_LAUNCHER was specified.
* G_APPLICATION_IS_LAUNCHER was specified.
*/
try = !(application->priv->flags & G_APPLICATION_FLAGS_IS_LAUNCHER);
try = !(application->priv->flags & G_APPLICATION_IS_LAUNCHER);
application->priv->impl =
g_application_impl_register (application, application->priv->id,
......@@ -867,7 +904,7 @@ g_application_activate (GApplication *application)
* for this functionality, you should use "".
*
* The application must be registered before calling this function and
* it must have the %G_APPLICATION_FLAGS_CAN_OPEN flag set. The open()
* it must have the %G_APPLICATION_CAN_OPEN flag set. The open()
* virtual function should also be implemented in order for anything
* meaningful to happen.
*
......@@ -881,7 +918,7 @@ g_application_open (GApplication *application,
{
g_return_if_fail (G_IS_APPLICATION (application));
g_return_if_fail (application->priv->flags &
G_APPLICATION_FLAGS_HANDLES_OPEN);
G_APPLICATION_HANDLES_OPEN);
g_return_if_fail (application->priv->is_registered);
if (application->priv->is_remote)
......@@ -913,7 +950,7 @@ g_application_open (GApplication *application,
* virtual function is invoked in the primary instance (which may or may
* not be this instance).
*
* If the application has the %G_APPLICATION_FLAGS_REMOTE_COMMAND_LINE
* If the application has the %G_APPLICATION_REMOTE_COMMAND_LINE
* flag set then the default implementation of handle_command_line()
* always returns %FALSE immediately, resulting in the commandline
* always being handled in the primary instance.
......@@ -924,7 +961,7 @@ g_application_open (GApplication *application,
* to register the application. If that works, then the command line
* arguments are inspected. If no commandline arguments are given, then
* g_application_activate() is called. If commandline arguments are
* given and the %G_APPLICATION_FLAGS_CAN_OPEN flags is set then they
* given and the %G_APPLICATION_CAN_OPEN flags is set then they
* are assumed to be filenames and g_application_open() is called.
*
* If you are interested in doing more complicated local handling of the
......@@ -984,12 +1021,39 @@ g_application_run_with_arguments (GApplication *application,
if (!G_APPLICATION_GET_CLASS (application)
->local_command_line (application, &arguments, &status))
{
g_assert_not_reached ();
GError *error = NULL;
if (!g_application_register (application, NULL, &error))
{
g_printerr ("%s", error->message);
g_error_free (error);
return 1;
}
if (application->priv->is_remote)
{
GVariant *platform_data;
platform_data = get_platform_data (application);
status = g_application_impl_command_line (application->priv->impl,
arguments, platform_data);
}
else
{
GApplicationCommandLine *cmdline;
cmdline = g_object_new (G_TYPE_APPLICATION_COMMAND_LINE,
"arguments", arguments, NULL);
g_signal_emit (application,
g_application_signals[SIGNAL_COMMAND_LINE],
0, cmdline, &status);
g_object_unref (cmdline);
}
}
g_variant_unref (arguments);
if (application->priv->flags & G_APPLICATION_FLAGS_IS_SERVICE &&
if (application->priv->flags & G_APPLICATION_IS_SERVICE &&
!application->priv->use_count &&
!application->priv->inactivity_timeout_id)
{
......
......@@ -93,15 +93,14 @@ struct _GApplicationClass
gint n_files,
const gchar *hint);
gpointer _reserved_1;
int (* command_line) (GApplication *application,
GApplicationCommandLine *command_line);
/* vfuncs */
gboolean (* local_command_line) (GApplication *application,
GVariant **arguments,
int *exit_status);
gpointer _reserved_2;
void (* before_emit) (GApplication *application,
GVariant *platform_data);
void (* after_emit) (GApplication *application,
......
#include "gapplicationcommandline.h"
#include <string.h>
#include <stdio.h>
G_DEFINE_TYPE (GApplicationCommandLine, g_application_command_line, G_TYPE_OBJECT)
enum
{
PROP_NONE,
PROP_ARGUMENTS,
PROP_PLATFORM_DATA,
PROP_IS_REMOTE
};
struct _GApplicationCommandLinePrivate
{
GVariant *platform_data;
GVariant *arguments;
GVariant *cwd;
gint exit_status;
};
#define IS_REMOTE(cmdline) ((cmdline)->priv->platform_data != NULL)
void
g_application_command_line_get_argc_argv (GApplicationCommandLine *cmdline,
int *argc,
char ***argv)
{
gsize len;
g_return_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline));
g_return_if_fail (argc != NULL && argv != NULL);
*argv = g_variant_dup_bytestring_array (cmdline->priv->arguments, &len);
*argc = len;
}
GVariant *
g_application_command_line_get_arguments (GApplicationCommandLine *cmdline)
{
return g_variant_ref (cmdline->priv->arguments);
}
const gchar *
g_application_command_line_get_cwd (GApplicationCommandLine *cmdline)
{
if (cmdline->priv->cwd)
return g_variant_get_bytestring (cmdline->priv->cwd);
else
return NULL;
}
GVariant *
g_application_command_line_get_cwd_variant (GApplicationCommandLine *cmdline)
{
if (cmdline->priv->cwd)
return g_variant_ref (cmdline->priv->cwd);
else
return NULL;
}
gboolean
g_application_command_line_get_is_remote (GApplicationCommandLine *cmdline)
{
return IS_REMOTE (cmdline);
}
static void
g_application_command_line_real_print_literal (GApplicationCommandLine *cmdline,
const gchar *message)
{
g_print ("%s\n", message);
}
static void
g_application_command_line_real_printerr_literal (GApplicationCommandLine *cmdline,
const gchar *message)
{
g_printerr ("%s\n", message);
}
void
g_application_command_line_print (GApplicationCommandLine *cmdline,
const gchar *format,
...)
{
gchar *message;
va_list ap;
g_return_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline));
g_return_if_fail (format != NULL);
va_start (ap, format);
message = g_strdup_vprintf (format, ap);
va_end (ap);
G_APPLICATION_COMMAND_LINE_GET_CLASS (cmdline)
->print_literal (cmdline, message);
g_free (message);
}
void
g_application_command_line_printerr (GApplicationCommandLine *cmdline,
const gchar *format,
...)
{
gchar *message;
va_list ap;
g_return_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline));
g_return_if_fail (format != NULL);
va_start (ap, format);
message = g_strdup_vprintf (format, ap);
va_end (ap);
G_APPLICATION_COMMAND_LINE_GET_CLASS (cmdline)
->printerr_literal (cmdline, message);
g_free (message);
}
void
g_application_command_line_set_exit_status (GApplicationCommandLine *cmdline,
int exit_status)
{
g_return_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline));
cmdline->priv->exit_status = exit_status;
}
int
g_application_command_line_get_exit_status (GApplicationCommandLine *cmdline)
{
g_return_val_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline), -1);
return cmdline->priv->exit_status;
}
static void
grok_platform_data (GApplicationCommandLine *cmdline)
{
GVariantIter iter;
const gchar *key;
GVariant *value;
g_variant_iter_init (&iter, cmdline->priv->platform_data);
while (g_variant_iter_loop (&iter, "{&sv}", &key, &value))
if (strcmp (key, "cwd") == 0)
{
if (!cmdline->priv->cwd)
cmdline->priv->cwd = g_variant_ref (value);
}
}
static void
g_application_command_line_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
GApplicationCommandLine *cmdline = G_APPLICATION_COMMAND_LINE (object);
switch (prop_id)
{
case PROP_ARGUMENTS:
g_value_set_variant (value, cmdline->priv->arguments);
break;
case PROP_PLATFORM_DATA:
g_value_set_variant (value, cmdline->priv->platform_data);
break;
case PROP_IS_REMOTE:
g_value_set_boolean (value, IS_REMOTE (cmdline));
break;
default:
g_assert_not_reached ();
}
}
static void
g_application_command_line_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
GApplicationCommandLine *cmdline = G_APPLICATION_COMMAND_LINE (object);
switch (prop_id)
{
case PROP_ARGUMENTS:
g_assert (cmdline->priv->arguments == NULL);
cmdline->priv->arguments = g_value_dup_variant (value);
break;
case PROP_PLATFORM_DATA:
g_assert (cmdline->priv->platform_data == NULL);
cmdline->priv->platform_data = g_value_dup_variant (value);
if (cmdline->priv->platform_data != NULL)
grok_platform_data (cmdline);
break;
default:
g_assert_not_reached ();
}
}
static void
g_application_command_line_finalize (GObject *object)
{
GApplicationCommandLine *cmdline = G_APPLICATION_COMMAND_LINE (object);
if (cmdline->priv->platform_data)
g_variant_unref (cmdline->priv->platform_data);
if (cmdline->priv->arguments)
g_variant_unref (cmdline->priv->arguments);
if (cmdline->priv->cwd)
g_variant_unref (cmdline->priv->cwd);
G_OBJECT_CLASS (g_application_command_line_parent_class)
->finalize (object);
}
static void
g_application_command_line_init (GApplicationCommandLine *cmdline)
{
cmdline->priv =
G_TYPE_INSTANCE_GET_PRIVATE (cmdline,
G_TYPE_APPLICATION_COMMAND_LINE,
GApplicationCommandLinePrivate);
}
static void
g_application_command_line_class_init (GApplicationCommandLineClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->get_property = g_application_command_line_get_property;
object_class->set_property = g_application_command_line_set_property;
object_class->finalize = g_application_command_line_finalize;
class->printerr_literal = g_application_command_line_real_printerr_literal;
class->print_literal = g_application_command_line_real_print_literal;
g_object_class_install_property (object_class, PROP_ARGUMENTS,
g_param_spec_variant ("arguments", "commandline arguments",
"the commandline that caused this cmdline",
G_VARIANT_TYPE_BYTESTRING_ARRAY, NULL,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_PLATFORM_DATA,
g_param_spec_variant ("platform-data", "platform data",
"platform-specific data for the cmdline",
G_VARIANT_TYPE ("a{sv}"), NULL,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_IS_REMOTE,
g_param_spec_boolean ("is-remote", "is remote",
"TRUE if this is a remote cmdline", FALSE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_type_class_add_private (class, sizeof (GApplicationCommandLinePrivate));
}
GVariant *
g_application_command_line_get_platform_data (GApplicationCommandLine *cmdline)
{
g_return_val_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline), NULL);
g_return_val_if_fail (IS_REMOTE (cmdline), NULL);
return g_variant_ref (cmdline->priv->platform_data);
}
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright © 2010 Codethink Limited
*
* 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 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, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#ifndef __G_APPLICATION_COMMAND_LINE_H__
#define __G_APPLICATION_COMMAND_LINE_H__
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_APPLICATION_COMMAND_LINE (g_application_command_line_get_type ())
#define G_APPLICATION_COMMAND_LINE(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
G_TYPE_APPLICATION_COMMAND_LINE, \
GApplicationCommandLine))
#define G_APPLICATION_COMMAND_LINE_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \
G_TYPE_APPLICATION_COMMAND_LINE, \
GApplicationCommandLineClass))
#define G_IS_APPLICATION_COMMAND_LINE(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
G_TYPE_APPLICATION_COMMAND_LINE))
#define G_IS_APPLICATION_COMMAND_LINE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \
G_TYPE_APPLICATION_COMMAND_LINE))
#define G_APPLICATION_COMMAND_LINE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \
G_TYPE_APPLICATION_COMMAND_LINE, \
GApplicationCommandLineClass))
typedef struct _GApplicationCommandLinePrivate GApplicationCommandLinePrivate;
typedef struct _GApplicationCommandLineClass GApplicationCommandLineClass;
/**
* GApplicationCommandLine:
*
* The <structname>GApplicationCommandLine</structname> structure contains private
* data and should only be accessed using the provided API
*
* Since: 2.26
*/
struct _GApplicationCommandLine
{
/*< private >*/
GObject parent_instance;
GApplicationCommandLinePrivate *priv;
};
/**
* GApplicationCommandLineClass:
*
* The <structname>GApplicationCommandLineClass</structname> structure contains
* private data only
*
* Since: 2.26
*/
struct _GApplicationCommandLineClass
{
/*< private >*/
GObjectClass parent_class;
void (* print_literal) (GApplicationCommandLine *command_line,
const gchar *message);
void (* printerr_literal) (GApplicationCommandLine *command_line,
const gchar *message);
gpointer padding[12];
};
GType g_application_command_line_get_type (void) G_GNUC_CONST;
void g_application_command_line_get_argc_argv (GApplicationCommandLine *command_line,
int *argc,
char ***argv);
GVariant * g_application_command_line_get_arguments (GApplicationCommandLine *command_line);
const gchar * g_application_command_line_get_cwd (GApplicationCommandLine *command_line);
GVariant * g_application_command_line_get_cwd_variant (GApplicationCommandLine *command_line);
gboolean g_application_command_line_get_is_remote (GApplicationCommandLine *command_line);
void g_application_command_line_output (GApplicationCommandLine *command_line,
gint fd,
gconstpointer buffer,
gssize length);
void g_application_command_line_print (GApplicationCommandLine *command_line,
const gchar *format,
...);
void g_application_command_line_printerr (GApplicationCommandLine *command_line,
const gchar *format,
...);
int g_application_command_line_get_exit_status (GApplicationCommandLine *command_line);
void g_application_command_line_set_exit_status (GApplicationCommandLine *command_line,
int exit_status);
GVariant * g_application_command_line_get_platform_data (GApplicationCommandLine *command_line);
G_END_DECLS
#endif /* __G_APPLICATION_COMMAND_LINE_H__ */
......@@ -37,6 +37,9 @@ static const GDBusArgInfo *activate_out[] = { NULL };
static const GDBusArgInfo *open_in[] = { &open_uris_arg, &open_hint_arg, &platform_data_arg, NULL };
static const GDBusArgInfo *open_out[] = { NULL };
static const GDBusArgInfo *cmdline_in[] = { &cmdline_path_arg, &cmdline_arguments_arg, &platform_data_arg, NULL };
static const GDBusArgInfo *cmdline_out[] = { &cmdline_exit_status_arg, NULL };
static const GDBusMethodInfo activate_method = {
-1, (gchar *) "Activate",
(GDBusArgInfo **) activate_in,
......@@ -49,11 +52,42 @@ static const GDBusMethodInfo open_method = {
(GDBusArgInfo **) open_out
};
static const GDBusMethodInfo command_line_method = {
-1, (gchar *) "CommandLine",
(GDBusArgInfo **) cmdline_in,
(GDBusArgInfo **) cmdline_out
};
static const GDBusMethodInfo *application_methods[] = {
&activate_method, &open_method, NULL
&activate_method, &open_method, &command_line_method, NULL
};
const GDBusInterfaceInfo org_gtk_Application = {
-1, (gchar *) "org.gtk.Application",
(GDBusMethodInfo **) application_methods
};
static const GDBusArgInfo message_arg = { -1, (gchar *) "message", (gchar *) "s" };
static const GDBusArgInfo *print_in[] = { &message_arg, NULL };
static const GDBusArgInfo *print_out[] = { NULL };
static const GDBusMethodInfo stdout_method = {
-1, (gchar *) "Print",
(GDBusArgInfo **) print_in,
(GDBusArgInfo **) print_out
};
static const GDBusMethodInfo stderr_method = {
-1, (gchar *) "PrintError",
(GDBusArgInfo **) print_in,
(GDBusArgInfo **) print_out
};
static const GDBusMethodInfo *cmdline_methods[] = {
&stdout_method, &stderr_method, NULL
};
const GDBusInterfaceInfo org_gtk_private_Cmdline = {
-1, (gchar *) "org.gtk.private.CommandLine",
(GDBusMethodInfo **) cmdline_methods
};
......@@ -28,9 +28,11 @@
#include "gdbuserror.h"
#include <string.h>
#include <stdio.h>
#include "gapplicationimpl-dbus-interface.c"
#include "gapplicationcommandline.h"
#include "gdbusmethodinvocation.h"
struct _GApplicationImpl
{
......@@ -39,10 +41,13 @@ struct _GApplicationImpl
gchar *object_path;
guint object_id;
gpointer app;
GMainLoop *cmdline_mainloop;
};
static GApplicationCommandLine *
g_dbus_command_line_new (GDBusMethodInvocation *invocation);
static void
g_application_impl_method_call (GDBusConnection *connection,
const gchar *sender,
......@@ -104,6 +109,22 @@ g_application_impl_method_call (GDBusConnection *connection,
g_free (files);
}
else if (strcmp (method_name, "CommandLine") == 0)
{
GApplicationCommandLine *cmdline;
GVariant *platform_data;
int status;
cmdline = g_dbus_command_line_new (invocation);
platform_data = g_variant_get_child_value (parameters, 2);
class->before_emit (impl->app, platform_data);
g_signal_emit_by_name (impl->app, "command-line", cmdline, &status);
g_application_command_line_set_exit_status (cmdline, status);
class->after_emit (impl->app, platform_data);
g_variant_unref (platform_data);
g_object_unref (cmdline);
}
else
g_assert_not_reached ();
}
......@@ -175,7 +196,7 @@ g_application_impl_register (GApplication *application,
impl->object_path = application_path_from_appid (appid);
if (flags & G_APPLICATION_FLAGS_IS_LAUNCHER)
if (flags & G_APPLICATION_IS_LAUNCHER)
{
impl->object_id = 0;
*is_remote = TRUE;
......@@ -238,7 +259,7 @@ g_application_impl_register (GApplication *application,
impl->object_id);
impl->object_id = 0;
if (flags & G_APPLICATION_FLAGS_IS_SERVICE)
if (flags & G_APPLICATION_IS_SERVICE)
{
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
"Unable to acquire bus name `%s'", appid);
......@@ -297,8 +318,211 @@ g_application_impl_open (GApplicationImpl *impl,
NULL, 0, -1, NULL, NULL, NULL);
}
static void
g_application_impl_cmdline_method_call (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,