2003-09-03 02:08:25 +00:00
|
|
|
/* -*- mode: C; c-file-style: "gnu" -*- */
|
|
|
|
|
/* dbus-gobject.c Exporting a GObject remotely
|
|
|
|
|
*
|
2004-06-20 15:28:15 +00:00
|
|
|
* Copyright (C) 2003, 2004 Red Hat, Inc.
|
2003-09-03 02:08:25 +00:00
|
|
|
*
|
2004-08-10 03:07:01 +00:00
|
|
|
* Licensed under the Academic Free License version 2.1
|
2003-09-03 02:08:25 +00:00
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program 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 General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
2004-06-20 15:28:15 +00:00
|
|
|
#include <dbus/dbus-glib.h>
|
|
|
|
|
#include <dbus/dbus-glib-lowlevel.h>
|
2003-09-03 02:08:25 +00:00
|
|
|
#include "dbus-gtest.h"
|
2003-09-17 03:52:07 +00:00
|
|
|
#include "dbus-gutils.h"
|
2004-03-29 13:24:50 +00:00
|
|
|
#include "dbus-gvalue.h"
|
2003-09-03 02:08:25 +00:00
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @addtogroup DBusGLibInternals
|
|
|
|
|
* @{
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static GStaticMutex info_hash_mutex = G_STATIC_MUTEX_INIT;
|
|
|
|
|
static GHashTable *info_hash = NULL;
|
|
|
|
|
|
|
|
|
|
static char*
|
2003-09-04 00:21:36 +00:00
|
|
|
wincaps_to_uscore (const char *caps)
|
2003-09-03 02:08:25 +00:00
|
|
|
{
|
|
|
|
|
const char *p;
|
|
|
|
|
GString *str;
|
|
|
|
|
|
|
|
|
|
str = g_string_new (NULL);
|
|
|
|
|
p = caps;
|
|
|
|
|
while (*p)
|
|
|
|
|
{
|
|
|
|
|
if (g_ascii_isupper (*p))
|
|
|
|
|
{
|
|
|
|
|
if (str->len > 0 &&
|
|
|
|
|
(str->len < 2 || str->str[str->len-2] != '_'))
|
|
|
|
|
g_string_append_c (str, '_');
|
|
|
|
|
g_string_append_c (str, g_ascii_tolower (*p));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
g_string_append_c (str, *p);
|
|
|
|
|
}
|
|
|
|
|
++p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return g_string_free (str, FALSE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char*
|
2003-09-04 00:21:36 +00:00
|
|
|
uscore_to_wincaps (const char *uscore)
|
2003-09-03 02:08:25 +00:00
|
|
|
{
|
|
|
|
|
const char *p;
|
|
|
|
|
GString *str;
|
|
|
|
|
gboolean last_was_uscore;
|
|
|
|
|
|
|
|
|
|
last_was_uscore = TRUE;
|
|
|
|
|
|
|
|
|
|
str = g_string_new (NULL);
|
|
|
|
|
p = uscore;
|
|
|
|
|
while (*p)
|
|
|
|
|
{
|
|
|
|
|
if (*p == '-' || *p == '_')
|
|
|
|
|
{
|
|
|
|
|
last_was_uscore = TRUE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (last_was_uscore)
|
|
|
|
|
{
|
|
|
|
|
g_string_append_c (str, g_ascii_toupper (*p));
|
|
|
|
|
last_was_uscore = FALSE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
g_string_append_c (str, *p);
|
|
|
|
|
}
|
|
|
|
|
++p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return g_string_free (str, FALSE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gobject_unregister_function (DBusConnection *connection,
|
|
|
|
|
void *user_data)
|
|
|
|
|
{
|
|
|
|
|
GObject *object;
|
|
|
|
|
|
|
|
|
|
object = G_OBJECT (user_data);
|
|
|
|
|
|
2003-09-17 03:52:07 +00:00
|
|
|
/* FIXME */
|
2003-09-03 02:08:25 +00:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
gtype_to_dbus_type (GType type)
|
|
|
|
|
{
|
|
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
case G_TYPE_CHAR:
|
|
|
|
|
case G_TYPE_UCHAR:
|
|
|
|
|
return DBUS_TYPE_BYTE;
|
|
|
|
|
|
|
|
|
|
case G_TYPE_BOOLEAN:
|
|
|
|
|
return DBUS_TYPE_BOOLEAN;
|
|
|
|
|
|
|
|
|
|
/* long gets cut to 32 bits so the remote API is consistent
|
|
|
|
|
* on all architectures
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
case G_TYPE_LONG:
|
|
|
|
|
case G_TYPE_INT:
|
|
|
|
|
return DBUS_TYPE_INT32;
|
|
|
|
|
case G_TYPE_ULONG:
|
|
|
|
|
case G_TYPE_UINT:
|
|
|
|
|
return DBUS_TYPE_UINT32;
|
|
|
|
|
|
|
|
|
|
case G_TYPE_INT64:
|
|
|
|
|
return DBUS_TYPE_INT64;
|
|
|
|
|
|
|
|
|
|
case G_TYPE_UINT64:
|
|
|
|
|
return DBUS_TYPE_UINT64;
|
|
|
|
|
|
|
|
|
|
case G_TYPE_FLOAT:
|
|
|
|
|
case G_TYPE_DOUBLE:
|
|
|
|
|
return DBUS_TYPE_DOUBLE;
|
|
|
|
|
|
|
|
|
|
case G_TYPE_STRING:
|
|
|
|
|
return DBUS_TYPE_STRING;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return DBUS_TYPE_INVALID;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-04-13 11:47:17 +00:00
|
|
|
static void
|
|
|
|
|
introspect_properties (GObject *object, GString *xml)
|
2003-09-03 02:08:25 +00:00
|
|
|
{
|
|
|
|
|
unsigned int i;
|
2004-04-13 11:47:17 +00:00
|
|
|
unsigned int n_specs;
|
2003-09-03 02:08:25 +00:00
|
|
|
GType last_type;
|
2004-04-13 11:47:17 +00:00
|
|
|
GParamSpec **specs;
|
2003-09-03 02:08:25 +00:00
|
|
|
|
|
|
|
|
last_type = G_TYPE_INVALID;
|
|
|
|
|
specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (object),
|
|
|
|
|
&n_specs);
|
|
|
|
|
|
2004-04-13 11:47:17 +00:00
|
|
|
for (i = 0; i < n_specs; i++ )
|
2003-09-03 02:08:25 +00:00
|
|
|
{
|
|
|
|
|
char *s;
|
|
|
|
|
int dbus_type;
|
2004-04-13 11:47:17 +00:00
|
|
|
gboolean can_set;
|
|
|
|
|
gboolean can_get;
|
|
|
|
|
GParamSpec *spec = specs[i];
|
2003-09-03 02:08:25 +00:00
|
|
|
|
|
|
|
|
dbus_type = gtype_to_dbus_type (G_PARAM_SPEC_VALUE_TYPE (spec));
|
|
|
|
|
if (dbus_type == DBUS_TYPE_INVALID)
|
2004-04-13 11:47:17 +00:00
|
|
|
continue;
|
2003-09-03 02:08:25 +00:00
|
|
|
|
|
|
|
|
if (spec->owner_type != last_type)
|
|
|
|
|
{
|
|
|
|
|
if (last_type != G_TYPE_INVALID)
|
|
|
|
|
g_string_append (xml, " </interface>\n");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* FIXME what should the namespace on the interface be in
|
|
|
|
|
* general? should people be able to set it for their
|
|
|
|
|
* objects?
|
|
|
|
|
*/
|
|
|
|
|
g_string_append (xml, " <interface name=\"org.gtk.objects.");
|
|
|
|
|
g_string_append (xml, g_type_name (spec->owner_type));
|
|
|
|
|
g_string_append (xml, "\">\n");
|
|
|
|
|
|
|
|
|
|
last_type = spec->owner_type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
can_set = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
|
2004-04-13 11:47:17 +00:00
|
|
|
(spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
|
|
|
|
|
|
2003-09-03 02:08:25 +00:00
|
|
|
can_get = (spec->flags & G_PARAM_READABLE) != 0;
|
|
|
|
|
|
2003-09-04 00:21:36 +00:00
|
|
|
s = uscore_to_wincaps (spec->name);
|
2003-09-03 02:08:25 +00:00
|
|
|
|
2005-01-29 19:52:19 +00:00
|
|
|
if (can_set || can_get)
|
2003-09-03 02:08:25 +00:00
|
|
|
{
|
2005-01-29 19:52:19 +00:00
|
|
|
g_string_append (xml, " <property name=\"");
|
2003-09-03 02:08:25 +00:00
|
|
|
g_string_append (xml, s);
|
2005-01-29 19:52:19 +00:00
|
|
|
g_string_append (xml, "\" type=\"");
|
2004-06-01 03:02:26 +00:00
|
|
|
g_string_append (xml, _dbus_gutils_type_to_string (dbus_type));
|
2005-01-30 07:44:08 +00:00
|
|
|
g_string_append (xml, "\" access=\"");
|
2003-09-03 02:08:25 +00:00
|
|
|
|
2005-01-29 19:52:19 +00:00
|
|
|
if (can_set && can_get)
|
|
|
|
|
g_string_append (xml, "readwrite");
|
|
|
|
|
else if (can_get)
|
|
|
|
|
g_string_append (xml, "read");
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
g_assert (can_set);
|
|
|
|
|
g_string_append (xml, "write");
|
|
|
|
|
}
|
2003-09-03 02:08:25 +00:00
|
|
|
|
2005-01-30 07:44:08 +00:00
|
|
|
g_string_append (xml, "\"/>\n");
|
2003-09-03 02:08:25 +00:00
|
|
|
}
|
2005-01-29 19:52:19 +00:00
|
|
|
|
2003-09-03 02:08:25 +00:00
|
|
|
g_free (s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (last_type != G_TYPE_INVALID)
|
|
|
|
|
g_string_append (xml, " </interface>\n");
|
|
|
|
|
|
|
|
|
|
g_free (specs);
|
2004-04-13 11:47:17 +00:00
|
|
|
}
|
2003-09-03 02:08:25 +00:00
|
|
|
|
2004-04-13 11:47:17 +00:00
|
|
|
static void
|
|
|
|
|
introspect_signals (GType type, GString *xml)
|
|
|
|
|
{
|
|
|
|
|
guint i;
|
|
|
|
|
guint *ids, n_ids;
|
|
|
|
|
|
|
|
|
|
ids = g_signal_list_ids (type, &n_ids);
|
|
|
|
|
if (!n_ids)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
g_string_append (xml, " <interface name=\"org.gtk.objects.");
|
|
|
|
|
g_string_append (xml, g_type_name (type));
|
|
|
|
|
g_string_append (xml, "\">\n");
|
|
|
|
|
|
|
|
|
|
/* FIXME: recurse to parent types ? */
|
|
|
|
|
for (i = 0; i < n_ids; i++)
|
2003-09-17 03:52:07 +00:00
|
|
|
{
|
2004-04-13 11:47:17 +00:00
|
|
|
guint arg;
|
|
|
|
|
GSignalQuery query;
|
|
|
|
|
|
|
|
|
|
g_signal_query (ids[i], &query);
|
|
|
|
|
|
|
|
|
|
if (query.return_type)
|
|
|
|
|
continue; /* FIXME: these could be listed as methods ? */
|
|
|
|
|
|
|
|
|
|
g_string_append (xml, " <signal name=\"");
|
|
|
|
|
g_string_append (xml, query.signal_name);
|
|
|
|
|
g_string_append (xml, "\">\n");
|
|
|
|
|
|
|
|
|
|
for (arg = 0; arg < query.n_params; arg++)
|
|
|
|
|
{
|
|
|
|
|
int dbus_type = gtype_to_dbus_type (query.param_types[arg]);
|
|
|
|
|
|
|
|
|
|
g_string_append (xml, " <arg type=\"");
|
2004-06-01 03:02:26 +00:00
|
|
|
g_string_append (xml, _dbus_gutils_type_to_string (dbus_type));
|
2004-04-13 11:47:17 +00:00
|
|
|
g_string_append (xml, "\"/>\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_string_append (xml, " </signal>\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_string_append (xml, " </interface>\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static DBusHandlerResult
|
|
|
|
|
handle_introspect (DBusConnection *connection,
|
|
|
|
|
DBusMessage *message,
|
|
|
|
|
GObject *object)
|
|
|
|
|
{
|
|
|
|
|
GString *xml;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
DBusMessage *ret;
|
|
|
|
|
char **children;
|
|
|
|
|
|
2004-06-05 16:32:00 +00:00
|
|
|
if (!dbus_connection_list_registered (connection,
|
|
|
|
|
dbus_message_get_path (message),
|
2004-04-13 11:47:17 +00:00
|
|
|
&children))
|
|
|
|
|
g_error ("Out of memory");
|
|
|
|
|
|
|
|
|
|
xml = g_string_new (NULL);
|
|
|
|
|
|
2005-01-30 05:18:44 +00:00
|
|
|
g_string_append (xml, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
|
|
|
|
|
|
2005-01-29 19:52:19 +00:00
|
|
|
g_string_append (xml, "<node>\n");
|
2005-01-29 20:12:22 +00:00
|
|
|
|
|
|
|
|
/* We are introspectable, though I guess that was pretty obvious */
|
|
|
|
|
g_string_append (xml, " <interface name=\"org.freedesktop.Introspectable\">\n");
|
|
|
|
|
g_string_append (xml, " <method name=\"Introspect\">\n");
|
2005-01-30 05:18:44 +00:00
|
|
|
g_string_append (xml, " <arg name=\"data\" direction=\"out\" type=\"string\"/>\n");
|
2005-01-29 20:12:22 +00:00
|
|
|
g_string_append (xml, " </method>\n");
|
|
|
|
|
g_string_append (xml, " </interface>\n");
|
|
|
|
|
|
|
|
|
|
/* We support get/set properties */
|
|
|
|
|
g_string_append (xml, " <interface name=\"org.freedesktop.Properties\">\n");
|
|
|
|
|
g_string_append (xml, " <method name=\"Get\">\n");
|
2005-01-30 05:18:44 +00:00
|
|
|
g_string_append (xml, " <arg name=\"interface\" direction=\"in\" type=\"string\"/>\n");
|
|
|
|
|
g_string_append (xml, " <arg name=\"propname\" direction=\"in\" type=\"string\"/>\n");
|
|
|
|
|
g_string_append (xml, " <arg name=\"value\" direction=\"out\" type=\"variant\"/>\n");
|
2005-01-29 20:12:22 +00:00
|
|
|
g_string_append (xml, " </method>\n");
|
|
|
|
|
g_string_append (xml, " <method name=\"Set\">\n");
|
2005-01-30 05:18:44 +00:00
|
|
|
g_string_append (xml, " <arg name=\"interface\" direction=\"in\" type=\"string\"/>\n");
|
|
|
|
|
g_string_append (xml, " <arg name=\"propname\" direction=\"in\" type=\"string\"/>\n");
|
|
|
|
|
g_string_append (xml, " <arg name=\"value\" direction=\"in\" type=\"variant\"/>\n");
|
2005-01-29 20:12:22 +00:00
|
|
|
g_string_append (xml, " </method>\n");
|
|
|
|
|
g_string_append (xml, " </interface>\n");
|
2005-01-29 19:52:19 +00:00
|
|
|
|
2004-04-13 11:47:17 +00:00
|
|
|
introspect_signals (G_OBJECT_TYPE (object), xml);
|
|
|
|
|
introspect_properties (object, xml);
|
|
|
|
|
|
|
|
|
|
/* Append child nodes */
|
|
|
|
|
for (i = 0; children[i]; i++)
|
2003-09-17 03:52:07 +00:00
|
|
|
g_string_append_printf (xml, " <node name=\"%s\"/>\n",
|
|
|
|
|
children[i]);
|
|
|
|
|
|
2003-09-03 02:08:25 +00:00
|
|
|
/* Close the XML, and send it to the requesting app */
|
|
|
|
|
g_string_append (xml, "</node>\n");
|
|
|
|
|
|
|
|
|
|
ret = dbus_message_new_method_return (message);
|
|
|
|
|
if (ret == NULL)
|
2003-09-17 03:52:07 +00:00
|
|
|
g_error ("Out of memory");
|
2003-09-03 02:08:25 +00:00
|
|
|
|
2005-01-30 07:44:08 +00:00
|
|
|
dbus_message_append_args (ret,
|
2005-01-15 Havoc Pennington <hp@redhat.com>
* Land the new message args API and type system.
This patch is huge, but the public API change is not
really large. The set of D-BUS types has changed somewhat,
and the arg "getters" are more geared toward language bindings;
they don't make a copy, etc.
There are also some known issues. See these emails for details
on this huge patch:
http://lists.freedesktop.org/archives/dbus/2004-December/001836.html
http://lists.freedesktop.org/archives/dbus/2005-January/001922.html
* dbus/dbus-marshal-*: all the new stuff
* dbus/dbus-message.c: basically rewritten
* dbus/dbus-memory.c (check_guards): with "guards" enabled, init
freed blocks to be all non-nul bytes so using freed memory is less
likely to work right
* dbus/dbus-internals.c (_dbus_test_oom_handling): add
DBUS_FAIL_MALLOC=N environment variable, so you can do
DBUS_FAIL_MALLOC=0 to skip the out-of-memory checking, or
DBUS_FAIL_MALLOC=10 to make it really, really, really slow and
thorough.
* qt/message.cpp: port to the new message args API
(operator<<): use str.utf8() rather than str.unicode()
(pretty sure this is right from the Qt docs?)
* glib/dbus-gvalue.c: port to the new message args API
* bus/dispatch.c, bus/driver.c: port to the new message args API
* dbus/dbus-string.c (_dbus_string_init_const_len): initialize the
"locked" flag to TRUE and align_offset to 0; I guess we never
looked at these anyhow, but seems cleaner.
* dbus/dbus-string.h (_DBUS_STRING_ALLOCATION_PADDING):
move allocation padding macro to this header; use it to implement
(_DBUS_STRING_STATIC): ability to declare a static string.
* dbus/dbus-message.c (_dbus_message_has_type_interface_member):
change to return TRUE if the interface is not set.
* dbus/dbus-string.[hc]: move the D-BUS specific validation stuff
to dbus-marshal-validate.[hc]
* dbus/dbus-marshal-basic.c (_dbus_type_to_string): move here from
dbus-internals.c
* dbus/Makefile.am: cut over from dbus-marshal.[hc]
to dbus-marshal-*.[hc]
* dbus/dbus-object-tree.c (_dbus_decompose_path): move this
function here from dbus-marshal.c
2005-01-15 07:15:38 +00:00
|
|
|
DBUS_TYPE_STRING, &xml->str,
|
2003-09-03 02:08:25 +00:00
|
|
|
DBUS_TYPE_INVALID);
|
|
|
|
|
|
2005-01-30 07:44:08 +00:00
|
|
|
dbus_connection_send (connection, ret, NULL);
|
|
|
|
|
dbus_message_unref (ret);
|
2003-09-03 02:08:25 +00:00
|
|
|
|
|
|
|
|
g_string_free (xml, TRUE);
|
|
|
|
|
|
2003-09-17 03:52:07 +00:00
|
|
|
dbus_free_string_array (children);
|
|
|
|
|
|
2003-09-03 02:08:25 +00:00
|
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static DBusMessage*
|
2005-01-29 19:52:19 +00:00
|
|
|
set_object_property (DBusConnection *connection,
|
|
|
|
|
DBusMessage *message,
|
|
|
|
|
DBusMessageIter *iter,
|
|
|
|
|
GObject *object,
|
|
|
|
|
GParamSpec *pspec)
|
2003-09-03 02:08:25 +00:00
|
|
|
{
|
2004-03-29 13:24:50 +00:00
|
|
|
GValue value = { 0, };
|
2003-09-03 02:08:25 +00:00
|
|
|
DBusMessage *ret;
|
2005-01-29 19:52:19 +00:00
|
|
|
DBusMessageIter sub;
|
2003-09-03 02:08:25 +00:00
|
|
|
|
2005-01-29 19:52:19 +00:00
|
|
|
dbus_message_iter_recurse (iter, &sub);
|
|
|
|
|
|
2003-09-03 02:08:25 +00:00
|
|
|
/* The g_object_set_property() will transform some types, e.g. it
|
|
|
|
|
* will let you use a uchar to set an int property etc. Note that
|
|
|
|
|
* any error in value range or value conversion will just
|
|
|
|
|
* g_warning(). These GObject skels are not for secure applications.
|
|
|
|
|
*/
|
2005-01-29 19:52:19 +00:00
|
|
|
if (dbus_gvalue_demarshal (&sub, &value))
|
2003-09-03 02:08:25 +00:00
|
|
|
{
|
|
|
|
|
g_object_set_property (object,
|
|
|
|
|
pspec->name,
|
|
|
|
|
&value);
|
|
|
|
|
|
2004-03-29 13:24:50 +00:00
|
|
|
g_value_unset (&value);
|
|
|
|
|
|
2003-09-03 02:08:25 +00:00
|
|
|
ret = dbus_message_new_method_return (message);
|
|
|
|
|
if (ret == NULL)
|
|
|
|
|
g_error ("out of memory");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ret = dbus_message_new_error (message,
|
|
|
|
|
DBUS_ERROR_INVALID_ARGS,
|
|
|
|
|
"Argument's D-BUS type can't be converted to a GType");
|
|
|
|
|
if (ret == NULL)
|
|
|
|
|
g_error ("out of memory");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static DBusMessage*
|
|
|
|
|
get_object_property (DBusConnection *connection,
|
|
|
|
|
DBusMessage *message,
|
|
|
|
|
GObject *object,
|
|
|
|
|
GParamSpec *pspec)
|
|
|
|
|
{
|
|
|
|
|
GType value_type;
|
|
|
|
|
GValue value;
|
2004-03-29 13:24:50 +00:00
|
|
|
DBusMessage *ret;
|
2003-09-03 02:08:25 +00:00
|
|
|
DBusMessageIter iter;
|
|
|
|
|
|
|
|
|
|
value_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
|
|
|
|
|
|
|
|
|
|
ret = dbus_message_new_method_return (message);
|
|
|
|
|
if (ret == NULL)
|
|
|
|
|
g_error ("out of memory");
|
|
|
|
|
|
|
|
|
|
g_value_init (&value, value_type);
|
|
|
|
|
g_object_get_property (object, pspec->name, &value);
|
|
|
|
|
|
|
|
|
|
value_type = G_VALUE_TYPE (&value);
|
|
|
|
|
|
2005-01-16 15:51:55 +00:00
|
|
|
dbus_message_iter_init_append (message, &iter);
|
2003-09-03 02:08:25 +00:00
|
|
|
|
2004-03-29 13:24:50 +00:00
|
|
|
if (!dbus_gvalue_marshal (&iter, &value))
|
2003-09-03 02:08:25 +00:00
|
|
|
{
|
|
|
|
|
dbus_message_unref (ret);
|
|
|
|
|
ret = dbus_message_new_error (message,
|
|
|
|
|
DBUS_ERROR_UNKNOWN_METHOD,
|
|
|
|
|
"Can't convert GType of object property to a D-BUS type");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static DBusHandlerResult
|
|
|
|
|
gobject_message_function (DBusConnection *connection,
|
|
|
|
|
DBusMessage *message,
|
|
|
|
|
void *user_data)
|
|
|
|
|
{
|
|
|
|
|
const DBusGObjectInfo *info;
|
|
|
|
|
GParamSpec *pspec;
|
|
|
|
|
GObject *object;
|
|
|
|
|
gboolean setter;
|
|
|
|
|
gboolean getter;
|
|
|
|
|
char *s;
|
2005-01-29 19:52:19 +00:00
|
|
|
const char *wincaps_propname;
|
2005-01-29 20:12:22 +00:00
|
|
|
/* const char *wincaps_propiface; */
|
2005-01-29 19:52:19 +00:00
|
|
|
DBusMessageIter iter;
|
2003-09-03 02:08:25 +00:00
|
|
|
|
|
|
|
|
object = G_OBJECT (user_data);
|
|
|
|
|
|
|
|
|
|
if (dbus_message_is_method_call (message,
|
|
|
|
|
DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE,
|
|
|
|
|
"Introspect"))
|
|
|
|
|
return handle_introspect (connection, message, object);
|
|
|
|
|
|
|
|
|
|
/* Try the metainfo, which lets us invoke methods */
|
|
|
|
|
|
|
|
|
|
g_static_mutex_lock (&info_hash_mutex);
|
|
|
|
|
/* FIXME this needs to walk up the inheritance tree, not
|
|
|
|
|
* just look at the most-derived class
|
|
|
|
|
*/
|
|
|
|
|
info = g_hash_table_lookup (info_hash,
|
|
|
|
|
G_OBJECT_GET_CLASS (object));
|
|
|
|
|
g_static_mutex_unlock (&info_hash_mutex);
|
|
|
|
|
|
|
|
|
|
if (info != NULL)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If no metainfo, we can still do properties and signals
|
|
|
|
|
* via standard GLib introspection
|
|
|
|
|
*/
|
2005-01-29 19:52:19 +00:00
|
|
|
getter = FALSE;
|
|
|
|
|
setter = FALSE;
|
|
|
|
|
if (dbus_message_is_method_call (message,
|
|
|
|
|
DBUS_INTERFACE_ORG_FREEDESKTOP_PROPERTIES,
|
|
|
|
|
"Get"))
|
|
|
|
|
getter = TRUE;
|
|
|
|
|
else if (dbus_message_is_method_call (message,
|
|
|
|
|
DBUS_INTERFACE_ORG_FREEDESKTOP_PROPERTIES,
|
|
|
|
|
"Set"))
|
|
|
|
|
setter = TRUE;
|
2003-09-03 02:08:25 +00:00
|
|
|
|
|
|
|
|
if (!(setter || getter))
|
|
|
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
|
|
|
|
2005-01-29 19:52:19 +00:00
|
|
|
dbus_message_iter_init (message, &iter);
|
|
|
|
|
|
|
|
|
|
if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
|
|
|
|
|
{
|
|
|
|
|
g_warning ("Property get or set does not have an interface string as first arg\n");
|
|
|
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
|
|
|
}
|
2005-01-29 20:12:22 +00:00
|
|
|
/* We never use the interface name; if we did, we'd need to
|
|
|
|
|
* remember that it can be empty string for "pick one for me"
|
|
|
|
|
*/
|
|
|
|
|
/* dbus_message_iter_get_basic (&iter, &wincaps_propiface); */
|
2005-01-29 19:52:19 +00:00
|
|
|
dbus_message_iter_next (&iter);
|
|
|
|
|
|
|
|
|
|
if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
|
|
|
|
|
{
|
|
|
|
|
g_warning ("Property get or set does not have a property name string as second arg\n");
|
|
|
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
|
|
|
}
|
|
|
|
|
dbus_message_iter_get_basic (&iter, &wincaps_propname);
|
|
|
|
|
dbus_message_iter_next (&iter);
|
|
|
|
|
|
|
|
|
|
s = wincaps_to_uscore (wincaps_propname);
|
2003-09-03 02:08:25 +00:00
|
|
|
|
|
|
|
|
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object),
|
|
|
|
|
s);
|
|
|
|
|
|
|
|
|
|
g_free (s);
|
|
|
|
|
|
|
|
|
|
if (pspec != NULL)
|
|
|
|
|
{
|
|
|
|
|
DBusMessage *ret;
|
|
|
|
|
|
|
|
|
|
if (setter)
|
|
|
|
|
{
|
2005-01-29 19:52:19 +00:00
|
|
|
if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_VARIANT)
|
|
|
|
|
{
|
|
|
|
|
g_warning ("Property set does not have a variant value as third arg\n");
|
|
|
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = set_object_property (connection, message, &iter,
|
2003-09-03 02:08:25 +00:00
|
|
|
object, pspec);
|
2005-01-29 19:52:19 +00:00
|
|
|
dbus_message_iter_next (&iter);
|
2003-09-03 02:08:25 +00:00
|
|
|
}
|
|
|
|
|
else if (getter)
|
2005-01-29 19:52:19 +00:00
|
|
|
{
|
2003-09-03 02:08:25 +00:00
|
|
|
ret = get_object_property (connection, message,
|
|
|
|
|
object, pspec);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
g_assert_not_reached ();
|
|
|
|
|
ret = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_assert (ret != NULL);
|
|
|
|
|
|
2005-01-29 19:52:19 +00:00
|
|
|
if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID)
|
|
|
|
|
g_warning ("Property get or set had too many arguments\n");
|
|
|
|
|
|
2003-09-03 02:08:25 +00:00
|
|
|
dbus_connection_send (connection, ret, NULL);
|
|
|
|
|
dbus_message_unref (ret);
|
|
|
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static DBusObjectPathVTable gobject_dbus_vtable = {
|
|
|
|
|
gobject_unregister_function,
|
|
|
|
|
gobject_message_function,
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** @} */ /* end of internals */
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @addtogroup DBusGLib
|
|
|
|
|
* @{
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Install introspection information about the given object class
|
|
|
|
|
* sufficient to allow methods on the object to be invoked by name.
|
|
|
|
|
* The introspection information is normally generated by
|
|
|
|
|
* dbus-glib-tool, then this function is called in the
|
|
|
|
|
* class_init() for the object class.
|
|
|
|
|
*
|
|
|
|
|
* Once introspection information has been installed, instances of the
|
2004-06-20 15:28:15 +00:00
|
|
|
* object registered with dbus_g_connection_register_g_object() can have
|
2003-09-03 02:08:25 +00:00
|
|
|
* their methods invoked remotely.
|
|
|
|
|
*
|
|
|
|
|
* @param object_class class struct of the object
|
|
|
|
|
* @param info introspection data generated by dbus-glib-tool
|
|
|
|
|
*/
|
|
|
|
|
void
|
2003-09-17 03:52:07 +00:00
|
|
|
dbus_g_object_class_install_info (GObjectClass *object_class,
|
|
|
|
|
const DBusGObjectInfo *info)
|
2003-09-03 02:08:25 +00:00
|
|
|
{
|
|
|
|
|
g_return_if_fail (G_IS_OBJECT_CLASS (object_class));
|
|
|
|
|
|
|
|
|
|
g_static_mutex_lock (&info_hash_mutex);
|
|
|
|
|
|
|
|
|
|
if (info_hash == NULL)
|
|
|
|
|
{
|
|
|
|
|
info_hash = g_hash_table_new (NULL, NULL); /* direct hash */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_hash_table_replace (info_hash, object_class, (void*) info);
|
|
|
|
|
|
|
|
|
|
g_static_mutex_unlock (&info_hash_mutex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Registers a GObject at the given path. Properties, methods, and signals
|
|
|
|
|
* of the object can then be accessed remotely. Methods are only available
|
|
|
|
|
* if method introspection data has been added to the object's class
|
|
|
|
|
* with g_object_class_install_info().
|
|
|
|
|
*
|
|
|
|
|
* The registration will be cancelled if either the DBusConnection or
|
|
|
|
|
* the GObject gets finalized.
|
|
|
|
|
*
|
|
|
|
|
* @param connection the D-BUS connection
|
|
|
|
|
* @param at_path the path where the object will live (the object's name)
|
|
|
|
|
* @param object the object
|
|
|
|
|
*/
|
|
|
|
|
void
|
2004-06-20 15:28:15 +00:00
|
|
|
dbus_g_connection_register_g_object (DBusGConnection *connection,
|
|
|
|
|
const char *at_path,
|
|
|
|
|
GObject *object)
|
2003-09-03 02:08:25 +00:00
|
|
|
{
|
|
|
|
|
g_return_if_fail (connection != NULL);
|
|
|
|
|
g_return_if_fail (at_path != NULL);
|
|
|
|
|
g_return_if_fail (G_IS_OBJECT (object));
|
|
|
|
|
|
2004-06-20 15:28:15 +00:00
|
|
|
if (!dbus_connection_register_object_path (DBUS_CONNECTION_FROM_G_CONNECTION (connection),
|
2004-06-05 16:32:00 +00:00
|
|
|
at_path,
|
2003-09-03 02:08:25 +00:00
|
|
|
&gobject_dbus_vtable,
|
|
|
|
|
object))
|
|
|
|
|
g_error ("Failed to register GObject with DBusConnection");
|
|
|
|
|
|
|
|
|
|
/* FIXME set up memory management (so we break the
|
|
|
|
|
* registration if object or connection vanishes)
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @} */ /* end of public API */
|
|
|
|
|
|
|
|
|
|
#ifdef DBUS_BUILD_TESTS
|
2003-09-04 00:21:36 +00:00
|
|
|
#include <stdlib.h>
|
2003-09-03 02:08:25 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @ingroup DBusGLibInternals
|
|
|
|
|
* Unit test for GLib GObject integration ("skeletons")
|
|
|
|
|
* @returns #TRUE on success.
|
|
|
|
|
*/
|
2004-06-20 15:28:15 +00:00
|
|
|
gboolean
|
2003-09-03 02:08:25 +00:00
|
|
|
_dbus_gobject_test (const char *test_data_dir)
|
|
|
|
|
{
|
2003-09-04 00:21:36 +00:00
|
|
|
int i;
|
|
|
|
|
static struct { const char *wincaps; const char *uscore; } name_pairs[] = {
|
|
|
|
|
{ "SetFoo", "set_foo" },
|
|
|
|
|
{ "Foo", "foo" },
|
|
|
|
|
{ "GetFooBar", "get_foo_bar" },
|
|
|
|
|
{ "Hello", "hello" }
|
|
|
|
|
|
|
|
|
|
/* Impossible-to-handle cases */
|
|
|
|
|
/* { "FrobateUIHandler", "frobate_ui_handler" } */
|
2003-09-03 02:08:25 +00:00
|
|
|
};
|
|
|
|
|
|
2003-09-04 00:21:36 +00:00
|
|
|
i = 0;
|
|
|
|
|
while (i < (int) G_N_ELEMENTS (name_pairs))
|
|
|
|
|
{
|
|
|
|
|
char *uscore;
|
|
|
|
|
char *wincaps;
|
|
|
|
|
|
|
|
|
|
uscore = wincaps_to_uscore (name_pairs[i].wincaps);
|
|
|
|
|
wincaps = uscore_to_wincaps (name_pairs[i].uscore);
|
|
|
|
|
|
|
|
|
|
if (strcmp (uscore, name_pairs[i].uscore) != 0)
|
|
|
|
|
{
|
|
|
|
|
g_printerr ("\"%s\" should have been converted to \"%s\" not \"%s\"\n",
|
|
|
|
|
name_pairs[i].wincaps, name_pairs[i].uscore,
|
|
|
|
|
uscore);
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp (wincaps, name_pairs[i].wincaps) != 0)
|
|
|
|
|
{
|
|
|
|
|
g_printerr ("\"%s\" should have been converted to \"%s\" not \"%s\"\n",
|
|
|
|
|
name_pairs[i].uscore, name_pairs[i].wincaps,
|
|
|
|
|
wincaps);
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_free (uscore);
|
|
|
|
|
g_free (wincaps);
|
|
|
|
|
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
|
2003-09-03 02:08:25 +00:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* DBUS_BUILD_TESTS */
|