mirror of
https://gitlab.freedesktop.org/dbus/dbus.git
synced 2026-02-12 11:10:32 +01:00
2003-09-01 Havoc Pennington <hp@pobox.com>
* glib/dbus-gparser.c: implement * glib/dbus-gobject.c: start implementing skeletons support * configure.in: when disabling checks/assert, also define G_DISABLE_ASSERT and G_DISABLE_CHECKS
This commit is contained in:
parent
7b4ac5de11
commit
d021cfae66
12 changed files with 1740 additions and 28 deletions
|
|
@ -1,3 +1,12 @@
|
|||
2003-09-01 Havoc Pennington <hp@pobox.com>
|
||||
|
||||
* glib/dbus-gparser.c: implement
|
||||
|
||||
* glib/dbus-gobject.c: start implementing skeletons support
|
||||
|
||||
* configure.in: when disabling checks/assert, also define
|
||||
G_DISABLE_ASSERT and G_DISABLE_CHECKS
|
||||
|
||||
2003-09-01 Havoc Pennington <hp@pobox.com>
|
||||
|
||||
* glib/Makefile.am: rearrange a bunch of files and get "make
|
||||
|
|
|
|||
|
|
@ -54,9 +54,11 @@ if test x$enable_verbose_mode = xyes; then
|
|||
fi
|
||||
if test x$enable_asserts = xno; then
|
||||
AC_DEFINE(DBUS_DISABLE_ASSERT,1,[Disable assertion checking])
|
||||
AC_DEFINE(G_DISABLE_ASSERT,1,[Disable GLib assertion macros])
|
||||
fi
|
||||
if test x$enable_checks = xno; then
|
||||
AC_DEFINE(DBUS_DISABLE_CHECKS,1,[Disable public API sanity checking])
|
||||
AC_DEFINE(G_DISABLE_CHECKS,1,[Disable GLib public API sanity checking])
|
||||
fi
|
||||
|
||||
#### gcc warning flags
|
||||
|
|
@ -545,7 +547,7 @@ AC_SUBST(DBUS_TEST_CFLAGS)
|
|||
AC_SUBST(DBUS_TEST_LIBS)
|
||||
|
||||
# Glib detection
|
||||
PKG_CHECK_MODULES(DBUS_GLIB, glib-2.0, have_glib=yes, have_glib=no)
|
||||
PKG_CHECK_MODULES(DBUS_GLIB, gobject-2.0, have_glib=yes, have_glib=no)
|
||||
PKG_CHECK_MODULES(DBUS_GLIB_THREADS, glib-2.0 gthread-2.0, have_glib_threads=yes, have_glib_threads=no)
|
||||
|
||||
if test x$have_glib = xno ; then
|
||||
|
|
@ -570,6 +572,11 @@ AC_SUBST(DBUS_GLIB_CFLAGS)
|
|||
AC_SUBST(DBUS_GLIB_LIBS)
|
||||
AC_SUBST(DBUS_GLIB_THREADS_LIBS)
|
||||
|
||||
DBUS_GLIB_TOOL_CFLAGS=$XML_CFLAGS
|
||||
DBUS_GLIB_TOOL_LIBS=$XML_LIBS
|
||||
AC_SUBST(DBUS_GLIB_TOOL_CFLAGS)
|
||||
AC_SUBST(DBUS_GLIB_TOOL_LIBS)
|
||||
|
||||
# Qt detection
|
||||
have_qt=no
|
||||
AC_MSG_CHECKING([for qglobal.h])
|
||||
|
|
|
|||
|
|
@ -1473,7 +1473,8 @@ _dbus_demarshal_string_array (const DBusString *str,
|
|||
#define VERBOSE_DECOMPOSE 0
|
||||
|
||||
/**
|
||||
* Demarshals an object path.
|
||||
* Demarshals an object path. A path of just "/" is
|
||||
* represented as an empty vector of strings.
|
||||
*
|
||||
* @param str the string containing the data
|
||||
* @param byte_order the byte order
|
||||
|
|
@ -1556,7 +1557,6 @@ _dbus_demarshal_object_path (const DBusString *str,
|
|||
i = j;
|
||||
}
|
||||
_dbus_assert (i == len);
|
||||
_dbus_assert (retval[0] != NULL);
|
||||
|
||||
*path = retval;
|
||||
if (path_len)
|
||||
|
|
|
|||
|
|
@ -87,6 +87,18 @@ extern "C" {
|
|||
#define DBUS_PATH_ORG_FREEDESKTOP_DBUS "/org/freedesktop/DBus"
|
||||
#define DBUS_PATH_ORG_FREEDESKTOP_LOCAL "/org/freedesktop/Local"
|
||||
|
||||
/* Interfaces, these #define don't do much other than
|
||||
* catch typos at compile time
|
||||
*/
|
||||
#define DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS "org.freedesktop.DBus"
|
||||
#define DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE "org.freedesktop.Introspectable"
|
||||
|
||||
/* This is a special interface whose methods can only be invoked
|
||||
* by the local implementation (messages from remote apps aren't
|
||||
* allowed to specify this interface).
|
||||
*/
|
||||
#define DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL "org.freedesktop.Local"
|
||||
|
||||
/* Service owner flags */
|
||||
#define DBUS_SERVICE_FLAG_PROHIBIT_REPLACEMENT 0x1
|
||||
#define DBUS_SERVICE_FLAG_REPLACE_EXISTING 0x2
|
||||
|
|
@ -101,17 +113,6 @@ extern "C" {
|
|||
#define DBUS_ACTIVATION_REPLY_ACTIVATED 0x0
|
||||
#define DBUS_ACTIVATION_REPLY_ALREADY_ACTIVE 0x1
|
||||
|
||||
/* Interfaces, these #define don't do much other than
|
||||
* catch typos at compile time
|
||||
*/
|
||||
#define DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS "org.freedesktop.DBus"
|
||||
|
||||
/* This is a special interface whose methods can only be invoked
|
||||
* by the local implementation (messages from remote apps aren't
|
||||
* allowed to specify this interface).
|
||||
*/
|
||||
#define DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL "org.freedesktop.Local"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
INCLUDES=-I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) $(DBUS_GLIB_CFLAGS) -DDBUS_COMPILATION=1
|
||||
INCLUDES=-I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) $(DBUS_GLIB_CFLAGS) $(DBUS_GLIB_TOOL_CFLAGS) -DDBUS_COMPILATION=1
|
||||
|
||||
dbusincludedir=$(includedir)/dbus-1.0/dbus
|
||||
|
||||
|
|
@ -10,6 +10,7 @@ dbusinclude_HEADERS= \
|
|||
|
||||
libdbus_glib_1_la_SOURCES = \
|
||||
dbus-gmain.c \
|
||||
dbus-gobject.c \
|
||||
dbus-gproxy.c \
|
||||
dbus-gtest.c \
|
||||
dbus-gtest.h \
|
||||
|
|
@ -23,12 +24,13 @@ dbus_glib_tool_SOURCES = \
|
|||
dbus-gidl.c \
|
||||
dbus-gidl.h \
|
||||
dbus-glib-tool.c \
|
||||
dbus-gloader-expat.c \
|
||||
dbus-gparser.c \
|
||||
dbus-gparser.h \
|
||||
dbus-gtool-test.h
|
||||
|
||||
|
||||
dbus_glib_tool_LDADD= libdbus-glib-1.la $(DBUS_GLIB_LIBS) $(top_builddir)/dbus/libdbus-1.la
|
||||
dbus_glib_tool_LDADD= $(DBUS_GLIB_TOOL_LIBS) libdbus-glib-1.la $(top_builddir)/dbus/libdbus-1.la
|
||||
|
||||
if DBUS_BUILD_TESTS
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,13 @@
|
|||
|
||||
#include "dbus-gidl.h"
|
||||
|
||||
struct NodeInfo
|
||||
{
|
||||
int refcount;
|
||||
char *name;
|
||||
GSList *interfaces;
|
||||
};
|
||||
|
||||
struct InterfaceInfo
|
||||
{
|
||||
int refcount;
|
||||
|
|
@ -54,6 +61,20 @@ struct ArgInfo
|
|||
ArgDirection direction;
|
||||
};
|
||||
|
||||
static void
|
||||
free_interface_list (GSList **interfaces_p)
|
||||
{
|
||||
GSList *tmp;
|
||||
tmp = *interfaces_p;
|
||||
while (tmp != NULL)
|
||||
{
|
||||
interface_info_unref (tmp->data);
|
||||
tmp = tmp->next;
|
||||
}
|
||||
g_slist_free (*interfaces_p);
|
||||
*interfaces_p = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
free_method_list (GSList **methods_p)
|
||||
{
|
||||
|
|
@ -82,6 +103,59 @@ free_signal_list (GSList **signals_p)
|
|||
*signals_p = NULL;
|
||||
}
|
||||
|
||||
NodeInfo*
|
||||
node_info_new (const char *name)
|
||||
{
|
||||
NodeInfo *info;
|
||||
|
||||
/* name can be NULL */
|
||||
|
||||
info = g_new0 (NodeInfo, 1);
|
||||
info->refcount = 1;
|
||||
info->name = g_strdup (name);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
void
|
||||
node_info_ref (NodeInfo *info)
|
||||
{
|
||||
info->refcount += 1;
|
||||
}
|
||||
|
||||
void
|
||||
node_info_unref (NodeInfo *info)
|
||||
{
|
||||
info->refcount -= 1;
|
||||
if (info->refcount == 0)
|
||||
{
|
||||
free_interface_list (&info->interfaces);
|
||||
g_free (info->name);
|
||||
g_free (info);
|
||||
}
|
||||
}
|
||||
|
||||
const char*
|
||||
node_info_get_name (NodeInfo *info)
|
||||
{
|
||||
return info->name;
|
||||
}
|
||||
|
||||
GSList*
|
||||
node_info_get_interfaces (NodeInfo *info)
|
||||
{
|
||||
return info->interfaces;
|
||||
}
|
||||
|
||||
void
|
||||
node_info_add_interface (NodeInfo *info,
|
||||
InterfaceInfo *interface)
|
||||
{
|
||||
interface_info_ref (interface);
|
||||
info->interfaces = g_slist_append (info->interfaces, interface);
|
||||
}
|
||||
|
||||
|
||||
InterfaceInfo*
|
||||
interface_info_new (const char *name)
|
||||
{
|
||||
|
|
@ -90,7 +164,7 @@ interface_info_new (const char *name)
|
|||
info = g_new0 (InterfaceInfo, 1);
|
||||
info->refcount = 1;
|
||||
info->name = g_strdup (name);
|
||||
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
|
@ -113,6 +187,12 @@ interface_info_unref (InterfaceInfo *info)
|
|||
}
|
||||
}
|
||||
|
||||
const char*
|
||||
interface_info_get_name (InterfaceInfo *info)
|
||||
{
|
||||
return info->name;
|
||||
}
|
||||
|
||||
GSList*
|
||||
interface_info_get_methods (InterfaceInfo *info)
|
||||
{
|
||||
|
|
@ -163,7 +243,7 @@ method_info_new (const char *name)
|
|||
info = g_new0 (MethodInfo, 1);
|
||||
info->refcount = 1;
|
||||
info->name = g_strdup (name);
|
||||
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
|
@ -213,7 +293,7 @@ signal_info_new (const char *name)
|
|||
info = g_new0 (SignalInfo, 1);
|
||||
info->refcount = 1;
|
||||
info->name = g_strdup (name);
|
||||
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
|
@ -264,10 +344,12 @@ arg_info_new (const char *name,
|
|||
|
||||
info = g_new0 (ArgInfo, 1);
|
||||
info->refcount = 1;
|
||||
|
||||
/* name can be NULL */
|
||||
info->name = g_strdup (name);
|
||||
info->direction = direction;
|
||||
info->type = type;
|
||||
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct NodeInfo NodeInfo;
|
||||
typedef struct InterfaceInfo InterfaceInfo;
|
||||
typedef struct MethodInfo MethodInfo;
|
||||
typedef struct SignalInfo SignalInfo;
|
||||
|
|
@ -40,9 +41,18 @@ typedef enum
|
|||
ARG_OUT
|
||||
} ArgDirection;
|
||||
|
||||
NodeInfo* node_info_new (const char *name);
|
||||
void node_info_ref (NodeInfo *info);
|
||||
void node_info_unref (NodeInfo *info);
|
||||
const char* node_info_get_name (NodeInfo *info);
|
||||
GSList* node_info_get_interfaces (NodeInfo *info);
|
||||
void node_info_add_interface (NodeInfo *info,
|
||||
InterfaceInfo *interface);
|
||||
|
||||
InterfaceInfo* interface_info_new (const char *name);
|
||||
void interface_info_ref (InterfaceInfo *info);
|
||||
void interface_info_unref (InterfaceInfo *info);
|
||||
const char* interface_info_get_name (InterfaceInfo *info);
|
||||
GSList* interface_info_get_methods (InterfaceInfo *info);
|
||||
GSList* interface_info_get_signals (InterfaceInfo *info);
|
||||
void interface_info_add_method (InterfaceInfo *info,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
#define DBUS_GLIB_H
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
|
@ -36,6 +36,30 @@ void dbus_connection_setup_with_g_main (DBusConnection *connection,
|
|||
void dbus_server_setup_with_g_main (DBusServer *server,
|
||||
GMainContext *context);
|
||||
|
||||
typedef struct DBusGObjectInfo DBusGObjectInfo;
|
||||
typedef struct DBusGMethodInfo DBusGMethodInfo;
|
||||
|
||||
struct DBusGMethodInfo
|
||||
{
|
||||
GCallback function;
|
||||
DBusHandleMessageFunction marshaller;
|
||||
int data_offset;
|
||||
};
|
||||
|
||||
struct DBusGObjectInfo
|
||||
{
|
||||
const DBusGMethodInfo *infos;
|
||||
const unsigned char *data;
|
||||
void *dbus_internal_padding1;
|
||||
void *dbus_internal_padding2;
|
||||
};
|
||||
|
||||
void dbus_gobject_class_install_info (GObjectClass *object_class,
|
||||
const DBusGObjectInfo *info);
|
||||
void dbus_connection_register_gobject (DBusConnection *connection,
|
||||
const char *at_path,
|
||||
GObject *object);
|
||||
|
||||
#undef DBUS_INSIDE_DBUS_GLIB_H
|
||||
|
||||
G_END_DECLS
|
||||
|
|
|
|||
254
glib/dbus-gloader-expat.c
Normal file
254
glib/dbus-gloader-expat.c
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
/* -*- mode: C; c-file-style: "gnu" -*- */
|
||||
/* dbus-gloader-expat.c expat XML loader
|
||||
*
|
||||
* Copyright (C) 2003 Red Hat, Inc.
|
||||
*
|
||||
* Licensed under the Academic Free License version 1.2
|
||||
*
|
||||
* 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 "dbus-gparser.h"
|
||||
#include <expat.h>
|
||||
|
||||
static void*
|
||||
expat_g_malloc (size_t sz)
|
||||
{
|
||||
return g_malloc (sz);
|
||||
}
|
||||
|
||||
static void*
|
||||
expat_g_realloc (void *mem, size_t sz)
|
||||
{
|
||||
return g_realloc (mem, sz);
|
||||
}
|
||||
|
||||
static XML_Memory_Handling_Suite memsuite =
|
||||
{
|
||||
expat_g_malloc,
|
||||
expat_g_realloc,
|
||||
g_free
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Parser *parser;
|
||||
const char *filename;
|
||||
GString *content;
|
||||
GError **error;
|
||||
gboolean failed;
|
||||
} ExpatParseContext;
|
||||
|
||||
static dbus_bool_t
|
||||
process_content (ExpatParseContext *context)
|
||||
{
|
||||
if (context->failed)
|
||||
return FALSE;
|
||||
|
||||
if (context->content->len > 0)
|
||||
{
|
||||
if (!parser_content (context->parser,
|
||||
context->content->str,
|
||||
context->content->len,
|
||||
context->error))
|
||||
{
|
||||
context->failed = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
g_string_set_size (context->content, 0);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
expat_StartElementHandler (void *userData,
|
||||
const XML_Char *name,
|
||||
const XML_Char **atts)
|
||||
{
|
||||
ExpatParseContext *context = userData;
|
||||
int i;
|
||||
char **names;
|
||||
char **values;
|
||||
|
||||
/* Expat seems to suck and can't abort the parse if we
|
||||
* throw an error. Expat 2.0 is supposed to fix this.
|
||||
*/
|
||||
if (context->failed)
|
||||
return;
|
||||
|
||||
if (!process_content (context))
|
||||
return;
|
||||
|
||||
/* "atts" is key, value, key, value, NULL */
|
||||
for (i = 0; atts[i] != NULL; ++i)
|
||||
; /* nothing */
|
||||
|
||||
g_assert (i % 2 == 0);
|
||||
names = g_new0 (char *, i / 2 + 1);
|
||||
values = g_new0 (char *, i / 2 + 1);
|
||||
|
||||
i = 0;
|
||||
while (atts[i] != NULL)
|
||||
{
|
||||
g_assert (i % 2 == 0);
|
||||
names [i / 2] = (char*) atts[i];
|
||||
values[i / 2] = (char*) atts[i+1];
|
||||
|
||||
i += 2;
|
||||
}
|
||||
|
||||
if (!parser_start_element (context->parser,
|
||||
name,
|
||||
(const char **) names,
|
||||
(const char **) values,
|
||||
context->error))
|
||||
{
|
||||
g_free (names);
|
||||
g_free (values);
|
||||
context->failed = TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
g_free (names);
|
||||
g_free (values);
|
||||
}
|
||||
|
||||
static void
|
||||
expat_EndElementHandler (void *userData,
|
||||
const XML_Char *name)
|
||||
{
|
||||
ExpatParseContext *context = userData;
|
||||
|
||||
if (!process_content (context))
|
||||
return;
|
||||
|
||||
if (!parser_end_element (context->parser,
|
||||
name,
|
||||
context->error))
|
||||
{
|
||||
context->failed = TRUE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* s is not 0 terminated. */
|
||||
static void
|
||||
expat_CharacterDataHandler (void *userData,
|
||||
const XML_Char *s,
|
||||
int len)
|
||||
{
|
||||
ExpatParseContext *context = userData;
|
||||
|
||||
if (context->failed)
|
||||
return;
|
||||
|
||||
g_string_append_len (context->content,
|
||||
s, len);
|
||||
}
|
||||
|
||||
Parser*
|
||||
description_load_from_file (const char *filename,
|
||||
GError **error)
|
||||
{
|
||||
char *contents;
|
||||
gsize len;
|
||||
Parser *parser;
|
||||
|
||||
contents = NULL;
|
||||
if (!g_file_get_contents (filename, &contents, &len, error))
|
||||
return NULL;
|
||||
|
||||
parser = description_load_from_string (contents, len, error);
|
||||
g_free (contents);
|
||||
|
||||
return parser;
|
||||
}
|
||||
|
||||
Parser*
|
||||
description_load_from_string (const char *str,
|
||||
int len,
|
||||
GError **error)
|
||||
{
|
||||
XML_Parser expat;
|
||||
ExpatParseContext context;
|
||||
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||||
|
||||
expat = NULL;
|
||||
context.parser = NULL;
|
||||
context.error = error;
|
||||
context.failed = FALSE;
|
||||
|
||||
expat = XML_ParserCreate_MM ("UTF-8", &memsuite, NULL);
|
||||
if (expat == NULL)
|
||||
g_error ("No memory to create XML parser\n");
|
||||
|
||||
context.parser = parser_new ();
|
||||
context.content = g_string_new (NULL);
|
||||
|
||||
XML_SetUserData (expat, &context);
|
||||
XML_SetElementHandler (expat,
|
||||
expat_StartElementHandler,
|
||||
expat_EndElementHandler);
|
||||
XML_SetCharacterDataHandler (expat,
|
||||
expat_CharacterDataHandler);
|
||||
|
||||
if (!XML_Parse (expat, str, len, TRUE))
|
||||
{
|
||||
if (context.error != NULL &&
|
||||
*context.error == NULL)
|
||||
{
|
||||
enum XML_Error e;
|
||||
|
||||
e = XML_GetErrorCode (expat);
|
||||
if (e == XML_ERROR_NO_MEMORY)
|
||||
g_error ("Not enough memory to parse XML document");
|
||||
else
|
||||
g_set_error (error,
|
||||
G_MARKUP_ERROR,
|
||||
G_MARKUP_ERROR_PARSE,
|
||||
"Error in D-BUS description XML, line %d, column %d: %s\n",
|
||||
XML_GetCurrentLineNumber (expat),
|
||||
XML_GetCurrentColumnNumber (expat),
|
||||
XML_ErrorString (e));
|
||||
}
|
||||
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (context.failed)
|
||||
goto failed;
|
||||
|
||||
if (!parser_finished (context.parser, error))
|
||||
goto failed;
|
||||
|
||||
XML_ParserFree (expat);
|
||||
g_string_free (context.content, TRUE);
|
||||
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||||
return context.parser;
|
||||
|
||||
failed:
|
||||
g_return_val_if_fail (error == NULL || *error != NULL, NULL);
|
||||
|
||||
g_string_free (context.content, TRUE);
|
||||
if (expat)
|
||||
XML_ParserFree (expat);
|
||||
if (context.parser)
|
||||
parser_unref (context.parser);
|
||||
return NULL;
|
||||
}
|
||||
780
glib/dbus-gobject.c
Normal file
780
glib/dbus-gobject.c
Normal file
|
|
@ -0,0 +1,780 @@
|
|||
/* -*- mode: C; c-file-style: "gnu" -*- */
|
||||
/* dbus-gobject.c Exporting a GObject remotely
|
||||
*
|
||||
* Copyright (C) 2003 Red Hat, Inc.
|
||||
*
|
||||
* Licensed under the Academic Free License version 1.2
|
||||
*
|
||||
* 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>
|
||||
#include "dbus-glib.h"
|
||||
#include "dbus-gtest.h"
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* @addtogroup DBusGLibInternals
|
||||
* @{
|
||||
*/
|
||||
|
||||
static GStaticMutex info_hash_mutex = G_STATIC_MUTEX_INIT;
|
||||
static GHashTable *info_hash = NULL;
|
||||
|
||||
static char*
|
||||
javacaps_to_uscore (const char *caps)
|
||||
{
|
||||
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*
|
||||
uscore_to_javacaps (const char *uscore)
|
||||
{
|
||||
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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *
|
||||
dbus_type_to_string (int type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case DBUS_TYPE_INVALID:
|
||||
return "invalid";
|
||||
case DBUS_TYPE_NIL:
|
||||
return "nil";
|
||||
case DBUS_TYPE_BOOLEAN:
|
||||
return "boolean";
|
||||
case DBUS_TYPE_INT32:
|
||||
return "int32";
|
||||
case DBUS_TYPE_UINT32:
|
||||
return "uint32";
|
||||
case DBUS_TYPE_DOUBLE:
|
||||
return "double";
|
||||
case DBUS_TYPE_STRING:
|
||||
return "string";
|
||||
case DBUS_TYPE_NAMED:
|
||||
return "named";
|
||||
case DBUS_TYPE_ARRAY:
|
||||
return "array";
|
||||
case DBUS_TYPE_DICT:
|
||||
return "dict";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static DBusHandlerResult
|
||||
handle_introspect (DBusConnection *connection,
|
||||
DBusMessage *message,
|
||||
GObject *object)
|
||||
{
|
||||
GString *xml;
|
||||
GParamSpec **specs;
|
||||
unsigned int n_specs;
|
||||
unsigned int i;
|
||||
GType last_type;
|
||||
DBusMessage *ret;
|
||||
|
||||
xml = g_string_new (NULL);
|
||||
|
||||
g_string_append (xml, "<node>\n");
|
||||
|
||||
last_type = G_TYPE_INVALID;
|
||||
|
||||
specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (object),
|
||||
&n_specs);
|
||||
|
||||
i = 0;
|
||||
while (i < n_specs)
|
||||
{
|
||||
GParamSpec *spec = specs[i];
|
||||
gboolean can_set;
|
||||
gboolean can_get;
|
||||
char *s;
|
||||
int dbus_type;
|
||||
|
||||
dbus_type = gtype_to_dbus_type (G_PARAM_SPEC_VALUE_TYPE (spec));
|
||||
if (dbus_type == DBUS_TYPE_INVALID)
|
||||
goto next;
|
||||
|
||||
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 &&
|
||||
(spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
|
||||
|
||||
can_get = (spec->flags & G_PARAM_READABLE) != 0;
|
||||
|
||||
s = uscore_to_javacaps (spec->name);
|
||||
|
||||
if (can_set)
|
||||
{
|
||||
g_string_append (xml, " <method name=\"set");
|
||||
g_string_append (xml, s);
|
||||
g_string_append (xml, "\">\n");
|
||||
|
||||
g_string_append (xml, " <arg type=\"");
|
||||
g_string_append (xml, dbus_type_to_string (dbus_type));
|
||||
g_string_append (xml, "\"/>\n");
|
||||
}
|
||||
|
||||
if (can_get)
|
||||
{
|
||||
g_string_append (xml, " <method name=\"get");
|
||||
g_string_append (xml, s);
|
||||
g_string_append (xml, "\">\n");
|
||||
|
||||
g_string_append (xml, " <arg type=\"");
|
||||
g_string_append (xml, dbus_type_to_string (dbus_type));
|
||||
g_string_append (xml, "\" direction=\"out\"/>\n");
|
||||
}
|
||||
|
||||
g_free (s);
|
||||
|
||||
next:
|
||||
++i;
|
||||
}
|
||||
|
||||
if (last_type != G_TYPE_INVALID)
|
||||
g_string_append (xml, " </interface>\n");
|
||||
|
||||
g_free (specs);
|
||||
|
||||
/* 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)
|
||||
g_error ("out of memory");
|
||||
|
||||
dbus_message_append_args (message,
|
||||
DBUS_TYPE_STRING, xml->str,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
dbus_connection_send (connection, message, NULL);
|
||||
dbus_message_unref (message);
|
||||
|
||||
g_string_free (xml, TRUE);
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
static DBusMessage*
|
||||
set_object_property (DBusConnection *connection,
|
||||
DBusMessage *message,
|
||||
GObject *object,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GValue value;
|
||||
DBusMessageIter iter;
|
||||
int type;
|
||||
gboolean can_set;
|
||||
DBusMessage *ret;
|
||||
|
||||
dbus_message_iter_init (message, &iter);
|
||||
type = dbus_message_get_type (message);
|
||||
|
||||
can_set = TRUE;
|
||||
switch (type)
|
||||
{
|
||||
case DBUS_TYPE_BYTE:
|
||||
{
|
||||
unsigned char b;
|
||||
|
||||
b = dbus_message_iter_get_byte (&iter);
|
||||
|
||||
g_value_init (&value, G_TYPE_UCHAR);
|
||||
|
||||
g_value_set_uchar (&value, b);
|
||||
}
|
||||
break;
|
||||
case DBUS_TYPE_BOOLEAN:
|
||||
{
|
||||
gboolean b;
|
||||
|
||||
b = dbus_message_iter_get_boolean (&iter);
|
||||
|
||||
g_value_init (&value, G_TYPE_BOOLEAN);
|
||||
|
||||
g_value_set_boolean (&value, b);
|
||||
}
|
||||
break;
|
||||
case DBUS_TYPE_INT32:
|
||||
{
|
||||
gint32 i;
|
||||
|
||||
i = dbus_message_iter_get_int32 (&iter);
|
||||
|
||||
g_value_init (&value, G_TYPE_INT);
|
||||
|
||||
g_value_set_int (&value, i);
|
||||
}
|
||||
break;
|
||||
case DBUS_TYPE_UINT32:
|
||||
{
|
||||
guint32 i;
|
||||
|
||||
i = dbus_message_iter_get_uint32 (&iter);
|
||||
|
||||
g_value_init (&value, G_TYPE_UINT);
|
||||
|
||||
g_value_set_uint (&value, i);
|
||||
}
|
||||
break;
|
||||
case DBUS_TYPE_INT64:
|
||||
{
|
||||
gint64 i;
|
||||
|
||||
i = dbus_message_iter_get_int64 (&iter);
|
||||
|
||||
g_value_init (&value, G_TYPE_INT64);
|
||||
|
||||
g_value_set_int64 (&value, i);
|
||||
}
|
||||
break;
|
||||
case DBUS_TYPE_UINT64:
|
||||
{
|
||||
guint64 i;
|
||||
|
||||
i = dbus_message_iter_get_uint64 (&iter);
|
||||
|
||||
g_value_init (&value, G_TYPE_UINT64);
|
||||
|
||||
g_value_set_uint64 (&value, i);
|
||||
}
|
||||
break;
|
||||
case DBUS_TYPE_DOUBLE:
|
||||
{
|
||||
double d;
|
||||
|
||||
d = dbus_message_iter_get_double (&iter);
|
||||
|
||||
g_value_init (&value, G_TYPE_DOUBLE);
|
||||
|
||||
g_value_set_double (&value, d);
|
||||
}
|
||||
break;
|
||||
case DBUS_TYPE_STRING:
|
||||
{
|
||||
char *s;
|
||||
|
||||
/* FIXME use a const string accessor */
|
||||
|
||||
s = dbus_message_iter_get_string (&iter);
|
||||
|
||||
g_value_init (&value, G_TYPE_STRING);
|
||||
|
||||
g_value_set_string (&value, s);
|
||||
|
||||
g_free (s);
|
||||
}
|
||||
break;
|
||||
|
||||
/* FIXME array and other types, especially byte array
|
||||
* converted to G_TYPE_STRING
|
||||
*/
|
||||
|
||||
default:
|
||||
can_set = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
if (can_set)
|
||||
{
|
||||
g_object_set_property (object,
|
||||
pspec->name,
|
||||
&value);
|
||||
|
||||
ret = dbus_message_new_method_return (message);
|
||||
if (ret == NULL)
|
||||
g_error ("out of memory");
|
||||
|
||||
g_value_unset (&value);
|
||||
}
|
||||
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;
|
||||
gboolean can_get;
|
||||
DBusMessage *ret;
|
||||
GValue value;
|
||||
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");
|
||||
|
||||
can_get = TRUE;
|
||||
g_value_init (&value, value_type);
|
||||
g_object_get_property (object, pspec->name, &value);
|
||||
|
||||
value_type = G_VALUE_TYPE (&value);
|
||||
|
||||
dbus_message_append_iter_init (message, &iter);
|
||||
|
||||
switch (value_type)
|
||||
{
|
||||
case G_TYPE_CHAR:
|
||||
dbus_message_iter_append_byte (&iter,
|
||||
g_value_get_char (&value));
|
||||
break;
|
||||
case G_TYPE_UCHAR:
|
||||
dbus_message_iter_append_byte (&iter,
|
||||
g_value_get_uchar (&value));
|
||||
break;
|
||||
case G_TYPE_BOOLEAN:
|
||||
dbus_message_iter_append_boolean (&iter,
|
||||
g_value_get_boolean (&value));
|
||||
break;
|
||||
case G_TYPE_INT:
|
||||
dbus_message_iter_append_int32 (&iter,
|
||||
g_value_get_int (&value));
|
||||
break;
|
||||
case G_TYPE_UINT:
|
||||
dbus_message_iter_append_uint32 (&iter,
|
||||
g_value_get_uint (&value));
|
||||
break;
|
||||
/* long gets cut to 32 bits so the remote API is consistent
|
||||
* on all architectures
|
||||
*/
|
||||
case G_TYPE_LONG:
|
||||
dbus_message_iter_append_int32 (&iter,
|
||||
g_value_get_long (&value));
|
||||
break;
|
||||
case G_TYPE_ULONG:
|
||||
dbus_message_iter_append_uint32 (&iter,
|
||||
g_value_get_ulong (&value));
|
||||
break;
|
||||
case G_TYPE_INT64:
|
||||
dbus_message_iter_append_int64 (&iter,
|
||||
g_value_get_int64 (&value));
|
||||
break;
|
||||
case G_TYPE_UINT64:
|
||||
dbus_message_iter_append_uint64 (&iter,
|
||||
g_value_get_uint64 (&value));
|
||||
break;
|
||||
case G_TYPE_FLOAT:
|
||||
dbus_message_iter_append_double (&iter,
|
||||
g_value_get_float (&value));
|
||||
break;
|
||||
case G_TYPE_DOUBLE:
|
||||
dbus_message_iter_append_double (&iter,
|
||||
g_value_get_double (&value));
|
||||
break;
|
||||
case G_TYPE_STRING:
|
||||
/* FIXME, the GValue string may not be valid UTF-8 */
|
||||
dbus_message_iter_append_string (&iter,
|
||||
g_value_get_string (&value));
|
||||
break;
|
||||
default:
|
||||
can_get = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
g_value_unset (&value);
|
||||
|
||||
if (!can_get)
|
||||
{
|
||||
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;
|
||||
const char *member;
|
||||
gboolean setter;
|
||||
gboolean getter;
|
||||
char *s;
|
||||
|
||||
object = G_OBJECT (user_data);
|
||||
|
||||
if (dbus_message_is_method_call (message,
|
||||
DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE,
|
||||
"Introspect"))
|
||||
return handle_introspect (connection, message, object);
|
||||
|
||||
member = dbus_message_get_member (message);
|
||||
|
||||
/* 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
|
||||
*/
|
||||
setter = (member[0] == 's' && member[1] == 'e' && member[2] == 't');
|
||||
getter = (member[0] == 'g' && member[1] == 'e' && member[2] == 't');
|
||||
|
||||
if (!(setter || getter))
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
|
||||
s = javacaps_to_uscore (&member[3]);
|
||||
|
||||
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object),
|
||||
s);
|
||||
|
||||
g_free (s);
|
||||
|
||||
if (pspec != NULL)
|
||||
{
|
||||
DBusMessage *ret;
|
||||
|
||||
if (setter)
|
||||
{
|
||||
ret = set_object_property (connection, message,
|
||||
object, pspec);
|
||||
}
|
||||
else if (getter)
|
||||
{
|
||||
ret = get_object_property (connection, message,
|
||||
object, pspec);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_not_reached ();
|
||||
ret = NULL;
|
||||
}
|
||||
|
||||
g_assert (ret != NULL);
|
||||
|
||||
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
|
||||
* object registered with dbus_connection_register_gobject() can have
|
||||
* their methods invoked remotely.
|
||||
*
|
||||
* @param object_class class struct of the object
|
||||
* @param info introspection data generated by dbus-glib-tool
|
||||
*/
|
||||
void
|
||||
dbus_gobject_class_install_info (GObjectClass *object_class,
|
||||
const DBusGObjectInfo *info)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
static char**
|
||||
split_path (const char *path)
|
||||
{
|
||||
int len;
|
||||
char **split;
|
||||
int n_components;
|
||||
int i, j, comp;
|
||||
|
||||
len = strlen (path);
|
||||
|
||||
n_components = 0;
|
||||
i = 0;
|
||||
while (i < len)
|
||||
{
|
||||
if (path[i] == '/')
|
||||
n_components += 1;
|
||||
++i;
|
||||
}
|
||||
|
||||
split = g_new0 (char*, n_components + 1);
|
||||
|
||||
comp = 0;
|
||||
i = 0;
|
||||
while (i < len)
|
||||
{
|
||||
if (path[i] == '/')
|
||||
++i;
|
||||
j = i;
|
||||
|
||||
while (j < len && path[j] != '/')
|
||||
++j;
|
||||
|
||||
/* Now [i, j) is the path component */
|
||||
g_assert (i < j);
|
||||
g_assert (path[i] != '/');
|
||||
g_assert (j == len || path[j] == '/');
|
||||
|
||||
split[comp] = g_strndup (&path[i], j - i + 1);
|
||||
|
||||
split[comp][j-i] = '\0';
|
||||
|
||||
++comp;
|
||||
i = j;
|
||||
}
|
||||
g_assert (i == len);
|
||||
|
||||
return split;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
dbus_connection_register_gobject (DBusConnection *connection,
|
||||
const char *at_path,
|
||||
GObject *object)
|
||||
{
|
||||
char **split;
|
||||
|
||||
g_return_if_fail (connection != NULL);
|
||||
g_return_if_fail (at_path != NULL);
|
||||
g_return_if_fail (G_IS_OBJECT (object));
|
||||
|
||||
split = split_path (at_path);
|
||||
|
||||
if (!dbus_connection_register_object_path (connection,
|
||||
(const char**) split,
|
||||
&gobject_dbus_vtable,
|
||||
object))
|
||||
g_error ("Failed to register GObject with DBusConnection");
|
||||
|
||||
g_strfreev (split);
|
||||
|
||||
/* FIXME set up memory management (so we break the
|
||||
* registration if object or connection vanishes)
|
||||
*/
|
||||
}
|
||||
|
||||
/** @} */ /* end of public API */
|
||||
|
||||
#ifdef DBUS_BUILD_TESTS
|
||||
|
||||
/**
|
||||
* @ingroup DBusGLibInternals
|
||||
* Unit test for GLib GObject integration ("skeletons")
|
||||
* @returns #TRUE on success.
|
||||
*/
|
||||
dbus_bool_t
|
||||
_dbus_gobject_test (const char *test_data_dir)
|
||||
{
|
||||
static struct { const char *javacaps; const char *uscore; } name_pairs[] = {
|
||||
{ "setFoo", "set_foo" },
|
||||
{ "foo", "foo" },
|
||||
{ "getFooBar", "get_foo_bar" },
|
||||
{ "Hello", "hello" },
|
||||
{ "frobateUIHandler", "frobate_ui_handler" }
|
||||
};
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif /* DBUS_BUILD_TESTS */
|
||||
|
|
@ -22,11 +22,152 @@
|
|||
*/
|
||||
#include "dbus-gparser.h"
|
||||
#include "dbus-gidl.h"
|
||||
#include <string.h>
|
||||
|
||||
#include <libintl.h>
|
||||
#define _(x) gettext ((x))
|
||||
#define N_(x) x
|
||||
|
||||
#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *name;
|
||||
const char **retloc;
|
||||
} LocateAttr;
|
||||
|
||||
static gboolean
|
||||
locate_attributes (const char *element_name,
|
||||
const char **attribute_names,
|
||||
const char **attribute_values,
|
||||
GError **error,
|
||||
const char *first_attribute_name,
|
||||
const char **first_attribute_retloc,
|
||||
...)
|
||||
{
|
||||
va_list args;
|
||||
const char *name;
|
||||
const char **retloc;
|
||||
int n_attrs;
|
||||
#define MAX_ATTRS 24
|
||||
LocateAttr attrs[MAX_ATTRS];
|
||||
gboolean retval;
|
||||
int i;
|
||||
|
||||
g_return_val_if_fail (first_attribute_name != NULL, FALSE);
|
||||
g_return_val_if_fail (first_attribute_retloc != NULL, FALSE);
|
||||
|
||||
retval = TRUE;
|
||||
|
||||
n_attrs = 1;
|
||||
attrs[0].name = first_attribute_name;
|
||||
attrs[0].retloc = first_attribute_retloc;
|
||||
*first_attribute_retloc = NULL;
|
||||
|
||||
va_start (args, first_attribute_retloc);
|
||||
|
||||
name = va_arg (args, const char*);
|
||||
retloc = va_arg (args, const char**);
|
||||
|
||||
while (name != NULL)
|
||||
{
|
||||
g_return_val_if_fail (retloc != NULL, FALSE);
|
||||
|
||||
g_assert (n_attrs < MAX_ATTRS);
|
||||
|
||||
attrs[n_attrs].name = name;
|
||||
attrs[n_attrs].retloc = retloc;
|
||||
n_attrs += 1;
|
||||
*retloc = NULL;
|
||||
|
||||
name = va_arg (args, const char*);
|
||||
retloc = va_arg (args, const char**);
|
||||
}
|
||||
|
||||
va_end (args);
|
||||
|
||||
if (!retval)
|
||||
return retval;
|
||||
|
||||
i = 0;
|
||||
while (attribute_names[i])
|
||||
{
|
||||
int j;
|
||||
gboolean found;
|
||||
|
||||
found = FALSE;
|
||||
j = 0;
|
||||
while (j < n_attrs)
|
||||
{
|
||||
if (strcmp (attrs[j].name, attribute_names[i]) == 0)
|
||||
{
|
||||
retloc = attrs[j].retloc;
|
||||
|
||||
if (*retloc != NULL)
|
||||
{
|
||||
g_set_error (error,
|
||||
G_MARKUP_ERROR,
|
||||
G_MARKUP_ERROR_PARSE,
|
||||
_("Attribute \"%s\" repeated twice on the same <%s> element"),
|
||||
attrs[j].name, element_name);
|
||||
retval = FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*retloc = attribute_values[i];
|
||||
found = TRUE;
|
||||
}
|
||||
|
||||
++j;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
g_set_error (error,
|
||||
G_MARKUP_ERROR,
|
||||
G_MARKUP_ERROR_PARSE,
|
||||
_("Attribute \"%s\" is invalid on <%s> element in this context"),
|
||||
attribute_names[i], element_name);
|
||||
retval = FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
check_no_attributes (const char *element_name,
|
||||
const char **attribute_names,
|
||||
const char **attribute_values,
|
||||
GError **error)
|
||||
{
|
||||
if (attribute_names[0] != NULL)
|
||||
{
|
||||
g_set_error (error,
|
||||
G_MARKUP_ERROR,
|
||||
G_MARKUP_ERROR_PARSE,
|
||||
_("Attribute \"%s\" is invalid on <%s> element in this context"),
|
||||
attribute_names[0], element_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
struct Parser
|
||||
{
|
||||
int refcount;
|
||||
|
||||
NodeInfo *result; /* Filled in when we pop the last node */
|
||||
GSList *node_stack;
|
||||
InterfaceInfo *interface;
|
||||
MethodInfo *method;
|
||||
SignalInfo *signal;
|
||||
ArgInfo *arg;
|
||||
};
|
||||
|
||||
Parser*
|
||||
|
|
@ -53,7 +194,8 @@ parser_unref (Parser *parser)
|
|||
parser->refcount -= 1;
|
||||
if (parser->refcount == 0)
|
||||
{
|
||||
|
||||
if (parser->result)
|
||||
node_info_unref (parser->result);
|
||||
|
||||
g_free (parser);
|
||||
}
|
||||
|
|
@ -64,11 +206,12 @@ parser_check_doctype (Parser *parser,
|
|||
const char *doctype,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (error == NULL || *error == NULL);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||
|
||||
if (strcmp (doctype, "dbus_description") != 0)
|
||||
{
|
||||
g_set_error (error,
|
||||
G_MARKUP_ERROR,
|
||||
G_MARKUP_ERROR_PARSE,
|
||||
"D-BUS description file has the wrong document type %s, use dbus_description",
|
||||
doctype);
|
||||
|
|
@ -78,6 +221,324 @@ parser_check_doctype (Parser *parser,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_node (Parser *parser,
|
||||
const char *element_name,
|
||||
const char **attribute_names,
|
||||
const char **attribute_values,
|
||||
GError **error)
|
||||
{
|
||||
const char *name;
|
||||
NodeInfo *node;
|
||||
|
||||
if (parser->interface ||
|
||||
parser->method ||
|
||||
parser->signal ||
|
||||
parser->arg)
|
||||
{
|
||||
g_set_error (error, G_MARKUP_ERROR,
|
||||
G_MARKUP_ERROR_PARSE,
|
||||
_("Can't put a <%s> element here"),
|
||||
element_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
name = NULL;
|
||||
if (!locate_attributes (element_name, attribute_names,
|
||||
attribute_values, error,
|
||||
"name", &name,
|
||||
NULL))
|
||||
return FALSE;
|
||||
|
||||
/* Only the root node can have no name */
|
||||
if (parser->node_stack != NULL && name == NULL)
|
||||
{
|
||||
g_set_error (error, G_MARKUP_ERROR,
|
||||
G_MARKUP_ERROR_PARSE,
|
||||
_("\"%s\" attribute required on <%s> element "),
|
||||
"name", element_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
node = node_info_new (name);
|
||||
parser->node_stack = g_slist_prepend (parser->node_stack,
|
||||
node);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_interface (Parser *parser,
|
||||
const char *element_name,
|
||||
const char **attribute_names,
|
||||
const char **attribute_values,
|
||||
GError **error)
|
||||
{
|
||||
const char *name;
|
||||
InterfaceInfo *iface;
|
||||
NodeInfo *top;
|
||||
|
||||
if (parser->interface ||
|
||||
parser->method ||
|
||||
parser->signal ||
|
||||
parser->arg ||
|
||||
(parser->node_stack == NULL))
|
||||
{
|
||||
g_set_error (error, G_MARKUP_ERROR,
|
||||
G_MARKUP_ERROR_PARSE,
|
||||
_("Can't put a <%s> element here"),
|
||||
element_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
name = NULL;
|
||||
if (!locate_attributes (element_name, attribute_names,
|
||||
attribute_values, error,
|
||||
"name", &name,
|
||||
NULL))
|
||||
return FALSE;
|
||||
|
||||
if (name == NULL)
|
||||
{
|
||||
g_set_error (error, G_MARKUP_ERROR,
|
||||
G_MARKUP_ERROR_PARSE,
|
||||
_("\"%s\" attribute required on <%s> element "),
|
||||
"name", element_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
top = parser->node_stack->data;
|
||||
|
||||
iface = interface_info_new (name);
|
||||
node_info_add_interface (top, iface);
|
||||
interface_info_unref (iface);
|
||||
|
||||
parser->interface = iface;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_method (Parser *parser,
|
||||
const char *element_name,
|
||||
const char **attribute_names,
|
||||
const char **attribute_values,
|
||||
GError **error)
|
||||
{
|
||||
const char *name;
|
||||
MethodInfo *method;
|
||||
NodeInfo *top;
|
||||
|
||||
if (parser->interface == NULL ||
|
||||
parser->node_stack == NULL ||
|
||||
parser->method ||
|
||||
parser->signal ||
|
||||
parser->arg)
|
||||
{
|
||||
g_set_error (error, G_MARKUP_ERROR,
|
||||
G_MARKUP_ERROR_PARSE,
|
||||
_("Can't put a <%s> element here"),
|
||||
element_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
name = NULL;
|
||||
if (!locate_attributes (element_name, attribute_names,
|
||||
attribute_values, error,
|
||||
"name", &name,
|
||||
NULL))
|
||||
return FALSE;
|
||||
|
||||
if (name == NULL)
|
||||
{
|
||||
g_set_error (error, G_MARKUP_ERROR,
|
||||
G_MARKUP_ERROR_PARSE,
|
||||
_("\"%s\" attribute required on <%s> element "),
|
||||
"name", element_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
top = parser->node_stack->data;
|
||||
|
||||
method = method_info_new (name);
|
||||
interface_info_add_method (parser->interface, method);
|
||||
method_info_unref (method);
|
||||
|
||||
parser->method = method;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_signal (Parser *parser,
|
||||
const char *element_name,
|
||||
const char **attribute_names,
|
||||
const char **attribute_values,
|
||||
GError **error)
|
||||
{
|
||||
const char *name;
|
||||
SignalInfo *signal;
|
||||
NodeInfo *top;
|
||||
|
||||
if (parser->interface == NULL ||
|
||||
parser->node_stack == NULL ||
|
||||
parser->signal ||
|
||||
parser->signal ||
|
||||
parser->arg)
|
||||
{
|
||||
g_set_error (error, G_MARKUP_ERROR,
|
||||
G_MARKUP_ERROR_PARSE,
|
||||
_("Can't put a <%s> element here"),
|
||||
element_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
name = NULL;
|
||||
if (!locate_attributes (element_name, attribute_names,
|
||||
attribute_values, error,
|
||||
"name", &name,
|
||||
NULL))
|
||||
return FALSE;
|
||||
|
||||
if (name == NULL)
|
||||
{
|
||||
g_set_error (error, G_MARKUP_ERROR,
|
||||
G_MARKUP_ERROR_PARSE,
|
||||
_("\"%s\" attribute required on <%s> element "),
|
||||
"name", element_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
top = parser->node_stack->data;
|
||||
|
||||
signal = signal_info_new (name);
|
||||
interface_info_add_signal (parser->interface, signal);
|
||||
signal_info_unref (signal);
|
||||
|
||||
parser->signal = signal;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
basic_type_from_string (const char *str)
|
||||
{
|
||||
if (strcmp (str, "string") == 0)
|
||||
return DBUS_TYPE_STRING;
|
||||
else if (strcmp (str, "int32") == 0)
|
||||
return DBUS_TYPE_INT32;
|
||||
else if (strcmp (str, "uint32") == 0)
|
||||
return DBUS_TYPE_UINT32;
|
||||
else if (strcmp (str, "int64") == 0)
|
||||
return DBUS_TYPE_INT64;
|
||||
else if (strcmp (str, "uint64") == 0)
|
||||
return DBUS_TYPE_UINT64;
|
||||
else if (strcmp (str, "double") == 0)
|
||||
return DBUS_TYPE_DOUBLE;
|
||||
else if (strcmp (str, "byte") == 0)
|
||||
return DBUS_TYPE_BYTE;
|
||||
else if (strcmp (str, "boolean") == 0)
|
||||
return DBUS_TYPE_BOOLEAN;
|
||||
else if (strcmp (str, "byte") == 0)
|
||||
return DBUS_TYPE_BYTE;
|
||||
else if (strcmp (str, "object") == 0)
|
||||
return DBUS_TYPE_OBJECT_PATH;
|
||||
else
|
||||
return DBUS_TYPE_INVALID;
|
||||
}
|
||||
|
||||
static int
|
||||
type_from_string (const char *str)
|
||||
{
|
||||
return basic_type_from_string (str);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_arg (Parser *parser,
|
||||
const char *element_name,
|
||||
const char **attribute_names,
|
||||
const char **attribute_values,
|
||||
GError **error)
|
||||
{
|
||||
const char *name;
|
||||
const char *type;
|
||||
const char *direction;
|
||||
ArgDirection dir;
|
||||
int t;
|
||||
ArgInfo *arg;
|
||||
|
||||
if (!(parser->method || parser->signal) ||
|
||||
parser->node_stack == NULL ||
|
||||
parser->arg)
|
||||
{
|
||||
g_set_error (error, G_MARKUP_ERROR,
|
||||
G_MARKUP_ERROR_PARSE,
|
||||
_("Can't put a <%s> element here"),
|
||||
element_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
name = NULL;
|
||||
if (!locate_attributes (element_name, attribute_names,
|
||||
attribute_values, error,
|
||||
"name", &name,
|
||||
"type", &type,
|
||||
"direction", &direction,
|
||||
NULL))
|
||||
return FALSE;
|
||||
|
||||
/* name can be null for args */
|
||||
|
||||
if (type == NULL)
|
||||
{
|
||||
g_set_error (error, G_MARKUP_ERROR,
|
||||
G_MARKUP_ERROR_PARSE,
|
||||
_("\"%s\" attribute required on <%s> element "),
|
||||
"type", element_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (direction == NULL)
|
||||
{
|
||||
/* methods default to in, signal to out */
|
||||
if (parser->method)
|
||||
direction = "in";
|
||||
else if (parser->signal)
|
||||
direction = "out";
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
if (strcmp (direction, "in") == 0)
|
||||
dir = ARG_IN;
|
||||
else if (strcmp (direction, "out") == 0)
|
||||
dir = ARG_OUT;
|
||||
else
|
||||
{
|
||||
g_set_error (error, G_MARKUP_ERROR,
|
||||
G_MARKUP_ERROR_PARSE,
|
||||
_("\"%s\" attribute on <%s> has value \"in\" or \"out\""),
|
||||
"direction", element_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
t = type_from_string (type);
|
||||
|
||||
arg = arg_info_new (name, dir, t);
|
||||
if (parser->method)
|
||||
method_info_add_arg (parser->method, arg);
|
||||
else if (parser->signal)
|
||||
signal_info_add_arg (parser->signal, arg);
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
|
||||
arg_info_unref (arg);
|
||||
|
||||
parser->arg = arg;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
parser_start_element (Parser *parser,
|
||||
const char *element_name,
|
||||
|
|
@ -85,8 +546,46 @@ parser_start_element (Parser *parser,
|
|||
const char **attribute_values,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (error == NULL || *error == NULL);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||
|
||||
if (ELEMENT_IS ("node"))
|
||||
{
|
||||
if (!parse_node (parser, element_name, attribute_names,
|
||||
attribute_values, error))
|
||||
return FALSE;
|
||||
}
|
||||
else if (ELEMENT_IS ("interface"))
|
||||
{
|
||||
if (!parse_interface (parser, element_name, attribute_names,
|
||||
attribute_values, error))
|
||||
return FALSE;
|
||||
}
|
||||
else if (ELEMENT_IS ("method"))
|
||||
{
|
||||
if (!parse_method (parser, element_name, attribute_names,
|
||||
attribute_values, error))
|
||||
return FALSE;
|
||||
}
|
||||
else if (ELEMENT_IS ("signal"))
|
||||
{
|
||||
if (!parse_signal (parser, element_name, attribute_names,
|
||||
attribute_values, error))
|
||||
return FALSE;
|
||||
}
|
||||
else if (ELEMENT_IS ("arg"))
|
||||
{
|
||||
if (!parse_arg (parser, element_name, attribute_names,
|
||||
attribute_values, error))
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_set_error (error, G_MARKUP_ERROR,
|
||||
G_MARKUP_ERROR_PARSE,
|
||||
_("Element <%s> not recognized"),
|
||||
element_name);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
|
@ -95,8 +594,40 @@ parser_end_element (Parser *parser,
|
|||
const char *element_name,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (error == NULL || *error == NULL);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||
|
||||
if (ELEMENT_IS ("interface"))
|
||||
{
|
||||
parser->interface = NULL;
|
||||
}
|
||||
else if (ELEMENT_IS ("method"))
|
||||
{
|
||||
parser->method = NULL;
|
||||
}
|
||||
else if (ELEMENT_IS ("signal"))
|
||||
{
|
||||
parser->signal = NULL;
|
||||
}
|
||||
else if (ELEMENT_IS ("arg"))
|
||||
{
|
||||
parser->arg = NULL;
|
||||
}
|
||||
else if (ELEMENT_IS ("node"))
|
||||
{
|
||||
NodeInfo *top;
|
||||
|
||||
g_assert (parser->node_stack != NULL);
|
||||
top = parser->node_stack->data;
|
||||
|
||||
parser->node_stack = g_slist_remove (parser->node_stack,
|
||||
top);
|
||||
|
||||
if (parser->node_stack == NULL)
|
||||
parser->result = top; /* We are done, store the result */
|
||||
}
|
||||
else
|
||||
g_assert_not_reached (); /* should have had an error on start_element */
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
|
@ -106,7 +637,7 @@ parser_content (Parser *parser,
|
|||
int len,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (error == NULL || *error == NULL);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
|
@ -115,7 +646,13 @@ gboolean
|
|||
parser_finished (Parser *parser,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (error == NULL || *error == NULL);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
NodeInfo*
|
||||
parser_get_nodes (Parser *parser)
|
||||
{
|
||||
return parser->result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,10 +25,10 @@
|
|||
|
||||
#include <dbus/dbus.h>
|
||||
#include <glib.h>
|
||||
#include "dbus-gidl.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
||||
typedef struct Parser Parser;
|
||||
|
||||
Parser* parser_new (void);
|
||||
|
|
@ -52,7 +52,13 @@ gboolean parser_content (Parser *parser,
|
|||
gboolean parser_finished (Parser *parser,
|
||||
GError **error);
|
||||
|
||||
Parser* description_load_from_file (const char *filename,
|
||||
GError **error);
|
||||
Parser* description_load_from_string (const char *str,
|
||||
int len,
|
||||
GError **error);
|
||||
|
||||
NodeInfo* parser_get_nodes (Parser *parser);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue