From edb7be93ef938375610177ecb2e0d18b1db8606b Mon Sep 17 00:00:00 2001 From: raster Date: Thu, 30 Aug 2012 09:54:57 +0000 Subject: [PATCH] EFL 1.7 svn doobies git-svn-id: svn+ssh://svn.enlightenment.org/var/svn/e/branches/e_dbus-1.7@75862 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33 --- .gitignore | 57 + AUTHORS | 9 + COPYING | 25 + ChangeLog | 77 + INSTALL | 236 ++ Makefile.am | 80 + NEWS | 51 + README | 84 + autogen.sh | 39 + configure.ac | 411 +++ doc/Doxyfile.in | 1679 +++++++++++ doc/Makefile.am | 32 + doc/e.css | 218 ++ doc/foot.html | 19 + doc/head.html | 67 + doc/images/e.png | Bin 0 -> 3825 bytes doc/images/edoxy.css | 483 +++ doc/images/foot_bg.png | Bin 0 -> 173 bytes doc/images/head_bg.png | Bin 0 -> 214 bytes doc/images/menu_bg.png | Bin 0 -> 192 bytes doc/images/menu_bg_current.png | Bin 0 -> 1200 bytes doc/images/menu_bg_hover.png | Bin 0 -> 3278 bytes doc/images/menu_bg_last.png | Bin 0 -> 637 bytes doc/images/menu_bg_unsel.png | Bin 0 -> 1596 bytes e_dbus.spec.in | 67 + ebluez.pc.in | 11 + econnman-0.7x.pc.in | 11 + edbus.pc.in | 11 + ehal.pc.in | 11 + enotify.pc.in | 11 + eofono.pc.in | 11 + eukit.pc.in | 11 + m4/ac_attribute.m4 | 47 + m4/efl_binary.m4 | 78 + m4/efl_compiler_flag.m4 | 24 + m4/efl_doxygen.m4 | 94 + src/Makefile.am | 2 + src/bin/Makefile.am | 207 ++ src/bin/async_client_test.c | 58 + src/bin/async_server_test.c | 124 + src/bin/e_dbus_bluez_test.c | 860 ++++++ src/bin/e_dbus_connman0_7x_test.c | 1726 +++++++++++ src/bin/e_dbus_connman0_7x_test_api.c | 619 ++++ src/bin/e_dbus_ofono_test.c | 520 ++++ src/bin/e_dbus_ukit_test.c | 154 + src/bin/logo.png | Bin 0 -> 30052 bytes src/bin/notification_daemon.c | 196 ++ src/bin/notify-send.c | 191 ++ src/bin/notify.c | 125 + src/bin/performance.c | 573 ++++ src/bin/test.c | 118 + src/bin/test_client.c | 75 + src/lib/Makefile.am | 27 + src/lib/bluez/E_Bluez.h | 157 + src/lib/bluez/Makefile.am | 24 + src/lib/bluez/e_bluez.c | 390 +++ src/lib/bluez/e_bluez_adapter.c | 443 +++ src/lib/bluez/e_bluez_device.c | 92 + src/lib/bluez/e_bluez_devicefound.c | 37 + src/lib/bluez/e_bluez_element.c | 2404 +++++++++++++++ src/lib/bluez/e_bluez_manager.c | 34 + src/lib/bluez/e_bluez_private.h | 143 + src/lib/connman0_7x/E_Connman.h | 251 ++ src/lib/connman0_7x/Makefile.am | 24 + src/lib/connman0_7x/e_connman.c | 439 +++ src/lib/connman0_7x/e_connman_element.c | 2504 ++++++++++++++++ src/lib/connman0_7x/e_connman_manager.c | 590 ++++ src/lib/connman0_7x/e_connman_private.h | 174 ++ src/lib/connman0_7x/e_connman_profile.c | 143 + src/lib/connman0_7x/e_connman_service.c | 1646 +++++++++++ src/lib/connman0_7x/e_connman_technology.c | 106 + src/lib/dbus/E_DBus.h | 434 +++ src/lib/dbus/Makefile.am | 26 + src/lib/dbus/e_dbus.c | 620 ++++ src/lib/dbus/e_dbus_interfaces.c | 176 ++ src/lib/dbus/e_dbus_message.c | 122 + src/lib/dbus/e_dbus_methods.c | 175 ++ src/lib/dbus/e_dbus_object.c | 713 +++++ src/lib/dbus/e_dbus_private.h | 53 + src/lib/dbus/e_dbus_signal.c | 271 ++ src/lib/dbus/e_dbus_util.c | 85 + src/lib/hal/E_Hal.h | 128 + src/lib/hal/HAL 0.5.10 Specification.html | 4050 ++++++++++++++++++++++++++ src/lib/hal/Makefile.am | 22 + src/lib/hal/e_hal_device.c | 383 +++ src/lib/hal/e_hal_device.h | 14 + src/lib/hal/e_hal_main.c | 53 + src/lib/hal/e_hal_manager.c | 146 + src/lib/hal/e_hal_manager.h | 21 + src/lib/hal/e_hal_private.h | 26 + src/lib/hal/e_hal_util.c | 120 + src/lib/hal/e_hal_util.h | 14 + src/lib/notification/E_Notification_Daemon.h | 95 + src/lib/notification/E_Notify.h | 190 ++ src/lib/notification/Makefile.am | 24 + src/lib/notification/client.c | 63 + src/lib/notification/daemon.c | 246 ++ src/lib/notification/e_notify_private.h | 109 + src/lib/notification/marshal.c | 706 +++++ src/lib/notification/notification-spec.xml | 1277 ++++++++ src/lib/notification/notification.c | 557 ++++ src/lib/ofono/E_Ofono.h | 122 + src/lib/ofono/Makefile.am | 24 + src/lib/ofono/e_ofono.c | 339 +++ src/lib/ofono/e_ofono_element.c | 2482 ++++++++++++++++ src/lib/ofono/e_ofono_manager.c | 45 + src/lib/ofono/e_ofono_modem.c | 68 + src/lib/ofono/e_ofono_network_reg.c | 94 + src/lib/ofono/e_ofono_private.h | 125 + src/lib/ofono/e_ofono_sms.c | 78 + src/lib/ukit/E_Ukit.h | 169 ++ src/lib/ukit/Makefile.am | 27 + src/lib/ukit/e_udisks.c | 209 ++ src/lib/ukit/e_ukit_main.c | 58 + src/lib/ukit/e_ukit_private.h | 43 + src/lib/ukit/e_ukit_private_util.c | 259 ++ src/lib/ukit/e_ukit_util.c | 129 + src/lib/ukit/e_upower.c | 131 + 118 files changed, 34451 insertions(+) create mode 100644 .gitignore create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100755 autogen.sh create mode 100644 configure.ac create mode 100644 doc/Doxyfile.in create mode 100644 doc/Makefile.am create mode 100644 doc/e.css create mode 100644 doc/foot.html create mode 100644 doc/head.html create mode 100644 doc/images/e.png create mode 100644 doc/images/edoxy.css create mode 100644 doc/images/foot_bg.png create mode 100644 doc/images/head_bg.png create mode 100644 doc/images/menu_bg.png create mode 100644 doc/images/menu_bg_current.png create mode 100644 doc/images/menu_bg_hover.png create mode 100644 doc/images/menu_bg_last.png create mode 100644 doc/images/menu_bg_unsel.png create mode 100644 e_dbus.spec.in create mode 100644 ebluez.pc.in create mode 100644 econnman-0.7x.pc.in create mode 100644 edbus.pc.in create mode 100644 ehal.pc.in create mode 100644 enotify.pc.in create mode 100644 eofono.pc.in create mode 100644 eukit.pc.in create mode 100644 m4/ac_attribute.m4 create mode 100644 m4/efl_binary.m4 create mode 100644 m4/efl_compiler_flag.m4 create mode 100644 m4/efl_doxygen.m4 create mode 100644 src/Makefile.am create mode 100644 src/bin/Makefile.am create mode 100644 src/bin/async_client_test.c create mode 100644 src/bin/async_server_test.c create mode 100644 src/bin/e_dbus_bluez_test.c create mode 100644 src/bin/e_dbus_connman0_7x_test.c create mode 100644 src/bin/e_dbus_connman0_7x_test_api.c create mode 100644 src/bin/e_dbus_ofono_test.c create mode 100644 src/bin/e_dbus_ukit_test.c create mode 100644 src/bin/logo.png create mode 100644 src/bin/notification_daemon.c create mode 100644 src/bin/notify-send.c create mode 100644 src/bin/notify.c create mode 100644 src/bin/performance.c create mode 100644 src/bin/test.c create mode 100644 src/bin/test_client.c create mode 100644 src/lib/Makefile.am create mode 100644 src/lib/bluez/E_Bluez.h create mode 100644 src/lib/bluez/Makefile.am create mode 100644 src/lib/bluez/e_bluez.c create mode 100644 src/lib/bluez/e_bluez_adapter.c create mode 100644 src/lib/bluez/e_bluez_device.c create mode 100644 src/lib/bluez/e_bluez_devicefound.c create mode 100644 src/lib/bluez/e_bluez_element.c create mode 100644 src/lib/bluez/e_bluez_manager.c create mode 100644 src/lib/bluez/e_bluez_private.h create mode 100644 src/lib/connman0_7x/E_Connman.h create mode 100644 src/lib/connman0_7x/Makefile.am create mode 100644 src/lib/connman0_7x/e_connman.c create mode 100644 src/lib/connman0_7x/e_connman_element.c create mode 100644 src/lib/connman0_7x/e_connman_manager.c create mode 100644 src/lib/connman0_7x/e_connman_private.h create mode 100644 src/lib/connman0_7x/e_connman_profile.c create mode 100644 src/lib/connman0_7x/e_connman_service.c create mode 100644 src/lib/connman0_7x/e_connman_technology.c create mode 100644 src/lib/dbus/E_DBus.h create mode 100644 src/lib/dbus/Makefile.am create mode 100644 src/lib/dbus/e_dbus.c create mode 100644 src/lib/dbus/e_dbus_interfaces.c create mode 100644 src/lib/dbus/e_dbus_message.c create mode 100644 src/lib/dbus/e_dbus_methods.c create mode 100644 src/lib/dbus/e_dbus_object.c create mode 100644 src/lib/dbus/e_dbus_private.h create mode 100644 src/lib/dbus/e_dbus_signal.c create mode 100644 src/lib/dbus/e_dbus_util.c create mode 100644 src/lib/hal/E_Hal.h create mode 100644 src/lib/hal/HAL 0.5.10 Specification.html create mode 100644 src/lib/hal/Makefile.am create mode 100644 src/lib/hal/e_hal_device.c create mode 100644 src/lib/hal/e_hal_device.h create mode 100644 src/lib/hal/e_hal_main.c create mode 100644 src/lib/hal/e_hal_manager.c create mode 100644 src/lib/hal/e_hal_manager.h create mode 100644 src/lib/hal/e_hal_private.h create mode 100644 src/lib/hal/e_hal_util.c create mode 100644 src/lib/hal/e_hal_util.h create mode 100644 src/lib/notification/E_Notification_Daemon.h create mode 100644 src/lib/notification/E_Notify.h create mode 100644 src/lib/notification/Makefile.am create mode 100644 src/lib/notification/client.c create mode 100644 src/lib/notification/daemon.c create mode 100644 src/lib/notification/e_notify_private.h create mode 100644 src/lib/notification/marshal.c create mode 100644 src/lib/notification/notification-spec.xml create mode 100644 src/lib/notification/notification.c create mode 100644 src/lib/ofono/E_Ofono.h create mode 100644 src/lib/ofono/Makefile.am create mode 100644 src/lib/ofono/e_ofono.c create mode 100644 src/lib/ofono/e_ofono_element.c create mode 100644 src/lib/ofono/e_ofono_manager.c create mode 100644 src/lib/ofono/e_ofono_modem.c create mode 100644 src/lib/ofono/e_ofono_network_reg.c create mode 100644 src/lib/ofono/e_ofono_private.h create mode 100644 src/lib/ofono/e_ofono_sms.c create mode 100644 src/lib/ukit/E_Ukit.h create mode 100644 src/lib/ukit/Makefile.am create mode 100644 src/lib/ukit/e_udisks.c create mode 100644 src/lib/ukit/e_ukit_main.c create mode 100644 src/lib/ukit/e_ukit_private.h create mode 100644 src/lib/ukit/e_ukit_private_util.c create mode 100644 src/lib/ukit/e_ukit_util.c create mode 100644 src/lib/ukit/e_upower.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4d3e8fd --- /dev/null +++ b/.gitignore @@ -0,0 +1,57 @@ +*.o +*.a +*.lo +*.la +Makefile +Makefile.in +.libs +.deps +/ABOUT-NLS +/aclocal.m4 +/autom4te.cache/ +/compile +/config.cache +/config.cache-env +/config.guess +/config.h +/config.h.in +/config.log +/config.status +/config.sub +/configure +/depcomp +/e_dbus.spec +/ebluez.pc +/econnman.pc +/econnman-0.7x.pc +/edbus.pc +/ehal.pc +/enotify.pc +/eofono.pc +/eukit.pc +/install-sh +/libtool +/ltmain.sh +/m4/libtool.m4 +/m4/ltoptions.m4 +/m4/ltsugar.m4 +/m4/ltversion.m4 +/m4/lt~obsolete.m4 +/missing +/src/bin/e-notify-send +/src/bin/e_dbus_bluez_test +/src/bin/e_dbus_connman_test +/src/bin/e_dbus_connman_test_api +/src/bin/e_dbus_connman0_7x_test +/src/bin/e_dbus_connman0_7x_test_api +/src/bin/e_dbus_notification_daemon +/src/bin/e_dbus_notify +/src/bin/e_dbus_ofono_test +/src/bin/e_dbus_test +/src/bin/e_dbus_test_client +/src/bin/e_dbus_performance +/src/bin/e_dbus_async_client_test +/src/bin/e_dbus_async_server_test +/src/bin/e_dbus_ukit_test +/stamp-h1 +/doc/Doxyfile diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..1e246e1 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,9 @@ +Brian Mattern +Mathieu Taillefumier +Mike Blumenkrantz (zmike/discomfitor) michael.blumenkrantz@gmail.com +Bruno Dilly +Gustavo F. Padovan +João Paulo Rechi Vita +Sebastian Dransfeld +Lucas De Marchi +Halton Huo diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..fcba206 --- /dev/null +++ b/COPYING @@ -0,0 +1,25 @@ +Copyright notice for E_Dbus: + +Copyright (C) 2006-2011 Brian Mattern and various contributors (see AUTHORS) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..4976c8b --- /dev/null +++ b/ChangeLog @@ -0,0 +1,77 @@ +2011-01-29 Carsten Haitzler (The Rasterman) + + 1.0.0 release + +2011-02-25 Cedric BAIL + + * Fix build with DSO. + +2011-03-19 Mike Blumenkrantz + + * Fix crash in e-notify-send when invalid DBUS_SESSION_BUS_ADDRESS is specified + +2011-04-27 Carsten Haitzler (The Rasterman) + + * Fix bug when app has no idle time (always busy), idlers + can't run to process edbus dispatches. Move this to an idle + enterer that wil be run. + +2011-07-26 Libor Zoubek + + * Add support for notification protocol 1.2 into e_dbus by + adding spec version string support. + +2011-10-27 Mike Blumenkrantz + + * Added e_notification_action_id_get and e_notification_action_name_get + +2011-11-03 Mike Blumenkrantz + + * Added more methods for manipulating notification hints + +2011-11-06 Mike Blumenkrantz + + * Add e_notification_image_init for filling in values from an Evas_Object + +2011-11-11 Carsten Haitzler (The Rasterman) + + * Fix DSO linking to edbus to include libdbus linking and includes + +2011-11-27 Mike Blumenkrantz + + * Fix case where fd handler could be added twice for dbus fd + +2011-12-02 Carsten Haitzler (The Rasterman) + + * 1.1.0 release + +2011-12-02 Mike Blumenkrantz + + * Fixed use of dbus_message_iter_open_container in enotify to prevent crashing when sending an image + +2011-12-10 Mike Blumenkrantz + + * Fixed use of eina_log in enotify-daemon + +2012-01-17 Dave Andreoli + + * Fixed use of the 'object path' type in ukit + +2012-04-26 Carsten Haitzler (The Rasterman) + + 1.2.0 release + +2012-07-05 José Roberto de Souza + + * Use idler instead of idle_enterer for processing messages + * Listen and update the 'sender unique name' of signals + +2012-07-13 José Roberto de Souza + + * Fix properties handling + * Check if interface already exists before attaching + * Fix missing DBus.Properties attach + +2012-08-20 Mike Blumenkrantz + + * Add null checks for dbus functions diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..23e5f25 --- /dev/null +++ b/INSTALL @@ -0,0 +1,236 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free +Software Foundation, Inc. + +This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + +These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. (Caching is +disabled by default to prevent problems with accidental use of stale +cache files.) + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You only need +`configure.ac' if you want to change it or regenerate `configure' using +a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + +Some systems require unusual options for compilation or linking that the +`configure' script does not know about. Run `./configure --help' for +details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + +You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not support the `VPATH' +variable, you have to compile the package for one architecture at a +time in the source code directory. After you have installed the +package for one architecture, use `make distclean' before reconfiguring +for another architecture. + +Installation Names +================== + +By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + +Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + +There may be some features `configure' cannot figure out automatically, +but needs to determine by the type of machine the package will run on. +Usually, assuming the package is built to be run on the _same_ +architectures, `configure' can figure that out, but if it prints a +message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + +If you want to set default values for `configure' scripts to share, you +can create a site shell script called `config.site' that gives default +values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + +Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). Here is a another example: + + /bin/bash ./configure CONFIG_SHELL=/bin/bash + +Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent +configuration-related scripts to be executed by `/bin/bash'. + +`configure' Invocation +====================== + +`configure' recognizes the following options to control how it operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..1d9b0ec --- /dev/null +++ b/Makefile.am @@ -0,0 +1,80 @@ +ACLOCAL_AMFLAGS = -I m4 + +SUBDIRS = src doc + +MAINTAINERCLEANFILES = \ +Makefile.in \ +aclocal.m4 \ +config.guess \ +config.h.in \ +config.h.in~ \ +config.sub \ +configure \ +depcomp \ +install-sh \ +ltmain.sh \ +missing \ +$(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.gz \ +$(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.bz2 \ +$(PACKAGE_TARNAME)-$(PACKAGE_VERSION)-doc.tar.bz2 \ +m4/libtool.m4 \ +m4/lt~obsolete.m4 \ +m4/ltoptions.m4 \ +m4/ltsugar.m4 \ +m4/ltversion.m4 + +pkgconfigdir = $(libdir)/pkgconfig + +if BUILD_EHAL +EHAL_PC = ehal.pc +endif + +if BUILD_ENOTIFY +ENOTIFY_PC = enotify.pc +endif + +if BUILD_ECONNMAN0_7X +ECONNMAN_PC = econnman-0.7x.pc +endif + +if BUILD_EBLUEZ +EBLUEZ_PC = ebluez.pc +endif + +if BUILD_EOFONO +EOFONO_PC = eofono.pc +endif + +if BUILD_EUKIT +EUKIT_PC = eukit.pc +endif + +pkgconfig_DATA = edbus.pc $(EBLUEZ_PC) $(ECONNMAN_PC) $(EHAL_PC) $(ENOTIFY_PC) $(EOFONO_PC) $(EUKIT_PC) + +EXTRA_DIST = \ +AUTHORS \ +COPYING \ +README \ +$(pkgconfig_DATA) \ +autogen.sh \ +ebluez.pc.in \ +econnman-0.7x.pc.in \ +edbus.pc.in \ +ehal.pc.in \ +enotify.pc.in \ +eofono.pc.in \ +eukit.pc.in \ +e_dbus.spec \ +e_dbus.spec.in \ +m4/ac_attribute.m4 \ +m4/efl_binary.m4 \ +m4/efl_compiler_flag.m4 \ +m4/efl_doxygen.m4 + +.PHONY: doc + +# Documentation + +doc: + @echo "entering doc/" + make -C doc doc diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..5dff7db --- /dev/null +++ b/NEWS @@ -0,0 +1,51 @@ +E_dbus 1.7.0 + +Changes since E_dbus 1.2.0: +--------------------------- + +Improvements: + + * More null checks for API's + +Fixes: + + * Properties handling + * Interface attach to not attach if already there + * Missing DBus.Properties attach + +Changes since E_dbus 1.1.0: +--------------------------- + +Fixes: + + * enotify: + - use of dbus_message_iter_open_container prevent crash on image send. + +Improvements: + + * eukit: + - Correctly handle the dbus type 'object path', it is a string after all. + * enotify: + - use eina_log better in enotify-daemon. + +Changes since E_dbus 1.0.0: +--------------------------- + +Additions: + + * e_notification_action_id_get + * e_notification_action_name_get + * e_notification_image_init + * e_connman: sync api with connman-0.7x + +Fixes: + + * DSO linking issues fixed + * crash on e-notify-send with invalid session bus set + * when app has no idle time, e_dbus's idlers dont process anything + * error when trying to add duplicate fd handler for dbus fd + +Improvements: + + * notification protocol 1.2 support + * e_connman supports connman-0.7x api, warns that it is unstable. diff --git a/README b/README new file mode 100644 index 0000000..e6f8005 --- /dev/null +++ b/README @@ -0,0 +1,84 @@ +E_dbus 1.7.0 + +****************************************************************************** + + FOR ANY ISSUES PLEASE EMAIL: + enlightenment-devel@lists.sourceforge.net + +****************************************************************************** + +Requirements: +------------- + +Must: + libc + libdbus + eina (at least 1.1.0) + ecore (at least 1.1.0) + +Recommended: + evas (at least 1.1.0) + +This is the start of some basic convenience wrappers around dbus to ease +integrating dbus with EFL based applications. + +When using e_dbus, direct use of the low level dbus api is still heavily +required for processing messages. + +A few things to note: + +e_dbus_bus_get() currently creates a new private connection to whichever bus +is passed in, and hooks this into the ecore main loop. At some point, we +should implement internal refcounting and sharing of these private +connections (one for each bus type) so that e.g. multiple modules in an app +can reuse the same connection. libdbus implements its own shared connections +(available via dbus_bus_get()), but the final reference is always retained by +libdbus iteself, causing any cleanup handlers on the connection to only be +called at app exit. Thus, if a module hooks a connection in to the mainloop, +there is no way to clean up fully before unloading the module, causing issues. + +Patches can be sent to the enlightenment dev mailing list, or, if you have +commit access, feel free to commit. + + +ABOUT PROVIDED MODULES: +----------------------- + +E_DBus provides easy C API for some D-Bus services, the +org.freedesktop.DBus interface is implemented into libedbus/E_DBus.h +itself, while the following modules provides more and their respective +version: + + * bluez(v4.9x): barebones to toggle state and change visibility. + * connman0.7x: complete manager, service and technology. + * hal(v0.5.x): deprecated, provides basic for e17. See ukit. + * notification(v0.7.x): complete client and server. + * ofono(v0.5x, v1.0): barebones to toggle state. + * ukit(upower-v0.9.x, udisks-1.0.x): most of upower, status api from udisks. + + +API STABILITY: +-------------- + +Everything but libedbus (E_DBus.h) is subject to API or ABI stability +of the provided service or protocol specification. If the service +change their API, we'll break our API. + +The library/module versioning will be used at the name, leaving the +soversion synchronized with libedbus.so. + +Examples: + * libeconnman0_7x.so.1.1.0 is the service connman at version + 0.7x created with libedbus.so.1.1.0. + * econnman-0.7x.pc (Version: 1.1.0) is the service connman at version + 0.7x created with edbus.pc (Version: 1.1.0). + + +------------------------------------------------------------------------------ +COMPILING AND INSTALLING: + + ./configure + make +(do this as root unless you are installing in your users directories): + make install + diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..00116ea --- /dev/null +++ b/autogen.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +rm -rf autom4te.cache +rm -f aclocal.m4 ltmain.sh + +touch README +touch ABOUT-NLS + +echo "Running aclocal..." ; aclocal $ACLOCAL_FLAGS -I m4 || exit 1 +echo "Running autoheader..." ; autoheader || exit 1 +echo "Running autoconf..." ; autoconf || exit 1 +echo "Running libtoolize..." ; (libtoolize --copy --automake || glibtoolize --automake) || exit 1 +echo "Running automake..." ; automake --add-missing --copy --gnu || exit 1 + +W=0 + +rm -f config.cache-env.tmp +echo "OLD_PARM=\"$@\"" >> config.cache-env.tmp +echo "OLD_CFLAGS=\"$CFLAGS\"" >> config.cache-env.tmp +echo "OLD_PATH=\"$PATH\"" >> config.cache-env.tmp +echo "OLD_PKG_CONFIG_PATH=\"$PKG_CONFIG_PATH\"" >> config.cache-env.tmp +echo "OLD_LDFLAGS=\"$LDFLAGS\"" >> config.cache-env.tmp + +cmp config.cache-env.tmp config.cache-env >> /dev/null +if [ $? -ne 0 ]; then + W=1; +fi + +if [ $W -ne 0 ]; then + echo "Cleaning configure cache..."; + rm -f config.cache config.cache-env + mv config.cache-env.tmp config.cache-env +else + rm -f config.cache-env.tmp +fi + +if [ -z "$NOCONFIGURE" ]; then + ./configure -C "$@" +fi diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..0c665b0 --- /dev/null +++ b/configure.ac @@ -0,0 +1,411 @@ +##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## +##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## +m4_define([v_maj], [1]) +m4_define([v_min], [7]) +m4_define([v_mic], [0]) +m4_define([v_rev], m4_esyscmd([(svnversion "${SVN_REPO_PATH:-.}" | grep -v '\(export\|Unversioned directory\)' || echo 0) | awk -F : '{printf("%s\n", $1);}' | tr -d ' :MSP\n'])) +m4_if(v_rev, [0], [m4_define([v_rev], m4_esyscmd([git log 2> /dev/null | (grep -m1 git-svn-id || echo 0) | sed -e 's/.*@\([0-9]*\).*/\1/' | tr -d '\n']))]) +##-- When released, remove the dnl on the below line +m4_undefine([v_rev]) +##-- When doing snapshots - change soname. remove dnl on below line +dnl m4_define([relname], [ver-pre-svn-07]) +dnl m4_define([v_rel], [-release relname]) +##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## +m4_ifdef([v_rev], [m4_define([v_ver], [v_maj.v_min.v_mic.v_rev])], +[m4_define([v_ver], [v_maj.v_min.v_mic])]) +m4_define([lt_cur], m4_eval(v_maj + v_min)) +m4_define([lt_rev], v_mic) +m4_define([lt_age], v_min) +##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## +##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## + +AC_INIT([e_dbus], [v_ver], [enlightenment-devel@lists.sourceforge.net]) +AC_PREREQ([2.52]) +AC_CONFIG_SRCDIR([configure.ac]) +AC_CONFIG_MACRO_DIR([m4]) + +AC_CONFIG_HEADERS([config.h]) +AH_TOP([ +#ifndef EFL_CONFIG_H__ +#define EFL_CONFIG_H__ +]) +AH_BOTTOM([ +#endif /* EFL_CONFIG_H__ */ +]) + +AM_INIT_AUTOMAKE([1.6 dist-bzip2]) +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +AC_LIBTOOL_WIN32_DLL +define([AC_LIBTOOL_LANG_CXX_CONFIG], [:])dnl +define([AC_LIBTOOL_LANG_F77_CONFIG], [:])dnl +AC_PROG_LIBTOOL + +##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## +##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## +m4_ifdef([v_rev], , [m4_define([v_rev], [0])]) +m4_ifdef([v_rel], , [m4_define([v_rel], [])]) +AC_DEFINE_UNQUOTED(VMAJ, [v_maj], [Major version]) +AC_DEFINE_UNQUOTED(VMIN, [v_min], [Minor version]) +AC_DEFINE_UNQUOTED(VMIC, [v_mic], [Micro version]) +AC_DEFINE_UNQUOTED(VREV, [v_rev], [Revison]) +version_info="lt_cur:lt_rev:lt_age" +release_info="v_rel" +AC_SUBST(version_info) +AC_SUBST(release_info) +##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## +##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## +VMAJ=v_maj +AC_SUBST(VMAJ) + +### Needed information + +AC_CANONICAL_BUILD +AC_CANONICAL_HOST + +want_ebluez="yes" +want_econnman0_7x="yes" +want_ehal="yes" +want_enotify="yes" +want_eofono="yes" +want_eukit="yes" + +case "$host_os" in + mingw*) + want_ebluez="no" + want_econnman0_7x="no" + want_ehal="no" + want_enotify="no" + want_eofono="no" + want_eukit="no" + ;; +esac + +requirement_ebluez="edbus >= 1.6.99" +requirement_econnman0_7x="edbus >= 1.6.99" +requirement_edbus="ecore >= 1.6.99 eina >= 1.6.99 dbus-1 >= 0.62" +requirement_ehal="edbus >= 1.6.99" +requirement_enotify="edbus >= 1.6.99" +requirement_eofono="edbus >= 1.6.99" +requirement_eukit="edbus >= 1.6.99" +requirement_dbus="dbus-1 >= 0.62" + +### Additional options to configure + +dnl Check enabled modules to build + +AC_ARG_ENABLE([ebluez], + [AC_HELP_STRING([--enable-ebluez], [Enable ebluez build])], + [enable_ebluez=$enableval], + [enable_ebluez="${want_ebluez}"]) + +AC_ARG_ENABLE([econnman0_7x], + [AC_HELP_STRING([--enable-econnman0_7x], [Enable econnman 0.7x build])], + [enable_econnman0_7x=$enableval], + [enable_econnman0_7x="${want_econnman0_7x}"]) + +AC_ARG_ENABLE([ehal], + [AC_HELP_STRING([--disable-ehal], [Disable ehal build])], + [enable_ehal=$enableval], + [enable_ehal="${want_ehal}"]) + +AC_ARG_ENABLE([enotify], + [AC_HELP_STRING([--disable-enotify], [Disable enotify build])], + [enable_enotify=$enableval], + [enable_enotify="${want_enotify}"]) + +AC_ARG_ENABLE([eofono], + [AC_HELP_STRING([--enable-eofono], [Enable eofono build])], + [enable_eofono=$enableval], + [enable_eofono="${want_eofono}"]) + +AC_ARG_ENABLE([eukit], + [AC_HELP_STRING([--disable-eukit], [Disable eukit build])], + [enable_eukit=$enableval], + [enable_eukit="${want_eukit}"]) + +### Checks for programs + +AC_PROG_CC +AM_PROG_CC_C_O + +# pkg-config +PKG_PROG_PKG_CONFIG + +# Check whether pkg-config supports Requires.private +if $PKG_CONFIG --atleast-pkgconfig-version 0.22 ; then + pkgconfig_requires_private="Requires.private" +else + pkgconfig_requires_private="Requires" +fi +AC_SUBST(pkgconfig_requires_private) + +# doxygen program for documentation building +EFL_CHECK_DOXYGEN([build_doc="yes"], [build_doc="no"]) + + +### Checks for libraries + +PKG_CHECK_MODULES([DBUS], [dbus-1 >= 0.62]) +dbus_libs="$DBUS_LIBS" +dbus_cflags="$DBUS_CFLAGS" +AC_SUBST(dbus_libs) +AC_SUBST(dbus_cflags) +PKG_CHECK_MODULES([EDBUS], [ecore >= 1.6.99 eina >= 1.6.99 dbus-1 >= 0.62]) + +# Find out the version of DBUS we're using +dbus_version=`pkg-config --modversion dbus-1` +DBUS_VERSION_MAJOR=`echo $dbus_version | awk -F. '{print $1}'` +DBUS_VERSION_MINOR=`echo $dbus_version | awk -F. '{print $2}'` +DBUS_VERSION_MICRO=`echo $dbus_version | awk -F. '{print $3}'` + +if test "z$DBUS_VERSION_MAJOR" = "z" ; then + DBUS_VERSION_MAJOR="0" +fi + +if test "z$DBUS_VERSION_MINOR" = "z" ; then + DBUS_VERSION_MINOR="0" +fi + +if test "z$DBUS_VERSION_MICRO" = "z" ; then + DBUS_VERSION_MICRO="0" +fi + +DBUS_VERSION_CFLAGS="$DBUS_VERSION_CFLAGS -DDBUS_VERSION_MAJOR=$DBUS_VERSION_MAJOR" +DBUS_VERSION_CFLAGS="$DBUS_VERSION_CFLAGS -DDBUS_VERSION_MINOR=$DBUS_VERSION_MINOR" +DBUS_VERSION_CFLAGS="$DBUS_VERSION_CFLAGS -DDBUS_VERSION_MICRO=$DBUS_VERSION_MICRO" +AC_SUBST(DBUS_VERSION_CFLAGS) + +# Dependencies for the libraries +if test "x${enable_enotify}" = "xyes" ; then + PKG_CHECK_MODULES([EVAS], + [evas >= 1.6.99], + [requirement_enotify="evas >= 1.6.99 ${requirement_enotify}"], + [enable_enotify="no"]) +fi + +AM_CONDITIONAL([BUILD_EBLUEZ], [test "x${enable_ebluez}" = "xyes"]) +AM_CONDITIONAL([BUILD_ECONNMAN0_7X], [test "x${enable_econnman0_7x}" = "xyes"]) +AM_CONDITIONAL([BUILD_EHAL], [test "x${enable_ehal}" = "xyes"]) +AM_CONDITIONAL([BUILD_ENOTIFY], [test "x${enable_enotify}" = "xyes"]) +AM_CONDITIONAL([BUILD_EOFONO], [test "x${enable_eofono}" = "xyes"]) +AM_CONDITIONAL([BUILD_EUKIT], [test "x${enable_eukit}" = "xyes"]) + +# Dependencies for the binaries + +EFL_ENABLE_BIN([edbus-test], ["yes"]) +EFL_ENABLE_BIN([edbus-test-client], ["yes"]) +EFL_ENABLE_BIN([edbus-bluez-test], [${enable_ebluez}]) +EFL_ENABLE_BIN([edbus-connman0_7x-test], [${enable_econnman0_7x}]) +EFL_ENABLE_BIN([edbus-notification-daemon-test], [${enable_enotify}]) +EFL_ENABLE_BIN([edbus-notify-send], [${enable_enotify}]) +EFL_ENABLE_BIN([edbus-notify-test], [${enable_enotify}]) +EFL_ENABLE_BIN([edbus-ofono-test], [${enable_eofono}]) +EFL_ENABLE_BIN([edbus-ukit-test], [${enable_eukit}]) +EFL_ENABLE_BIN([edbus-performance-test], ["no"]) +EFL_ENABLE_BIN([edbus-async-test], ["yes"]) + +if test "x${have_edbus_test}" = "xyes" ; then + PKG_CHECK_MODULES([EDBUS_TEST], + [ecore >= 1.6.99 dbus-1 >= 0.62], + [have_edbus_test="yes"], + [have_edbus_test="no"]) +fi + +if test "x${have_edbus_test_client}" = "xyes" ; then + PKG_CHECK_MODULES([EDBUS_TEST_CLIENT], + [ecore >= 1.6.99 dbus-1 >= 0.62], + [have_edbus_test_client="yes"], + [have_edbus_test_client="no"]) +fi + +if test "x${have_edbus_bluez_test}" = "xyes" ; then + PKG_CHECK_MODULES([EDBUS_BLUEZ_TEST], + [ecore >= 1.6.99 eina >= 1.6.99 dbus-1 >= 0.62], + [have_edbus_bluez_test="yes"], + [have_edbus_bluez_test="no"]) +fi + +if test "x${have_edbus_connman0_7x_test}" = "xyes" ; then + PKG_CHECK_MODULES([EDBUS_CONNMAN0_7X_TEST], + [ecore >= 1.6.99 eina >= 1.6.99 dbus-1 >= 0.62], + [have_edbus_connman0_7x_test="yes"], + [have_edbus_connman0_7x_test="no"]) +fi + +if test "x${have_edbus_notification_daemon_test}" = "xyes" ; then + PKG_CHECK_MODULES([EDBUS_NOTIFICATION_DAEMON_TEST], + [ecore >= 1.6.99 evas >= 1.6.99 eina >= 1.6.99 dbus-1 >= 0.62], + [have_edbus_notification_daemon_test="yes"], + [have_edbus_notification_daemon_test="no"]) +fi + +if test "x${have_edbus_notify_send}" = "xyes" ; then + PKG_CHECK_MODULES([EDBUS_NOTIFY_SEND], + [ecore >= 1.6.99 evas >= 1.6.99 eina >= 1.6.99 dbus-1 >= 0.62], + [have_edbus_notify_send="yes"], + [have_edbus_notify_send="no"]) +fi + +if test "x${have_edbus_notify_test}" = "xyes" ; then + PKG_CHECK_MODULES([EDBUS_NOTIFY_TEST], + [ecore >= 1.6.99 ecore-evas >= 1.6.99 evas >= 1.6.99 eina >= 1.6.99 dbus-1 >= 0.62], + [have_edbus_notify_test="yes"], + [have_edbus_notify_test="no"]) +fi + +if test "x${have_edbus_ofono_test}" = "xyes" ; then + PKG_CHECK_MODULES([EDBUS_OFONO_TEST], + [ecore >= 1.6.99 eina >= 1.6.99 dbus-1 >= 0.62], + [have_edbus_ofono_test="yes"], + [have_edbus_ofono_test="no"]) +fi + +if test "x${have_edbus_ukit_test}" = "xyes" ; then + PKG_CHECK_MODULES([EDBUS_UKIT_TEST], + [ecore >= 1.6.99 eina >= 1.6.99 dbus-1 >= 0.62], + [have_edbus_ukit_test="yes"], + [have_edbus_ukit_test="no"]) +fi + +if test "x${have_edbus_performance_test}" = "xyes" ; then + PKG_CHECK_MODULES([EDBUS_PERFORMANCE_TEST], + [ecore >= 1.6.99 eina >= 1.6.99 dbus-1 >= 0.62 ecore-evas >= 1.6.99 elementary >= 1.6.99 evas >= 1.6.99], + [have_edbus_performance_test="yes"], + [have_edbus_performance_test="no"]) +fi + +AM_CONDITIONAL([BUILD_EDBUS_PERFORMANCE_TEST], [test "x${have_edbus_performance_test}" = "xyes"]) + +if test "x${have_edbus_async_test}" = "xyes" ; then + PKG_CHECK_MODULES([EDBUS_ASYNC_TEST], + [ecore >= 1.6.99 dbus-1 >= 0.62], + [have_edbus_async_test="yes"], + [have_edbus_async_test="no"]) +fi + +### Checks for header files + + +### Checks for types + + +### Checks for structures + + +### Checks for compiler characteristics + +EFL_EDBUS_BUILD="" +case "$host_os" in + mingw*) + EFL_EDBUS_BUILD="-DEFL_EDBUS_BUILD" + ;; +esac +AC_SUBST(EFL_EDBUS_BUILD) + +AC_HEADER_STDC +AC_C___ATTRIBUTE__ + +if ! test "x${VMIC}" = "x" ; then + EFL_COMPILER_FLAG([-Wall]) + EFL_COMPILER_FLAG([-W]) +fi + +EFL_COMPILER_FLAG([-Wshadow]) + + +### Checks for linker characteristics + +lt_enable_auto_import="" +case "$host_os" in + mingw*) + lt_enable_auto_import="-Wl,--enable-auto-import" + ;; +esac +AC_SUBST(lt_enable_auto_import) + + +### Checks for library functions + +AC_FUNC_ALLOCA + + +AC_SUBST(requirement_ebluez) +AC_SUBST(requirement_econnman0_7x) +AC_SUBST(requirement_edbus) +AC_SUBST(requirement_ehal) +AC_SUBST(requirement_enotify) +AC_SUBST(requirement_eofono) +AC_SUBST(requirement_eukit) +AC_SUBST(requirement_dbus) + +AC_OUTPUT([ +e_dbus.spec +Makefile +doc/Makefile +doc/Doxyfile +src/Makefile +src/lib/Makefile +src/lib/bluez/Makefile +src/lib/connman0_7x/Makefile +src/lib/dbus/Makefile +src/lib/hal/Makefile +src/lib/notification/Makefile +src/lib/ofono/Makefile +src/lib/ukit/Makefile +src/bin/Makefile +ebluez.pc +econnman-0.7x.pc +edbus.pc +ehal.pc +enotify.pc +eofono.pc +eukit.pc +]) + + +##################################################################### +## Info + +echo +echo +echo +echo "------------------------------------------------------------------------" +echo "$PACKAGE $VERSION" +echo "------------------------------------------------------------------------" +echo +echo "Configuration Options Summary:" +echo +echo " Modules:" +echo +echo " EBluez.............: $enable_ebluez" +echo " EConnman (0.7x)....: $enable_econnman0_7x" +echo " EHal...............: $enable_ehal" +echo " ENotify............: $enable_enotify" +echo " EOfono.............: $enable_eofono" +echo " EUkit..............: $enable_eukit" +echo +echo " Binaries:" +echo +echo " EDbus test.........: $have_edbus_test" +echo " EDbus client test..: $have_edbus_test_client" +echo " EDbus async test...: $have_edbus_async_test" +echo " EDbus performance..: $have_edbus_performance_test" +echo " EBluez test........: $have_edbus_bluez_test" +echo " EConnman (0.7x)test: $have_edbus_connman0_7x_test" +echo " ENotify Daemon test: $have_edbus_notification_daemon_test" +echo " ENotify send.......: $have_edbus_notify_send" +echo " ENotify test.......: $have_edbus_notify_test" +echo " EOfono test........: $have_edbus_ofono_test" +echo " EUkit test.........: $have_edbus_ukit_test" +echo +echo "Documentation..........: ${build_doc}" +echo +echo "Compilation............: make (or gmake)" +echo " CPPFLAGS.............: $CPPFLAGS" +echo " CFLAGS...............: $CFLAGS" +echo " LDFLAGS..............: $LDFLAGS" +echo +echo "Installation...........: make install (as root if needed, with 'su' or 'sudo')" +echo " prefix...............: $prefix" +echo diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in new file mode 100644 index 0000000..0572ae9 --- /dev/null +++ b/doc/Doxyfile.in @@ -0,0 +1,1679 @@ +# Doxyfile 1.7.3 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = e_dbus + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = @PACKAGE_VERSION@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description for a project that appears at the top of each page and should give viewer a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = . + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = YES + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = ../src/ + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = YES + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = YES + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even if there is only one candidate or it is obvious which candidate to choose by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = e_dbus_doxy_warnings.txt + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = @top_srcdir@/src/lib + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = @top_srcdir@/doc/images/ + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 2 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = e_ E_ + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = @srcdir@/head.html + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = @srcdir@/foot.html + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = @srcdir@/e.css + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the stylesheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = YES + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = YES + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.enlightenment.EDbus + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.enlightenment.EDbus + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Enlightenment + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.enlightenment.EDbus + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.enlightenment.EDbus + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = YES + +# This tag can be used to set the number of enum values (range [0,1..20]) +# that doxygen will group on one line in the generated HTML documentation. +# Note that a value of 0 will completely suppress the enum values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 1 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the mathjax.org site, so you can quickly see the result without installing +# MathJax, but it is strongly recommended to install a local copy of MathJax +# before deployment. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = NO + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = __UNUSED__= + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will write a font called Helvetica to the output +# directory and reference it in all dot files that doxygen generates. +# When you want a differently looking font you can specify the font name +# using DOT_FONTNAME. You need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, svg, gif or svg. +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 0000000..881eb10 --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,32 @@ +MAINTAINERCLEANFILES = Makefile.in e_dbus_doxy_warnings.txt + +.PHONY: doc + +PACKAGE_DOCNAME = $(PACKAGE_TARNAME)-$(PACKAGE_VERSION)-doc + +if EFL_BUILD_DOC + +doc-clean: + rm -rf html/ latex/ $(top_builddir)/$(PACKAGE_DOCNAME).tar* + +doc: all + $(efl_doxygen) + cp $(srcdir)/images/* html/ + rm -rf $(PACKAGE_DOCNAME).tar* + mkdir -p $(PACKAGE_DOCNAME)/doc + cp -R html/ latex/ $(PACKAGE_DOCNAME)/doc + tar cf $(PACKAGE_DOCNAME).tar $(PACKAGE_DOCNAME)/ + bzip2 -9 $(PACKAGE_DOCNAME).tar + rm -rf $(PACKAGE_DOCNAME)/ + mv $(PACKAGE_DOCNAME).tar.bz2 $(top_builddir) + +clean-local: doc-clean + +else + +doc: + @echo "Documentation not built. Run ./configure --help" + +endif + +EXTRA_DIST = Doxyfile.in $(wildcard images/*.*) e.css head.html foot.html diff --git a/doc/e.css b/doc/e.css new file mode 100644 index 0000000..8697a3a --- /dev/null +++ b/doc/e.css @@ -0,0 +1,218 @@ +/* + Author: + Andres Blanc + DaveMDS Andreoli + + Supported Browsers: + ie7, opera9, konqueror4 and firefox3 + + Please use a different file for ie6, ie5, etc. hacks. +*/ + + +/* Necessary to place the footer at the bottom of the page */ +html, body { + height: 100%; + margin: 0px; + padding: 0px; +} + +#container { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -53px; +} + +#footer, #push { + height: 53px; +} + + +* html #container { + height: 100%; +} + +/* Prevent floating elements overflowing containers */ +.clear { + clear: both; + width: 0px; + height: 0px; +} + +/* Flexible & centered layout from 750 to 960 pixels */ +.layout { + max-width: 960px; + min-width: 760px; + margin-left: auto; + margin-right: auto; +} + +body { + /*font-family: Lucida Grande, Helvetica, sans-serif;*/ + font-family: "Bitstream Vera","Vera","Trebuchet MS",Trebuchet,Tahoma,sans-serif +} + +/* Prevent design overflowing the viewport in small resolutions */ +#container { + padding-right: 17px; + padding-left: 17px; + background-image: url(head_bg.png); + background-repeat: repeat-x; +} + +#header { + width: 100%; + height: 102px; +} + +#header h1 { + width: 63px; + height: 63px; + background-image: url(e.png); + background-repeat: no-repeat; + position: absolute; + margin: 0px; +} + +#header h1 span { + display: none; +} + +#header h2 { + display: none; +} + +/* .menu-container is used to set properties common to .menu and .submenu */ +#header .menu-container { +} + +#header .menu-container ul { + list-style-type: none; + list-style-position: inside; + margin: 0; +} + +#header .menu-container li { + display: block; + float: right; +} + +#header .menu { + height: 63px; + display: block; + background-image: url(menu_bg.png); + background-repeat: repeat-x; +} + +#header .menu ul { + height: 100%; + display: block; + background-image: url(menu_bg_last.png); + background-repeat: no-repeat; + background-position: top right; + padding-right: 17px; +} + +#header .menu li { + height: 100%; + text-align: center; + background-image: url(menu_bg_unsel.png); + background-repeat: no-repeat; +} + +#header .menu a { + height: 100%; + display: block; + color: #cdcdcd; + text-decoration: none; + font-size: 10pt; + line-height: 59px; + text-align: center; + padding: 0px 15px 0px 15px; +} + +#header .menu li:hover { + background-image: url(menu_bg_hover.png); + background-repeat: no-repeat; +} + +#header .menu li:hover a { + color: #FFFFFF; +} + +#header .menu li.current { + background-image: url(menu_bg_current.png); + background-repeat: no-repeat; +} + +#header .menu li.current a { + color: #646464; +} + + +/* Hide all the submenus but the current */ +#header .submenu ul { + display: none; +} + +#header .submenu .current { + display: block; +} + +#header .submenu { + font: bold 10px verdana,'Bitstream Vera Sans',helvetica,arial,sans-serif; + margin-top: 10px; +} + +#header .submenu a { + color: #888888; + text-decoration: none; + font-size: 0.9em; + line-height: 15px; + padding:0px 5px 0px 5px; +} + +#header .submenu a:hover { + color: #444444; +} + +#header .submenu li { + border-left: 1px solid #DDDDDD; +} + +#header .submenu li:last-child { + border-left: 0; +} + +#header .doxytitle { + position: absolute; + font-size: 1.8em; + font-weight: bold; + color: #444444; + line-height: 35px; +} + +#header small { + font-size: 0.4em; +} + +#footer { + background-image: url(foot_bg.png); + width: 100%; +} + +#footer table { + width: 100%; + text-align: center; + white-space: nowrap; + padding: 5px 30px 5px 30px; + font-size: 0.8em; + font-family: "Bitstream Vera","Vera","Trebuchet MS",Trebuchet,Tahoma,sans-serif; + color: #888888; +} + +#footer td.copyright { + width: 100%; +} + diff --git a/doc/foot.html b/doc/foot.html new file mode 100644 index 0000000..78ef911 --- /dev/null +++ b/doc/foot.html @@ -0,0 +1,19 @@ + +
+ + + + + + + + + + + diff --git a/doc/head.html b/doc/head.html new file mode 100644 index 0000000..1293f78 --- /dev/null +++ b/doc/head.html @@ -0,0 +1,67 @@ + + + $title + + + + + + + + + + + + + + +
+ + + +
+
diff --git a/doc/images/e.png b/doc/images/e.png new file mode 100644 index 0000000000000000000000000000000000000000..b3884a5cbca7bce5747eb3b8a92145d60511a718 GIT binary patch literal 3825 zcmW-k2|UyPAIGQMS98Ry6)m}iA@`NJZE_o#Ovc73e^-^QHU$G0h_3|cuDljd5QbT#Je?6pNPP^G($^aTorE>q=yral%n2Rktlk|u< zB6SbBG`&ZF=tG%tj9|RLw-;=+A0K(VYgY{_m@_Mu;#9$;A9&jo($nUG3m{pPG9sKJ z)-SrEJ!oZpgRkTbv#N2w?Avv1g`*hj3@X!Cn+Jh2Ml;^V7@mz6b|Fo-YFT&Oc=qw1 z@}>V_jJMQ>kyo}tc5CX?e65}5iLW`a*?=JPI$Jx)*JdpOpdFk$7LNZlSowU@cr#Mjx)>#Vh2qRYMG@4%U~V=*(;bxq6%o;REM)$>G`C7nd~m&p*#P>~ zXHur`Yquq~Y&12(1U{S4izUj!VqeX6#jVc9i4wfL*s)~-o}QjXMEK_BCOa2bR1e|w zfOiwc9TpT4;xRu`3f2tu^&xUXTv-ZkyvdBZz~6YF9A*}l+0tzM1w6ihdTKaGCcM-b zM}+kR##h<)t_vDPGSvAu;zhryW3SGaF8dM)M|gO64tMi+Iy@ydmbFeUae-c2{QNoG z(9m#bJ|Lb-MPIsvICAtT6Qr%J&FlJgLwoz<%m(`4rx$N)fyA(+zm0CXPABZ`Y;G;L z{_bdyy$#F#KH(XXsO3Jb1Q#zc1fq{*U zjYV~JP>U?NSCL2Ad=K{iTFJ6FGWAV|A0&YtJIM(N3JQvei!+~p$aC|>Dl6&yL|#Xg zIX+2Weg&eas2K77JvQjh9i*O~D5!_YojcEE+(AXktE#$w{w%LP_%H5M=b04kBWh}D zxc2O9VLyNW)gECtF*P+|5T~J=o2apg$$#_(d-_h^TCnHtq71c!2zRWxWDpjn!_UuO zcWv<0kxLtcNl8iN)zxTQTLC2}o9m(byTL;$JWs}{A3oS^ZEvS;Z{E>Cp-_rSN|AlCExY6x%V{lP3aGQ&Z}<7hzfQ zF07zj10`OsC@iK}tTCCaB&2|+E-(9l0(Hy>@X!C-*+HtR-ujI2c$AdYyXh^~)F4Il zzzMlLC8a)YPnWbhjIFPa%gD$GmE?PCduwZ^?eCPKudkxBi;LOH8N8LwW`;LSS$4%) zx?ENsdRA0Kgd_bexl4mJQOGB;;>g}!^1**?gy$7$wPR|Ei zVs8!%jpMNr88)kWN^?ogde$9cWo2b)X&FJ2;y!k)F9OgLp&JTI(cO{q^YfcwF#H{j z%mHD&8k`>k(lj9Q-C;Og+VS&=mg%exwn**okO~FI#6R7i^cR~amJZy`{L_nl_Uzd# zP+riFoNM<}Q<iF240XUkipnh)?rRKXHCsH%<~~eP9Nw zkqau%sz*Z!{@*$FcNP?o@?cZr%EaUjMedC|I-tV!fV+9RVQ_4jZKfCbIfH;Kg45E_jJnnwJDq^pApCdwVgPKmHB z!=6@FPNzCr)C6drgvA1wG8MJ9x;!~AS&DhwVSc;3B#qv%<6L7Hx)0{h+mg+0 z0jqK$SZNPm{q3QtspPPKfBq{eiS~+YJS!pb6?`OSoRqamr_%>N_q^qN00y+O`IQf2 z+ONq@jb3~&ch@XXS3^S)t+$ts^fgp^l*D2hS>*bVDk_42(C2Bz#jeZyq=~0Iw_8tY z=KJ_03RcSYhZI2ZA^5$2n^Z zhKHkI8AsB4C}Cb-U2XZaezlWJK@9Yhu?X!asz+tBKS$Zr%IdX3`!1>nqM47)$jG={ z6Hw*j?B(e@8paOM)YQD_>MGBrf)C%jMf%)G{A_Ab!GaWc-rAa)nHj@)ozmsgEFfWL zXIE5Pd(7NL^`S8bMGS3ZguyK+PiESq78xme#+H`U#qGE0lGfJNvkkwHO>4CJD>xwo z1A{jM11ylm>2^1Vo*e(Pl1LDQ5Ws)HygDLK6i)xZ|&>dfsOPKeXm+M(}+hAe4X z6^(1_>#6teN95%pc2{4Es^8g=`ug?jBeWA}$-@!V(E(v)V{=!8L18fV@bGXsTtjrf znx^KxYlUG#U=E@&U;>bLx5xnj0W$+?(|PNsIb8382bo;|XlLL`xUQxqTtr0V=kjuF zbF&(t;zeg?&f;NqvRMLSa&mHZZZ58_PO-bU7l}ggM@B{}!jl?)gFFKHeR&Zq0D+2% zretIY!nj${CML!8^?U#T%DQSrV~56cXy{^gjJZ5-NJt2yBl4*FtzUx9&dx=n^A{Z* z<3>hoGP1Hd>2zVBB+57`HanY674K}2^S*xF8jnx@@#E^^RNIN1oSaWB#cMgJ*Otw*rB&I6XkW|M$G6uvyt`6=4Q|K`kZ_71o=n5$YD92 zJ{1C@Q+zl8Tt!rSyB;@d6scf;|L*S2Lai5x)ZLRPtfZ|?1=~Oy8x#I6Pp+)3br$G^ z42_NbX`h$s5(7DsmzRH{J7@@G0_>a>lRU=kBQ^b#^fF9~Kp@mz9e@J|CF~Z; z=i}oO1g5^dRdnUai@D1~BN_7E-_c)hH&#LIcW)}f^hZ^u!v4CB{}Fsm_VV)Tm@5qD zjdcf6#)5f&1{ecDqyO64P9`KI#K*@&x)g(!n&@@JQ3+sRI=q{XKyeVzcDKRmK@VQGJz^%tG zO*DH_jWQ#&1@Sat zu45x^o;XwQrjLI%y5Cr6|6eo|$`MN}mI6S}%?;gTi+K0Wd__BDY6`#c=MU}03j`ox z^qWHiar!iqj*br68Cc1Nu2gRuM_H0oc5HR5WMzw#YBJ+Wsm%9p=9#t=@;89dymqau zKP+D>F!{yAmoIV_{<5;N^>;SCQP(Ui607kFI%@i(8yU7p?}*InZkocy4qt5dWs-8? zfgi_Qz*j9%K=LhN|IXK+Sz20ZXd>1-)p-pgacy h3 { + margin-top: 0; +} +.directory p { + margin: 0px; + white-space: nowrap; +} +.directory div { + display: none; + margin: 0px; +} +.directory img { + vertical-align: -30%; +} +/* these are for tree view when not used as main index */ +.directory-alt { + font-size: 100%; + font-weight: bold; +} +.directory-alt h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; +} +.directory-alt > h3 { + margin-top: 0; +} +.directory-alt p { + margin: 0px; + white-space: nowrap; +} +.directory-alt div { + display: none; + margin: 0px; +} +.directory-alt img { + vertical-align: -30%; +} + diff --git a/doc/images/foot_bg.png b/doc/images/foot_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..b24f3a48b4e076b88cf1e802b46087ffbb3fabd5 GIT binary patch literal 173 zcmeAS@N?(olHy`uVBq!ia0vp^j6iJ9!3HE1Qx}DR1d4;)ofy`glX(f`uqAoByD;nl zL8a%*b^=8>3p^r=85p>QL70(Y)*K0-AbW|YuPgga7CvEi%SZD}LFVdux;TbNTu#2R zIsJTn+kYTPulo1Ll3AD;2!xfDmD#wLvj}>v&@k-CVP=T_%(cphxuXQAk-^i|&t;uc GLK6Ub>Mc;VkfoEM{Qf z76xHPhFNnYfP(BLp1!W^H+WcuRXD>A=l%i;Wq7(chG?8W+i%Esz<`6f_y7NWHH!5o z63R1{^0cb0vMSkFaE_%x<+7O{*?Xs&Ff%xKT5jO&GtAqcqoa~}b>qg&H07ky z617wnX2HUfTT*8{k002J-1^@s6XaB>y00001b5ch_0Itp) z=>Px#32;bRa{vGf6951U69E94oEQKA00(qQO+^RT3Fc5TrKj#zpOzs?z2=A0p@aw&u*pMn|G)ug+?Vh;0+zHe5dVLoF=!DBW&zJ?E zp99|S_j0@4%Io#|xG+sqdB5NP{rn5Cj%O+78UIn|DpKcsK9>`B2V6JsOB3oD&r;4a z{-e%Sw9ela{A&{jdu5*U-Tgr1~ zQc-ZvZCmQQrCnTG+COg8zWQ3Lv1sjNIKQPlR|Xsq_}(oT0MbDS91lPg3>xeBFKokrh3I+f~!N36>5GNtP zFb=56FWNP#oK=?bNEQ~!rtXW9X1GX=k~FL`OjOeF$R)LGB-wAZi(5xddgID^{pUl5 z$!2zVWE!iC5c%CAFL=jfl{TW1M$7Wjcr9z}SyPKmlC20r0B}G6Q80PClsJ^eQ9Rdb zuHflIJ5saoBdvW)J5o7`LrQ$sb0li*FS@pt^@$vLe#kJ^<1ntHwKLacEE8!V_VYY{ z^jk0Fa-9o0ILfhhgnDcg*`~%yk#UNgFLkMLQ1xp&>oHLMt>=krgKLZ9aoj&)eVyWk z+VI9yb#%knV zBgOT1iW^1q->g9a$6utrxv^4Ql-s5#yS%xS5>LwpJyaYZ1w-06F426k%%gi2p;YTL zn*Zi$*TU&q<}Gq{z5F%GPa}Dt9$}k233)sorIb>h&nNZ+VE-)`LNtIN1b_p=0f8Pk zSwvore6GJZg3A%uh&UU`%8A$z3ApNcvGym`^9BW5y;mkO7OVYaI#wj=im2;bzSt*w z8j*3rXhhk-tiA%ik?Y6P7yM_$}>d^Cl%aa8a!%A!J=gv)gqCjA%RAmjShY zk&jin$jBj)wrhPzE#o_v^@kLW;DnK~k;Uxogc+{8mlL+=3tKP%4hR4$2Lynj z95C}RO)LJ$<@I?2i7h|tH#%lcU&}tk-(ol0<1_foFDdWsgqjV%=7cRA;M<%KLNw4f z2!R6vT{&RuTk7799C>JZ4;!>{ThW;;ZOYD_SA%VO2ZR&C3B68;Eg1NXEf@e0gaF^` zfS#=57JO;X{zkCF($|*uL^8|wn48N;fkQ7R9I3CpA0+QRH%LYRy5b)R!`g7p731Il O0000002J-1^@s6XaB>y00001b5ch_0Itp) z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iOZ7 z02vXk7GypE01Qq^L_t(|+U;H2a^tuXlN0^d3_%Mmt|S*_xtkl^78E$bGzMsW@O^E@B1-+ z=cVS*V0UU7pRP@U;-OvwL>DMu{R0abUb{x*}m z@B4oYcs3YunesE)Z}j8zrzRR*2Qx`$S!kAh|3>CJWrAG*GLX2Fe)h(rzjg+&sxS80 zcbMC=ef;e}EdiJ(`3%q$9hv{=thAZ zpj~02U6We1r_3JuyTgdH571c<%rJWAn5)2<+gWe7+X22Y2-~(nHi`f1_)l)SMgf#Jj8JEL5q`z9;(>xI3~8m`p)OaYtLX&lh0g*>LDJVk86(i z=kyov4*{0}DZk>FF#wNa4hGB@`||R#3~-LWl@Egki#?GMP;Am(US5vA=z0`0WFWFJQh<&3f+l(2_hZcBJtII5vSC1p*H_JX1~&q0d?50; z9+i-IF}K_8xX5gx6*IWER<=amoW`jd#YBk4T_A$cGxKdSUi@`qKv&aoVEA{nDHR+_2yWJasfY5BO>R2DM#*|ftn40E*Ih_;(Ua&ul4 zh-X-Wv9g=*`;3Z+YsQ>oJK&jZ#~tem*arhL|A(2liun{cE3;aeebuf@K z<{@$WJbN5uKxH0Oawoia%4=cqI_8 z@atR?uKZ`Gy>$Q&W$P$}#1#?lnW*=w=zGSDRyFp{|IueF%YZXbRXH<9LGqa_H^Z!7wXo>3 z0MudBnQQ0*%}I+r0dV+$PEer|sA_fDtl!x6!Mf(HQlMPz{9mChbjxs8KNzZ?j@kVX zgyaHqPMa_gx$0w97CQr!*}zAspBlr?S!q5>pR8ll>^(m=-luk6H5 zDh7cN0W_QIN=oGd$WiU)+P0b)Cm_l9kc;^F`dn2}Y{MA~M;^u`amch*0t9wHNZg== zu^-YfqhOqGZk!l}q47e8q$++sBw10+s!U&gS}Jt~@;woIxD4j_gkumL)pBM4XDLGr z0+AwHS*WfFjbnr)S`}+4Kr$r_(h)Ap|28Mea;%P1kD_P?{qED|RA;X6o!p2zi#;p$ zR7)A$dpaW?=mrNJ?W2;dq2th8uWxT}%iG)A^85GiCw~o@(#&SezeC%6Ak)sycgkj& zQI%6)ol~t%QGK9ATuK92N!ne1u>(3MO~w2;<>x7LkNVBh{3jEfI~@ZaPl&oM1(LDX zjI*Ag3M|wruZ|)&uem_Jsro$rj`x;>Af}(p>SA>ton5Xf-%SBMUq1o}`mNO36}x6% z@u?J-pVqj@1^FS2$+8HMu~S^^l-WWplxI)0s)#6)sQOdx24gKZkaEnFYgUz_Tbb4T zV6c^RNXecVUrG9qkGO5yzdj&iKnFyX%v`O-je(Cku9B~-XuY$&nv;N^O$2ur!*}w2 zGr2vc7Ds@sa(WDi$~+%w7{@eJ;eWLtx&l{6ZIh$*D$(7Y9M*RAV7R$C_x-Go=a%do zU{wj_$|@kH{ZI)(QY;r>in&mpPURU{Uad3nPKu=pRMgs%%G_65ZOjH2-F8(U87S_y zB;BCHWzdx^*;AE?Gfby^M+N+uw90JRuUkc_62LP6R3KDr(=Det<+C%)Sp7cofR!2R zfUC3rN@Xgl6pdRptk}?DRkt)Y6GU`O>umpyFX!YVs|t?`z5{?NeUNj5SAZ~AL0I{! z+6JWS<2jM@%yx8DiE^fnJDY&6f}Jj)oCLFz%w~W-7%-+=DmJQ2Xom?ExLDe!5-4;{ zrkm2K7HoBZa5kdt_EkltGmO_U&FmUimcwBa*qpdhwWTf#XnrKCCYs)P=%?EVbX z&6;N?rQ$52ciGXc>^%8^c})lt3!ojU6wsW{6x%WLdM&yE11b~CQYGE=OeJ)<5VW9y=&YuLAfe%Wnt==upw4Z%rPz^ADi2AcH>mlm8?>U_3Y8dOv1O4 zV>yC_$}i0K5rw?y;c>RjW%Bt-w4gwQ9wxN=DQW{<|6zhrfWfx7jp>?kakty;!{leS zLyF(rcHRm&GiSWMthmlMu8QMIhR;~{3KOihaf^V-(k2Bu1pNFEDW46M<`DZaJs%S^ zD0d*@=!k*`5c6b5hGIcU25)3LBw|(ivMljO=4*%&BKQ=r)OKtvDn@MT1RK?Et|-`Z zTngovW)Lfrn~6iQc6^_S%FQ7vYp`wGvcA8+KV4jwNX7TrKgt6)CM|yXVU~0;m2AAZ z$b@<0Z8rGmxaun=DydnV@JcVpHWqhsglZT044iyETcyS}V2|@r$*~aQWf13kir_}T z&tv-(1*4o9(g#N0im7WEaGCfZABR?kjnAY1>8xN=B|ZdvM?TLa_y~M%WdcR~%>2gr zLP}&LG(~0^EeOl!xi#d7BiCFXL5M2Dv6Kv|TEri;YkLH7J|})weg8xTVYNQk*}F!q zVC(LK*AXzNh#8r$9N=|CqZv?WXN;MYjaqu39=c|yS9zjUX=T%jZs!oe`3P82x zoCIb8gsR3f`)rcc z&IiudZ)HD{0aMjs&nzpb#0-vh4&}`l=o~;fU~}GACC856SOSnmxRr2J`Gv?obbyLX zbru_-pRNQ;=?7<+PAQ1kmlLA2iOYnuz^eocGZAui%=-TRF25GkVKk*?VLM7WBGYPB zT*tpuphVY?L>99zcgqh{8?J&32Rv?+%)Z_WCB$d`hC#@mKYy$$hzm4z911e3T@BQE z6}a5Q^z2GI$3(jeiH2?d26Vp6Zr1N{77{V3le;970pcooz1rBP zTCCqm&A0&u`_5tKoG#cZcARCvr+{+b_xPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iOZ8 z6%`IG`XD&~00I3;L_t(o!^Kz8j>8}fODg_+AF{uksuh}hxC0rJP`W$mkkHl^jB%XM z>f`Y+`Dtd>`gxwMmr`2KIk$cshdiH8N#}XW6$o}5WA?p%5)sKc%eHO9fR!#HlFUqU z&XQ6Z1~3DUmCHm#rjx?$lu|lm24QA+Eh4g2CJ}X<;QbGo;J_$CohD|s1Tu}z3KIrH zRtpQ8-Z|UyQP4caCB~&Z3QG0mMzcQLYIcQ!(sFW-xf9knF@|(?iY~XAOhHzd%#8OD2m>x}XSSsVf@;^obfpX48<@ZgFF$M4@xtjbM#B=Ojp!9yfFNKXj22K7STvD?00000NkvXXu0mjfB76)l literal 0 HcmV?d00001 diff --git a/doc/images/menu_bg_unsel.png b/doc/images/menu_bg_unsel.png new file mode 100644 index 0000000000000000000000000000000000000000..50e5fd8d3d17114bc71e14c483532f2c082920b2 GIT binary patch literal 1596 zcmV-C2E+M@P)002J-1^@s6XaB>y00001b5ch_0Itp) z=>Px#24YJ`L;wH)0002_L%V+f000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iOZ6 z5FZU~yaYY~00q2BL_t(|+U;H2a;q>5blUuRzqvD+7VvpFWE>%_)?7k5*?npguqAmd z8(5Uf*Vk7qf^ZV9(=;gx!g&NNrIcY9%E!ltq9Ht=fVI{#3`4nIuZo7C3@E%I0mpGH z+6xLIpzyv+um~ueb`dgXz*=j$TrP@=0TUO+Zl@)sQ3vT-lF8GjhNS)Y)Np5NBKI{~L@T3m#*2iTKp zt@p;JX@cbN4E2Ix|BjCFg536j+O}xz_u6Q`^V-S$v$poy&cxQ*Y0r;rMq}sMi^c@` z(Y9kbHGfVm!D!}MMH6RQOQ=PVd8HOe5K@dz0av&_)W-mG0@FVnX-$jszG6Cpd6iG4)S zD4R33W^wegr6p&!^ui1{3`424F0w(E#-nu32HZMgjcogm$jB1kn}usg#Vp4?lQGIP zmJDq6inbT557~swV%+wP_%pYAzYQ~vV>tL(hik9?P_~PVkL=oF50e?tN^s;oR77r% zlT|&VC}Ga0&tEJ%k7- zoJv4FMiNe2g8%YGh@d4{5VmDN2~q_u!NU6zP)o2N0s>_EwNi4;;4^G#zyC`L>8-YI zIsZ<;Sq-zMv%STs5t%X@*w0xZXGHX=S58L)URT4u^Pb?4-gC!@j zGZO+Nz0VPNkPS1cMpr9pK zPzLm{wVGyxy%obpDUi2TLEY1CBu?G4a_w4M*7W_50a2rjQkgL}dUVxOnilo5_XJ|? z^0b6KgUQ;2DyO!8Gi7vpi4|3GMVi2@-Nh0d2`yX4qE34Ty0;9h7ucR!#o2^lg6|D> zq>N@KDl1M!v148j=!p*!BwK!Xg({IeZ9%}cIAvzMSJ)Ae5UHkn2oMDWN!z_bn~X9f z@-5QES=A0wM|(C$1t(DkMz$~$G8#J+SMOZ5Dyv8A`w~27zqUXna*hph7O3bmD~Gp^ z*;fa=*%<}bla(WZrnkg+@^mC+kFs@kF8aj)Hk+cY4=Q!sYBVE3usz00Cd{g}$bgX+ zEs`2}PM|AlPwbhc>#U3~dKV?KArhvt%U4th9!;L?oeXA`K|g&EO%g=b=rg7ws`Z0W zfG^}&b8$*k*B4={a+xJocKjaIK(oCqTOT@;wzHCX)@Jr%Fnd8LdMj})ftln4_kht_ z?hOuGQ#w0_X^UxNi{yO`dG|z5aq*tmGIh6m-ucZ4$E=q33^;eTcR-I+1i!M{_!a$H z+wPGv{WJp#!l6$>RG?fqm4Ls$zapS;ZU)p6ES#PJ^<&F|2q=hvg78!V{`~xifWmnM uoCT@+p&H=~TxzX#9LMtc`Kf3KPscx4r4cOw-wj9r0000} +Vendor: %{?_vendorinfo:%{_vendorinfo}}%{!?_vendorinfo:The Enlightenment Project (http://www.enlightenment.org/)} +Distribution: %{?_distribution:%{_distribution}}%{!?_distribution:%{_vendor}} +Obsoletes: ecore-dbus <= 0.9.9.040 +BuildRoot: %{_tmppath}/%{name}-%{version}-root + +%description +e_dbus provides a convenience wrapper for EFL applications using DBus. + +%package devel +Summary: e_dbus headers, static libraries, documentation and test programs +Group: System Environment/Libraries +Requires: %{name} = %{version} + +%description devel +Headers, static libraries, test programs and documentation for e_dbus + +%prep +%setup -q + +%build +%{configure} --prefix=%{_prefix} +%{__make} %{?_smp_mflags} %{?mflags} + +%install +%{__make} %{?mflags_install} DESTDIR=$RPM_BUILD_ROOT install + +# Get rid of unneeded testing cruft. +%{__rm} -rf $RPM_BUILD_ROOT%{_datadir}/%{name} + +%clean +test "x$RPM_BUILD_ROOT" != "x/" && rm -rf $RPM_BUILD_ROOT + +%post +/sbin/ldconfig + +%postun +/sbin/ldconfig + +%files +%defattr(-, root, root) +%doc AUTHORS COPYING* README +%{_bindir}/* +%{_libdir}/*.so.* + +%files devel +%defattr(-, root, root) +%{_includedir}/e_dbus-1/*.h +%{_includedir}/e_dbus-1/*/*.h +%{_libdir}/*.so +%{_libdir}/*.la +%{_libdir}/*.a +%{_libdir}/pkgconfig/* + +%changelog diff --git a/ebluez.pc.in b/ebluez.pc.in new file mode 100644 index 0000000..8630de5 --- /dev/null +++ b/ebluez.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: ebluez +Description: bluetooth device manager (bluez) +@pkgconfig_requires_private@: @requirement_ebluez@ +Version: @VERSION@ +Libs: -L${libdir} -lebluez +Cflags: -I${includedir}/e_dbus-@VMAJ@ diff --git a/econnman-0.7x.pc.in b/econnman-0.7x.pc.in new file mode 100644 index 0000000..f3ba96a --- /dev/null +++ b/econnman-0.7x.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: econnman-0.7x +Description: network connection manager (connman v0.7x) +@pkgconfig_requires_private@: @requirement_econnman0_7x@ +Version: @VERSION@ +Libs: -L${libdir} -leconnman0_7x +Cflags: -I${includedir}/e_dbus-@VMAJ@ diff --git a/edbus.pc.in b/edbus.pc.in new file mode 100644 index 0000000..e3e368c --- /dev/null +++ b/edbus.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: edbus +Description: DBus convenience library +@pkgconfig_requires_private@: @requirement_edbus@ +Version: @VERSION@ +Libs: -L${libdir} -ledbus @dbus_libs@ +Cflags: -I${includedir}/e_dbus-@VMAJ@ diff --git a/ehal.pc.in b/ehal.pc.in new file mode 100644 index 0000000..c62f906 --- /dev/null +++ b/ehal.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: ehal +Description: Hal convenience library +@pkgconfig_requires_private@: @requirement_ehal@ +Version: @VERSION@ +Libs: -L${libdir} -lehal +Cflags: -I${includedir}/e_dbus-@VMAJ@ diff --git a/enotify.pc.in b/enotify.pc.in new file mode 100644 index 0000000..4219e9c --- /dev/null +++ b/enotify.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: enotify +Description: Notification convenience library +@pkgconfig_requires_private@: @requirement_enotify@ +Version: @VERSION@ +Libs: -L${libdir} -lenotify +Cflags: -I${includedir}/e_dbus-@VMAJ@ diff --git a/eofono.pc.in b/eofono.pc.in new file mode 100644 index 0000000..bc2dc5f --- /dev/null +++ b/eofono.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: eofono +Description: oFono D-Bus wrappers +@pkgconfig_requires_private@: @requirement_eofono@ +Version: @VERSION@ +Libs: -L${libdir} -leofono +Cflags: -I${includedir}/e_dbus-@VMAJ@ diff --git a/eukit.pc.in b/eukit.pc.in new file mode 100644 index 0000000..63df1d5 --- /dev/null +++ b/eukit.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: eukit +Description: udisks/upower convenience library +@pkgconfig_requires_private@: @requirement_eofono@ +Version: @VERSION@ +Libs: -L${libdir} -leukit +Cflags: -I${includedir}/e_dbus-@VMAJ@ diff --git a/m4/ac_attribute.m4 b/m4/ac_attribute.m4 new file mode 100644 index 0000000..23479a9 --- /dev/null +++ b/m4/ac_attribute.m4 @@ -0,0 +1,47 @@ +dnl Copyright (C) 2004-2008 Kim Woelders +dnl Copyright (C) 2008 Vincent Torri +dnl That code is public domain and can be freely used or copied. +dnl Originally snatched from somewhere... + +dnl Macro for checking if the compiler supports __attribute__ + +dnl Usage: AC_C___ATTRIBUTE__ +dnl call AC_DEFINE for HAVE___ATTRIBUTE__ and __UNUSED__ +dnl if the compiler supports __attribute__, HAVE___ATTRIBUTE__ is +dnl defined to 1 and __UNUSED__ is defined to __attribute__((unused)) +dnl otherwise, HAVE___ATTRIBUTE__ is not defined and __UNUSED__ is +dnl defined to nothing. + +AC_DEFUN([AC_C___ATTRIBUTE__], +[ + +AC_MSG_CHECKING([for __attribute__]) + +AC_CACHE_VAL([ac_cv___attribute__], + [AC_TRY_COMPILE( + [ +#include + +int func(int x); +int foo(int x __attribute__ ((unused))) +{ + exit(1); +} + ], + [], + [ac_cv___attribute__="yes"], + [ac_cv___attribute__="no"] + )]) + +AC_MSG_RESULT($ac_cv___attribute__) + +if test "x${ac_cv___attribute__}" = "xyes" ; then + AC_DEFINE([HAVE___ATTRIBUTE__], [1], [Define to 1 if your compiler has __attribute__]) + AC_DEFINE([__UNUSED__], [__attribute__((unused))], [Macro declaring a function argument to be unused]) + else + AC_DEFINE([__UNUSED__], [], [Macro declaring a function argument to be unused]) +fi + +]) + +dnl End of ac_attribute.m4 diff --git a/m4/efl_binary.m4 b/m4/efl_binary.m4 new file mode 100644 index 0000000..0ad38ce --- /dev/null +++ b/m4/efl_binary.m4 @@ -0,0 +1,78 @@ +dnl Copyright (C) 2010 Vincent Torri +dnl That code is public domain and can be freely used or copied. + +dnl Macro that checks if a binary is built or not + +dnl Usage: EFL_ENABLE_BIN(binary, dep[, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) +dnl Call AC_SUBST(BINARY_PRG) (BINARY is the uppercase of binary, - being transformed into _) +dnl Define have_binary (- is transformed into _) +dnl Define conditional BUILD_BINARY (BINARY is the uppercase of binary, - being transformed into _) + +AC_DEFUN([EFL_ENABLE_BIN], +[ + +m4_pushdef([UP], m4_translit([[$1]], [-a-z], [_A-Z]))dnl +m4_pushdef([DOWN], m4_translit([[$1]], [-A-Z], [_a-z]))dnl + +dnl configure option + +AC_ARG_ENABLE([$1], + [AC_HELP_STRING([--disable-$1], [disable building of ]DOWN)], + [ + if test "x${enableval}" = "xyes" ; then + have_[]m4_defn([DOWN])="yes" + else + have_[]m4_defn([DOWN])="no" + fi + ], + [have_[]m4_defn([DOWN])=$2]) + +AC_MSG_CHECKING([whether to build ]DOWN[ binary]) +AC_MSG_RESULT([$have_[]m4_defn([DOWN])]) + +if test "x$have_[]m4_defn([DOWN])" = "xyes"; then + UP[]_PRG=DOWN[${EXEEXT}] +fi + +AC_SUBST(UP[]_PRG) + +AM_CONDITIONAL(BUILD_[]UP, test "x$have_[]m4_defn([DOWN])" = "xyes") + +AS_IF([test "x$have_[]m4_defn([DOWN])" = "xyes"], [$3], [$4]) + +]) + +dnl Macro that specifies the binary to be used + +dnl Usage: EFL_WITH_BIN(binary, package, msg) +dnl Call AC_SUBST(BINARY_PRG) (BINARY is the uppercase of binary, - being transformed into _) +dnl Define with_binary (- is transformed into _) +dnl Define conditional BUILD_BINARY (BINARY is the uppercase of binary, - being transformed into _) + +AC_DEFUN([EFL_WITH_BIN], +[ + +m4_pushdef([UP], m4_translit([[$1]], [-a-z], [_A-Z]))dnl +m4_pushdef([DOWN], m4_translit([[$1]], [-A-Z], [_a-z]))dnl + +AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +AC_MSG_NOTICE([$PKG_CONFIG]) + +with_[]m4_defn([DOWN])=m4_esyscmd($PKG_CONFIG --variable=prefix $2)/bin/m4_defn([DOWN]) + +dnl configure option + +AC_ARG_WITH([$1], + [AC_HELP_STRING([--with-$1-bin=PATH], [specify a specific path to ]DOWN)], + [ + with_[]m4_defn([DOWN])=$withval + _efl_msg="( explicitely set)" + ]) + +AC_MSG_NOTICE([$msg: ]m4_defn([DOWN])[$_efl_msg]) + +AC_SUBST(with_[]m4_defn([DOWN])) + +AS_IF([test "x$have_[]m4_defn([DOWN])" = "xyes"], [$4], [$5]) + +]) diff --git a/m4/efl_compiler_flag.m4 b/m4/efl_compiler_flag.m4 new file mode 100644 index 0000000..e3fc821 --- /dev/null +++ b/m4/efl_compiler_flag.m4 @@ -0,0 +1,24 @@ +dnl Checks if a given compiler switch is supported. +dnl If so, this macro adds the flag to the CFLAGS + +AC_DEFUN([EFL_COMPILER_FLAG], +[ + +CFLAGS_save="${CFLAGS}" +CFLAGS="${CFLAGS} $1" + +AC_LANG_PUSH([C]) +AC_MSG_CHECKING([whether the compiler supports $1]) + +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[]])], + [have_flag="yes"], + [have_flag="no"]) +AC_MSG_RESULT([${have_flag}]) + +if test "x${have_flag}" = "xno" ; then + CFLAGS="${CFLAGS_save}" +fi +AC_LANG_POP([C]) + +]) diff --git a/m4/efl_doxygen.m4 b/m4/efl_doxygen.m4 new file mode 100644 index 0000000..7324af3 --- /dev/null +++ b/m4/efl_doxygen.m4 @@ -0,0 +1,94 @@ +dnl Copyright (C) 2008 Vincent Torri +dnl That code is public domain and can be freely used or copied. + +dnl Macro that check if doxygen is available or not. + +dnl EFL_CHECK_DOXYGEN([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) +dnl Test for the doxygen program +dnl Defines efl_doxygen +dnl Defines the automake conditionnal EFL_BUILD_DOC +dnl +AC_DEFUN([EFL_CHECK_DOXYGEN], +[ + +dnl +dnl Disable the build of the documentation +dnl +AC_ARG_ENABLE([doc], + [AC_HELP_STRING( + [--disable-doc], + [Disable documentation build @<:@default=enabled@:>@])], + [ + if test "x${enableval}" = "xyes" ; then + efl_enable_doc="yes" + else + efl_enable_doc="no" + fi + ], + [efl_enable_doc="yes"]) + +AC_MSG_CHECKING([whether to build documentation]) +AC_MSG_RESULT([${efl_enable_doc}]) + +if test "x${efl_enable_doc}" = "xyes" ; then + +dnl Specify the file name, without path + + efl_doxygen="doxygen" + + AC_ARG_WITH([doxygen], + [AC_HELP_STRING( + [--with-doxygen=FILE], + [doxygen program to use @<:@default=doxygen@:>@])], + +dnl Check the given doxygen program. + + [efl_doxygen=${withval} + AC_CHECK_PROG([efl_have_doxygen], + [${efl_doxygen}], + [yes], + [no]) + if test "x${efl_have_doxygen}" = "xno" ; then + echo "WARNING:" + echo "The doxygen program you specified:" + echo "${efl_doxygen}" + echo "was not found. Please check the path and make sure " + echo "the program exists and is executable." + AC_MSG_WARN([no doxygen detected. Documentation will not be built]) + fi + ], + [AC_CHECK_PROG([efl_have_doxygen], + [${efl_doxygen}], + [yes], + [no]) + if test "x${efl_have_doxygen}" = "xno" ; then + echo "WARNING:" + echo "The doxygen program was not found in your execute path." + echo "You may have doxygen installed somewhere not covered by your path." + echo "" + echo "If this is the case make sure you have the packages installed, AND" + echo "that the doxygen program is in your execute path (see your" + echo "shell manual page on setting the \$PATH environment variable), OR" + echo "alternatively, specify the program to use with --with-doxygen." + AC_MSG_WARN([no doxygen detected. Documentation will not be built]) + fi + ]) +else + efl_have_doxygen="no" +fi + +dnl +dnl Substitution +dnl +AC_SUBST([efl_doxygen]) + +if ! test "x${efl_have_doxygen}" = "xyes" ; then + efl_enable_doc="no" +fi + +AM_CONDITIONAL(EFL_BUILD_DOC, test "x${efl_have_doxygen}" = "xyes") + +AS_IF([test "x$efl_have_doxygen" = "xyes"], [$1], [$2]) +]) + +dnl End of efl_doxygen.m4 diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..97baf85 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,2 @@ +MAINTAINERCLEANFILES = Makefile.in +SUBDIRS = lib bin diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am new file mode 100644 index 0000000..2041091 --- /dev/null +++ b/src/bin/Makefile.am @@ -0,0 +1,207 @@ +MAINTAINERCLEANFILES = Makefile.in + +AM_CFLAGS = \ +-DPACKAGE_DATA_DIR=\"$(datadir)/$(PACKAGE)\" + +EDBUS_CPPFLAGS = @EDBUS_CFLAGS@ + +bin_PROGRAMS = + +if BUILD_EDBUS_TEST +bin_PROGRAMS += e_dbus_test +endif + +if BUILD_EDBUS_TEST_CLIENT +bin_PROGRAMS += e_dbus_test_client +endif + +if BUILD_EDBUS_ASYNC_TEST +bin_PROGRAMS += e_dbus_async_server_test +bin_PROGRAMS += e_dbus_async_client_test +endif + +if BUILD_EDBUS_BLUEZ_TEST +bin_PROGRAMS += e_dbus_bluez_test +endif + +if BUILD_EDBUS_CONNMAN0_7X_TEST +bin_PROGRAMS += e_dbus_connman0_7x_test +endif + +if BUILD_EDBUS_NOTIFICATION_DAEMON_TEST +bin_PROGRAMS += e_dbus_notification_daemon +endif + +if BUILD_EDBUS_NOTIFY_SEND +bin_PROGRAMS += e-notify-send +endif + +if BUILD_EDBUS_NOTIFY_TEST +bin_PROGRAMS += e_dbus_notify +endif + +if BUILD_EDBUS_OFONO_TEST +bin_PROGRAMS += e_dbus_ofono_test +endif + +if BUILD_EDBUS_UKIT_TEST +bin_PROGRAMS += e_dbus_ukit_test +endif + +if BUILD_EDBUS_PERFORMANCE_TEST +bin_PROGRAMS += e_dbus_performance +endif + +noinst_PROGRAMS = + +if BUILD_EDBUS_CONNMAN0_7X_TEST +noinst_PROGRAMS += e_dbus_connman0_7x_test_api +endif + +if BUILD_EDBUS_TEST +e_dbus_test_SOURCES = test.c +e_dbus_test_CPPFLAGS = \ +-I$(top_srcdir)/src/lib/dbus \ +@EDBUS_TEST_CFLAGS@ +e_dbus_test_LDADD = \ +$(top_builddir)/src/lib/dbus/libedbus.la \ +@EDBUS_TEST_LIBS@ +endif + +if BUILD_EDBUS_TEST_CLIENT +e_dbus_test_client_SOURCES = test_client.c +e_dbus_test_client_CPPFLAGS = \ +-I$(top_srcdir)/src/lib/dbus \ +@EDBUS_TEST_CLIENT_CFLAGS@ +e_dbus_test_client_LDADD = \ +$(top_builddir)/src/lib/dbus/libedbus.la \ +@EDBUS_TEST_CLIENT_LIBS@ +endif + +if BUILD_EDBUS_ASYNC_TEST +e_dbus_async_server_test_SOURCES = async_server_test.c +e_dbus_async_server_test_CPPFLAGS = \ +-I$(top_srcdir)/src/lib/dbus \ +@EDBUS_ASYNC_TEST_CFLAGS@ +e_dbus_async_server_test_LDADD = \ +$(top_builddir)/src/lib/dbus/libedbus.la \ +@EDBUS_ASYNC_TEST_LIBS@ + +e_dbus_async_client_test_SOURCES = async_client_test.c +e_dbus_async_client_test_CPPFLAGS = \ +-I$(top_srcdir)/src/lib/dbus \ +@EDBUS_ASYNC_TEST_CFLAGS@ +e_dbus_async_client_test_LDADD = \ +$(top_builddir)/src/lib/dbus/libedbus.la \ +@EDBUS_ASYNC_TEST_LIBS@ +endif + +if BUILD_EDBUS_BLUEZ_TEST +e_dbus_bluez_test_SOURCES = e_dbus_bluez_test.c +e_dbus_bluez_test_CPPFLAGS = \ +-I$(top_srcdir)/src/lib/dbus \ +-I$(top_srcdir)/src/lib/bluez \ +@EDBUS_BLUEZ_TEST_CFLAGS@ +e_dbus_bluez_test_LDADD = \ +$(top_builddir)/src/lib/dbus/libedbus.la \ +$(top_builddir)/src/lib/bluez/libebluez.la \ +@EDBUS_BLUEZ_TEST_LIBS@ +endif + +if BUILD_EDBUS_CONNMAN0_7X_TEST +e_dbus_connman0_7x_test_SOURCES = e_dbus_connman0_7x_test.c +e_dbus_connman0_7x_test_CPPFLAGS = \ +-I$(top_srcdir)/src/lib/dbus \ +-I$(top_srcdir)/src/lib \ +@EDBUS_CONNMAN0_7X_TEST_CFLAGS@ +e_dbus_connman0_7x_test_LDADD = \ +$(top_builddir)/src/lib/dbus/libedbus.la \ +$(top_builddir)/src/lib/connman0_7x/libeconnman0_7x.la \ +@EDBUS_CONNMAN0_7X_TEST_LIBS@ + +e_dbus_connman0_7x_test_api_SOURCES = e_dbus_connman0_7x_test_api.c +e_dbus_connman0_7x_test_api_CPPFLAGS = \ +-I$(top_srcdir)/src/lib/dbus \ +-I$(top_srcdir)/src/lib \ +@EDBUS_CONNMAN0_7X_TEST_CFLAGS@ +e_dbus_connman0_7x_test_api_LDADD = \ +$(top_builddir)/src/lib/dbus/libedbus.la \ +$(top_builddir)/src/lib/connman0_7x/libeconnman0_7x.la \ +@EDBUS_CONNMAN0_7X_TEST_LIBS@ +endif + +ENOTIFY_LIBS = $(top_builddir)/src/lib/notification/libenotify.la + +if BUILD_EDBUS_NOTIFICATION_DAEMON_TEST +e_dbus_notification_daemon_SOURCES = notification_daemon.c +e_dbus_notification_daemon_CPPFLAGS = \ +-I$(top_srcdir)/src/lib/dbus \ +-I$(top_srcdir)/src/lib/notification \ +@EDBUS_NOTIFICATION_DAEMON_TEST_CFLAGS@ +e_dbus_notification_daemon_LDADD = \ +$(ENOTIFY_LIBS) \ +$(top_builddir)/src/lib/dbus/libedbus.la \ +@EDBUS_NOTIFICATION_DAEMON_TEST_LIBS@ +endif + +if BUILD_EDBUS_NOTIFY_SEND +e_notify_send_SOURCES = notify-send.c +e_notify_send_CPPFLAGS = \ +-I$(top_srcdir)/src/lib/dbus \ +-I$(top_srcdir)/src/lib/notification \ +@EDBUS_NOTIFY_SEND_CFLAGS@ +e_notify_send_LDADD = \ +$(ENOTIFY_LIBS) \ +$(top_builddir)/src/lib/dbus/libedbus.la \ +@EDBUS_NOTIFY_SEND_LIBS@ +endif + +if BUILD_EDBUS_NOTIFY_TEST +EXTRA_DIST = logo.png +logodir = $(datadir)/e_dbus +logo_DATA = logo.png +e_dbus_notify_SOURCES = notify.c +e_dbus_notify_CPPFLAGS = \ +-I$(top_srcdir)/src/lib/dbus \ +-I$(top_srcdir)/src/lib/notification \ +@EDBUS_NOTIFY_TEST_CFLAGS@ +e_dbus_notify_LDADD = \ +$(ENOTIFY_LIBS) \ +$(top_builddir)/src/lib/dbus/libedbus.la \ +@EDBUS_NOTIFY_TEST_LIBS@ +endif + +if BUILD_EDBUS_OFONO_TEST +e_dbus_ofono_test_SOURCES = e_dbus_ofono_test.c +e_dbus_ofono_test_CPPFLAGS = \ +-I$(top_srcdir)/src/lib/dbus \ +-I$(top_srcdir)/src/lib/ofono \ +@EDBUS_OFONO_TEST_CFLAGS@ +e_dbus_ofono_test_LDADD = \ +$(top_builddir)/src/lib/dbus/libedbus.la \ +$(top_builddir)/src/lib/ofono/libeofono.la \ +@EDBUS_OFONO_TEST_LIBS@ +endif + +if BUILD_EDBUS_UKIT_TEST +e_dbus_ukit_test_SOURCES = e_dbus_ukit_test.c +e_dbus_ukit_test_CPPFLAGS = \ +-I$(top_srcdir)/src/lib/dbus \ +-I$(top_srcdir)/src/lib/ukit \ +@EDBUS_UKIT_TEST_CFLAGS@ +e_dbus_ukit_test_LDADD = \ +$(top_builddir)/src/lib/dbus/libedbus.la \ +$(top_builddir)/src/lib/ukit/libeukit.la \ +@EDBUS_UKIT_TEST_LIBS@ +endif + +if BUILD_EDBUS_PERFORMANCE_TEST +e_dbus_performance_SOURCES = performance.c +e_dbus_performance_CFLAGS = \ +-I$(top_srcdir)/src/lib/dbus \ +@EDBUS_PERFORMANCE_TEST_CFLAGS@ +e_dbus_performance_LDADD = \ +$(top_builddir)/src/lib/dbus/libedbus.la \ +@EDBUS_PERFORMANCE_TEST_LIBS@ +endif + diff --git a/src/bin/async_client_test.c b/src/bin/async_client_test.c new file mode 100644 index 0000000..04db1f5 --- /dev/null +++ b/src/bin/async_client_test.c @@ -0,0 +1,58 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "E_DBus.h" + +#define DBUS_NAME "com.profusion" +#define OBJECT_PATH "/com/profusion/test" +#define IFACE_NAME "com.Profusion.Test" + +static E_DBus_Connection *conn = NULL; + +static void +_cb_resp(void *data, DBusMessage *msg, DBusError *error) +{ + DBusError new_error; + int size; + + if (dbus_error_is_set(error)) + { + printf("dbus error\nName: %s\nDescription: %s\n", error->name, + error->message); + ecore_main_loop_quit(); + return; + } + + dbus_error_init(&new_error); + dbus_message_get_args(msg, &new_error, DBUS_TYPE_INT32, &size, + DBUS_TYPE_INVALID); + if (dbus_error_is_set(&new_error)) + printf("dbus error\nName: %s\nDescription: %s\n", new_error.name, + new_error.message); + else printf("size = %d\n", size); + + ecore_main_loop_quit(); +} + +int +main(int argc, char *argv[]) +{ + char *string = "lalala"; + DBusMessage *msg; + + e_dbus_init(); + conn = e_dbus_bus_get(DBUS_BUS_SESSION); + + msg = dbus_message_new_method_call(DBUS_NAME, OBJECT_PATH, + IFACE_NAME, "string_len_async"); + dbus_message_append_args(msg, DBUS_TYPE_STRING, &string, DBUS_TYPE_INVALID); + e_dbus_message_send(conn, msg, _cb_resp, -1, NULL); + dbus_message_unref(msg); + + ecore_main_loop_begin(); + + e_dbus_shutdown(); + return 0; +} diff --git a/src/bin/async_server_test.c b/src/bin/async_server_test.c new file mode 100644 index 0000000..3560e3b --- /dev/null +++ b/src/bin/async_server_test.c @@ -0,0 +1,124 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "E_DBus.h" + +#define DBUS_NAME "com.profusion" +#define OBJECT_PATH "/com/profusion/test" +#define IFACE_NAME "com.Profusion.Test" + +static E_DBus_Connection *conn = NULL; +static E_DBus_Object *obj_path = NULL; + +typedef struct _EDBus_Method + { + char *member; + char *signature; + char *reply_signature; + E_DBus_Method_Cb func; + } EDBus_Method; + +static void +obj_register(char *path_name, char *iface_name, EDBus_Method *methods) +{ + obj_path = e_dbus_object_add(conn, path_name, NULL); + E_DBus_Interface *iface = e_dbus_interface_new(iface_name); + const EDBus_Method *_method; + + e_dbus_object_interface_attach(obj_path, iface); + e_dbus_interface_unref(iface); + + for (_method = methods; _method != NULL && _method->member != NULL; _method++) + e_dbus_interface_method_add(iface, _method->member, + _method->signature, + _method->reply_signature, + _method->func); +} + +static void +_dbus_error_check(DBusError *error) +{ + if (dbus_error_is_set(error)) + { + printf("dbus error\nName: %s\nDescription: %s\n", error->name, + error->message); + ecore_main_loop_quit(); + } +} + +Eina_Bool +_resp_async(void *data) +{ + DBusMessage *msg = data; + DBusMessage *reply; + DBusError new_err; + char *string; + int size; + + dbus_error_init(&new_err); + dbus_message_get_args(msg, &new_err, DBUS_TYPE_STRING, &string, + DBUS_TYPE_INVALID); + _dbus_error_check(&new_err); + + reply = dbus_message_new_method_return(msg); + size = strlen(string); + dbus_message_append_args(reply, DBUS_TYPE_INT32, &size, DBUS_TYPE_INVALID); + e_dbus_message_send(conn, reply, NULL, -1, NULL); + + dbus_message_unref(msg); + dbus_message_unref(reply); + + return ECORE_CALLBACK_CANCEL; +} + +static DBusMessage * +_async(E_DBus_Object *obj, DBusMessage *msg) +{ + dbus_message_ref(msg); + printf("received a string_len_async call\n"); + printf("response will be send in 5 seconds\n"); + ecore_timer_add (5, _resp_async, msg); + return NULL; +} + +static void +_cb_dbus_request_name(void *context, DBusMessage *msg, DBusError *err) +{ + DBusError new_err; + dbus_uint32_t msgtype; + EDBus_Method table_methods[] = + { + { "string_len_async", "s", "d", _async}, + { NULL, NULL, NULL, NULL } + }; + + _dbus_error_check(err); + dbus_error_init(&new_err); + dbus_message_get_args(msg, &new_err, DBUS_TYPE_UINT32, &msgtype, + DBUS_TYPE_INVALID); + if (msgtype != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) + { + printf("Could not get the DBus name: reply=%d", msgtype); + ecore_main_loop_quit(); + } + + obj_register(OBJECT_PATH, IFACE_NAME, table_methods); +} + +int +main(int argc, char *argv[]) +{ + e_dbus_init(); + + conn = e_dbus_bus_get(DBUS_BUS_SESSION); + + e_dbus_request_name(conn, DBUS_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE, + _cb_dbus_request_name, NULL); + + ecore_main_loop_begin(); + + e_dbus_shutdown(); + return 0; +} diff --git a/src/bin/e_dbus_bluez_test.c b/src/bin/e_dbus_bluez_test.c new file mode 100644 index 0000000..d2f479e --- /dev/null +++ b/src/bin/e_dbus_bluez_test.c @@ -0,0 +1,860 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include "E_Bluez.h" + +typedef struct _E_Msgbus_Data E_Msgbus_Data; + +struct _E_Msgbus_Data { + E_DBus_Connection *conn; + E_DBus_Object *obj; +}; + +static E_Msgbus_Data *_msgbus_data = NULL; + +static E_DBus_Interface *iface = NULL; + +static void +_method_success_check(void *data, __UNUSED__ DBusMessage *msg, DBusError *error) +{ + const char *name = data; + + if ((!error) || (!dbus_error_is_set(error))) + { + printf("SUCCESS: method %s() finished successfully.\n", name); + return; + } + + printf("FAILURE: method %s() finished with error: %s %s\n", + name, error->name, error->message); + dbus_error_free(error); +} + +static void +_default_adapter_callback(__UNUSED__ void *data, DBusMessage *msg, __UNUSED__ DBusError *err) +{ + E_Bluez_Element *element; + const char *path; + + if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID) == EINA_FALSE) + printf("FAILURE: failed to get default adapter\n"); + + printf("SUCCESS: default adapter: %s\n", path); + + element = e_bluez_element_get(path); + e_bluez_element_print(stdout, element); + return; + +} + +static void +_create_paired_device_cb(void *data, DBusMessage *msg, DBusError *err) +{ + e_dbus_object_interface_detach(_msgbus_data->obj, iface); + e_dbus_object_free(_msgbus_data->obj); + e_dbus_interface_unref(iface); + _method_success_check(data, msg, err); +} + +static DBusMessage* +_request_pincode_cb(__UNUSED__ E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessage *reply; + char pin[16]; + char *p = pin; + int ret; + + printf("Enter PIN Code:\n"); + ret = scanf("%15s", p); + if (ret != 1) + return NULL; + + reply = dbus_message_new_method_return(msg); + dbus_message_append_args(reply, DBUS_TYPE_STRING, &p, + DBUS_TYPE_INVALID); + + return reply; +} + +static void +_elements_print(E_Bluez_Element **elements, unsigned int count) +{ + unsigned int i; + for (i = 0; i < count; i++) + { + printf("--- element %d:\n", i); + e_bluez_element_print(stdout, elements[i]); + } + free(elements); + printf("END: all elements count = %u\n", count); +} + +static Eina_Bool +_on_element_add(__UNUSED__ void *data, __UNUSED__ int type, void *info) +{ + E_Bluez_Element *element = info; + printf(">>> %s\n", element->path); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_element_del(__UNUSED__ void *data, __UNUSED__ int type, void *info) +{ + E_Bluez_Element *element = info; + printf("<<< %s\n", element->path); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_element_updated(__UNUSED__ void *data, __UNUSED__ int type, void *info) +{ + E_Bluez_Element *element = info; + printf("!!! %s\n", element->path); + e_bluez_element_print(stderr, element); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_device_found(__UNUSED__ void *data, int __UNUSED__ type, void *info) +{ + E_Bluez_Device_Found *device = info; + printf("!!! %s\n", device->adapter->path); + printf(":::DeviceFound %s\n", device->name); + e_bluez_element_array_print(stderr, device->array); + printf("\n"); + + e_bluez_devicefound_free(device); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_quit(__UNUSED__ char *cmd, __UNUSED__ char *args) +{ + fputs("Bye!\n", stderr); + ecore_main_loop_quit(); + return EINA_FALSE; +} + +static Eina_Bool +_on_cmd_sync(__UNUSED__ char *cmd, __UNUSED__ char *args) +{ + e_bluez_manager_sync_elements(); + return ECORE_CALLBACK_RENEW; +} + +static char * +_tok(char *p) +{ + p = strchr(p, ' '); + if (!p) + return NULL; + + *p = '\0'; + p++; + while (isspace(*p)) + p++; + if (*p == '\0') + return NULL; + + return p; +} + +static Eina_Bool +_on_cmd_get_all(__UNUSED__ char *cmd, char *args) +{ + E_Bluez_Element **elements; + char *type; + unsigned int count; + Eina_Bool ret; + + if (!args) + type = NULL; + else + type = args; + + if (type) + ret = e_bluez_elements_get_all_type(type, &count, &elements); + else + ret = e_bluez_elements_get_all(&count, &elements); + + if (!ret) + fputs("ERROR: could not get elements\n", stderr); + else + { + printf("BEG: all elements type=%s count = %d\n", type, count); + _elements_print(elements, count); + } + + return ECORE_CALLBACK_RENEW; +} + +static E_Bluez_Element * +_element_from_args(char *args, char **next_args) +{ + E_Bluez_Element *element; + + if (!args) + { + fputs("ERROR: missing element path\n", stderr); + *next_args = NULL; + return NULL; + } + + *next_args = _tok(args); + element = e_bluez_element_get(args); + if (!element) + fprintf(stderr, "ERROR: no element called \"%s\".\n", args); + + return element; +} + +static Eina_Bool +_on_cmd_print(__UNUSED__ char *cmd, char *args) +{ + char *next_args; + E_Bluez_Element *element = _element_from_args(args, &next_args); + if (element) + e_bluez_element_print(stdout, element); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_get_properties(__UNUSED__ char *cmd, char *args) +{ + char *next_args; + E_Bluez_Element *element = _element_from_args(args, &next_args); + if (element) + e_bluez_element_properties_sync(element); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_property_set(__UNUSED__ char *cmd, char *args) +{ + char *next_args, *name, *p; + E_Bluez_Element *element = _element_from_args(args, &next_args); + void *value; + long vlong; + unsigned short vu16; + unsigned int vu32; + int type; + + if (!element) + return ECORE_CALLBACK_RENEW; + + if (!next_args) + { + fputs("ERROR: missing parameters name, type and value.\n", stderr); + return ECORE_CALLBACK_RENEW; + } + + name = next_args; + p = _tok(name); + if (!p) + { + fputs("ERROR: missing parameters type and value.\n", stderr); + return ECORE_CALLBACK_RENEW; + } + + next_args = _tok(p); + if (!next_args) + { + fputs("ERROR: missing parameter value.\n", stderr); + return ECORE_CALLBACK_RENEW; + } + + type = p[0]; + switch (type) + { + case DBUS_TYPE_BOOLEAN: + vlong = !!atol(next_args); + value = &vlong; + fprintf(stderr, "DBG: boolean is: %ld\n", vlong); + break; + case DBUS_TYPE_UINT16: + vu16 = strtol(next_args, &p, 0); + if (p == next_args) + { + fprintf(stderr, "ERROR: invalid number \"%s\".\n", next_args); + return ECORE_CALLBACK_RENEW; + } + value = &vu16; + fprintf(stderr, "DBG: u16 is: %hu\n", vu16); + break; + case DBUS_TYPE_UINT32: + vu32 = strtol(next_args, &p, 0); + if (p == next_args) + { + fprintf(stderr, "ERROR: invalid number \"%s\".\n", next_args); + return ECORE_CALLBACK_RENEW; + } + value = &vu32; + fprintf(stderr, "DBG: u16 is: %u\n", vu32); + break; + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + p = next_args + strlen(next_args); + if (p > next_args) + p--; + while (p > next_args && isspace(*p)) + p--; + if (p <= next_args) + { + fprintf(stderr, "ERROR: invalid string \"%s\".\n", next_args); + } + p[1] = '\0'; + value = next_args; + fprintf(stderr, "DBG: string is: \"%s\"\n", next_args); + break; + default: + fprintf(stderr, "ERROR: don't know how to parse type '%c' (%d)\n", + type, type); + return ECORE_CALLBACK_RENEW; + } + + fprintf(stderr, "set_property %s [%p] %s %c %p...\n", + args, element, name, type, value); + if (!e_bluez_element_property_set(element, name, type, value)) + fputs("ERROR: error setting property.\n", stderr); + + return ECORE_CALLBACK_RENEW; +} + +/* Manager Commands */ + +static Eina_Bool +_on_cmd_manager_get(__UNUSED__ char *cmd, __UNUSED__ char *args) +{ + E_Bluez_Element *element; + element = e_bluez_manager_get(); + e_bluez_element_print(stderr, element); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_manager_default_adapter(__UNUSED__ char *cmd, __UNUSED__ char *args) +{ + return e_bluez_manager_default_adapter(_default_adapter_callback, NULL); +} + +/* Adapter Commands */ + +static Eina_Bool +_on_cmd_adapter_register_agent(__UNUSED__ char *cmd, char *args) +{ + char *next_args, *path, *cap; + E_Bluez_Element *element = _element_from_args(args, &next_args); + + if (!element) + return ECORE_CALLBACK_RENEW; + + if (!next_args) { + fputs("ERROR: missing parameters name, type and value.\n", stderr); + return ECORE_CALLBACK_RENEW; + } + + path = next_args; + cap = _tok(path); + if (!cap) { + fputs("ERROR: missing parameters name, type and value.\n", stderr); + return ECORE_CALLBACK_RENEW; + } + + if (e_bluez_adapter_agent_register(element, + path, cap, _method_success_check, "adapter_register_agent")) + printf(":::Registering agent %s (%s)...\n", path, cap); + else + fprintf(stderr, "ERROR: can't register agent %s\n", path); + + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_adapter_unregister_agent(__UNUSED__ char *cmd, char *args) +{ + char *path, *next_args; + E_Bluez_Element *element = _element_from_args(args, &next_args); + + if (!element) + return ECORE_CALLBACK_RENEW; + + if (!args) + { + fputs("ERROR: missing the object path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + + path = next_args; + if (e_bluez_adapter_agent_unregister(element, + path, _method_success_check, "adapter_unregister_agent")) + printf(":::Unregistering agent %s...\n", path); + else + fprintf(stderr, "ERROR: can't unregister agent %s\n", path); + + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_adapter_get_address(__UNUSED__ char *cmd, char *args) +{ + const char *address; + char *next_args; + E_Bluez_Element *element = _element_from_args(args, &next_args); + + if (!element) + return ECORE_CALLBACK_RENEW; + + if (e_bluez_adapter_address_get(element, &address)) + printf(":::Adapter address = \"%s\"\n", address); + else + fputs("ERROR: can't get adapter address\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_adapter_get_name(__UNUSED__ char *cmd, char *args) +{ + const char *name; + char *next_args; + E_Bluez_Element *element = _element_from_args(args, &next_args); + + if (!element) + return ECORE_CALLBACK_RENEW; + + if (e_bluez_adapter_name_get(element, &name)) + printf(":::Adapter name = \"%s\"\n", name); + else + fputs("ERROR: can't get adapter name\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_adapter_set_name(__UNUSED__ char *cmd, char *args) +{ + char *next_args; + E_Bluez_Element *element = _element_from_args(args, &next_args); + + if (!element) + return ECORE_CALLBACK_RENEW; + + if (!next_args) { + fprintf(stderr, "ERROR: missing name value\n"); + return ECORE_CALLBACK_RENEW; + } + + if (e_bluez_adapter_name_set(element, next_args, _method_success_check, + "adapter_set_name")) + printf(":::Adapter %s Name set to %s\n", element->path, next_args); + else + fputs("ERROR: can't set adapter name\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_adapter_get_powered(__UNUSED__ char *cmd, char *args) +{ + char *next_args; + Eina_Bool powered; + E_Bluez_Element *element = _element_from_args(args, &next_args); + + if (!element) + return ECORE_CALLBACK_RENEW; + + if (e_bluez_adapter_powered_get(element, &powered)) + printf(":::Adapter powered = \"%hhu\"\n", powered); + else + fputs("ERROR: can't get adapter powered\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_adapter_set_powered(__UNUSED__ char *cmd, char *args) +{ + char *next_args; + Eina_Bool powered; + E_Bluez_Element *element = _element_from_args(args, &next_args); + + if (!element) + return ECORE_CALLBACK_RENEW; + + if (!args) + { + fputs("ERROR: missing the powered value\n", stderr); + return ECORE_CALLBACK_RENEW; + } + + powered = !!atol(next_args); + + if (e_bluez_adapter_powered_set + (element, powered, _method_success_check, "adapter_set_powered")) + printf(":::Adapter %s Powered set to %hhu\n", element->path, powered); + else + fputs("ERROR: can't set device powered\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_adapter_get_discoverable(__UNUSED__ char *cmd, char *args) +{ + char *next_args; + Eina_Bool discoverable; + E_Bluez_Element *element = _element_from_args(args, &next_args); + + if (!element) + return ECORE_CALLBACK_RENEW; + + if (e_bluez_adapter_discoverable_get(element, &discoverable)) + printf(":::Adapter discoverable = \"%hhu\"\n", discoverable); + else + fputs("ERROR: can't get adapter discoverable\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_adapter_set_discoverable(__UNUSED__ char *cmd, char *args) +{ + char *next_args; + Eina_Bool discoverable; + E_Bluez_Element *element = _element_from_args(args, &next_args); + + if (!element) + return ECORE_CALLBACK_RENEW; + + if (!args) + { + fputs("ERROR: missing the discoverable value\n", stderr); + return ECORE_CALLBACK_RENEW; + } + + discoverable = !!atol(next_args); + + if (e_bluez_adapter_discoverable_set + (element, discoverable, _method_success_check, "adapter_set_discoverable")) + printf(":::Adapter %s discoverable set to %hhu\n", element->path, discoverable); + else + fputs("ERROR: can't set adapter discoverable\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_adapter_get_discoverable_timeout(__UNUSED__ char *cmd, char *args) +{ + char *next_args; + unsigned int timeout; + E_Bluez_Element *element = _element_from_args(args, &next_args); + + if (!element) + return ECORE_CALLBACK_RENEW; + + if (e_bluez_adapter_discoverable_timeout_get(element, &timeout)) + printf(":::Adapter %s DiscovableTimeout = %hu\n", element->path, timeout); + else + fputs("ERROR: can't get adapter discoverable timeout\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_adapter_set_discoverable_timeout(__UNUSED__ char *cmd, char *args) +{ + char *next_args, *p; + unsigned int timeout; + E_Bluez_Element *element = _element_from_args(args, &next_args); + + if (!element) + return ECORE_CALLBACK_RENEW; + + if (!next_args) { + fprintf(stderr, "ERROR: missing timeout value\n"); + return ECORE_CALLBACK_RENEW; + } + + timeout = strtol(next_args, &p, 0); + if (p == next_args) + { + fprintf(stderr, "ERROR: invalid number \"%s\".\n", next_args); + return ECORE_CALLBACK_RENEW; + } + + if (e_bluez_adapter_discoverable_timeout_set(element, timeout, + _method_success_check, + "adapter_set_discoverable_timeout")) + printf(":::Adapter %s scan interval set to %hu\n", element->path, timeout); + else + fputs("ERROR: can't set adapter discoverable timeout\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_adapter_get_discovering(__UNUSED__ char *cmd, char *args) +{ + char *next_args; + Eina_Bool discovering; + E_Bluez_Element *element = _element_from_args(args, &next_args); + + if (!element) + return ECORE_CALLBACK_RENEW; + + if (e_bluez_adapter_discovering_get(element, &discovering)) + printf(":::Adapter discovering = \"%hhu\"\n", discovering); + else + fputs("ERROR: can't get adapter's Discovering\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_adapter_start_discovery(__UNUSED__ char *cmd, char *args) +{ + char *next_args; + E_Bluez_Element *element = _element_from_args(args, &next_args); + + if (!element) + return ECORE_CALLBACK_RENEW; + + if (e_bluez_adapter_start_discovery(element, + _method_success_check, "adapter_start_discovery")) + printf(":::Adapter Start Discovery for %s\n", element->path); + else + fputs("ERROR: can't start discovery on adapter \n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_adapter_stop_discovery(__UNUSED__ char *cmd, char *args) +{ + char *next_args; + E_Bluez_Element *element = _element_from_args(args, &next_args); + + if (!element) + return ECORE_CALLBACK_RENEW; + + if (e_bluez_adapter_stop_discovery(element, + _method_success_check, "adapter_stop_discovery")) + printf(":::Adapter Stop Discovery for %s\n", element->path); + else + fputs("ERROR: can't stop discovery on adapter \n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_adapter_create_paired_device(__UNUSED__ char *cmd, char *args) +{ + char *next_args, *path, *cap, *device; + E_Bluez_Element *element = _element_from_args(args, &next_args); + + if (!element) + return ECORE_CALLBACK_RENEW; + + if (!next_args) { + fputs("ERROR: missing parameters name, type and value.\n", stderr); + return ECORE_CALLBACK_RENEW; + } + + path = next_args; + cap = _tok(path); + if (!cap) { + fputs("ERROR: missing parameters name, type and value.\n", stderr); + return ECORE_CALLBACK_RENEW; + } + device = _tok(cap); + if (!device) { + fputs("ERROR: missing parameters name, type and value.\n", stderr); + return ECORE_CALLBACK_RENEW; + } + + if (e_bluez_adapter_create_paired_device(element, path, cap, device, + _create_paired_device_cb, "adapter_create_paired_device")) { + printf(":::Creating Paired Device %s (%s)...\n", path, cap); + iface = e_dbus_interface_new("org.bluez.Agent"); + if (!iface) { + fputs("WARNING: Cannot add org.bluez.Agent interface",stderr); + return EINA_FALSE; + } + _msgbus_data->obj = e_dbus_object_add(_msgbus_data->conn, path, NULL); + e_dbus_object_interface_attach(_msgbus_data->obj, iface); + e_dbus_interface_method_add(iface, "RequestPinCode", "o", "s", + _request_pincode_cb); + + } + else + fprintf(stderr, "ERROR: can't create paired device %s\n", path); + + return ECORE_CALLBACK_RENEW; +} + +/* Devices Commands */ + +static Eina_Bool +_on_cmd_device_get_name(__UNUSED__ char *cmd, char *args) +{ + const char *name; + char *next_args; + E_Bluez_Element *element = _element_from_args(args, &next_args); + + if (!element) + return ECORE_CALLBACK_RENEW; + + if (e_bluez_device_name_get(element, &name)) + printf(":::Device name = \"%s\"\n", name); + else + fputs("ERROR: can't get device name\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_input(__UNUSED__ void *data, Ecore_Fd_Handler *fd_handler) +{ + char buf[256]; + char *cmd, *args; + const struct { + const char *cmd; + Eina_Bool (*cb)(char *cmd, char *args); + } *itr, maps[] = { + {"quit", _on_cmd_quit}, + {"sync", _on_cmd_sync}, + {"get_all", _on_cmd_get_all}, + {"print", _on_cmd_print}, + {"get_properties", _on_cmd_get_properties}, + {"set_property", _on_cmd_property_set}, + {"manager_get", _on_cmd_manager_get}, + {"manager_default_adapter", _on_cmd_manager_default_adapter}, + {"adapter_register_agent", _on_cmd_adapter_register_agent}, + {"adapter_unregister_agent", _on_cmd_adapter_unregister_agent}, + {"adapter_get_address", _on_cmd_adapter_get_address}, + {"adapter_get_name", _on_cmd_adapter_get_name}, + {"adapter_set_name", _on_cmd_adapter_set_name}, + {"adapter_get_powered", _on_cmd_adapter_get_powered}, + {"adapter_set_powered", _on_cmd_adapter_set_powered}, + {"adapter_get_discoverable", _on_cmd_adapter_get_discoverable}, + {"adapter_set_discoverable", _on_cmd_adapter_set_discoverable}, + {"adapter_get_discoverable_timeout", _on_cmd_adapter_get_discoverable_timeout}, + {"adapter_set_discoverable_timeout", _on_cmd_adapter_set_discoverable_timeout}, + {"adapter_get_discovering", _on_cmd_adapter_get_discovering}, + {"adapter_start_discovery", _on_cmd_adapter_start_discovery}, + {"adapter_stop_discovery", _on_cmd_adapter_stop_discovery}, + {"adapter_create_paired_device", _on_cmd_adapter_create_paired_device}, + {"device_get_name", _on_cmd_device_get_name}, + {NULL, NULL} + }; + + + if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_ERROR)) + { + fputs("ERROR: reading from stdin, exit\n", stderr); + return EINA_FALSE; + } + + if (!ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ)) + { + fputs("ERROR: nothing to read?\n", stderr); + return EINA_FALSE; + } + + if (!fgets(buf, sizeof(buf), stdin)) + { + fprintf(stderr, "ERROR: could not read command: %s\n", strerror(errno)); + ecore_main_loop_quit(); + return EINA_FALSE; + } + + cmd = buf; + while (isspace(*cmd)) + cmd++; + + args = strchr(cmd, ' '); + if (args) + { + char *p; + + *args = '\0'; + args++; + + while (isspace(*args)) + args++; + + p = args + strlen(args) - 1; + if (*p == '\n') + *p = '\0'; + } + else + { + char *p; + + p = cmd + strlen(cmd) - 1; + if (*p == '\n') + *p = '\0'; + } + + if (strcmp(cmd, "help") == 0) + { + if (args) + { + printf("Commands with '%s' in the name:\n", args); + for (itr = maps; itr->cmd; itr++) + if (strstr(itr->cmd, args)) + printf("\t%s\n", itr->cmd); + } + else + { + fputs("Commands:\n", stdout); + for (itr = maps; itr->cmd; itr++) + printf("\t%s\n", itr->cmd); + } + fputc('\n', stdout); + return ECORE_CALLBACK_RENEW; + } + + for (itr = maps; itr->cmd; itr++) + if (strcmp(itr->cmd, cmd) == 0) + return itr->cb(cmd, args); + + printf("unknown command \"%s\", args=%s\n", cmd, args); + return ECORE_CALLBACK_RENEW; +} + +int +main(__UNUSED__ int argc,__UNUSED__ char *argv[]) +{ + ecore_init(); + e_dbus_init(); + eina_init(); + + _msgbus_data = calloc(1, sizeof(E_Msgbus_Data)); + _msgbus_data->conn = e_dbus_bus_get(DBUS_BUS_SYSTEM); + if (!_msgbus_data->conn) { + printf("ERROR: can't connect to system session\n"); + return -1; + } + + e_bluez_system_init(_msgbus_data->conn); + + ecore_event_handler_add(E_BLUEZ_EVENT_ELEMENT_ADD, _on_element_add, NULL); + ecore_event_handler_add(E_BLUEZ_EVENT_ELEMENT_DEL, _on_element_del, NULL); + ecore_event_handler_add(E_BLUEZ_EVENT_ELEMENT_UPDATED, + _on_element_updated, NULL); + ecore_event_handler_add(E_BLUEZ_EVENT_DEVICE_FOUND, + _on_device_found, NULL); + + ecore_main_fd_handler_add + (0, ECORE_FD_READ | ECORE_FD_ERROR, _on_input, NULL, NULL, NULL); + + ecore_main_loop_begin(); + + e_bluez_system_shutdown(); + + e_dbus_connection_close(_msgbus_data->conn); + eina_shutdown(); + e_dbus_shutdown(); + ecore_shutdown(); + + fputs("DBG: clean exit.\n", stderr); + + return 0; +} diff --git a/src/bin/e_dbus_connman0_7x_test.c b/src/bin/e_dbus_connman0_7x_test.c new file mode 100644 index 0000000..e84d4e3 --- /dev/null +++ b/src/bin/e_dbus_connman0_7x_test.c @@ -0,0 +1,1726 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#define E_CONNMAN_I_KNOW_THIS_API_IS_SUBJECT_TO_CHANGE 1 +#include "connman0_7x/E_Connman.h" + +static void +_method_success_check(void *data, __UNUSED__ DBusMessage *msg, DBusError *error) +{ + const char *name = data; + + if ((!error) || (!dbus_error_is_set(error))) + { + printf("SUCCESS: method %s() finished successfully.\n", name); + return; + } + + printf("FAILURE: method %s() finished with error: %s %s\n", + name, error->name, error->message); + dbus_error_free(error); +} + +static void +_elements_print(E_Connman_Element **elements, unsigned int count) +{ + unsigned int i; + for (i = 0; i < count; i++) + { + printf("--- element %d:\n", i); + e_connman_element_print(stdout, elements[i]); + } + free(elements); + printf("END: all elements count = %u\n", count); +} + +static void +_strings_print(const char **strings, unsigned int count) +{ + unsigned int i; + for (i = 0; i < count; i++) + printf("--- strings %d: \"%s\"\n", i, strings[i]); + free(strings); + printf("END: all strings count = %u\n", count); +} + +static Eina_Bool +_on_element_add(__UNUSED__ void *data, __UNUSED__ int type, void *info) +{ + E_Connman_Element *element = info; + printf(">>> %s\n", element->path); + return ECORE_CALLBACK_PASS_ON; +} + +static Eina_Bool +_on_element_del(__UNUSED__ void *data, __UNUSED__ int type, void *info) +{ + E_Connman_Element *element = info; + printf("<<< %s\n", element->path); + return ECORE_CALLBACK_PASS_ON; +} + +static Eina_Bool +_on_element_updated(__UNUSED__ void *data, __UNUSED__ int type, void *info) +{ + E_Connman_Element *element = info; + printf("!!! %s\n", element->path); + e_connman_element_print(stderr, element); + return ECORE_CALLBACK_PASS_ON; +} + +static Eina_Bool +_on_cmd_quit(__UNUSED__ char *cmd, __UNUSED__ char *args) +{ + fputs("Bye!\n", stderr); + ecore_main_loop_quit(); + return ECORE_CALLBACK_CANCEL; +} + +static Eina_Bool +_on_cmd_sync(__UNUSED__ char *cmd, __UNUSED__ char *args) +{ + e_connman_manager_sync_elements(); + return ECORE_CALLBACK_RENEW; +} + +static char * +_tok(char *p) +{ + p = strchr(p, ' '); + if (!p) + return NULL; + + *p = '\0'; + p++; + while (isspace(*p)) + p++; + if (*p == '\0') + return NULL; + + return p; +} + +static Eina_Bool +_on_cmd_get_all(__UNUSED__ char *cmd, char *args) +{ + E_Connman_Element **elements; + char *type; + unsigned int count; + Eina_Bool ret; + + if (!args) + type = NULL; + else + type = args; + + if (type) + ret = e_connman_elements_get_all_type(type, &count, &elements); + else + ret = e_connman_elements_get_all(&count, &elements); + + if (!ret) + fputs("ERROR: could not get elements\n", stderr); + else + { + printf("BEG: all elements type=%s count = %d\n", type, count); + _elements_print(elements, count); + } + + return ECORE_CALLBACK_RENEW; +} + +static E_Connman_Element * +_element_from_args(char *args, char **next_args) +{ + E_Connman_Element *element; + + if (!args) + { + fputs("ERROR: missing element path\n", stderr); + *next_args = NULL; + return NULL; + } + + *next_args = _tok(args); + element = e_connman_element_get(args); + if (!element) + fprintf(stderr, "ERROR: no element called \"%s\".\n", args); + + return element; +} + +static Eina_Bool +_on_cmd_print(__UNUSED__ char *cmd, char *args) +{ + char *next_args; + E_Connman_Element *element = _element_from_args(args, &next_args); + if (element) + e_connman_element_print(stdout, element); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_get_properties(__UNUSED__ char *cmd, char *args) +{ + char *next_args; + E_Connman_Element *element = _element_from_args(args, &next_args); + if (element) + e_connman_element_properties_sync(element); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_property_set(__UNUSED__ char *cmd, char *args) +{ + char *next_args, *name, *p; + E_Connman_Element *element = _element_from_args(args, &next_args); + void *value; + long vlong; + unsigned short vu16; + unsigned int vu32; + int type; + + if (!element) + return ECORE_CALLBACK_RENEW; + + if (!next_args) + { + fputs("ERROR: missing parameters name, type and value.\n", stderr); + return ECORE_CALLBACK_RENEW; + } + + name = next_args; + p = _tok(name); + if (!p) + { + fputs("ERROR: missing parameters type and value.\n", stderr); + return ECORE_CALLBACK_RENEW; + } + + next_args = _tok(p); + if (!next_args) + { + fputs("ERROR: missing parameter value.\n", stderr); + return ECORE_CALLBACK_RENEW; + } + + type = p[0]; + switch (type) + { + case DBUS_TYPE_BOOLEAN: + vlong = !!atol(next_args); + value = &vlong; + fprintf(stderr, "DBG: boolean is: %ld\n", vlong); + break; + case DBUS_TYPE_UINT16: + vu16 = strtol(next_args, &p, 0); + if (p == next_args) + { + fprintf(stderr, "ERROR: invalid number \"%s\".\n", next_args); + return ECORE_CALLBACK_RENEW; + } + value = &vu16; + fprintf(stderr, "DBG: u16 is: %hu\n", vu16); + break; + case DBUS_TYPE_UINT32: + vu32 = strtol(next_args, &p, 0); + if (p == next_args) + { + fprintf(stderr, "ERROR: invalid number \"%s\".\n", next_args); + return ECORE_CALLBACK_RENEW; + } + value = &vu32; + fprintf(stderr, "DBG: u16 is: %u\n", vu32); + break; + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + p = next_args + strlen(next_args); + if (p > next_args) + p--; + while (p > next_args && isspace(*p)) + p--; + if (p <= next_args) + { + fprintf(stderr, "ERROR: invalid string \"%s\".\n", next_args); + } + p[1] = '\0'; + value = next_args; + fprintf(stderr, "DBG: string is: \"%s\"\n", next_args); + break; + default: + fprintf(stderr, "ERROR: don't know how to parse type '%c' (%d)\n", + type, type); + return ECORE_CALLBACK_RENEW; + } + + fprintf(stderr, "set_property %s [%p] %s %c %p...\n", + args, element, name, type, value); + if (!e_connman_element_property_set(element, name, type, value)) + fputs("ERROR: error setting property.\n", stderr); + + return ECORE_CALLBACK_RENEW; +} + + +/* Manager Commands */ + +static Eina_Bool +_on_cmd_manager_get(__UNUSED__ char *cmd, __UNUSED__ char *args) +{ + E_Connman_Element *element; + element = e_connman_manager_get(); + e_connman_element_print(stderr, element); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_manager_get_profiles(__UNUSED__ char *cmd, __UNUSED__ char *args) +{ + unsigned int count; + E_Connman_Element **profiles; + + if (!e_connman_manager_profiles_get(&count, &profiles)) + { + fputs("ERROR: can't get profiles\n", stderr); + return ECORE_CALLBACK_RENEW; + } + printf("BEG: all manager profiles elements count = %d\n", count); + _elements_print(profiles, count); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_manager_get_services(__UNUSED__ char *cmd, __UNUSED__ char *args) +{ + unsigned int count; + E_Connman_Element **services; + + if (!e_connman_manager_services_get(&count, &services)) + { + fputs("ERROR: can't get services\n", stderr); + return ECORE_CALLBACK_RENEW; + } + printf("BEG: all manager services elements count = %d\n", count); + _elements_print(services, count); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_manager_register_agent(__UNUSED__ char *cmd, char *args) +{ + char *path; + + if (!args) + { + fputs("ERROR: missing the object path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + + path = args; + if (e_connman_manager_agent_register + (path, _method_success_check, "manager_register_agent")) + printf(":::Registering agent %s...\n", path); + else + fprintf(stderr, "ERROR: can't register agent %s\n", path); + + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_manager_unregister_agent(__UNUSED__ char *cmd, char *args) +{ + char *path; + + if (!args) + { + fputs("ERROR: missing the object path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + + path = args; + if (e_connman_manager_agent_unregister + (path, _method_success_check, "manager_unregister_agent")) + printf(":::Unregistering agent %s...\n", path); + else + fprintf(stderr, "ERROR: can't unregister agent %s\n", path); + + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_manager_get_state(__UNUSED__ char *cmd, __UNUSED__ char *args) +{ + const char *state; + if (e_connman_manager_state_get(&state)) + printf(":::Manager state = \"%s\"\n", state); + else + fputs("ERROR: can't get manager state\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_manager_get_offline_mode(__UNUSED__ char *cmd, __UNUSED__ char *args) +{ + Eina_Bool offline; + if (e_connman_manager_offline_mode_get(&offline)) + printf(":::Manager Offline Mode = %hhu\n", offline); + else + fputs("ERROR: can't get manager offline mode\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_manager_set_offline_mode(__UNUSED__ char *cmd, char *args) +{ + Eina_Bool offline; + if (!args) + { + fputs("ERROR: missing the offline mode value\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + offline = !!atol(args); + if (e_connman_manager_offline_mode_set + (offline, _method_success_check, "manager_set_offline_mode")) + + printf(":::Manager Offline Mode set to %hhu\n", offline); + else + fputs("ERROR: can't set manager offline mode\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_manager_request_scan(__UNUSED__ char *cmd, char *args) +{ + if (args) + _tok(args); + + if (!args) + args = ""; + + if (e_connman_manager_request_scan + (args, _method_success_check, "manager_request_scan")) + printf(":::Manager Request Scan for %s\n", args[0] ? args : ""); + else + fputs("ERROR: can't request scan on manager\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_manager_technology_enable(__UNUSED__ char *cmd, char *args) +{ + if (!args) + { + fputs("ERROR: missing the technology type\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + + if (e_connman_manager_technology_enable + (args, _method_success_check, "manager_technology_enable")) + printf(":::Manager Enable Technology %s\n", args); + else + fputs("ERROR: can't enable technology on manager\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_manager_technology_disable(__UNUSED__ char *cmd, char *args) +{ + if (!args) + { + fputs("ERROR: missing the technology type\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + + if (e_connman_manager_technology_disable + (args, _method_success_check, "manager_technology_disable")) + printf(":::Manager Disable Technology %s\n", args); + else + fputs("ERROR: can't disable technology on manager\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_manager_get_technologies_available(__UNUSED__ char *cmd, __UNUSED__ char *args) +{ + const char **strings; + unsigned int count; + + if (!e_connman_manager_technologies_available_get(&count, &strings)) + fputs("ERROR: can't get available technologies\n", stderr); + else + { + printf("BEG: available technologies count = %u\n", count); + _strings_print(strings, count); + } + + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_manager_get_technologies_enabled(__UNUSED__ char *cmd, __UNUSED__ char *args) +{ + const char **strings; + unsigned int count; + + if (!e_connman_manager_technologies_enabled_get(&count, &strings)) + fputs("ERROR: can't get enabled technologies\n", stderr); + else + { + printf("BEG: enabled technologies count = %u\n", count); + _strings_print(strings, count); + } + + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_manager_get_technologies_connected(__UNUSED__ char *cmd, __UNUSED__ char *args) +{ + const char **strings; + unsigned int count; + + if (!e_connman_manager_technologies_connected_get(&count, &strings)) + fputs("ERROR: can't get connected technologies\n", stderr); + else + { + printf("BEG: connected technologies count = %u\n", count); + _strings_print(strings, count); + } + + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_manager_profile_remove(__UNUSED__ char *cmd, char *args) +{ + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the profile path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + + e = e_connman_profile_get(args); + if (e_connman_manager_profile_remove + (e, _method_success_check, "manager_profile_remove")) + printf(":::Manager Remove Profile %s\n", args); + else + fputs("ERROR: can't remove profile from manager\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_manager_profile_get_active(__UNUSED__ char *cmd, __UNUSED__ char *args) +{ + E_Connman_Element *e; + + if (!e_connman_manager_profile_active_get(&e)) + fputs("ERROR: can't active_get profile from manager\n", stderr); + else + e_connman_element_print(stderr, e); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_manager_profile_set_active(__UNUSED__ char *cmd, char *args) +{ + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the profile path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + + e = e_connman_profile_get(args); + if (e_connman_manager_profile_active_set + (e, _method_success_check, "manager_profile_set_active")) + printf(":::Manager Active Profile set to %s\n", args); + else + fputs("ERROR: can't set active profile\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +/* Profile Commands */ + +static Eina_Bool +_on_cmd_profile_get_name(__UNUSED__ char *cmd, char *args) +{ + const char *name, *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the profile path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_profile_get(path); + if (e_connman_profile_name_get(e, &name)) + printf(":::Profile %s Name = \"%s\"\n", path, name); + else + fputs("ERROR: can't get profile name\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_profile_set_name(__UNUSED__ char *cmd, char *args) +{ + char *path, *next_args; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the profile path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + path = args; + next_args = _tok(args); + if (!next_args) + { + fputs("ERROR: missing the offline mode value\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(next_args); + + e = e_connman_profile_get(path); + if (e_connman_profile_name_set + (e, next_args, _method_success_check, "profile_set_name")) + printf(":::Profile %s Name set to %s\n", path, next_args); + else + fputs("ERROR: can't set profile name\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_profile_get_offline_mode(__UNUSED__ char *cmd, char *args) +{ + char *path; + Eina_Bool offline; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the profile path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_profile_get(path); + if (e_connman_profile_offline_mode_get(e, &offline)) + printf(":::Profile %s Offline Mode = %hhu\n", path, offline); + else + fputs("ERROR: can't get profile offline mode\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_profile_set_offline_mode(__UNUSED__ char *cmd, char *args) +{ + char *path, *next_args; + Eina_Bool offline; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the profile path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + path = args; + next_args = _tok(args); + if (!next_args) + { + fputs("ERROR: missing the offline mode value\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(next_args); + offline = !!atol(next_args); + + e = e_connman_profile_get(path); + if (e_connman_profile_offline_mode_set + (e, offline, _method_success_check, "profile_set_offline_mode")) + printf(":::Profile %s Offline Mode set to %hhu\n", path, offline); + else + fputs("ERROR: can't set profile offline mode\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_profile_get_services(__UNUSED__ char *cmd, char *args) +{ + E_Connman_Element **services; + E_Connman_Element *e; + unsigned int count; + char *path; + + if (!args) + { + fputs("ERROR: missing the profile path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_profile_get(path); + if (!e_connman_profile_services_get(e, &count, &services)) + { + fputs("ERROR: can't get services\n", stderr); + return ECORE_CALLBACK_RENEW; + } + printf("BEG: all profile services count = %d\n", count); + _elements_print(services, count); + return ECORE_CALLBACK_RENEW; +} + + +/* Services Commands */ +static Eina_Bool +_on_cmd_service_connect(__UNUSED__ char *cmd, char *args) +{ + char *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_connect + (e, _method_success_check, "service_connect")) + printf(":::Connecting to Service %s...\n", path); + else + fputs("ERROR: can't connect to service\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_disconnect(__UNUSED__ char *cmd, char *args) +{ + char *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_disconnect + (e, _method_success_check, "service_disconnect")) + printf(":::Disconnecting Service %s...\n", path); + else + fputs("ERROR: can't disconnect service\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_remove(__UNUSED__ char *cmd, char *args) +{ + char *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_remove + (e, _method_success_check, "service_remove")) + printf(":::Removing Service %s...\n", path); + else + fputs("ERROR: can't remove service\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_move_before(__UNUSED__ char *cmd, char *args) +{ + char *path, *service_path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + service_path = args; + path = _tok(args); + + if (!path) + { + fputs("ERROR: missing the object service\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(path); + + e = e_connman_service_get(service_path); + if (e_connman_service_move_before + (e, path, _method_success_check, "service_move_before")) + printf(":::Moving before %s...\n", path); + else + fputs("ERROR: can't move before\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_move_after(__UNUSED__ char *cmd, char *args) +{ + char *path, *service_path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + service_path = args; + path = _tok(args); + + if (!path) + { + fputs("ERROR: missing the object service\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(path); + + e = e_connman_service_get(service_path); + if (e_connman_service_move_after + (e, path, _method_success_check, "service_move_after")) + printf(":::Moving after %s...\n", path); + else + fputs("ERROR: can't move after\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_state(__UNUSED__ char *cmd, char *args) +{ + const char *state, *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_state_get(e, &state)) + printf(":::Service %s State = \"%s\"\n", path, state); + else + fputs("ERROR: can't get service state\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_error(__UNUSED__ char *cmd, char *args) +{ + const char *error, *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_error_get(e, &error)) + printf(":::Service %s Error = \"%s\"\n", path, error); + else + fputs("ERROR: can't get service error\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_name(__UNUSED__ char *cmd, char *args) +{ + const char *name, *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_name_get(e, &name)) + printf(":::Service %s Name = \"%s\"\n", path, name); + else + fputs("ERROR: can't get service name\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_type(__UNUSED__ char *cmd, char *args) +{ + const char *type, *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_type_get(e, &type)) + printf(":::Service %s Type = \"%s\"\n", path, type); + else + fputs("ERROR: can't get service type\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_security(__UNUSED__ char *cmd, char *args) +{ + unsigned int count; + const char **security; + const char *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_security_get(e, &count, &security)) + { + unsigned int i; + for (i = 0; i < count; i++) + printf("\"%s\", ", security[i]); + putchar('\n'); + } + else + fputs("ERROR: can't get service security\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_passphrase(__UNUSED__ char *cmd, char *args) +{ + const char *passphrase, *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_passphrase_get(e, &passphrase)) + printf(":::Service %s Passphrase = \"%s\"\n", path, passphrase); + else + fputs("ERROR: can't get service passphrase\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_set_passphrase(__UNUSED__ char *cmd, char *args) +{ + char *passphrase, *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + path = args; + passphrase = _tok(args); + + if (!passphrase) + { + fputs("ERROR: missing the passphrase value\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(passphrase); + + e = e_connman_service_get(path); + if (e_connman_service_passphrase_set + (e, passphrase, _method_success_check, "service_set_passphrase")) + printf(":::Service %s passphrase set to \"%s\"\n", path, passphrase); + else + fputs("ERROR: can't set service passphrase\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_passphrase_required(__UNUSED__ char *cmd, char *args) +{ + const char *path; + Eina_Bool passphrase; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_passphrase_required_get(e, &passphrase)) + printf(":::Service %s Passphrase Required = %hhu\n", path, passphrase); + else + fputs("ERROR: can't get service passphrase required\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_strength(__UNUSED__ char *cmd, char *args) +{ + const char *path; + unsigned char strength; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_strength_get(e, &strength)) + printf(":::Service %s Strength = %#02hhx (%d)\n", path, strength, strength); + else + fputs("ERROR: can't get service strength\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_favorite(__UNUSED__ char *cmd, char *args) +{ + const char *path; + Eina_Bool favorite; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_favorite_get(e, &favorite)) + printf(":::Service %s Favorite = %hhu\n", path, favorite); + else + fputs("ERROR: can't get service favorite\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_immutable(__UNUSED__ char *cmd, char *args) +{ + const char *path; + Eina_Bool immutable; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_immutable_get(e, &immutable)) + printf(":::Service %s Immutable = %hhu\n", path, immutable); + else + fputs("ERROR: can't get service immutable\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_auto_connect(__UNUSED__ char *cmd, char *args) +{ + const char *path; + Eina_Bool auto_connect; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_auto_connect_get(e, &auto_connect)) + printf(":::Service %s Auto Connect = %hhu\n", path, auto_connect); + else + fputs("ERROR: can't get service auto connect\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_set_auto_connect(__UNUSED__ char *cmd, char *args) +{ + char *path, *next_args; + Eina_Bool auto_connect; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + path = args; + next_args = _tok(args); + + if (!next_args) + { + fputs("ERROR: missing the auto connect value\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(next_args); + auto_connect = !!atol(next_args); + + e = e_connman_service_get(path); + if (e_connman_service_auto_connect_set + (e, auto_connect, _method_success_check, "service_set_auto_connect")) + printf(":::Service %s auto connect set to %d\n", path, auto_connect); + else + fputs("ERROR: can't set service auto connect\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_roaming(__UNUSED__ char *cmd, char *args) +{ + const char *path; + Eina_Bool roaming; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_roaming_get(e, &roaming)) + printf(":::Service %s Roaming = %hhu\n", path, roaming); + else + fputs("ERROR: can't get service roaming\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_ipv4_method(__UNUSED__ char *cmd, char *args) +{ + const char *ipv4_method, *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_ipv4_method_get(e, &ipv4_method)) + printf(":::Service %s IPv4 Method = \"%s\"\n", path, ipv4_method); + else + fputs("ERROR: can't get service ipv4 method\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_ipv4_address(__UNUSED__ char *cmd, char *args) +{ + const char *ipv4_address, *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_ipv4_address_get(e, &ipv4_address)) + printf(":::Service %s IPv4 Address = \"%s\"\n", path, ipv4_address); + else + fputs("ERROR: can't get service ipv4 address\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_ipv4_gateway(__UNUSED__ char *cmd, char *args) +{ + const char *ipv4_gateway, *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_ipv4_gateway_get(e, &ipv4_gateway)) + printf(":::Service %s IPv4 Gateway = \"%s\"\n", path, ipv4_gateway); + else + fputs("ERROR: can't get service ipv4 gateway\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_ipv4_netmask(__UNUSED__ char *cmd, char *args) +{ + const char *ipv4_netmask, *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_ipv4_netmask_get(e, &ipv4_netmask)) + printf(":::Service %s IPv4 Netmask = \"%s\"\n", path, ipv4_netmask); + else + fputs("ERROR: can't get service ipv4 netmask\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_ipv4_configuration_method(__UNUSED__ char *cmd, char *args) +{ + const char *ipv4_method, *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_ipv4_configuration_method_get(e, &ipv4_method)) + printf(":::Service %s IPv4 Configuration Method = \"%s\"\n", + path, ipv4_method); + else + fputs("ERROR: can't get service ipv4_configuration method\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_ipv4_configuration_address(__UNUSED__ char *cmd, char *args) +{ + const char *ipv4_address, *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_ipv4_configuration_address_get(e, &ipv4_address)) + printf(":::Service %s IPv4 Configuration Address = \"%s\"\n", + path, ipv4_address); + else + fputs("ERROR: can't get service ipv4_configuration address\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_ipv4_configuration_gateway(__UNUSED__ char *cmd, char *args) +{ + const char *ipv4_gateway, *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_ipv4_configuration_gateway_get(e, &ipv4_gateway)) + printf(":::Service %s IPv4 Configuration Gateway = \"%s\"\n", + path, ipv4_gateway); + else + fputs("ERROR: can't get service ipv4_configuration gateway\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_ipv4_configuration_netmask(__UNUSED__ char *cmd, char *args) +{ + const char *ipv4_netmask, *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_ipv4_configuration_netmask_get(e, &ipv4_netmask)) + printf(":::Service %s IPv4 Configuration Netmask = \"%s\"\n", + path, ipv4_netmask); + else + fputs("ERROR: can't get service ipv4 configuration netmask\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_ipv4_configure_dhcp(__UNUSED__ char *cmd, char *args) +{ + char *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + path = args; + _tok(args); + + e = e_connman_service_get(path); + if (e_connman_service_ipv4_configure_dhcp + (e, _method_success_check, "service_ipv4_configure_dhcp")) + printf(":::Service %s IPv4 Configuration set to DHCP\n", path); + else + fputs("ERROR: can't set service ipv4_configuration dhcp\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_ipv4_configure_manual(__UNUSED__ char *cmd, char *args) +{ + char *path, *next_args, *address, *netmask = NULL, *gateway = NULL; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + path = args; + next_args = _tok(args); + if (!next_args) + { + fputs("ERROR: missing the service address\n", stderr); + return ECORE_CALLBACK_RENEW; + } + + address = next_args; + next_args = _tok(next_args); + if (next_args) + netmask = next_args; + + next_args = _tok(next_args); + if (next_args) + { + gateway = next_args; + _tok(next_args); + } + + e = e_connman_service_get(path); + if (e_connman_service_ipv4_configure_manual + (e, address, netmask, gateway, + _method_success_check, "service_ipv4_configure_manual")) + printf(":::Service %s IPv4 Configuration set to Manual (%s/%s) gw %s\n", + path, address, netmask, gateway); + else + fputs("ERROR: can't set service ipv4_configuration manual\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_ethernet_method(__UNUSED__ char *cmd, char *args) +{ + const char *ethernet_method, *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_ethernet_method_get(e, ðernet_method)) + printf(":::Service %s Ethernet Method = \"%s\"\n", path, ethernet_method); + else + fputs("ERROR: can't get service ethernet method\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_ethernet_address(__UNUSED__ char *cmd, char *args) +{ + const char *ethernet_address, *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_ethernet_address_get(e, ðernet_address)) + printf(":::Service %s Ethernet Address = \"%s\"\n", + path, ethernet_address); + else + fputs("ERROR: can't get service ethernet address\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_service_get_ethernet_mtu(__UNUSED__ char *cmd, char *args) +{ + const char *path; + E_Connman_Element *e; + unsigned short ethernet_mtu; + + if (!args) + { + fputs("ERROR: missing the service path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_service_get(path); + if (e_connman_service_ethernet_mtu_get(e, ðernet_mtu)) + printf(":::Service %s Ethernet MTU = %hu\n", path, ethernet_mtu); + else + fputs("ERROR: can't get service ethernet mtu\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_technology_get_state(__UNUSED__ char *cmd, char *args) +{ + const char *state, *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the technology path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_technology_get(path); + if (e_connman_technology_state_get(e, &state)) + printf(":::Technology %s State = \"%s\"\n", path, state); + else + fputs("ERROR: can't get technology state\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_technology_get_type(__UNUSED__ char *cmd, char *args) +{ + const char *type, *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the technology path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_technology_get(path); + if (e_connman_technology_type_get(e, &type)) + printf(":::Technology %s Type = \"%s\"\n", path, type); + else + fputs("ERROR: can't get technology type\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_cmd_technology_get_name(__UNUSED__ char *cmd, char *args) +{ + const char *name, *path; + E_Connman_Element *e; + + if (!args) + { + fputs("ERROR: missing the technology path\n", stderr); + return ECORE_CALLBACK_RENEW; + } + _tok(args); + path = args; + + e = e_connman_technology_get(path); + if (e_connman_technology_name_get(e, &name)) + printf(":::Technology %s Name = \"%s\"\n", path, name); + else + fputs("ERROR: can't get technology name\n", stderr); + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_on_input(__UNUSED__ void *data, Ecore_Fd_Handler *fd_handler) +{ + char buf[256]; + char *cmd, *args; + const struct { + const char *cmd; + Eina_Bool (*cb)(char *cmd, char *args); + } *itr, maps[] = { + {"quit", _on_cmd_quit}, + {"sync", _on_cmd_sync}, + {"get_all", _on_cmd_get_all}, + {"print", _on_cmd_print}, + {"get_properties", _on_cmd_get_properties}, + {"set_property", _on_cmd_property_set}, + {"manager_get", _on_cmd_manager_get}, + {"manager_get_profiles", _on_cmd_manager_get_profiles}, + {"manager_get_services", _on_cmd_manager_get_services}, + {"manager_register_agent", _on_cmd_manager_register_agent}, + {"manager_unregister_agent", _on_cmd_manager_unregister_agent}, + {"manager_get_state", _on_cmd_manager_get_state}, + {"manager_get_offline_mode", _on_cmd_manager_get_offline_mode}, + {"manager_set_offline_mode", _on_cmd_manager_set_offline_mode}, + {"manager_request_scan", _on_cmd_manager_request_scan}, + {"manager_technology_enable", _on_cmd_manager_technology_enable}, + {"manager_technology_disable", _on_cmd_manager_technology_disable}, + {"manager_get_technologies_available", _on_cmd_manager_get_technologies_available}, + {"manager_get_technologies_enabled", _on_cmd_manager_get_technologies_enabled}, + {"manager_get_technologies_connected", _on_cmd_manager_get_technologies_connected}, + {"manager_profile_remove", _on_cmd_manager_profile_remove}, + {"manager_profile_get_active", _on_cmd_manager_profile_get_active}, + {"manager_profile_set_active", _on_cmd_manager_profile_set_active}, + {"profile_get_name", _on_cmd_profile_get_name}, + {"profile_set_name", _on_cmd_profile_set_name}, + {"profile_get_offline_mode", _on_cmd_profile_get_offline_mode}, + {"profile_set_offline_mode", _on_cmd_profile_set_offline_mode}, + {"profile_get_services", _on_cmd_profile_get_services}, + {"service_connect", _on_cmd_service_connect}, + {"service_disconnect", _on_cmd_service_disconnect}, + {"service_remove", _on_cmd_service_remove}, + {"service_move_before", _on_cmd_service_move_before}, + {"service_move_after", _on_cmd_service_move_after}, + {"service_get_state", _on_cmd_service_get_state}, + {"service_get_error", _on_cmd_service_get_error}, + {"service_get_name", _on_cmd_service_get_name}, + {"service_get_type", _on_cmd_service_get_type}, + {"service_get_security", _on_cmd_service_get_security}, + {"service_get_passphrase", _on_cmd_service_get_passphrase}, + {"service_set_passphrase", _on_cmd_service_set_passphrase}, + {"service_get_passphrase_required", _on_cmd_service_get_passphrase_required}, + {"service_get_strength", _on_cmd_service_get_strength}, + {"service_get_favorite", _on_cmd_service_get_favorite}, + {"service_get_immutable", _on_cmd_service_get_immutable}, + {"service_get_auto_connect", _on_cmd_service_get_auto_connect}, + {"service_set_auto_connect", _on_cmd_service_set_auto_connect}, + {"service_get_roaming", _on_cmd_service_get_roaming}, + {"service_get_ipv4_method", _on_cmd_service_get_ipv4_method}, + {"service_get_ipv4_address", _on_cmd_service_get_ipv4_address}, + {"service_get_ipv4_gateway", _on_cmd_service_get_ipv4_gateway}, + {"service_get_ipv4_netmask", _on_cmd_service_get_ipv4_netmask}, + {"service_get_ipv4_configuration_method", _on_cmd_service_get_ipv4_configuration_method}, + {"service_get_ipv4_configuration_address", _on_cmd_service_get_ipv4_configuration_address}, + {"service_get_ipv4_configuration_gateway", _on_cmd_service_get_ipv4_configuration_gateway}, + {"service_get_ipv4_configuration_netmask", _on_cmd_service_get_ipv4_configuration_netmask}, + {"service_ipv4_configure_dhcp", _on_cmd_service_ipv4_configure_dhcp}, + {"service_ipv4_configure_manual", _on_cmd_service_ipv4_configure_manual}, + {"service_get_ethernet_method", _on_cmd_service_get_ethernet_method}, + {"service_get_ethernet_address", _on_cmd_service_get_ethernet_address}, + {"service_get_ethernet_mtu", _on_cmd_service_get_ethernet_mtu}, + {"technology_get_state", _on_cmd_technology_get_state}, + {"technology_get_type", _on_cmd_technology_get_type}, + {"technology_get_name", _on_cmd_technology_get_name}, + {NULL, NULL} + }; + + + if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_ERROR)) + { + fputs("ERROR: reading from stdin, exit\n", stderr); + return ECORE_CALLBACK_CANCEL; + } + + if (!ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ)) + { + fputs("ERROR: nothing to read?\n", stderr); + return ECORE_CALLBACK_CANCEL; + } + + if (!fgets(buf, sizeof(buf), stdin)) + { + fprintf(stderr, "ERROR: could not read command: %s\n", strerror(errno)); + ecore_main_loop_quit(); + return ECORE_CALLBACK_CANCEL; + } + + cmd = buf; + while (isspace(*cmd)) + cmd++; + + args = strchr(cmd, ' '); + if (args) + { + char *p; + + *args = '\0'; + args++; + + while (isspace(*args)) + args++; + + p = args + strlen(args) - 1; + if (*p == '\n') + *p = '\0'; + } + else + { + char *p; + + p = cmd + strlen(cmd) - 1; + if (*p == '\n') + *p = '\0'; + } + + if (strcmp(cmd, "help") == 0) + { + if (args) + { + printf("Commands with '%s' in the name:\n", args); + for (itr = maps; itr->cmd; itr++) + if (strstr(itr->cmd, args)) + printf("\t%s\n", itr->cmd); + } + else + { + fputs("Commands:\n", stdout); + for (itr = maps; itr->cmd; itr++) + printf("\t%s\n", itr->cmd); + } + fputc('\n', stdout); + return ECORE_CALLBACK_RENEW; + } + + for (itr = maps; itr->cmd; itr++) + if (strcmp(itr->cmd, cmd) == 0) + return itr->cb(cmd, args); + + printf("unknown command \"%s\", args=%s\n", cmd, args); + return ECORE_CALLBACK_RENEW; +} + +int +main(__UNUSED__ int argc, __UNUSED__ char *argv[]) +{ + E_DBus_Connection *c; + + ecore_init(); + e_dbus_init(); + eina_init(); + + c = e_dbus_bus_get(DBUS_BUS_SYSTEM); + if (!c) { + printf("ERROR: can't connect to system session\n"); + return -1; + } + + e_connman_system_init(c); + ecore_event_handler_add(E_CONNMAN_EVENT_ELEMENT_ADD, _on_element_add, NULL); + ecore_event_handler_add(E_CONNMAN_EVENT_ELEMENT_DEL, _on_element_del, NULL); + ecore_event_handler_add(E_CONNMAN_EVENT_ELEMENT_UPDATED, + _on_element_updated, NULL); + + ecore_main_fd_handler_add + (0, ECORE_FD_READ | ECORE_FD_ERROR, _on_input, NULL, NULL, NULL); + + ecore_main_loop_begin(); + + e_connman_system_shutdown(); + + e_dbus_connection_close(c); + eina_shutdown(); + e_dbus_shutdown(); + ecore_shutdown(); + + fputs("DBG: clean exit.\n", stderr); + + return 0; +} diff --git a/src/bin/e_dbus_connman0_7x_test_api.c b/src/bin/e_dbus_connman0_7x_test_api.c new file mode 100644 index 0000000..c8450e8 --- /dev/null +++ b/src/bin/e_dbus_connman0_7x_test_api.c @@ -0,0 +1,619 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define E_CONNMAN_I_KNOW_THIS_API_IS_SUBJECT_TO_CHANGE 1 +#include "connman0_7x/E_Connman.h" +#include +#include + +#define DBG(...) EINA_LOG_DBG(__VA_ARGS__) +#define INF(...) EINA_LOG_INFO(__VA_ARGS__) +#define WRN(...) EINA_LOG_WARN(__VA_ARGS__) +#define ERR(...) EINA_LOG_ERR(__VA_ARGS__) + +static int success = 0; +static int failure = 0; +static Ecore_Timer *exiter = NULL; + +static Eina_Bool +_test_string_get(E_Connman_Element *element, const char *name, Eina_Bool (*func)(const E_Connman_Element *element, const char **value)) +{ + const char *value; + Eina_Bool ret; + + INF("BEGIN: testing string get %s of element %s...", name, element->path); + ret = func(element, &value); + if (ret) + INF("SUCCESS: testing string get %s of element %s: %s", + name, element->path, value); + else + WRN("FAILURE: testing string get %s of element %s", + name, element->path); + + return ret; +} + +static Eina_Bool +_test_string_array_get(E_Connman_Element *element, const char *name, Eina_Bool (*func)(const E_Connman_Element *element, unsigned int *count, const char ***value)) +{ + const char **value; + unsigned int count; + Eina_Bool ret; + + INF("BEGIN: testing string array get %s of element %s...", + name, element->path); + ret = func(element, &count, &value); + if (ret) + { + INF("SUCCESS: testing string array get %s of element %s: %p[%u]", + name, element->path, value, count); + } + else + WRN("FAILURE: testing string get %s of element %s", + name, element->path); + + return ret; +} + +static Eina_Bool +_test_bool_get(E_Connman_Element *element, const char *name, Eina_Bool (*func)(const E_Connman_Element *element, Eina_Bool *value)) +{ + Eina_Bool value, ret; + + INF("BEGIN: testing Eina_Bool get %s of element %s...", name, element->path); + ret = func(element, &value); + if (ret) + INF("SUCCESS: testing Eina_Bool get %s of element %s: %hhu", + name, element->path, value); + else + WRN("FAILURE: testing Eina_Bool get %s of element %s", + name, element->path); + + return ret; +} + +static Eina_Bool +_test_uchar_get(E_Connman_Element *element, const char *name, Eina_Bool (*func)(const E_Connman_Element *element, unsigned char *value)) +{ + unsigned char value; + Eina_Bool ret; + + INF("BEGIN: testing uchar get %s of element %s...", name, element->path); + ret = func(element, &value); + if (ret) + INF("SUCCESS: testing uchar get %s of element %s: %hhu", + name, element->path, value); + else + WRN("FAILURE: testing uchar get %s of element %s", + name, element->path); + + return ret; +} + +static Eina_Bool +_test_ushort_get(E_Connman_Element *element, const char *name, Eina_Bool (*func)(const E_Connman_Element *element, unsigned short *value)) +{ + unsigned short value; + Eina_Bool ret; + + INF("BEGIN: testing ushort get %s of element %s...", name, element->path); + ret = func(element, &value); + if (ret) + INF("SUCCESS: testing ushort get %s of element %s: %hu", + name, element->path, value); + else + WRN("FAILURE: testing ushort get %s of element %s", + name, element->path); + + return ret; +} + +static Eina_Bool +_test_uchar_array_get(E_Connman_Element *element, const char *name, Eina_Bool (*func)(const E_Connman_Element *element, unsigned int *count, unsigned char **value)) +{ + unsigned char *value; + unsigned int count; + Eina_Bool ret; + + INF("BEGIN: testing uchar array get %s of element %s...", + name, element->path); + ret = func(element, &count, &value); + if (ret) + { + INF("SUCCESS: testing uchar array get %s of element %s: %p[%u]", + name, element->path, value, count); + free(value); + } + else + WRN("FAILURE: testing uchar array get %s of element %s", + name, element->path); + + return ret; +} + +static Eina_Bool +_test_element_get(E_Connman_Element *element, const char *name, Eina_Bool (*func)(const E_Connman_Element *element, E_Connman_Element **value)) +{ + E_Connman_Element *value; + Eina_Bool ret; + + INF("BEGIN: testing element get %s of element %s...", name, element->path); + ret = func(element, &value); + if (ret) + INF("SUCCESS: testing element get %s of element %s: %p", + name, element->path, value); + else + WRN("FAILURE: testing element get %s of element %s", + name, element->path); + + return ret; +} + +static Eina_Bool +_test_elements_get(E_Connman_Element *element, const char *name, Eina_Bool (*func)(const E_Connman_Element *element, unsigned int *count, E_Connman_Element ***elements)) +{ + E_Connman_Element **value; + unsigned int count; + Eina_Bool ret; + + INF("BEGIN: testing elements get %s of element %s...", + name, element->path); + ret = func(element, &count, &value); + if (ret) + { + INF("SUCCESS: testing elements get %s of element %s: %p", + name, element->path, value); + free(value); + } + else + WRN("FAILURE: testing elements get %s of element %s", + name, element->path); + + return ret; +} + +static Eina_Bool +_test_element_get_global(const char *name, Eina_Bool (*func)(E_Connman_Element **value)) +{ + E_Connman_Element *value; + Eina_Bool ret; + + INF("BEGIN: testing element get %s", name); + ret = func(&value); + if (ret) + INF("SUCCESS: testing element get %s: %p", name, value); + else + WRN("FAILURE: testing element get %s", name); + + return ret; +} + +static Eina_Bool +_test_elements_get_global(const char *name, Eina_Bool (*func)(unsigned int *count, E_Connman_Element ***elements)) +{ + E_Connman_Element **value; + unsigned int count; + Eina_Bool ret; + + INF("BEGIN: testing elements get %s", name); + ret = func(&count, &value); + if (ret) + { + INF("SUCCESS: testing elements get %s: %p", name, value); + free(value); + } + else + WRN("FAILURE: testing elements get %s", name); + + return ret; +} + +static Eina_Bool +_test_string_get_global(const char *name, Eina_Bool (*func)(const char **value)) +{ + const char *value; + Eina_Bool ret; + + INF("BEGIN: testing string get %s...", name); + ret = func(&value); + if (ret) + INF("SUCCESS: testing string get %s: %s", name, value); + else + WRN("FAILURE: testing string get %s", name); + + return ret; +} + +static Eina_Bool +_test_bool_get_global(const char *name, Eina_Bool (*func)(Eina_Bool *value)) +{ + Eina_Bool value, ret; + + INF("BEGIN: testing Eina_Bool get %s...", name); + ret = func(&value); + if (ret) + INF("SUCCESS: testing Eina_Bool get %s: %hhu", name, value); + else + WRN("FAILURE: testing Eina_Bool get %s", name); + + return ret; +} + +static Eina_Bool +_test_strings_get_global(const char *name, Eina_Bool (*func)(unsigned int *count, const char ***elements)) +{ + const char **value; + unsigned int count; + Eina_Bool ret; + + INF("BEGIN: testing strings get %s", name); + ret = func(&count, &value); + if (ret) + { + INF("SUCCESS: testing strings get %s: %p", name, value); + free(value); + } + else + WRN("FAILURE: testing strings get %s", name); + + return ret; +} + + +struct test_desc +{ + const char *name; + enum { + TEST_DESC_TYPE_STRING_GET, + TEST_DESC_TYPE_STRING_ARRAY_GET, + TEST_DESC_TYPE_BOOL_GET, + TEST_DESC_TYPE_UCHAR_GET, + TEST_DESC_TYPE_USHORT_GET, + TEST_DESC_TYPE_UCHAR_ARRAY_GET, + TEST_DESC_TYPE_ELEMENT_GET, + TEST_DESC_TYPE_ELEMENTS_GET, + TEST_DESC_TYPE_ELEMENT_GET_GLOBAL, + TEST_DESC_TYPE_ELEMENTS_GET_GLOBAL, + TEST_DESC_TYPE_STRING_GET_GLOBAL, + TEST_DESC_TYPE_BOOL_GET_GLOBAL, + TEST_DESC_TYPE_STRINGS_GET_GLOBAL, + TEST_DESC_TYPE_LAST + } type; + union { + Eina_Bool (*string_get)(const E_Connman_Element *element, const char **value); + Eina_Bool (*string_array_get)(const E_Connman_Element *element, unsigned int *count, const char ***value); + Eina_Bool (*bool_get)(const E_Connman_Element *element, Eina_Bool *value); + Eina_Bool (*uchar_get)(const E_Connman_Element *element, unsigned char *value); + Eina_Bool (*ushort_get)(const E_Connman_Element *element, unsigned short*value); + Eina_Bool (*uchar_array_get)(const E_Connman_Element *element, unsigned int *count, unsigned char **value); + Eina_Bool (*element_get)(const E_Connman_Element *element, E_Connman_Element **value); + Eina_Bool (*elements_get)(const E_Connman_Element *element, unsigned int *count, E_Connman_Element ***elements); + Eina_Bool (*element_get_global)(E_Connman_Element **element); + Eina_Bool (*elements_get_global)(unsigned int *count, E_Connman_Element ***elements); + Eina_Bool (*string_get_global)(const char **value); + Eina_Bool (*bool_get_global)(Eina_Bool *value); + Eina_Bool (*strings_get_global)(unsigned int *count, const char ***strings); + void *dummy; + } func; + Eina_Bool may_fail; +}; + +#define TEST_DESC_STRING_GET(_func, may_fail) \ + {#_func, TEST_DESC_TYPE_STRING_GET, .func.string_get=_func, may_fail} +#define TEST_DESC_STRING_ARRAY_GET(_func, may_fail) \ + {#_func, TEST_DESC_TYPE_STRING_ARRAY_GET, .func.string_array_get=_func, may_fail} +#define TEST_DESC_BOOL_GET(_func, may_fail) \ + {#_func, TEST_DESC_TYPE_BOOL_GET, .func.bool_get=_func, may_fail} +#define TEST_DESC_UCHAR_GET(_func, may_fail) \ + {#_func, TEST_DESC_TYPE_UCHAR_GET, .func.uchar_get=_func, may_fail} +#define TEST_DESC_USHORT_GET(_func, may_fail) \ + {#_func, TEST_DESC_TYPE_USHORT_GET, .func.ushort_get=_func, may_fail} +#define TEST_DESC_UCHAR_ARRAY_GET(_func, may_fail) \ + {#_func, TEST_DESC_TYPE_UCHAR_ARRAY_GET, .func.uchar_array_get=_func, may_fail} +#define TEST_DESC_ELEMENT_GET(_func, may_fail) \ + {#_func, TEST_DESC_TYPE_ELEMENT_GET, .func.element_get=_func, may_fail} +#define TEST_DESC_ELEMENTS_GET(_func, may_fail) \ + {#_func, TEST_DESC_TYPE_ELEMENTS_GET, .func.elements_get=_func, may_fail} +#define TEST_DESC_ELEMENT_GET_GLOBAL(_func, may_fail) \ + {#_func, TEST_DESC_TYPE_ELEMENT_GET_GLOBAL, .func.element_get_global=_func, may_fail} +#define TEST_DESC_ELEMENTS_GET_GLOBAL(_func, may_fail) \ + {#_func, TEST_DESC_TYPE_ELEMENTS_GET_GLOBAL, .func.elements_get_global=_func, may_fail} +#define TEST_DESC_STRING_GET_GLOBAL(_func, may_fail) \ + {#_func, TEST_DESC_TYPE_STRING_GET_GLOBAL, .func.string_get_global=_func, may_fail} +#define TEST_DESC_BOOL_GET_GLOBAL(_func, may_fail) \ + {#_func, TEST_DESC_TYPE_BOOL_GET_GLOBAL, .func.bool_get_global=_func, may_fail} +#define TEST_DESC_STRINGS_GET_GLOBAL(_func, may_fail) \ + {#_func, TEST_DESC_TYPE_STRINGS_GET_GLOBAL, .func.strings_get_global=_func, may_fail} +#define TEST_DESC_SENTINEL {NULL, TEST_DESC_TYPE_LAST, .func.dummy=NULL} + +static Eina_Bool +_test_element(E_Connman_Element *element, const struct test_desc *test_descs) +{ + const struct test_desc *itr; + int total, ok = 0, fail = 0; + Eina_Bool ret = EINA_TRUE; + + for (itr = test_descs; itr->type != TEST_DESC_TYPE_LAST; itr++) + { + Eina_Bool r; + + switch (itr->type) + { + case TEST_DESC_TYPE_STRING_GET: + r = _test_string_get(element, itr->name, itr->func.string_get); + break; + case TEST_DESC_TYPE_STRING_ARRAY_GET: + r = _test_string_array_get + (element, itr->name, itr->func.string_array_get); + break; + case TEST_DESC_TYPE_BOOL_GET: + r = _test_bool_get(element, itr->name, itr->func.bool_get); + break; + case TEST_DESC_TYPE_UCHAR_GET: + r = _test_uchar_get(element, itr->name, itr->func.uchar_get); + break; + case TEST_DESC_TYPE_USHORT_GET: + r = _test_ushort_get(element, itr->name, itr->func.ushort_get); + break; + case TEST_DESC_TYPE_UCHAR_ARRAY_GET: + r = _test_uchar_array_get + (element, itr->name, itr->func.uchar_array_get); + break; + case TEST_DESC_TYPE_ELEMENT_GET: + r = _test_element_get + (element, itr->name, itr->func.element_get); + break; + case TEST_DESC_TYPE_ELEMENTS_GET: + r = _test_elements_get + (element, itr->name, itr->func.elements_get); + break; + case TEST_DESC_TYPE_ELEMENT_GET_GLOBAL: + r = _test_element_get_global + (itr->name, itr->func.element_get_global); + break; + case TEST_DESC_TYPE_ELEMENTS_GET_GLOBAL: + r = _test_elements_get_global + (itr->name, itr->func.elements_get_global); + break; + case TEST_DESC_TYPE_STRING_GET_GLOBAL: + r = _test_string_get_global + (itr->name, itr->func.string_get_global); + break; + case TEST_DESC_TYPE_BOOL_GET_GLOBAL: + r = _test_bool_get_global + (itr->name, itr->func.bool_get_global); + break; + case TEST_DESC_TYPE_STRINGS_GET_GLOBAL: + r = _test_strings_get_global + (itr->name, itr->func.strings_get_global); + break; + default: + ERR("unknown test type %d (%s)", itr->type, itr->name); + r = 0; + break; + } + + if (r || itr->may_fail) + ok++; + else + { + ERR("test failed %s, element %s [%s]", + itr->name, element->path, element->interface); + fail++; + ret = EINA_FALSE; + } + } + + total = ok + failure; + success += ok; + failure += fail; + if (total == 0) + { + INF("no tests for %s [%s]", element->path, element->interface); + return EINA_TRUE; + } + + INF("testing %s success: %d, failure: %d: %d%% [%s]", + element->path, ok, fail, (ok * 100) / total, + element->interface); + + return ret; +} + +static const struct test_desc test_desc_manager[] = { + TEST_DESC_STRING_GET_GLOBAL(e_connman_manager_state_get, 0), + TEST_DESC_BOOL_GET_GLOBAL(e_connman_manager_offline_mode_get, 0), + //TEST_DESC_STRING_SET_GLOBAL(e_connman_manager_request_scan, 0), + //TEST_DESC_BOOL_SET_GLOBAL(e_connman_manager_offline_mode_set, 0), + TEST_DESC_ELEMENTS_GET_GLOBAL(e_connman_manager_profiles_get, 0), + TEST_DESC_ELEMENTS_GET_GLOBAL(e_connman_manager_services_get, 1), + TEST_DESC_STRING_GET_GLOBAL(e_connman_manager_technology_default_get, 0), + TEST_DESC_STRINGS_GET_GLOBAL(e_connman_manager_technologies_available_get, 0), + TEST_DESC_STRINGS_GET_GLOBAL(e_connman_manager_technologies_enabled_get, 0), + TEST_DESC_STRINGS_GET_GLOBAL(e_connman_manager_technologies_connected_get, 0), + TEST_DESC_ELEMENT_GET_GLOBAL(e_connman_manager_profile_active_get, 0), + TEST_DESC_SENTINEL +}; + +static const struct test_desc test_desc_profile[] = { + TEST_DESC_STRING_GET(e_connman_profile_name_get, 1), + //TEST_DESC_STRING_SET(e_connman_profile_name_set, 1), + TEST_DESC_BOOL_GET(e_connman_profile_offline_mode_get, 0), + //TEST_DESC_BOOL_SET(e_connman_profile_offline_mode_set, 0), + TEST_DESC_ELEMENTS_GET(e_connman_profile_services_get, 1), + TEST_DESC_SENTINEL +}; + +static const struct test_desc test_desc_service[] = { + /* TODO: need to check exactly what properties may fail */ + TEST_DESC_STRING_GET(e_connman_service_state_get, 1), + TEST_DESC_STRING_GET(e_connman_service_error_get, 1), + TEST_DESC_STRING_GET(e_connman_service_name_get, 0), + TEST_DESC_STRING_GET(e_connman_service_type_get, 0), + TEST_DESC_STRING_ARRAY_GET(e_connman_service_security_get, 1), + TEST_DESC_STRING_GET(e_connman_service_passphrase_get, 1), + //TEST_DESC_STRING_SET(e_connman_service_passphrase_set, 1), + TEST_DESC_BOOL_GET(e_connman_service_passphrase_required_get, 1), + TEST_DESC_UCHAR_GET(e_connman_service_strength_get, 1), + TEST_DESC_BOOL_GET(e_connman_service_favorite_get, 0), + TEST_DESC_BOOL_GET(e_connman_service_immutable_get, 0), + TEST_DESC_BOOL_GET(e_connman_service_auto_connect_get, 0), + //TEST_DESC_BOOL_SET(e_connman_service_auto_connect_set, 1), + TEST_DESC_BOOL_GET(e_connman_service_roaming_get, 1), + TEST_DESC_STRING_GET(e_connman_service_ipv4_method_get, 1), + TEST_DESC_STRING_GET(e_connman_service_ipv4_address_get, 1), + TEST_DESC_STRING_GET(e_connman_service_ipv4_gateway_get, 1), + TEST_DESC_STRING_GET(e_connman_service_ipv4_netmask_get, 1), + TEST_DESC_STRING_GET(e_connman_service_ipv4_configuration_method_get, 1), + TEST_DESC_STRING_GET(e_connman_service_ipv4_configuration_address_get, 1), + TEST_DESC_STRING_GET(e_connman_service_ipv4_configuration_gateway_get, 1), + TEST_DESC_STRING_GET(e_connman_service_ipv4_configuration_netmask_get, 1), + TEST_DESC_STRING_GET(e_connman_service_ethernet_method_get, 1), + TEST_DESC_STRING_GET(e_connman_service_ethernet_address_get, 1), + TEST_DESC_USHORT_GET(e_connman_service_ethernet_mtu_get, 1), + TEST_DESC_SENTINEL +}; + +static Eina_Bool +_quit(__UNUSED__ void *data) +{ + ecore_main_loop_quit(); + return ECORE_CALLBACK_CANCEL; +} + +static Eina_Bool +_on_exiter(__UNUSED__ void *data) +{ + e_connman_system_shutdown(); + ecore_idle_enterer_add(_quit, NULL); + exiter = NULL; + return ECORE_CALLBACK_CANCEL; +} + +static void +_exiter_reschedule(void) +{ + if (exiter) + ecore_timer_del(exiter); + exiter = ecore_timer_add(10, _on_exiter, NULL); +} + +struct test_element_timer_data +{ + E_Connman_Element *element; + Ecore_Timer *timer; +}; + +static Eina_Bool +_test_element_timer(void *data) +{ + struct test_element_timer_data *d = data; + E_Connman_Element *element = d->element; + + if (e_connman_element_is_profile(element)) + _test_element(element, test_desc_profile); + else if (e_connman_element_is_manager(element)) + _test_element(element, test_desc_manager); + else if (e_connman_element_is_service(element)) + _test_element(element, test_desc_service); + else + ERR("!!! don't know how to test %s [%s]", + element->path, element->interface); + + _exiter_reschedule(); + + d->timer = NULL; + return ECORE_CALLBACK_CANCEL; +} + +static void +_element_listener(void *data, __UNUSED__ const E_Connman_Element *element) +{ + struct test_element_timer_data *d = data; + if (d->timer) + ecore_timer_del(d->timer); + d->timer = ecore_timer_add(1.0, _test_element_timer, d); + _exiter_reschedule(); +} + +static void +_element_listener_free(void *data) +{ + struct test_element_timer_data *d = data; + if (d->timer) + ecore_timer_del(d->timer); + free(d); +} + +static Eina_Bool +_on_element_add(__UNUSED__ void *data, __UNUSED__ int type, void *info) +{ + E_Connman_Element *element = info; + struct test_element_timer_data *d; + + d = malloc(sizeof(*d)); + if (!d) + return ECORE_CALLBACK_PASS_ON; + + d->element = element; + d->timer = ecore_timer_add(1.0, _test_element_timer, d); + e_connman_element_listener_add + (element, _element_listener, d, _element_listener_free); + + return ECORE_CALLBACK_PASS_ON; +} + +static Eina_Bool +_on_element_del(__UNUSED__ void *data, __UNUSED__ int type, __UNUSED__ void *info) +{ + return ECORE_CALLBACK_PASS_ON; +} + +static Eina_Bool +_on_element_updated(__UNUSED__ void *data, __UNUSED__ int type, __UNUSED__ void *info) +{ + return ECORE_CALLBACK_PASS_ON; +} + +int +main(__UNUSED__ int argc, __UNUSED__ char *argv[]) +{ + E_DBus_Connection *c; + int total; + + ecore_init(); + e_dbus_init(); + eina_init(); + + c = e_dbus_bus_get(DBUS_BUS_SYSTEM); + if (!c) { + printf("ERROR: can't connect to system session\n"); + return -1; + } + + e_connman_system_init(c); + ecore_event_handler_add(E_CONNMAN_EVENT_ELEMENT_ADD, _on_element_add, NULL); + ecore_event_handler_add(E_CONNMAN_EVENT_ELEMENT_DEL, _on_element_del, NULL); + ecore_event_handler_add(E_CONNMAN_EVENT_ELEMENT_UPDATED, + _on_element_updated, NULL); + + _exiter_reschedule(); + + ecore_main_loop_begin(); + + e_dbus_connection_close(c); + eina_stringshare_dump(); + eina_shutdown(); + e_dbus_shutdown(); + ecore_shutdown(); + + total = success + failure; + if (total == 0) + fputs("DBG: clean exit, no tests executed.\n", stderr); + else + fprintf(stderr, "DBG: clean exit, success: %d, failure: %d, %d%%\n", + success, failure, (success * 100) / total); + + return 0; +} diff --git a/src/bin/e_dbus_ofono_test.c b/src/bin/e_dbus_ofono_test.c new file mode 100644 index 0000000..f6e22af --- /dev/null +++ b/src/bin/e_dbus_ofono_test.c @@ -0,0 +1,520 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include "E_Ofono.h" + +static void +_method_success_check(void *data, DBusMessage *msg __UNUSED__, DBusError *error) +{ + const char *name = data; + + if ((!error) || (!dbus_error_is_set(error))) + { + printf("SUCCESS: method %s() finished successfully.\n", name); + return; + } + + printf("FAILURE: method %s() finished with error: %s %s\n", + name, error->name, error->message); + dbus_error_free(error); +} + +static void +_elements_print(E_Ofono_Element **elements, unsigned int count) +{ + unsigned int i; + for (i = 0; i < count; i++) + { + printf("--- element %d:\n", i); + e_ofono_element_print(stdout, elements[i]); + } + free(elements); + printf("END: all elements count = %u\n", count); +} + +static Eina_Bool +_on_element_add(void *data __UNUSED__, int type __UNUSED__, void *info) +{ + E_Ofono_Element *element = info; + printf(">>> %s %s\n", element->path, element->interface); + return EINA_TRUE; +} + +static Eina_Bool +_on_element_del(void *data __UNUSED__, int type __UNUSED__, void *info) +{ + E_Ofono_Element *element = info; + printf("<<< %s %s\n", element->path, element->interface); + return EINA_TRUE; +} + +static Eina_Bool +_on_element_updated(void *data __UNUSED__, int type __UNUSED__, void *info) +{ + E_Ofono_Element *element = info; + printf("!!! %s %s\n", element->path, element->interface); + e_ofono_element_print(stderr, element); + return EINA_TRUE; +} +static Eina_Bool +_on_cmd_quit(char *cmd __UNUSED__, char *args __UNUSED__) +{ + fputs("Bye!\n", stderr); + ecore_main_loop_quit(); + return EINA_FALSE; +} + +static Eina_Bool +_on_cmd_sync(char *cmd __UNUSED__, char *args __UNUSED__) +{ + e_ofono_manager_sync_elements(); + return EINA_TRUE; +} + +static char * +_tok(char *p) +{ + p = strchr(p, ' '); + if (!p) + return NULL; + + *p = '\0'; + p++; + while (isspace(*p)) + p++; + if (*p == '\0') + return NULL; + + return p; +} + +static Eina_Bool +_on_cmd_get_all(char *cmd __UNUSED__, char *args) +{ + E_Ofono_Element **elements; + char *type; + unsigned int count; + Eina_Bool ret; + + if (!args) + type = NULL; + else + type = args; + + if (type) + ret = e_ofono_elements_get_all_type(type, &count, &elements); + else + ret = e_ofono_elements_get_all(&count, &elements); + + if (!ret) + fputs("ERROR: could not get elements\n", stderr); + else + { + printf("BEG: all elements type=%s count = %d\n", type, count); + _elements_print(elements, count); + } + + return EINA_TRUE; +} + +static E_Ofono_Element * +_element_from_args(char *interface, char *args, char **next_args) +{ + E_Ofono_Element *element; + + if (!args) + { + fputs("ERROR: missing element path\n", stderr); + *next_args = NULL; + return NULL; + } + + if (!interface) + { + interface = _tok(args); + *next_args = _tok(interface); + } + else + *next_args = _tok(args); + + element = e_ofono_element_get(args, interface); + if (!element) + fprintf(stderr, "ERROR: no element called \"%s %s\".\n", args, interface); + + return element; +} + +static Eina_Bool +_on_cmd_print(char *cmd __UNUSED__, char *args) +{ + char *next_args; + E_Ofono_Element *element = _element_from_args(NULL, args, &next_args); + if (element) + e_ofono_element_print(stdout, element); + return EINA_TRUE; +} + +static Eina_Bool +_on_cmd_get_properties(char *cmd __UNUSED__, char *args) +{ + char *next_args; + E_Ofono_Element *element = _element_from_args(NULL, args, &next_args); + if (element) + e_ofono_element_properties_sync(element); + return EINA_TRUE; +} + +static Eina_Bool +_on_cmd_property_set(char *cmd __UNUSED__, char *args) +{ + char *next_args, *name, *p; + E_Ofono_Element *element = _element_from_args(NULL, args, &next_args); + void *value; + long vlong; + unsigned short vu16; + unsigned int vu32; + int type; + + if (!element) + return EINA_TRUE; + + if (!next_args) + { + fputs("ERROR: missing parameters name, type and value.\n", stderr); + return EINA_TRUE; + } + + name = next_args; + p = _tok(name); + if (!p) + { + fputs("ERROR: missing parameters type and value.\n", stderr); + return EINA_TRUE; + } + + next_args = _tok(p); + if (!next_args) + { + fputs("ERROR: missing parameter value.\n", stderr); + return EINA_TRUE; + } + + type = p[0]; + switch (type) + { + case DBUS_TYPE_BOOLEAN: + vlong = !!atol(next_args); + value = &vlong; + fprintf(stderr, "DBG: boolean is: %ld\n", vlong); + break; + case DBUS_TYPE_UINT16: + vu16 = strtol(next_args, &p, 0); + if (p == next_args) + { + fprintf(stderr, "ERROR: invalid number \"%s\".\n", next_args); + return EINA_TRUE; + } + value = &vu16; + fprintf(stderr, "DBG: u16 is: %hu\n", vu16); + break; + case DBUS_TYPE_UINT32: + vu32 = strtol(next_args, &p, 0); + if (p == next_args) + { + fprintf(stderr, "ERROR: invalid number \"%s\".\n", next_args); + return EINA_TRUE; + } + value = &vu32; + fprintf(stderr, "DBG: u16 is: %u\n", vu32); + break; + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + p = next_args + strlen(next_args); + if (p > next_args) + p--; + while (p > next_args && isspace(*p)) + p--; + if (p <= next_args) + { + fprintf(stderr, "ERROR: invalid string \"%s\".\n", next_args); + } + p[1] = '\0'; + value = next_args; + fprintf(stderr, "DBG: string is: \"%s\"\n", next_args); + break; + default: + fprintf(stderr, "ERROR: don't know how to parse type '%c' (%d)\n", + type, type); + return EINA_TRUE; + } + + fprintf(stderr, "set_property %s [%p] %s %c %p...\n", + args, element, name, type, value); + if (!e_ofono_element_property_set(element, name, type, value)) + fputs("ERROR: error setting property.\n", stderr); + + return EINA_TRUE; +} + +/* Manager Commands */ + +static Eina_Bool +_on_cmd_manager_get(char *cmd __UNUSED__, char *args __UNUSED__) +{ + E_Ofono_Element *element; + element = e_ofono_manager_get(); + e_ofono_element_print(stderr, element); + return EINA_TRUE; +} + +static Eina_Bool +_on_cmd_manager_modems_get(char *cmd __UNUSED__, char *args __UNUSED__) +{ + char *path; + Eina_Array_Iterator iterator; + unsigned int i; + Eina_Array *modems = NULL; + + if(e_ofono_manager_modems_get(&modems)) + { + printf("["); + if (modems) + EINA_ARRAY_ITER_NEXT(modems, i, path, iterator) + printf(" %s", path); + printf(" ]\n"); + } + + return EINA_TRUE; +} + +/* Modem Commands */ + +static Eina_Bool +_on_cmd_modem_set_powered(char *cmd __UNUSED__, char *args) +{ + char *next_args; + Eina_Bool powered; + E_Ofono_Element *element = _element_from_args("org.ofono.Modem", args, &next_args); + + if (!element) + return EINA_TRUE; + + if (!args) + { + fputs("ERROR: missing the powered value\n", stderr); + return EINA_TRUE; + } + + powered = !!atol(next_args); + + if (e_ofono_modem_powered_set + (element, powered, _method_success_check, "modem_set_powered")) + printf(":::Modem %s Powered set to %hhu\n", element->path, powered); + else + fputs("ERROR: can't set Modem Powered\n", stderr); + return EINA_TRUE; +} + +/* SMS Commands */ + +static Eina_Bool +_on_cmd_sms_sca_set(char *cmd __UNUSED__, char *args) +{ + char *next_args, *sca; + E_Ofono_Element *element = _element_from_args("org.ofono.SmsManager", args, + &next_args); + + if (!element) + return EINA_TRUE; + + if (!args) + { + fputs("ERROR: missing service center address\n", stderr); + return EINA_TRUE; + } + + sca = next_args; + if (e_ofono_sms_sca_set(element, sca, _method_success_check, + "sms_sca_set")) + printf(":::Service Center Address on modem %s set to %s\n", + element->path, sca); + else + fputs("ERROR: couldn't change Service Center Address\n", stderr); + + return EINA_TRUE; +} + +static Eina_Bool +_on_cmd_sms_send_message(char *cmd __UNUSED__, char *args) +{ + char *next_args, *number, *message; + E_Ofono_Element *element = _element_from_args("org.ofono.SmsManager", args, + &next_args); + + if (!element) + return EINA_TRUE; + + number = next_args; + if (!number) + { + fputs("ERROR: missing recipient number and message text.\n", stderr); + return EINA_TRUE; + } + + message = _tok(number); + if (!message) + { + fputs("ERROR: missing message text.\n", stderr); + return EINA_TRUE; + } + + if (!e_ofono_sms_send_message(element, number, message, + _method_success_check, "sms_send_message")) + fputs("ERROR: error setting property.\n", stderr); + + return EINA_TRUE; +} + +static Eina_Bool +_on_input(void *data __UNUSED__, Ecore_Fd_Handler *fd_handler) +{ + char buf[256]; + char *cmd, *args; + const struct { + const char *cmd; + Eina_Bool (*cb)(char *cmd, char *args); + } *itr, maps[] = { + {"quit", _on_cmd_quit}, + {"sync", _on_cmd_sync}, + {"get_all", _on_cmd_get_all}, + {"print", _on_cmd_print}, + {"get_properties", _on_cmd_get_properties}, + {"set_property", _on_cmd_property_set}, + {"manager_get", _on_cmd_manager_get}, + {"manager_modems_get", _on_cmd_manager_modems_get}, + {"modem_set_powered", _on_cmd_modem_set_powered}, + {"sms_sca_set", _on_cmd_sms_sca_set}, + {"sms_send_message", _on_cmd_sms_send_message}, + {NULL, NULL} + }; + + + if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_ERROR)) + { + fputs("ERROR: reading from stdin, exit\n", stderr); + return EINA_FALSE; + } + + if (!ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ)) + { + fputs("ERROR: nothing to read?\n", stderr); + return EINA_FALSE; + } + + if (!fgets(buf, sizeof(buf), stdin)) + { + fprintf(stderr, "ERROR: could not read command: %s\n", strerror(errno)); + ecore_main_loop_quit(); + return EINA_FALSE; + } + + cmd = buf; + while (isspace(*cmd)) + cmd++; + + args = strchr(cmd, ' '); + if (args) + { + char *p; + + *args = '\0'; + args++; + + while (isspace(*args)) + args++; + + p = args + strlen(args) - 1; + if (*p == '\n') + *p = '\0'; + } + else + { + char *p; + + p = cmd + strlen(cmd) - 1; + if (*p == '\n') + *p = '\0'; + } + + if (strcmp(cmd, "help") == 0) + { + if (args) + { + printf("Commands with '%s' in the name:\n", args); + for (itr = maps; itr->cmd; itr++) + if (strstr(itr->cmd, args)) + printf("\t%s\n", itr->cmd); + } + else + { + fputs("Commands:\n", stdout); + for (itr = maps; itr->cmd; itr++) + printf("\t%s\n", itr->cmd); + } + fputc('\n', stdout); + return EINA_TRUE; + } + + for (itr = maps; itr->cmd; itr++) + if (strcmp(itr->cmd, cmd) == 0) + return itr->cb(cmd, args); + + printf("unknown command \"%s\", args=%s\n", cmd, args); + return EINA_TRUE; +} + +int +main(int argc __UNUSED__, char *argv[] __UNUSED__) +{ + E_DBus_Connection *c; + + ecore_init(); + e_dbus_init(); + eina_init(); + + c = e_dbus_bus_get(DBUS_BUS_SYSTEM); + if (!c) { + printf("ERROR: can't connect to system session\n"); + return -1; + } + + e_ofono_system_init(c); + + ecore_event_handler_add(E_OFONO_EVENT_ELEMENT_ADD, _on_element_add, NULL); + ecore_event_handler_add(E_OFONO_EVENT_ELEMENT_DEL, _on_element_del, NULL); + ecore_event_handler_add(E_OFONO_EVENT_ELEMENT_UPDATED, + _on_element_updated, NULL); + + ecore_main_fd_handler_add + (0, ECORE_FD_READ | ECORE_FD_ERROR, _on_input, NULL, NULL, NULL); + + ecore_main_loop_begin(); + + e_ofono_system_shutdown(); + + e_dbus_connection_close(c); + eina_shutdown(); + e_dbus_shutdown(); + ecore_shutdown(); + + fputs("DBG: clean exit.\n", stderr); + + return 0; +} diff --git a/src/bin/e_dbus_ukit_test.c b/src/bin/e_dbus_ukit_test.c new file mode 100644 index 0000000..1755444 --- /dev/null +++ b/src/bin/e_dbus_ukit_test.c @@ -0,0 +1,154 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include + +#include +#include + +static E_DBus_Connection *econ = NULL; + +Eina_Bool +print_prop(const Eina_Hash *hash, const void *key, void *data, __UNUSED__ void *fdata) +{ + const Eina_List *strlist, *l; + char *y; + E_Ukit_Property *p = data; + E_Ukit_Properties props; + int err = 0; + + props.properties = (Eina_Hash*)hash; + switch (p->type) + { + case E_UKIT_PROPERTY_TYPE_STRING: + printf("\t%s = [%s]\n", (char*)key, e_ukit_property_string_get(&props, key, &err)); + break; + case E_UKIT_PROPERTY_TYPE_INT: + printf("\t%s = [%d]\n", (char*)key, e_ukit_property_int_get(&props, key, &err)); + break; + case E_UKIT_PROPERTY_TYPE_UINT32: + printf("\t%s = [%llu]\n", (char*)key, (long long unsigned)e_ukit_property_uint32_get(&props, key, &err)); + break; + case E_UKIT_PROPERTY_TYPE_UINT64: + printf("\t%s = [%llu]\n", (char*)key, (long long unsigned)e_ukit_property_uint64_get(&props, key, &err)); + break; + case E_UKIT_PROPERTY_TYPE_INT64: + printf("\t%s = [%lld]\n", (char*)key, (long long int)e_ukit_property_int64_get(&props, key, &err)); + break; + case E_UKIT_PROPERTY_TYPE_BOOL: + printf("\t%s = [%d]\n", (char*)key, e_ukit_property_bool_get(&props, key, &err)); + break; + case E_UKIT_PROPERTY_TYPE_DOUBLE: + printf("\t%s = [%f]\n", (char*)key, e_ukit_property_double_get(&props, key, &err)); + break; + case E_UKIT_PROPERTY_TYPE_STRLIST: + printf("\t%s = [", (char*)key); + strlist = e_ukit_property_strlist_get(&props, key, &err); + EINA_LIST_FOREACH(strlist, l, y) + printf("%s%s", y, (l->next) ? " " : ""); + printf("]\n"); + break; + } + + return EINA_TRUE; +} + +static void +hash_props(void *user_data, void *reply_data, DBusError *error) +{ + printf("%s:\n", (char*)user_data); + E_Ukit_Get_All_Properties_Return *ret = reply_data; + + if (!ret || dbus_error_is_set(error)) + { + free(user_data); + dbus_error_free(error); + return; + } + + eina_hash_foreach(ret->properties, print_prop, NULL); + printf("\n"); + free(user_data); +} + +static void +test_mount(void *user_data, void *reply_data, DBusError *error) +{ + E_Ukit_Get_Property_Return *ret = reply_data; + + if (!ret || dbus_error_is_set(error)) + { + free(user_data); + dbus_error_free(error); + return; + } + + if (ret->val.b) + { + printf("[%s] is mounted!\n\tGrabbing more stats to fill your screen...\n", (char*)user_data); + e_udisks_get_all_properties(econ, user_data, hash_props, strdup(user_data)); + } + else printf("[%s] is not mounted!\n", (char*)user_data); + free(user_data); +} + +static void +print_devs(void *user_data, void *reply_data, DBusError *error) +{ + E_Ukit_Get_All_Devices_Return *ret = reply_data; + Eina_List *l; + char *udi; + + if (!ret || !ret->strings || dbus_error_is_set(error)) + { + free(user_data); + dbus_error_free(error); + return; + } + + EINA_LIST_FOREACH(ret->strings, l, udi) + { + if (!strcmp((char*)user_data, "disks")) + e_udisks_get_property(econ, udi, "DeviceIsMounted", test_mount, strdup(udi)); + else + e_upower_get_all_properties(econ, udi, hash_props, strdup(udi)); + } + free(user_data); +} + +static Eina_Bool +my_quit(__UNUSED__ void *data) +{ + ecore_main_loop_quit(); + return ECORE_CALLBACK_CANCEL; +} + +int main(void) +{ + ecore_init(); + eina_init(); + e_dbus_init(); + e_ukit_init(); + + econ = e_dbus_bus_get(DBUS_BUS_SYSTEM); + if (econ) + { + e_udisks_get_all_devices(econ, print_devs, strdup("disks")); + e_upower_get_all_devices(econ, print_devs, strdup("power")); + } + /*add a short timer to quit to try and ensure that all the tests run*/ + ecore_timer_add(1, my_quit, NULL); + ecore_main_loop_begin(); + + if (econ) e_dbus_connection_close(econ); + e_ukit_shutdown(); + e_dbus_shutdown(); + eina_shutdown(); + ecore_shutdown(); + + return 0; +} diff --git a/src/bin/logo.png b/src/bin/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d42aeb420098578b19a3eab3d6d1e6e73e3277bc GIT binary patch literal 30052 zcmXtA1z1&0x88Ik9RgAUhmiV^M(OTu=@JkSNr59J-Q6kOARS7BNF&|dDWSj}|L3_V zDkz-Ud(T?ytua(pSq2C5DJBF#IC8R*>frYz@IHW!3PA`y{;&)10m6HA8F8rcC*>aa z0M$%EMiP4b_amphC?0$U!%0@}Jp^G9{(DD&(lW@vC(&Hxl%&vpVLd?;K!}OKo`WD7 zNKR5r(_{Y7$NeLb)~`o*tFMfv@iC~Mo{PdoLogU}Wn~J2=`cfx_Gm)Z>0wf$wsaxr z{zS}}@{?n1n#}4_1%bwiF@Zelyy^DYxA!&fap{9U7(d+zkN$aBzWume*IM_V`?1LN z;`^T>#ITib2|J84OWjP$8M7)KZxNgz&CLX#Plo0{wuX~05Q=>t@2`&_VMdj#MM^jt zL=6c}H1#>~=~U<^iZ2`Dh+f_Qxi;{9xZSDm{`>?h+xKp-6PJ>o20|C~IL(2&;(1Dw z4rsButpEHz5{9Qi-w~!FqN7iow}yx>ZZCGI5sHy=`n*rtukqnI;03V?KC?m6l$~A+ zUmcRf0#I+?t#tU1LEDomuiN{bDRq;2LkXFnobB_i;S-@uWz8yMDv0-$*QE_TRAt&7 ze6`r-dEt&dU6=MWQu_LKC;Oe}&8g}8nq089=$rfNl`4c)tC6%1NjsjwJD&WR%527M z{FfDt%kug)W#U8Wgzs)==WXXokSW$V`QS3c_g? zr|Ea2qDhRR$EqJ`0bQWhyhd9nBXauCjekwVnK8Y24(5u(!8&g5!u!jyabvwVJpb-;FFq+g9;s{^GrSkEY+8T4 zwbx8cU44R%nmU`mXi|@rot=FZEck zmcqUsi6=R);HIt;oAwwq*zg?fG6kM(xg||0ZWdfoJopSZPM9@z`_E~p1qn}BSQwc+ ztP46t97FFiN$vZ-YfT0<^t_gkka(k=gJM_fxS)Y!luV8zyqoKKFr%bO6Gr*TnGkXK zZu99&+~6|?_~9WcFCs+C$S9prY&VfgMCt{Ddu@*5LQ$=@rvlKO^G2T~6oP#ERLSe* zRD?E~YM1k(MA772s;YL>Uuz}~LxdbuI2Gg-=XOqxA;K3UE#Pln1N}s5Qh3^|WL1d? zS9FPv%_xM*5mu2`pTN<^u)A>1RT0FZ?OopBLKJ&75}`p{7B;-xZ-WY$f{@4D&Z#9{ z4f_-iN<8Iu{Yvyyxc6*IF(o%fL+RBAD5Y`Pi#6r%sHTODAas}Z^6dDER%WHY+H&prC*i;#q3>P*&%4wcjvZrn4guRW{S^eXS1(x3{(V>r@&0 zI4-w&Cd-+Zqf-Z)lD!kd)Ag4*+?y;q$rf^tBK9njE%nSo8@=!V1!#xmpR?up${Va9 zStj}!6>b-u+xYv50=X|79XE6q`%@*+nk--kpmcNo1eM!}x_StXT=b%2|H07fK}XlVY{}+MZzTDq|y3QR7bE`?L@HQ?~2r?>Yp+x?Xzxy*VSzP-dF{ z>hPAD*FJ?qk4mb`oRr;gx6R{>4wO(7l~C#9Y4?*s@9X0&n+|4l#IN>q1n4v-U2ue7 zpm+!f2tFcgPDFaTf@Q%s_$gm~mDt${s9F_W8BxSC;}I)k>~lCo-`@b&NQMKD^3lye`$2nk*6T ztX$teV>!jr_qp+Ue_6g$%nx0T!F;4SOr7NmN-*IzPsCv ztzsel+99fd`g6!eJ|#f4uLNOrveHOM;b5jBa-#2tBn+eEP3MCbB(lhpfYBALs0|~J z4SSaNDFg8v2Q3fJB@FHec_Hj7r5s_Mot^ndN2`eVBTNq}?iX@u;Ikh+F?k_o^2y(l z>a0$?LSF?P^@jNjIjT!srsYItGsFbi7%UR)nKU_fsnBGP?`DqryCauU^<-@mH7wsi zb_2#ip&6@EgJ$P$ALWhuh0Vw<M19zU-wyWp{Z^h)MsaVn@7tPck>IL|Z3mpXie4i=l=GeR8@b~3xsP%5|O5E8Tu zwSlf2@4C^46C1b-c`!YHz8M=8KDLR7gfw?PB)6_Ub~0DD;|gYol-}~`lKTq7d&gc?a7#V4tWWCwlExz!AMwM9 zEi-p_chfmK|GKzCQDCAlfZ=#591W%hDl|Zv+dqJwtF8dH zx_UqVw#qUj(^QYdpN7bOxviODE>1Vg`?wk2;qzx%Q9&U-Q$A7}2@EMq(45lcn0$h9 zsqQF-Ydg$#ki|sh<>ebU`lBBi86sTpF80RrWrAmEgH5Z!r0;{cA>Eyk^y|%`MAn!v zeHRTmKgQ?Jk?|>Ub|;HQiEGS-N4r;6{EoLr(qA)n@@aW$%IdaPI@Mr5=aMmE zWx&{R1BR&*vY3aY6Rg_vV!M$n*mNsLa~XGgbJMj2bfFK>w3)eiRR~p?Q7bQKKiJ6c zJoRs`F>!E46y)Xe7Rr%mA|u2lQY^I~t^EA_=;Y+WajG)CIz|A7@FdC3kOnIdkUwWK zBv3s(JOmLi7k!aLV`YzGkt7LabV;qF%u9wuPcJT7<&gJPQy&91FX>S8XkaZ|G&Cj@ zrp)q`;u5;5FnFINd~stR9np*T==GM z(eh#4l^c`06*9qr-zJcB;nI+cVu>SHA&&9BTc#{2DOrhlCere`d##!xvai&tuGwVr zi;^$yXC%qNJ>=7_g#~X>W8-B#?m}Vsl|@dT1zn7{XH%?6`@pX7pl9OYSvQ7rB;qME zxq}k-5-;Kzp4uDHV;uTXMm(=Qv3Xt?t;h!I3Yk3ixT=8~>!)L3XgJiVpO8lt-$oGK z9UaLjGK44ZcuHIjAN%DM@vq1*X}(ZXNeMxVB6J=J9Xmsxq`HI+b@agn*ad16%NOhV6ogqKbi<>{lcnK ziJpL3whv&cRIKcioS~6)zW3jZ7svDSy>qjF*%0q^O2w zKR=7J23oi{#LW0RAU3IP3rg$?FQ zM-oU`vIfoKii^I4rogcKBVM8XdC#>4Re03=boHB9UWo%}_yPb2{mu(JDYa;D&P8o0JIfaO}J*U|ljmC#Pa&E3ob{3( zfR27uJd3aInK#9(e~m{%;#TMP_^<>O@BB!8f14luDhx6Kz$I!YhE^hO`)B4`8jU@Z z=jn*utpMt$*w|-ee>|5XgQjEYWgY}Q&!0=Yb?H%I@CmF3jW%F||3}(OR!X=JfKRMf zUOw-#cFw?JgHDCB6)c(wj$wA-O1gu6);2c&bLtHv{s5hVp(X3WPg$EeI5Hw~fA#Cl zC{L>5dSCJtc%$Sp>%mk}8HEXioC!zM%S6?;x$aGDsO3xh?5(YhUxagX6f2k8YL`@o zqvTv)UEvH24kAEpx7+D{<5c*s!FZmJv$>LFo|cot&iigWT5F%^zU;XZVc3uROqk3a z>?Nk|V4Od0pe~jMafg{@2s8t6J(bgVn5XS|lXu4eW|esal+ZeqYXDP}lMH)bE}k*6 zLZ@o^LQYPOln)H^V=%S3)zar!l+t)?mqAO6oQQImt)@*tj|fUQ)GF1WgzihV%Hja% z*idrcJ*et*6f=y8fNrS${+|5=)320;)+_s!&kcC9%bP%fubi%R`xOa&`E_sxcHAvN zvCtyR^4-;ejyOfr+h3J4ZmlB3qQ04eA3kRJJ=}WB%d$`ZoT@bP?guoM5r3JSAMnK$ z+s*5Xi}XkE4PN9VI+i0B@tkx%`nL3%eewX)DWhiLXb(E4r>74{=dpE(eps>R4S0AV z@@}z-%L&|&U+6zAL_AJc>F3Q^1O#r8YR~lqG_cTe!ld&(Ra_+iGWm>4wXgi!CnP-l zkHRJFJwK&X_PTrPlJRBwt9Q$7dvkeGSmtJC>vL06Q)s59ri4_uGUN9h9UU^yUi)US z(FrQNtGl~CdCVQLVtp#5uRl-VFYyylcgvQ55F)dQ70{S323jM2RT>>wH|U$z&6(8A z;gOSj2J&i-XR4=%hT+B!CWv1M4mea* zXqPo&_wF#a=4S32%0Qh-@Bch6^`(3Mi&}vX*I!eaQ8n(rr7%@in#z~agLA=6!LVmQ z3emh7{?H+-)2rU@9`a0l3cp|s&X3OO$%rG$;=J!IR-(b1KEels?Z!?mh%K_K?w`_; zXe7ceZNvRVwW%~vql(eHE2I`-qlA<{pnhZ9^CBtQ&GD?DNKVdiU_fZ z#s_fSvTC+b+(9}{!a;FEWcQHu73K>?G;yROm-B>p9xLQAWpRnswKdbiuS=Fx>vRM3 zWAY4R2iPXaA67ihI}r8HEg16aV=2xd?7S6qU0rnO>)6;>4YBGDKd1^Jg5ttBEf(=3 zn$*PO`YGnJ-)beAu@b@9&9bh$k8u);iSi79McOhIzKqQ>F4L~?qFa9LcBrdnz9FHx zJnkg$MyBm0(=aRTam!XHbaar__6iH7<;EoUa0d8$|Nsz1HM(m z&&NvZz#Lbcs5u^;XEwuCLt5pJdMm2v+5r9CJUV)T^eS6S9V5{)C$&ub%+^rDt;&Ps(O%MF{k(`Qp!35%Lv zTDop3xV+(mD(q&;1!D0Am;+&aj2gP>u0x{e9-)~LWFebKU0~KUM67Bc!>a*n{EI8< zaZu5SY}T+ZzKF6hINbYRmf;Cv&OMNSxW(EO2Xo#c*_d#WNou6??Ug4dkkkg+h0AMD zamBx+p{cxG59iocI}XNy(Df^{(Q$E?`TQO}9-vWwjNy5|tw;}l1#kvw?$t9>0gAyB zlXIcCAJR01^E0xA1k!mlk&I6QV@9ENV-CDwO@|>%XFffmJe0R0p0-p&M>K(=3*tPU z>qA(K9zWy&?YWr3+h&m#w!z4ohf$Y4pCHHb+N_naMqp1=imk7X@e{eMS~I~C(vYv6?L?og z(KUSwE*0~_GebpZF;-s{j;<#vI%H5da>$;Jh=r1}-4Y35G?}SC3uh6RPpFX>6i>H7 zVIY)g{`!t+vnOgI9UY<9nT=NtjdH54EpFt-R)}FBgh_c{P=)y#C3LODPYZu}Ii#XllykDjZ77bgI~; z|1|wF_EvgSF$j8;Q~2)y6^{j)2Yl&-;iGsxiwl87m9?~VaOwcASE@G;bh!i6MtSn! zC4`0v_@FjjPE$+_-Uxss;)9Turi_$w`QXsd-5D4d%3q)_dCEA3xEF#1tI|lCuK43V zcY8$@65`^5>Z6lzeEQQV0Q&w!BJy0EAd}=fk=UY8i+J_)_VxL;0$h0p!!6_BniX=@T=>0dh9>U zS!#bkPC)+gt1)VI!)kFqSxzI+MINHut8rNPN;EzT1ObkJZNLXY2P4p!RlB^2em`l% z>b;Ay^3Nd9`Qrg>e7KlzcuNj++-m)b2>puS#>PfGeEi(VkPsV6e0==3oMifYS9fGd zmI7jUr<;TE0RRM1Vu+FnxLEv9;ApRZgMF4RBpv~>hXJ?Z{pDv&NJ_6%5|}!kfB2VxgE}mVPIgay!JeojKM#Zq&wt#J5$~O zzEL|_@F)2~e;Ra8Kot(kwactdmfPPRFE*R!_Sk+R%Si!DrqO3BQO61e6?NwE&q-%& zdO9nrcap7@TG=!@I=ZNnQ}qSd&O03scL#I8FKFdpg8xk6+E5Dbjnk;mZ|HdS>Xma) zP>>Ywk^71dUxIxgTf)~CWJN|`UHEPEMGF0}`j5lfN&@JtRaO-SjSt3n$c#)(*8M;^SSgHfU-$U|=7QN9 z&#lCKfbATbtVgpt84QTTFm&ypG$5e`eYKt3=I(bc)~oyC1TK%^HK20y1qILFM?VuW z(*T-1yHQI518<)*Wh-F5J}Ozlq1;a+3*q#nMd$U~%1?D&qS%3qbhGxEdRed0amlzP z)bIY_jRCMJE}r%~i-TT}4yf>7EGQQ+rD|O^ze?SjJc@?c0>0?lT*dmgd0D1p(Tyv< ze-K`p_ySL3O}k7>F{-QMdfEHkbg`PbJkTqfp$*(RFdZ_pL^IHYfwq$;hglVIZoj1LikByCu=*N$L*#f?e zHh#`Ba~wgW(jLg?9sy#`E4eWWENDboP6<1F=aa}TRHhZJx`7h2sffp ze_}r~Xms=edoCXPt(8u>t}{$Ppc!0fFq^3k-Ea90bfDXB{M%8y);}|vWt{C^%;9_d zYgwI6lshN53qWfR2ZRSjokh`F>XQ_bJfGE%lsQ{l+mohzB>-b7+`yI`hV41Q04Vkh?FB*X6D*QNN=Ng2%qA;L1PQ+Er$lj-I~8W;~B3&-JU8 z1^L9K{4a35_{v4^Q-u&8hB-QW)a*Q2q2-m8mG?sl%yOV8UL*@M^j6mj8*FZJKsG?5 zSqGh6DBSp!#mU|@=yv(o-8V3V3a|>@dK)zw%Vc*^+8m>nW^IJA{VT7P-$x6tpKDt; zZ}o%m#FK=WfCK&Kyxv0$dbZC96`X~zt^i(U(8%8L=eV`Htxfp9FeH>R(CpS=!?nKh zdD4oCKSqF~kyp_ZE5^m0&X|$~T8DzNqT-ZnbC$i+86F$uRn|KQAO@*U1Y~g>dPxzD+6ufB~iOnpk<#9x*iHL}R zP#6cJ%D73*&VD=t(hJ|WEk1kN#yubZ^Z@5VaiD)9wjg3i>#YkGQQIV9hgvrHYx)k3&*TnQ< z{hxyyUvJKU`|jPlo7-E<9DX7-qD`BH6k<_RK>R$^JW0vP1>WS3*FO|-DR^a?raVB) zQqKEW>2vlE0UJ%*!lU#;LKJ2$3j3)}VtAG6U(HbAIqC$Gk<$6ehBD{p=O(=A?=%dE z=T%`X()#Fqp-&a>!H`>izVZP8#%w0U%38Q(-CS5(ja0NfFjdRT*@D$yTtFFofPi9# zzJd98czOnH@9eC^6#L|;*G|N!n#@V$k5ARl*-zY4sxWV51g;tk%PdB&%b-ENrqBHexX{R@+-wB?HDe#g@HSc z9AOhzWRnxsPU~-59GbSkfBjj+?LB&YY#|)pdyZ!*$rpZ}_zeDOqbWHnaKKZ3w(X#PJeg6n?Fg5&;9RuI2INb>%lW7Lp@*eJ<3sRsLs+tjOsrt*ao^YrCCtZtpHEYwZJN z1SmRwI0Ftx;Rf(&wA8pGbu2v=stGLL#9Hc$ukb)tNFER5%eR<@KjF=ihc&YOdG)c| zvP^9-I29dP(Q{=QPEIE)7ngJOeTKv}+#XqEtHd76ie!XEG^bnkQ6Ky8fBxDmtLpHISieE}ZA&Q;R$T_ba1p?^#KX>%X?*WG z9uMd0CPP0Kejd*El8}v;!jhY#BLO}xMqM{Z!U}9k23L;z5r{mX zh4)V2vHNXd#z)cp=L1?xwk+pXhkW5Y+NyU`+t-CIV!WvU*~{;Kci8CYvr0*CFqj0y zl}o)nJSc-$r3WODPZtJU-NCkK;7(VTz>q7Bz_;L*)v zR5nF>8pg_)d{fX81w3^XOn1$WC0LJB2WLqTlHa_TMo7_|ygpNs>6Sula%5GEO0LP>_%ibB}qR0!NebD$Vt34D}7-cS%((i0oIk|TziAvk2N2KS9_T*XHHIQcC$}9Eo zKxjZU;0MZ;14;PSGO*eSxw*NO)#jm+>{1%F+f#_FLYrb1I4-Zi;t#z*5QIg@?a)U2 zVL`s>nQl@Lm;@HQ>F;4@GMlb$&@dQX9~BuV!xO$r-Z?TYB7Ygc_(=I748ON3W{X@} zl`rFCl~_ITOWBxAQ6AJax=4BjuXtn$W2W>ivhO^barUodX8(!|`dzJtEn+e_eC{Tm zWtNnbv}e>0-iJkfBK*V3-Y7vNmMo-~5R1B;X-{vIb%~7*X|g1iR&=k;it!hc9nl|? zlOt-5pX=+G-CM3T30J*i!X_y2*tY{I};&SsR9rE!uy3Y-1cG`Szl=R;*?c(VlFQ1*oiMu^vt~EpD|{i{!JS`1h7ysmpHCL5Cy%Kr z*j=UZU6c$Xnc(>my5%Lx3Q88&Kh9mFRmFOlAx-J%oK0;ekLDX1 zw9BqIVAh^297lf|k;;lCs03ZdSWJ{+&**u1-2x@=HRyKRfV*%6QbX&if?8s+`!*wK zFA2feBDAcolAO8+wbuH#(Rn7X;s=0)Rr#|lib#@5ib~7O%*@{dF$nWQn&qR-=u4O& z;21yNuY!U{F&iosXFitvQ>db@9`^y1jL6`-3Q43g)EuKGCsU15>c`)^f4HxVr1PE6 zth0S)P#KVZDm-rje4Y%{)-$_oSys|%Utiy&2x)q-wNV)YegP%nUj*m(<{;q+cnPJr zLVY^jzrCbygoEUjW7HU{VVxt~wiCcLbMNi%r;dZ6?;$w%{Q9pA7@w}=2>39&vguM* z7_5s4crkP+#dHfRfTt>_j97tQT~Y5)lL-_F9}hq}GPxJk-LM4&{4{Z^qo`k_LW;7o z=vdcM>}1d(z?P{2Xitt~pmG~dt2%PVcnn}SJ!)Z=gU9sy<AYu zWBALg26^6|_vX(ZG`R)T9AN9^`-zHnCB!qS__9lk%9kSteCvYXRqqV9rh0Z~YY36x zo|n7$z(iWmG8>C=p=amgyDkUKOAr_*zlqwJ^_6dLZ>#6*S3Nt9KU4e8CZfs?C%w=K zD@iYdiUF953h4Pa^YoQ#uTq%HtYn`NYu zY=h!VAoMgq%IHhu3;97%Ali+Tgd`8R@vR2+Hu$m#5eI;bM}Ge<&QFOuYtIXaixpvs z_%{&2a6iDr!U{iEGN{mIGi*Ao1w_}=!@~xc@lM?q+|Fjl2RS)84+K;xZa}19($LVD z?wQorjzY>Y)6;GMeZ9W<{m!#ebR%;X2!;#a-yP5ItLnI8u>%U(Ac496Q+TC$e-tes z4u9e;^=|I&%)rd8xIUb_AceLU-gS!T7Ro2WfY0YDc!Yw2LJD@RigCxs+tGtxRZOow z-l%e>Dn?B11%gbHe?R~}6_t??BO~L`% zz7Pgibs-Z$hW{zfm`kdqnvh9(akk~d+ZDhqjb-HC)XJ-|k|H<&I@b;|7haQQHIm@^ zghIXWvEZJ6Z)&v4`(hFkgJ>m!Gk_6#oqc_>vH}3by9(VJvrC{DWvh~qeD*hs_5=U+ zV)XT68}=&Zcyk%##5Roie*1@~EwCZ`UEaUHo+(z_pc2Ddb>i>35N0D8HXpDSS_gMG zCM6|hw*erVR$^jZE#lt#L{^=wsyCglUW4dXPjz**ST-=a@a?vNQl0~xLaAK0XzJGw zzJR>e0mmx#i3ytgFF|v)mOdh)6pSbE9nWXrb?0zRSo@t1LEyl#x2K0~i~nbMV9#y7 zMVPa6p?b+b;Yn?7ES+qe-n5z8&3SUG4FAxRiDbFlOkk#g&v|aqf zU<{4y zxsuG=e{KE8NbdWo{V0vvy{_IpPZWz~ncs88{C8DiQ0Gjk*6Uk02coY1YcYX}xA1f-T@Jd1NdqKXre7y2Hqz;K=g0Dl2Ci`oDQ z{|h4+Q1*LtLn|?K?|?a2wIm=4zStPD)e9e z0sN#N#2+7!Et{MW@q%$EX1)T6d0}szLm`>nAt5X40huAwKHLQ4Yy;f{GaYY0Gv-ya zLytua4`4;p-uU&#@BRn)WyznYiv*^T@D%PlUvJxrgDSz+&G{Dy$*okcegERVR4A{i zs`3T3`R32;WJ=C%d+x^pUbc!Zl z5+EZ0H@i4;{Sre;oBqj@CyrqJ{E3f?`}^uj+gXHsfZtAh7!$=nlTHuLZ-1;l_e_F-57up6Aq0 zBflhNXH!$+_Gf~xQWgX{2u_k^GZ5=4^}v3osK@jGiOCTr#gu9dMFrL61lBE)MN5Ia zDqEhXQxyh$maAQ#sLHk9szKTy(qWgX_{IO9gro)>Y2kn9*3HA?rT_?8U=$sK;KVVI zfwFR3xAA%tRHX=Nl#4Dhz!O?=b>N8FhynBq)P2K+&8H8*c3!czu;9^_$IkDh%J9Ik%@6B^p%Ek~JCX+}zyT z&imgBDw20ozzFL?!6Gr__x{bM5zEleiEYnopbv=Y&wq>E0mrVWRpzk>sqo09|7xYhO?lfw4DKs38Iz+>I1WVon6?{*{U|wE07e$6a z^&&TT4@~yakdP2^?Cxq}IZ>eg)_s=vjC({w@Blu$&(U`JeGuS*Uwo;GW`RM(+|ELx zbGLOokSnkogj+zJd7K5Go}QM12*`0ZkYr(`_>Bukwm{XVSJ%*}Q$6&yORlyy?n{F0 zv@n;t^2El*Hh|W2PTcRjyoEpl@c==!4@|_3-s`r@2?Zq3f7{6!meN@g2udXdg{Y~i zpAgJ-?Sat;M*6Y5Ko+7j2H0IsJ;4rsOXRfxTi4U(2`HzOeQyuVgNSa2C!k8D=L8Qx zSv~}+;%}kN`>3uiQAXh2QZpDa4jUop{~R$u5^V*+yjE~dVQk2h&}YQ1;nggd_;3Ab zQW)wOBV{bocpdgvKxpIl+S-~DXy@wjrev9e7I%L@#?c+@>S!b&KKlH)Xv-Pn8#hqp zbJ-ji(XaUGt4H=Pp8@!_8n^{$#g;lO{cof$^;~VuUP4R2i)vhIeqW@aN0tcM(scCe zhbHCkMmB<75oBEJiC}QO7#tnF76iK`LwU>NG9`s9FgSP}xI4on?1oL~3{)-($Y0#| zCM->Whc#8pZwW?Sjm2Qxso}4DJI~0JYT-!%JR%C=cM(lR#EM|DUeGAaO0- z4oaZ^_u*W(@LO$OF+99y&p`k3GXb(~m4+7=zR5FSV#NXjVkMD5UAJO}3y6yk2n=;k zwa|4L=+*%&IrDXNJmzs99<||ls0Tr$Ib_u!qMQ^E1v7CF6Z@@SJHaMR5%K2Dv{2-Vq|(84EIyg3SXc%fATKX) z!mLfXX!3F+`ZaDc0#U2W){sVMXedDB=AotEFR#P0@*II1X4vVQc?7;FowA>*)9=w2 zGm;#ne6E2?PcIOKrQ!8XULF&O@w@k613r@?*LpqlL*t(%HmIkkN8Lid3mha3g{+<4 zLwmC+!Bzkr;{P>B)g+cLY=> zD{1g zIzR}3I_;8)#VMIC(69MPH2TJ$?UR^y7Ac>~YN7#)T1{?J%a% z3(lvJpxN}da!0v2a)qOGp&%n8{{=^R3Lyr>QF~tMd}s@T!{JaiNb&KpjJlMSYlJa? z)FC(o~h|#$876wIphQ=r-#lVoU#iXk`O>+pszgL zFROAK92{hJuBsOoxHk~AY1Y6Hl|KqQ0i#VHXNY|t*`yUH^|Xa&U?PGOGG!JYhlhID z5{;{<5HV8ETc3iSXMBZsAy8bUO}A8$DK0MO#Hs=Ey&f-DgV^NI*^_Etc8 zm|m58ea5jyH%j)CQY03zqm3*UOBOl0K~{Z^{Jmf%jE!ya#kfnDcm@*u9vqa?I9_i5 zQyOA<%(3{Bm?ySln?Rb>(K)m4XY!iGfr{QLkblqggu zmYWC5>dbXbgiw3QpErZOiv~tzW?o+2wk=QESK^o6T2sP|?k`@vzya$L1Emw)Bd4US z{LtOsFEay~ai_+_#^NIdJq6LTHwwX~AEhuv=;@JwKMWb(nt$~1iOtOPG04LuNmSzv z{fzAg-~&1Ecuhf`{_ZOs#ZD1I+@;{?#VH5_g1DAE$sgc59j&wH=Hx(N9W;gY5CjlV zX#{;n&ZbL`m#3%J$ml4?G=AB17?{)O{;Dtpl8PC;=c2A4d$rv8@z$ju73b-XNgE!A z2iwCuKW{IunNO^BhHBZB#_grENxf!gs&RD|Mp4LO=&FmJtrL3k6U`!3|WkU(-@IYz4a9 z5u!n#$8G-p!@L|&3YJcA0Rf@d+y6GFz7va3;>si`b`n3yTecdKSVe$?EFUpK($rjJ zvR+sr991W&e&4B3$~2o8CNBeI2W}SCq=YFE*5pQR2^)=1>3xt6YHlqwsG&{BrB$60 zD6W_l8?jzck?32^hSfAAlicc6NrR5EH7yttDdN^&bLr*dD!>`t$0q2Fj#K>+u}2Rw zmS8~Bvun`LaeZpu$(>{>4JIEV)C$vKZen#~Eh^$rYAwJhDTKU{b#Tiy5>eQ)b~G&i zG6^P&t&S%BGH6CO!oVDU2ErJ?z_90FcxXdBBOTeUTa`b)z{z@rQUlxn7%_4C%=rCv z1Tc$mFn!PD1&BPdb{^d(b9kPX36n9(CKYOl4=WNSF?>s$KKfAyg`1MaBdfB(2e^~g z7#QyQSjL}z?mlZ*5AgK#9JArcR6WCcS%NLiG&`D%_bBdfDWT?e=gN@QPy*!fR%TI@ zgbl9$hVY$ft;o+mv2tj!Ep9FVKhTlIaBt_bSoF6P!@~2hx(}1hQmhOkw;+bV!HGSI z-?hhmtQ5dnWr}B^-Z;!KN-A`WM5FczqsxS|%Z6trkkz@4SS9u)Nuh-F6oHK3-PE^4 zQ-|)!G46nG5OA#g5gX|?7a-qY*7pPsuEYs8BuXEjFgE~htyX2+e&q2rZ)V~;ba;Y| z-Yf+Y4rHaa3)$FoaWspywqI-m=hyxO+#F= zZ+kJfdfD`C>SI$CK3w+!Xq^}y9X(oX0UsRaF2jEDpSr)jI*NdmpIwHd5yKq!Y%@-; zmKR#$)eJacI#>rF=iDP+R{p z-Ey$OK~8-=I%`i5bTRMj?DX?%I;U-s>%?q~sGw}Hm(UR3mP*L4;e`CDD6)IOKv z`Atpy2yjmGz6c_sWaCpYXR9+*C@sdfk=~lMDemysKNe13t3IS<)MeDK30;EOJi0ei zkq!=SV4bgjl`Kouie2&pIr>0wmi7zKa__Z>c0}1#@;FH#9iY<<0Q5z53r^pmfV4J& z189<%#>U1KB^9vDJz-WpBnr82I^i?r&Q7z#4o&MJHDoHpf};JRPB>rPTr) zTI(N+d_hM?CwOpmSN(5eEOq*I+Hl5-074oBg4W|my~oI|1DKhj*-tPq$bs-0KLkA2t-RUdoBZ`(9gbueZYnF zt3n6jO+PXpdAh*mN~u~}^k|*W7`!v0nV%KS^DRZvIr>is;qo0Z?WL4%;pAT2ATJ$e zr3Pnr|d08Rq$pdllpgA+H4GD()+c@IZozpaxV@v5~6zaNL1={QsYdXx(8AOmz7 zRFqwy~8IW zR`3Z6`bal`9f(l;?*w#mR|Ys#jLwp{4>L^6?6I5+*i8-ot`(MBi;y6h;I_0@$~f&0 zfpfTCRc+_;uS(>#T>l||pL4$#O{HaHy~(BcUj9O+JXaBwa}?&glF^RnR{SasJxgRRm7dfNzbUS$;Qo*B%7G^+#`jT5(`TqroKnBIw^DX)b%x>kVX~b5g^g z*Y@0A?D(o~Q5&U-AVPEtGH=~LAqZqpNJ7rz_lpWPx$^?T8xaY~3_kKV$;kBF@@+SH zKW#JfBX$#msWOHC?#lo!ssnPLI=UE|{9UpOh_O9di&Oo77U0vk4KgGQl!1_l2p*sp z`Qm@Wv=vmgwNn(B?94H_(*elL8RV3FJohFFrS=LzIBwUl!A=V&fz79oPT|lV)Zi#U zfbJ55Eu?Jnf2{b6-_|nw2!z@N%^W#lcmE?IuVX8CV83q&wJ1wjv`YUWUCFv>0XG~EsRR8b3k3claW&DawZJ#{ce-?t{_g z(p?tf4HAmdAUX5|^qRM@qvhuf7>rsbe~BleC$&i^3Ln2r7IFe~|3uX=JyB+lPQuLK z&F~Kv)kEg{+#i0Fmi%0Uia9s!ts_Dw!p0hWga}chxzC zNApaGVFBaQ{wT-hpBLnJgX%&EDj0G(7mNH5TOya!tmPe>CU5no@HHj(;k4)or2}zZ zem`WATOiUh>HXX%{{$bQBW<_H2B}=?ykpv;6O(*ykk)(8^(T2?-oqfI0K&rd@5-d)XxNdu1k5ME~z(9-7swX{8x(GZ2;#iX%Kh$N94`zP;sF)+``>A4z& zR9l7?KZJ4w1t12(@N@!L`Q;oj!iQNtLt1H9YRbqiq&%vQX&@ab2gKYkcJd^Q9Tk2TxY%doMtW`slsy;yhLf`i$xqT6TiJrR|C;8krqGg!N+Dt? zHp)CdezXf&YA9&xEC#=AobR@R6<{PMJ~2 zWm7gA=%#IGx9e3dqX~8rA7>mJub=WenLJ5gpB_vYCt-DX3%Z9!dwaV8OlazX+DvOk z%!U26QmFY}7)RPAaEj6ZvoxM6hC)=+uGiK zwLe?4aR;=(KZ7(V2G1-&YFy$aKsaMSm+u2X)iKA?Eoo(_6kJ4Le_tQ_v|Yna?xEx+ zZmN8jInc@PT&k}h-{C7Hp@242-V02NJ{QXVK^n4y;GjfN+$2j5-~*y|p7l{=*RYkT zi?Yx8pKTg{_~#K4c4jtlnfFaOPXqG*$iMA--2NZr2 zShvSLN(WT6rJ#e^f`Ei1N*WrPkBIQAhupWA&5&Oo=c$NpE-j6qjctXXG#_T)4AY~i zoJADa|E@9Z@9ljED4;L&eCGtYT%LuC1>x^V;0pIaQB4(SdE zK6dzNBZdJ0IyNjE`F<->;T-Tp(It~!=eVSr)m5tt_*#dEW!JJCr_lf7{4lWgGk8Qqbf-X_>lNvK%*{=*IOSDL zL+uwrss4tB+cRWkWi`?6OwRIKraj{pms5*nC|U0ET9Ug4RR!;&x?{~|H{g=#GNgUH zlSXV_z5xU18Niw)%BqQEH$?C{W@rB)56kqpftc><>N1(Cw5_)6j%MM_8E)G~}1 z9vmF>238_HEhUBE|3g&3`t}AO`Fmth(L5fx$V{|LY*8VSy6?x2AAxFG=qiCGAK!G*6?0)q4O(VX3n z#KAdmp;UW$dd~0t`-f@}mzJgnZNRqLOJ*yZCvt0u)nH*k-!UwXChK&Xb)Qa-kFPp{T~H5m-sRHf=AR$X!)&^|mHO1pUME*dhT#qcf;SG; z!OX^10wDHfX+c5399+KwK2ghUOD6Bi_)Db60TX{>;ma0~{s13gojAzwiu#G4paRZlkcbWcriB(BL#bN9Bk zwP8|(vw*FDYp)4-tcL(|Qy_kTOebWxE4{GeuXU?<)QPylEy;x9?DBc1im&pqRJa+j(w0%gabDddbq!Dw>&qe3D#uZ{9o3h z&;llczJ(5xK~7%Yy%EIj7k|MIp01Fy`$mK&_wOhmvYlY{*oFsU9_V@3QP$oMydJF2 zp`1g}vMqu(u{Z<9k0y;tmiJc<54dZ87HG8=fLf#vsLqgtgfRjb7=3@OH5@?#RU~lC zzuqLt4tC{YWP(TLA9{ISf#g%{K1)4z;YhNbAoT}`Dmm*^O%cAZ7f!)uUs;NKXeX;M z!MWnS(qFCko<;T_H?a&j=SB(XBx+vj>FGJ}@|INT7mdys@oR(2QxLd%9L8qg2_|^JOD_(f;#hsmmcDvP7VfTpOZkrCCxjym&*^9}$O1U2K`{w( z=`2(nXAs(ySz%AXfb3NXf3t`_3{edt6_oIfz z_*YX0D4l9Qy1O;QzYGI&gih-iv@Cy0O|&34ak;fcbR?v={kt~u=TGXNYYkz=Z`Ow7 zw>LL8Pk;n?ks!o~?@Zb^qzx>kH>7-B8G(XuXyeDPUpLkA%fYI4Fa<@~9*6)9(x*i# zph&s^2zdYAz+MoDU!Hw~MVI7bJGjP#k(n)z*;V?MT zbnb&h$KKHJcg^hVY=V3GSXdWxI&3iO!EE_WC~~Wle}??EEc&bmuYiHsR|mDasMl$Q z5nfA=z^VX%t&QduKf+A`VM>GQk%(BJF8S_`L3V{8R z547S{a1zHPEopA>6ShGkW#w_SMLPw4_iw=ay#&r(>n>DP;P6P+HBs1ZxijRFJ)Go) zzt%`dND$J}(vCDQlvGt4PeC2CAq|dmKTyA#!+Dd7$5ZMH@I@PlEv654ch}nZ2-)H~ ztSEZ+8uoynXsfdS<3ak*$y^QF#TergsPZ0Hf?Ftj!{zM|XrL&6H@+Q1x)YxY+$e1S zy9R4(2g%d2PZ&*s4O6Tm?GoOJ0#ln|Gr$6;8DAegHm-szKs z|Bj`KWoi%n&DgozZ+|$Q$s2u)x&-x5i=D6;Xyxo)yjbagjcqptM{Wohyw)GzDCR(b@8LZR^&`Zv zueWloCz-D$jMzwC3_n&c3ihG?tS%ozBf7G(k}_IsboTSjn9cjd?(FOg54!hrYU*t0 z6>LI5bFa==Jtup6`$hOW-th?u)!*vtr`$kFaJjs^Opfkk{TVu)&5#rSwzhhblauelSC`U$^5njpGY|CIo0!BBj!`S_E3hr;MBH{v zF#UdqZ-4so<;$u#d`K}=h9cKmLrozeA*Sm?i!yioR-OH=8)WjZ_;!Wb_#Od!r>~z} zpPfDb2Rx#Uj<6Qh{7X|v<^9RIbjkX_w|JlKL%ugWYFFP>rx57e$Khgffh1dkxUFAz zDF`G*L~zqr+zUreAyKLEFyq@?T46jLEs379U!`^g7NZaqiWw{Pq7 zva(tn?mxrRz$U2LeIDZ1%p$3tEQkjf(UN+qNa+Q!&vDy52q@6?f7FUO`|8yzGx*J& zz(Fe&q=^|7E0FD~!c>Get&)R5u{gCE-)nLB_wU~+bQZ|~zZktP$96jL_m7!m!Sne` z$e+L|swPYOT!v6bvZk9DMB~p7TrBbWOgt~2XegV>Lux>u#lgj8;N5$-T4LEzgcY6$ zc!)B6ZxXH7)5LNaqR4R{p)!y%Ha46fc*nUqtZtG#17Pv0!@m0MBmEaBRG2B$$q$N( ziazv#mha{#HeJR5Og@`}h!E=7ZgZ)EBl+tS@8xe|IhEq1;`j=4ajhpqo8bL%&}!bC z_}%8#*5xL&UC*9Bjn~#rQ&U&}z`@IVW#R38SqEf)G5CoWX+oRD@z6X&hTW_yl!d2=MFwOcRyF z(s=w#sfwss2tvZJF;c9mYPagve~ym29J@B1{ZY5EwLQbF5|}jMvuZkO+`iysWnC5? zlwvjRT-X(~1Cd^QON*>{*P9pPCNks^H?*%CdZ!=5i0H0qX}MfTCO?RPEDf~iYMsAu zuV3G2<%CYs$Dn0IPdN4(=z(ez}sE@p! z`3K9^Pq+6wW6)w_Hni&c=@w7bJ(_IVbU{E@`K+q(LCS3uu4)M2j}VVTCTAW+v;JaKJFam_2VaKqge z7Z*R0Q&N_E2oLAVWk-pplMy0{8xQr}8mHR^B=&xMn4FqY6zYTInPGH%sU%oCxPUsx z~uOD9UXlF65Fr6LWVQC zJm#nVh+lE<&NH<~RhXm|<>aU+@9OWrET8bCY~$mf^N%nuW9Q(QQ_qmP=z%UjLpO7t zd+wE#wnospl`j5&|0zB|OC94p;CpY`TUpiQ6&7-_^I;>%rTO@Fs?|aN;tob3Qb#M%FNts;UrLY&7=+=7kmbvd~_rE{X8y;>rg9v4_|#-+`5Yv2}=t{JDO>^i8(W zm>A4PMY`){6Ly;~U%q^n4q*Ha=v|1O=;{iK(hy~Y@y3};9HNt77EsE{^kJc)PwzxV z<1%*@UP$ur5J3v+CTIpP`$3C6iII4-l_(dlnvw)}(YWrc0u10s7eM=5ZMTQs6dc$y z8`^hEcdf3hY~1N)%h)h~s;l3qrdqRK&WXkmtRZMYFD)U#RS_1pjgb*898y>zZAdMK zSv?gZXx>0U1pFJ03TsSZW#z#SU>atQ&t|T^m3;bS{RnAQfzez+CT|T%Z_6OUy-GDr zTs85_mFtExMo9_J6*N=N&{8yj93Rl;rUE z@h@H=AIgX?S#CB4x-m%O~yda9Ra=~Bfu|m z($dmOVb}O+xJfbZ1Y!yZ5lF0Ey%wVd`S}fc`k5oR#S*0e(lh~%U-CnqX$i|)i2{V~ zGl?)vu_ot5nwdsI$B-!c%Ce7ux8Z9xfQHvDFIdMNKT!w~Ws;f|4fXc)NL_-y@#e>o z%K+>cw?j9HN{~7>mOyF(+@2qC_;SCXY$$<|?J6cU)zEWiRu*;=KXMs`P!Ce1yli(w+)E$4o^_sad;(fJ0B z0K`LCS=k2~A*aVR&IyB7?XYbPs_iBOn)35sL^ZSc<4}@g6tExXd)Xd%G?q3roDa~_ z((XY5Kr2Svz)E3ZLBY6)h{$;OpcnP|-;7^^eP2MF+zFNUbO7cJ4b+8PCv_>w>f08r ze2s$SXw0|$siJ_g(Suel*U<69?vA?fF6J0Wn6@KBLr-LDlFd9lB|u=xW&C!tazr@g z<-1uf!X6^3uo8`8qV?^Xh4 `kPGGuU|L4CGI8lY!k$3V<(&mZ=vuiQ_1>{KXa9cXfFTd%-}Uu3 z=-eu?G_^6^Cr_T_E>CT&t{w|WNlh0FT3K=!#q57+KLSvztG}P;b**8@T?%EkSc8~A z`O-&ghD?7UUgx&d=aP~Pmh~eF?Hc@ny#REmoHu+F!TA|mXsiv^F~s_vuK^fAC+akh zC6sQrjnXWVvAaiBOMnOq4<8&J-d{&&qq!fu8pmq!NFuzPoOXSHPnVwyRE2PcJ1`c{ zL*q|LlCCb}9*!4)Z4(|Io}ik@HS$_mG7Jc*+n^A{f%fp520x|-Pr|w#46xM* zs({9C%oc<&rR{>^wyvnCNUh_kdJsOh7IVO;m7oF-B(%IzRaGrZbXCOoF!TaCJs%yN zkzRa(xUTv&0O!Xi%3oaV!bbfMMIc*usdu@JqHU4TtGu#xaL5Pi0mb3b(N$x9ekVrI zG6!_5ATbjy7+a_0ES0RBsn9Rdc5yjuhs#PUlj-x_h{B83kF;WdFH;VlSUNK7RT*N9 z!&_t1)0g)kpS5Lm%YBGWuHx64dDHQHcoFQ25(xHkiBx#ZKI1;;zkCczSO#&wk467Y(R6D5`Sa%uoSQy?UOTKGc!aCv z_hOSc)%xD-KYEB9aMR$+7zlyXSGh)G2T+BI5(H6`N@aWq4-FlJII;DI$hc$UoI!>= zcd`phOLqfN!)*mwcrENG%%9MJ~90wVXMj+`)c|?pTO*7FLUi2o075K zXAN#nl)jB*@@s(XJG>7Ktxd^WJN}B9$CE?iBf4QU)O251xYbU$#DZ#B-T$5e^$l@S zoL_(bc>OftlaRZ^>=$^Po=*2MFK_yh<}hpeO0FKsTeg2K2<0MH_Uk4X1A9tC{ste&9B8yq7P6QBv|E z55Dz*8FJ!@s6R66+{aG%HbSL#IoUR^qOx+tj@J&BFP^OwJq#6Z@L7SQbEfg*F#rT;m&!@*A^BCv8%UzwL#&aRd(j| z4g>qGa6QNP1(BxXJPkU+Ha`23vBLN+h8F>`c21thFJ4)@|ELk9vd6OH?yb|;*FQuY zN99=qyng=R{lZojJ12}ae1w?Ed5Zlgyy}nTT2%O1lLYN=%$|;JSDs69_zC_b;ojr+ z6Z%g-I9MoflB!f=>vns{orVt>Fp9i)^uo$_%)KyR#IU9z9GKwMVPi z{M{+FK0kj26LE&syMG69I}rO!pE#s#Fzlre7{il+uk(>KeK%- zrnC7oElhatNj`JglCjF23-p#rN(>w!9^sSk61Fk_6in-LGPDpmqQL4}w2Ri3PQwzY zMwB(-Z6_dV&eml>Zbu6OP8MGz`3d9>}I@X9-4;;yFjx?B3}?v2uE>W2Mjm;Ou{D}B=Zx~FP>KE%38+RPm|9T1Crrb zYp96P)rs^9`l#(>ePDLI(}^P3^jl{cH|-r!d1#npdzWnHMYb?vUyXgov~&LHL~2$@ z=4o?{OZ-0Km<^C}yiTKY=(u71rYK$@&7_nDBV0O9r_ddikH^$C9K@4M#vUfrQo#)7&J2QKJ^gJXPh)k=F4}+#d)`i`ZNF5GT)me`cqVVyD3EjR z-{_lNALQ~#3_L6uQN-YEqTmY3IuV4spQl26(5|;Ydsupa#fG8zgC@f z)DVQoigGbisSM2Eq$4Okx%<-duXp}$KnyK3h#=C-G`L`g!o)pZwvmB+BG}@(eX#Z6+zD^Zw8yc#;Cb2Q-WaU5Bwd#Q09`zK$%uC zZamkS5O{BWdfHeFkZ9e6-Zwp)F4ZKguC;p4zjDVc_L}PidhyDxCg5M*skb9Hr&}1Y zbH(0?;&X6!56GPSh=s3;6MHs0h|mul2y0F9f?E^^6ogT7UVTV#a6if7mlA42=crvQ z_lWCS^<;5@3bH#hjJTKgu!tOmBA;Lj7wFE*j2uyMcHAiqE5|TMrDLt(1GF(0*qj)+ zTSw32XQ_$%v{Y9Uw8~tuqAYwmSP2ow_k1ZWVta1mS7P|9UuE;x-7JuGUPYc0HK#gm ze>WG%f}*zqop-nq&yzHNzJX`h^`aVl%%!-ru|}wW-$t`%o1_eFPpH0MLwGZGY4e2I zItZ@}k0+?buE6@GC<)=cKr6r*D zyp?3aqLVz^d>b!VyIMu#$<5KuSUlCC)YwJ7Qu3FYHLlPvs7-7hLjD(}^z-Ve6u2ck zhD)>fSOW3Q7>%-J$~WM5xj#Q75&dqkbLD_2QErw)^${qUy?BtRYr|Q$?@1>^>wEiQ z=DVB-pEqyb#H!^RXzJ^GIDi5_r){p_A{oK0$7IL;py^mrbI0den|*F7zyFWxv-ud+ zyCm%7-GpS?%_apF5s!-IUy~*&^NK!4p0gTe8H=H9jgE5t^E4 zfI6#6eeh660TvnunHf!;D|_87;=IR(up%wu+6!BrEy~i ziX0>GwCBE(vN&2J60wjm2}or&jgn3z+554_u;)9#;yw%{-;XhFuZQek=WZ>hw9 zJN`2;arar8NeA>1YY5?>M`ZyR6X2UYhDf>`FuI>t0y2)U$Y8o6_wWATJSg}QZD{F- zCVLCG$gxd`y@NNTdLbRfhHfT@~05t0&m!OC;_uwXDVhGrh)2hI*IPr$ZH;Yc?46y)XQr{U)_ z7cx%OU9y?(T6jVN^VtoVnR5*k+mqRo0Qq91?pdCmb&Bu>1-7Q*yd~2~R zZ$ulZLNH!hVvn+8CkT#deCBhb3LXSsu6_B4jZtb!imlh-#0v+<`af^*TTjW6qwMjC z85vDHY-}sJ(pM(~ne6yLa|VytT%g~BK#Us`t$Rl9Aa%3(CN6)l)G(OOBLSFU0BHeC zQ5Vpf$DuPNT_6rSho;kuabrk>{kHX==g38Kb8|8taA4X)b^Py-{QwOWl?gn*do+?B z+Liqco`C!^val@80`Fn(=hMd*y;H)daZmIs!w3pMWgk6l?fqGRU2m0)g+tyR+EykG z^Yk8O;Mg6@-{xQbyBz@lkkc>K`!A>Q&j@qNb+4^ne$q9iqYNyWpPw&rKq>bHQ(HJ?Ph(9Mmi7+qVZNCPJa{ z6QOzUq-vSQXNmz1c3a@PbU8mh>n6yLDo8x9Br}W!f5H~L>cQJ-vFk<=^y6oZFm$xp zR94q4Wth@lB!T_JjH`Bae^Wps-wV##<1ZycQ>& zzn5(-#=*n0H37@>3Br>H`HoKJ^VeF3YZ?qjW;uiJVC5fz8~YD8v#lY8_-Fy1tza?> zq~z6~o}CF{tQTI}!3Zvo5RD3d9HC+f{&nee5vO@fDD3I?r`;ANK7al!+3Na8?Wz9p zc?#lNxR2kDB|b%Z-}^`ju0$+7|BLq+G7tmU4}|E2K|nwN2W!k)*MQIE9yz`M71LYY zdweuKo=m|8UEn0W{J!>Diq$GwHXt1ThV~f3?L&3}M*7MJOgB=Q%81EFdndWK_?cqr z4n_^=w>CE$uRsgJM;%!M|Ihlh_80w5j%QPLRh<4ZDCOclL?VU>dGOxSrOu}$@@9L4 z1D`b6w~zT0^V z3~1icRsu<10f~VHnFr>;ZveBn5ne`yQAH5l%0na~J(U?YpLL$<8Jh-dVLDRzKD2pp zN3Ww1F%E>ZEgI!D2gS@9IAyJ@FW#DnV4WA&Z#ko_!^#cD4rxDBT1asHhM5_!Z>1n+ z#}EQo{ew4g4Yf%!lQ9{vn?tDnBxv+mw#E#P!i4Y^02z8|myN`sJ5xw)bO@oVSQ<3J zx0&wT5isHz6Q}8%FxQU268P`41wPr!DwD(!Lu|x9({XoC)OhNfh*9<%+LVaoGDd$Vw zZszpk(h!hmc%xfY1?Xzh(5v^lQVTH!>Xd@vU;4y_wucjUk{;)NJXx^$+H80 zyG3l#K2?t2?jJqoz)xH9tk!vn78YzRPeS+_IATnJt5fg6a^0O?KuQqK5`B8d_x8{Z zf)UO?7Z$F|ACI{((E7D{$NnJk)Y!s_GWZewljlBX&kYs4_!qA{JWkmF!EnMT2u4VR z%{`}KmVG9sd2v%+qICUJetM5n{U0WAC*UplQu?+)BULsZKrA)B4bdo};wfL5lyp`L z7=)9CaLpUp;;iP$3Pjmz+=?$otG`w5-<~=C9*K#G$q&TqLx4qa4c_jwTq}j7yVktC zJYhVM#bB*nseJr-WeNn~@;d+;NP}L3o^(|2)1sd~Ypsp#%zp%LDy96H_sVVX3bhdi zICvWKuB@QD@CFCE76eUUIMN4Wj$2{)B;2oCM*fsOpCg_@GuRTPEz9g!8e}hoQwHu4 zmN!8GNX{;J>rgX?Q_}HV)cDC0*W`s7`?+M4I5}?C<>M%sXv{DSl9Z$*Q8QpfUjgo% z1P%T%O&eb+4551l`ufY|+{!;N&8ctz+UfeWcoTV!}Zo!!s|-;TzhyDzY6d3`AkwU`@H{D17-CL!KuMoeNojC244C`qkBm zm2%FDwy3Q{OS0ee&A>da+CR}C3jz_@9Oz45K})!J#K}7)cSBGT%EBA5KhI0X48HH3 z^F$B{=}wxUZU+-TDkW>i#+Lb1in(@ufJ}S2-Xi&<;rI&f;#5h-pY!K z1`hW}x2V(}b!nccQU>qwPiZgnSf-SaNpo{@KCrc2N4K6A#JEDcc*%6Q__Ex=wrUN? zS#tjq8C9}ba$kzro%ampA7ohZ^))UYW5104C>wn&qDYGBv)mi-l$yDKn@A$~F$6)Q zF({S0_mr!khuQCpA5l*xtV81~$+=8WFQYR_Mj@*FRrWuvjTy>zq;W?rH{B=1B`!k; z;6F@E3$Ne2IVchcU{M;37SC7;rG1PwHfextCjUZ3QsyT}b4@5z;+{YNjkXM9qk=Oy z*E`{FDpB>DHYmCkpiaC}Bfh9(?}XYF|MBO_VL>dZ-mp__IG zc6R22r~5h`L740IIRyCy>}D(QbVl7I(%Yzv>fXibabHV__2+O3b!=YR4#uAk_atf1 z!HbgcBMT3f(%PCVKzJwTZNqa%VSRn-xJHPb>C&)qd1~>h9G7TY8B3ROAlCY(3m7V`)Cg8F{nAxPk^c7=gLOvdsLnXTQ~$YpXX zzL{FPJ!AQrxnqKX<6lKU1`K*uyLNg*`IOVJ6KjJMD{=_;2QOv!nSk$$6Xd z;#kz-)f{t){MJsk$tB6{TkO^qvHm%F-SUFEnGsy#<~t+As3osV|Jyd$x*gXvnKW)y zwpug)Rpi7NW6?WRx}b>r>m0p&nPf8JT6jF|MqZTgc-$m`oZHO3;Mo!l#>uvCbBCjn zyBo?b5K|OCCiXdtg1p>wk)+tieEd-b*J+#CzvwzL4Ik9lpok3b$_o`BUI-;%tRdOE z*}|Njm(}u7(GEgQe9+yjoE&2Kevw3Egj0i(*x178a?_9e%WV#gV){=Ck{r4!9P#h) z5%}y@-%!3$A)5@oNdbzhClLwq$KR3zk9I3Uf=;EGgZv0&c84d&2l0C_wF6%mn-cl@ zlw`<&IjF@+hx|dU{3 zJ?m|medZd-l+TeQ;dl$#a>d1S*(C6_7$<3G#^s!sj+e=Zjgl-0UV(~Gl2?-}eqiGN EKVH2_NB{r; literal 0 HcmV?d00001 diff --git a/src/bin/notification_daemon.c b/src/bin/notification_daemon.c new file mode 100644 index 0000000..aa9f0a6 --- /dev/null +++ b/src/bin/notification_daemon.c @@ -0,0 +1,196 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include + +typedef struct Daemon_Data Daemon_Data; +typedef struct Timer_Data Timer_Data; + +struct Timer_Data +{ + Daemon_Data *d; + E_Notification *n; +}; + +struct Daemon_Data +{ + E_Notification_Daemon *daemon; + Eina_List *open_notes; + + Eina_List *history; + int history_start; + int max_history_length; + int default_timeout; + + int next_id; +}; + +void +daemon_note_close(Daemon_Data *dd, E_Notification *n, int reason) +{ + printf("Close notification #%d\n", e_notification_id_get(n)); + + if (eina_list_data_find(dd->open_notes, n)) + { + dd->open_notes = eina_list_remove(dd->open_notes, n); + e_notification_closed_set(n, 1); + e_notification_daemon_signal_notification_closed(dd->daemon, e_notification_id_get(n), reason); + e_notification_unref(n); + } +} + +Eina_Bool +cb_note_close_timer(void *data) +{ + Timer_Data *td = data; + + if (!e_notification_closed_get(td->n)) + daemon_note_close(td->d, td->n, E_NOTIFICATION_CLOSED_EXPIRED); + + e_notification_unref(td->n); + free(td); + + return ECORE_CALLBACK_CANCEL; +} + +void +daemon_note_show(Daemon_Data *d, E_Notification *n) +{ + e_notification_ref(n); + d->open_notes = eina_list_append(d->open_notes, n); + e_notification_ref(n); + d->history = eina_list_append(d->history, n); + + // adjust history + if ((int) eina_list_count(d->history) > d->max_history_length) + { + E_Notification *old; + old = eina_list_data_get(d->history); + d->history = eina_list_remove_list(d->history, d->history); + d->history_start = e_notification_id_get(old) + 1; + e_notification_unref(old); + } + + { + int timeout; + + timeout = e_notification_timeout_get(n); + Timer_Data *td = calloc(1, sizeof(Timer_Data)); + td->d = d; + e_notification_ref(n); + td->n = n; + ecore_timer_add(timeout == -1 ? d->default_timeout : (float)timeout / 1000, cb_note_close_timer, td); + } + + printf("Received notification from %s:\n%s\n%s\n\n", + e_notification_app_name_get(n), + e_notification_summary_get(n), e_notification_body_get(n) + ); +} + +E_Notification * +daemon_note_open_find(Daemon_Data *d, unsigned int id) +{ + E_Notification *n; + Eina_List *l; + + EINA_LIST_FOREACH(d->open_notes, l, n) + if (e_notification_id_get(n) == id) return n; + + return NULL; +} + +E_Notification * +daemon_note_history_find(Daemon_Data *d, int id) +{ + if (id < d->history_start) return NULL; + + // TODO + + return NULL; +} + + + +int +cb_notify(E_Notification_Daemon *ndaemon, E_Notification *n) +{ + Daemon_Data *dd; + unsigned int replaces_id; + unsigned int new_id; + + dd = e_notification_daemon_data_get(ndaemon); + replaces_id = e_notification_replaces_id_get(n); + if (replaces_id) + { + // close old one flagged as replaced + } + + new_id = dd->next_id++; + e_notification_id_set(n, new_id); + + daemon_note_show(dd, n); + + return new_id; +} + +void +cb_close_notification(E_Notification_Daemon *ndaemon, unsigned int notification_id) +{ + Daemon_Data *dd; + E_Notification *n; + dd = e_notification_daemon_data_get(ndaemon); + n = daemon_note_open_find(dd, notification_id); + if (n) + daemon_note_close(dd, n, E_NOTIFICATION_CLOSED_REQUESTED); + // else send error? +} + + +int +main() +{ + E_Notification_Daemon *d; + E_Notification *n; + Daemon_Data *dd; + + + ecore_init(); + + dd = calloc(1, sizeof(Daemon_Data)); + dd->open_notes = NULL; + dd->history = NULL; + dd->next_id = dd->history_start = 1; + dd->max_history_length = 5; + dd->default_timeout = 5; + + /* set up the daemon */ + d = e_notification_daemon_add("e_notification_module", "Enlightenment"); + e_notification_daemon_data_set(d, dd); + dd->daemon = d; + e_notification_daemon_callback_notify_set(d, cb_notify); + e_notification_daemon_callback_close_notification_set(d, cb_close_notification); + + ecore_main_loop_begin(); + while (dd->open_notes) + { + n = eina_list_data_get(dd->open_notes); + e_notification_unref(n); + dd->open_notes = eina_list_remove_list(dd->open_notes, dd->open_notes); + } + while (dd->history) + { + n = eina_list_data_get(dd->history); + e_notification_unref(n); + dd->history = eina_list_remove_list(dd->history, dd->history); + } + free(dd); + e_notification_daemon_free(d); + ecore_shutdown(); + + return 0; +} diff --git a/src/bin/notify-send.c b/src/bin/notify-send.c new file mode 100644 index 0000000..4c89430 --- /dev/null +++ b/src/bin/notify-send.c @@ -0,0 +1,191 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define S(X) #X + +#define CHECK(X) do { \ + if (!X) \ + { \ + fprintf(stderr, "%s:%i: Error calling %s!\n", __FILE__, __LINE__, S(X)); \ + exit(1); \ + } \ +} while (0) + +void +version(void) +{ + printf("e-notify-send "VERSION"\n"); +} + +void +usage(void) +{ + printf("Usage:\n" + " e-notify-send [OPTION...] [BODY] - create a notification\n" + "\n" + "Help Options:\n" + " -?, --help Show help options\n" + "\n" + "Application Options:\n" + " -n, --name=NAME Specifies the application name to use (default is e-notify-send).\n" + " -u, --urgency=LEVEL Specifies the urgency level (low, normal, critical).\n" + " -t, --expire-time=TIME Specifies the timeout in milliseconds at which to expire the notification.\n" + " -r, --replace=ID Specifies the ID of notification to replace.\n" + " -p, --print-id Prints the ID of notification to STDOUT.\n" + " -i, --icon=ICON Specifies an icon filename or stock icon to display.\n" + " -c, --category=TYPE Specifies the notification category.\n" + " -v, --version Version of the package.\n" + "\n"); +} + +int +read_int_arg(long long *result, const char *name, intmax_t min, intmax_t max) +{ + char *endptr; + + errno = 0; + *result = strtoll(optarg, &endptr, 10); + if ((errno != 0 && *result == 0) || endptr == optarg) + { + fprintf(stderr, "Cannot parse integer value '%s' for %s\n", optarg, name); + return 0; + } + else if (*result > max || *result < min) + { + fprintf(stderr, "Integer value '%s' for %s out of range\n", optarg, name); + return 0; + } + + return 1; +} + +void +send_cb(void *user_data __UNUSED__, void *method_return, DBusError *error __UNUSED__) +{ + E_Notification_Return_Notify *r = method_return; + + if(!r) + return; + + printf("%u\n", r->notification_id ); + + ecore_main_loop_quit(); +} + +int +main(int argc, char **argv) +{ + int ch; + long long value; + int print_id = 0; + E_Notification *n; + + CHECK(eina_init()); + CHECK(ecore_init()); + CHECK(e_notification_init()); + n = e_notification_new(); + e_notification_app_name_set(n, "e-notify-send"); + e_notification_timeout_set(n, -1); + + /* options descriptor */ + static struct option longopts[] = { + { "help", no_argument, NULL, '?' }, + { "name", required_argument, NULL, 'n' }, + { "urgency", required_argument, NULL, 'u' }, + { "expire-time", required_argument, NULL, 't' }, + { "replace", required_argument, NULL, 'r' }, + { "print-id", no_argument, NULL, 'p' }, + { "icon", required_argument, NULL, 'i' }, + { "category", required_argument, NULL, 'c' }, + { "version", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 } + }; + + while ((ch = getopt_long(argc, argv, "p?vn:u:t:r:i:c:", longopts, NULL)) != -1) + switch (ch) { + case '?': + usage(); + return EXIT_SUCCESS; + break; + case 'v': + version(); + return EXIT_SUCCESS; + break; + case 'n': + e_notification_app_name_set(n, optarg); + break; + case 'u': + if (!strcasecmp(optarg, "low")) + e_notification_hint_urgency_set(n, E_NOTIFICATION_URGENCY_LOW); + else if (!strcasecmp(optarg, "normal")) + e_notification_hint_urgency_set(n, E_NOTIFICATION_URGENCY_NORMAL); + else if (!strcasecmp(optarg, "critical")) + e_notification_hint_urgency_set(n, E_NOTIFICATION_URGENCY_CRITICAL); + else + printf("Urgency level must be: low, normal or critical\n"); + break; + case 't': + if (!read_int_arg(&value, "-t", INT_MIN, INT_MAX)) + return EXIT_FAILURE; + else + e_notification_timeout_set(n, (int)value); + break; + case 'r': + if (!read_int_arg(&value, "-r", 0, UINT_MAX)) + return EXIT_FAILURE; + else + e_notification_replaces_id_set(n, (unsigned int)value); + break; + case 'i': + e_notification_app_icon_set(n, optarg); + break; + case 'c': + e_notification_hint_category_set(n, optarg); + break; + case 'p': + print_id = 1; + break; + default: + usage(); + return EXIT_FAILURE; + } + argc -= optind; + argv += optind; + + if (argc < 1) + { + usage(); + return EXIT_FAILURE; + } + + e_notification_summary_set(n, argv[0]); + if (argc > 1) e_notification_body_set(n, argv[1]); + + + if (print_id) + { + e_notification_send(n, send_cb, NULL); + ecore_main_loop_begin(); + } + else + e_notification_send(n, NULL, NULL); + + e_notification_unref(n); + e_notification_shutdown(); + ecore_shutdown(); + eina_shutdown(); + + return EXIT_SUCCESS; +} diff --git a/src/bin/notify.c b/src/bin/notify.c new file mode 100644 index 0000000..375c632 --- /dev/null +++ b/src/bin/notify.c @@ -0,0 +1,125 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include +#include +#include +#include +#include + +#define LOGO PACKAGE_DATA_DIR "/logo.png" + +void +cb_sent(void *data __UNUSED__, void *ret, DBusError *err) +{ + E_Notification_Return_Notify *notify; + notify = ret; + if (notify) + { + printf("id: %d\n", notify->notification_id); + } + else if (dbus_error_is_set(err)) + { + printf("Error: %s\n", err->message); + } +} + +Eina_Bool +cb_timer(void *data __UNUSED__) +{ + E_Notification *n; + char buf[1024]; + static int num = 0; + static const char *icons[] = { + "xterm", + "firefox", + "gvim", + "e" + }; + + snprintf(buf, sizeof(buf), "%s says Hello #%d", icons[num % 4], num % 4); + n = e_notification_full_new(icons[num % 4], 0, (icons[num % 4][0] != 'e') ? icons[num % 4] : NULL, "Summary", buf, -1); + + if (!e_notification_app_icon_get(n)) + { + Ecore_Evas *ee; + Evas_Object *img; + E_Notification_Image *i; + ee = ecore_evas_buffer_new(1, 1); + if (ee) + { + img = evas_object_image_add(ecore_evas_get(ee)); + evas_object_image_file_set(img, LOGO, NULL); + if (evas_object_image_load_error_get(img) != EVAS_LOAD_ERROR_NONE) + evas_object_image_file_set(img, "logo.png", NULL); + if (evas_object_image_load_error_get(img) != EVAS_LOAD_ERROR_NONE) + { + fprintf(stderr, "ERROR LOADING IMAGE: %s\n", LOGO); + evas_object_del(img); + img = NULL; + } + else + ecore_evas_manual_render(ee); + + i = e_notification_image_new(); + if (e_notification_image_init(i, img)) + e_notification_hint_image_data_set(n, i); + evas_object_del(img); + ecore_evas_free(ee); + } + } + + e_notification_send(n, cb_sent, NULL); + e_notification_unref(n); + num++; + + return ECORE_CALLBACK_RENEW; +} + +void +cb_action_invoked(void *data __UNUSED__, int type __UNUSED__, void *event) +{ + E_Notification_Event_Action_Invoked *ev; + + ev = event; + printf("Action (%d): %s\n", ev->notification_id, ev->action_id); + free(ev); +} + +void +cb_note_closed(void *data __UNUSED__, int type __UNUSED__, void *event) +{ + E_Notification_Event_Notification_Closed *ev; + static const char *reasons[] = { + "Expired", + "Dismissed", + "Requested", + "Undefined" + }; + + ev = event; + printf("Note %d closed: %s\n", ev->notification_id, reasons[ev->reason]); + free(ev); +} + +int +main() +{ + int ret = 0; + ecore_init(); + ecore_evas_init(); + if (e_notification_init()) + { + ecore_timer_add(1, cb_timer, NULL); + ecore_main_loop_begin(); + e_notification_shutdown(); + } + + ecore_evas_shutdown(); + ecore_shutdown(); + return ret; +} diff --git a/src/bin/performance.c b/src/bin/performance.c new file mode 100644 index 0000000..b1d1607 --- /dev/null +++ b/src/bin/performance.c @@ -0,0 +1,573 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define SPECTATOR_MODE 1//in spectator mode messages are send as signals + +#define SIZE_W 800 +#define SIZE_H 600 + +#define SHIP_SIZE 50 + +#define BUS_NAME_SERVER "com.profusion.benchmak.server" +#define PATH_NAME_SERVER "/com/profusion/benchmak/server" +#define IFACE_NAME_SERVER "com.profusion.benchmak.server.game" + +#define BUS_NAME_CLIENT "com.profusion.benchmark.client" +#define PATH_NAME_CLIENT "/com/profusion/benchmak/client" +#define IFACE_NAME_CLIENT "com.profusion.benchmark.client.game" + +#define MOVEMENT_DELAY 0.01//10ms between movements +#define MOVEMENT_Y 5//increment/decrement ship movement + +#define MOVEMENT_BULLET 10 +#define BULLET_SIZE_W 100 +#define BULLET_SIZE_H 50 + +#define TIMEOUT -1 + +E_DBus_Connection *conn; +E_DBus_Object *obj_path; +Evas_Object *client_ship, *server_ship; +char can_join = 1; +char player; +int player_y_start; +int player_y_end; +int player_x; +int action;//1 up, 2 down + +Evas_Object *win; + +typedef struct _bullet + { + Evas_Object *o; + int x; + int y; + char direction; + } bullet; + + +typedef struct _EDBus_Method + { + char *member; + char *signature; + char *reply_signature; + E_DBus_Method_Cb func; + char *c_symbol; + int annotations; + } EDBus_Method; + +typedef struct _EDBus_Signal + { + char *name; + char *signature; + char *c_symbol; + int annotations; + } EDBus_Signal; + +static void obj_register(char *path_name, char *iface_name, EDBus_Method *methods, EDBus_Signal *signals); + +static Eina_Bool _move_bullet(void *context) +{ + bullet *b = context; + + if (b->direction == '+') + { + b->x += MOVEMENT_BULLET; + evas_object_move(b->o, b->x, b->y); + if ((b->x + MOVEMENT_BULLET) < SIZE_W) + return ECORE_CALLBACK_RENEW; + } + else + { + b->x -= MOVEMENT_BULLET; + evas_object_move(b->o, b->x, b->y); + if ((b->x - MOVEMENT_BULLET) > 0) + return ECORE_CALLBACK_RENEW; + } + + evas_object_hide(b->o); + evas_object_del(b->o); + free(b); + return ECORE_CALLBACK_CANCEL; +} + +static void +_shoot(char _player, int x, int y) +{ + bullet *b = malloc(sizeof(bullet)); + + if (_player == 1) b->direction = '+'; + else b->direction = '-'; + + b->x = x; + b->y = y; + + b->o = elm_progressbar_add(win); + elm_progressbar_pulse(b->o, TRUE); + evas_object_resize(b->o, BULLET_SIZE_W, BULLET_SIZE_H); + evas_object_move(b->o, x, y); + + evas_object_show(b->o); + + ecore_timer_add(0.2, _move_bullet, b); +} + +static void +_dbus_error_check(DBusError *error) +{ + if (dbus_error_is_set(error)) + { + printf("dbus error\nName: %s\nDescription: %s\n", error->name, + error->message); + elm_exit(); + } +} + +void +create_ship(Evas_Object **_obj, Evas *canvas, char _player) +{ + Evas_Object *o; + + o = evas_object_polygon_add(canvas); + + if (_player == 1) + { + evas_object_polygon_point_add(o, 0, SIZE_H - SHIP_SIZE); + evas_object_polygon_point_add(o, SHIP_SIZE / 2, SIZE_H - (SHIP_SIZE / 2)); + evas_object_polygon_point_add(o, 0, SIZE_H); + + evas_object_color_set(o, 255, 0, 0, 255); + } + else + { + evas_object_polygon_point_add(o, SIZE_W, 0); + evas_object_polygon_point_add(o, SIZE_W - (SHIP_SIZE / 2), SHIP_SIZE / 2); + evas_object_polygon_point_add(o, SIZE_W, SHIP_SIZE); + + evas_object_color_set(o, 0, 0, 255, 255); + } + evas_object_show(o); + + *_obj = o; +} + +static void +update(char _player, int x, int y) +{ + if (_player == 1) + evas_object_move(server_ship, x, y); + else + evas_object_move(client_ship, x, y); +} + +static void +_cb_move_ack(void *data, DBusMessage *msg, DBusError *error) +{ + _dbus_error_check(error); +} + +#if SPECTATOR_MODE == 1 +static void +send_my_position() +{ + DBusMessage *msg; + + if (player == 1) + msg = dbus_message_new_signal(PATH_NAME_SERVER, IFACE_NAME_SERVER, + "moveOfServer"); + else + msg = dbus_message_new_signal(PATH_NAME_CLIENT, IFACE_NAME_CLIENT, + "moveOfClient"); + + dbus_message_append_args(msg, DBUS_TYPE_INT32, &player_x, DBUS_TYPE_INT32, + &player_y_start, DBUS_TYPE_INVALID); + e_dbus_message_send(conn, msg, _cb_move_ack, TIMEOUT, NULL); + dbus_message_unref(msg); +} +#else +static void +send_my_position() +{ + DBusMessage *msg; + + if (player == 1) + msg = dbus_message_new_method_call(BUS_NAME_CLIENT, PATH_NAME_CLIENT, + IFACE_NAME_CLIENT, "moveOfServer"); + else + msg = dbus_message_new_method_call(BUS_NAME_SERVER, PATH_NAME_SERVER, + IFACE_NAME_SERVER, "moveOfClient"); + + dbus_message_append_args(msg, DBUS_TYPE_INT32, &player_x, DBUS_TYPE_INT32, + &player_y_start, DBUS_TYPE_INVALID); + e_dbus_message_send(conn, msg, _cb_move_ack, TIMEOUT, NULL); + dbus_message_unref(msg); +} +#endif + +static Eina_Bool +_movement(void *data) +{ + if (action == 1) + { + if ((player_y_start - MOVEMENT_Y) >= 0) + { + player_y_start -= MOVEMENT_Y; + player_y_end = player_y_start + SHIP_SIZE; + send_my_position(); + update(player, player_x, player_y_start); + if ((rand() % 6) == 5) + _shoot(player, player_x, player_y_start); + return ECORE_CALLBACK_RENEW; + } + else + { + action = 2; + return _movement(NULL); + } + } + else + { + if ((player_y_end + MOVEMENT_Y) <= SIZE_H) + { + player_y_end += MOVEMENT_Y; + player_y_start = player_y_end - SHIP_SIZE; + send_my_position(); + update(player, player_x, player_y_start); + if ((rand() % 6) == 5) + _shoot(player, player_x, player_y_start); + return ECORE_CALLBACK_RENEW; + } + else + { + action = 1; + return _movement(NULL); + } + } +} + +void +create_auto_move_event(char p) +{ + player = action = p; + player_y_start = 0; + player_y_end = SHIP_SIZE; + + if (player == 1) + { + player_x = 0; + player_y_start = SIZE_H - SHIP_SIZE; + player_y_end = SIZE_H; + } + else + { + player_x = SIZE_W - (SHIP_SIZE / 2); + player_y_start = SIZE_H - SHIP_SIZE; + player_y_end = SIZE_H; + + player_y_start = 0; + player_y_end = SHIP_SIZE; + } + + ecore_timer_add(MOVEMENT_DELAY, _movement, NULL); +} + +static DBusMessage * +_move_(Evas_Object *ship, E_DBus_Object *_obj, DBusMessage *msg) +{ + DBusMessage *reply = dbus_message_new_method_return(msg); + int x, y; + DBusError new_err; + + dbus_error_init(&new_err); + dbus_message_get_args(msg, &new_err, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, + &y, DBUS_TYPE_INVALID); + _dbus_error_check(&new_err); + + evas_object_move(ship, x, y); + + return reply; +} + +#if SPECTATOR_MODE == 1 +static void +_move_client(void *context, DBusMessage *msg) +{ + _move_(client_ship, NULL, msg); +} + +static void +_move_server(void *context, DBusMessage *msg) +{ + _move_(server_ship, NULL, msg); +} +#else +static DBusMessage * +_move_client(E_DBus_Object *obj, DBusMessage *msg) +{ + return _move_(client_ship, obj, msg); +} + +static DBusMessage * +_move_server(E_DBus_Object *obj, DBusMessage *msg) +{ + return _move_(server_ship, obj, msg); +} +#endif + +static DBusMessage * +_can_join(E_DBus_Object *_obj, DBusMessage *msg) +{ + DBusMessage *reply = dbus_message_new_method_return(msg); + + if (can_join) + { + dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &can_join, + DBUS_TYPE_INVALID); + can_join = 0; + +#if SPECTATOR_MODE == 1 + e_dbus_signal_handler_add(conn, BUS_NAME_CLIENT, PATH_NAME_CLIENT, + IFACE_NAME_CLIENT, "moveOfClient", + _move_client, NULL); +#endif + + create_auto_move_event(1); + printf("game started\n"); + } + else + { + dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &can_join, + DBUS_TYPE_INVALID); + } + + return reply; +} + +static void +_cb_dbus_request_name_client(void *context, DBusMessage *msg, DBusError *err) +{ + DBusError new_err; + int flag; + _dbus_error_check(err); + + dbus_error_init(&new_err); + dbus_message_get_args(msg, &new_err, DBUS_TYPE_UINT32, &flag, + DBUS_TYPE_INVALID); + + if (flag == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) + { + EDBus_Method table_methods[] = + { +#if SPECTATOR_MODE == 0 + { "moveOfServer", "ii", "", _move_server, "move_server", 0}, +#endif + { NULL, NULL, NULL, NULL, NULL, 0 } + }; + + EDBus_Signal table_signal[] = + { +#if SPECTATOR_MODE == 1 + { "moveOfClient", "ii", "move_client", 0 }, +#endif + { NULL, NULL, NULL, 0 } + }; + + /*e_dbus_interface_register(conn, PATH_NAME_CLIENT, IFACE_NAME_CLIENT, + table, table_signal, NULL, NULL);*/ + obj_register(PATH_NAME_CLIENT, IFACE_NAME_CLIENT, table_methods, table_signal); + +#if SPECTATOR_MODE == 1 + e_dbus_signal_handler_add(conn, BUS_NAME_SERVER, PATH_NAME_SERVER, + IFACE_NAME_SERVER, "moveOfServer", + _move_server, NULL); +#endif + + create_auto_move_event(2); + printf("game started\n"); + } + else printf("dbus_request_name fail, the client name was already in use.\n"); +} + +static void +_cb_can_join(void *data, DBusMessage *msg, DBusError *error) +{ + char bool; + DBusError new_err; + + _dbus_error_check(error); + + dbus_error_init(&new_err); + dbus_message_get_args(msg, &new_err, DBUS_TYPE_BOOLEAN, &bool, + DBUS_TYPE_INVALID); + _dbus_error_check(&new_err); + + if (bool) + { + e_dbus_request_name(conn, BUS_NAME_CLIENT, DBUS_NAME_FLAG_DO_NOT_QUEUE, + _cb_dbus_request_name_client, NULL); + } + else + { +#if SPECTATOR_MODE == 1 + printf("Spectator mode\n"); + e_dbus_signal_handler_add(conn, BUS_NAME_SERVER, PATH_NAME_SERVER, + IFACE_NAME_SERVER, "moveOfServer", + _move_server, NULL); + e_dbus_signal_handler_add(conn, BUS_NAME_CLIENT, PATH_NAME_CLIENT, + IFACE_NAME_CLIENT, "moveOfClient", + _move_client, NULL); +#else + printf("server full, try later!\n"); + elm_exit(); +#endif + } +} + +static void +_ask_if_can_join() +{ + DBusMessage *msg = + dbus_message_new_method_call(BUS_NAME_SERVER, PATH_NAME_SERVER, + IFACE_NAME_SERVER, "canJoin"); + e_dbus_message_send(conn, msg, _cb_can_join, -1, NULL); + dbus_message_unref(msg); +} + +static void +_cb_dbus_request_name_server(void *context, DBusMessage *msg, DBusError *err) +{ + DBusError new_err; + int flag = 0; + _dbus_error_check(err); + + dbus_error_init(&new_err); + dbus_message_get_args(msg, &new_err, DBUS_TYPE_UINT32, &flag, + DBUS_TYPE_INVALID); + + if (flag == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) + { + EDBus_Method table_methods[] = + { + { "canJoin", "", "b", _can_join, "can_join", 0 }, +#if SPECTATOR_MODE == 0 + { "moveOfClient", "ii", "", _move_client, "move_client", 0}, +#endif + { NULL, NULL, NULL, NULL, NULL, 0 } + }; + + EDBus_Signal table_signal[] = + { +#if SPECTATOR_MODE == 1 + { "moveOfServer", "ii", "move_server", 0 }, +#endif + { NULL, NULL, NULL, 0 } + }; + + /*e_dbus_interface_register(conn, PATH_NAME_SERVER, IFACE_NAME_SERVER, + table, table_signal, NULL, NULL);*/ + obj_register(PATH_NAME_SERVER, IFACE_NAME_SERVER, table_methods, table_signal); + + printf("Waiting for another player\n"); + + } + else _ask_if_can_join(); +} + +static void +obj_register(char *path_name, char *iface_name, EDBus_Method *methods, EDBus_Signal *signals) +{ + obj_path = e_dbus_object_add(conn, path_name, NULL); + E_DBus_Interface *iface = e_dbus_interface_new(iface_name); + const EDBus_Method *_method; + const EDBus_Signal *_signal; + + e_dbus_object_interface_attach(obj_path, iface); + e_dbus_interface_unref(iface); + + for (_method = methods; _method != NULL && _method->member != NULL; _method++) + e_dbus_interface_method_add(iface, _method->member, + _method->signature, + _method->reply_signature, + _method->func); + + for (_signal = signals; _signal != NULL && _signal->name != NULL; _signal++) + e_dbus_interface_signal_add(iface, _signal->name, _signal->signature); +} + +static void +obj_unregister() +{ + if (!obj_path) return; + e_dbus_object_free(obj_path); +} + +static void +_mouse_handler(void *data, Evas *e, Evas_Object *_obj, void *event_info) +{ + Evas_Event_Mouse_Move *mouse_info = event_info; + Evas_Object *square = data; + + evas_object_move(square, mouse_info->cur.canvas.x, mouse_info->cur.canvas.y); +} + +static void +on_done(void *data, Evas_Object *_obj, void *event_info) +{ + elm_exit(); +} + +int +elm_main(int argc, char **argv) +{ + Evas *canvas; + Evas_Object *bg, *square; + srand(time(NULL)); + + e_dbus_init(); + + win = elm_win_add(NULL, "bg-plain", ELM_WIN_BASIC); + elm_win_title_set(win, "Benchmark"); + evas_object_smart_callback_add(win, "delete,request", on_done, NULL); + elm_win_autodel_set(win, EINA_TRUE); + evas_object_resize(win, SIZE_W, SIZE_H); + canvas = evas_object_evas_get(win); + + bg = elm_bg_add(win); + evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + elm_win_resize_object_add(win, bg); + elm_bg_color_set(bg, 255, 255, 255); + evas_object_show(bg); + + square = evas_object_rectangle_add(canvas); + evas_object_resize(square, 30, 30); + evas_object_color_set(square, 0, 255, 0, 255); + evas_object_show(square); + evas_object_event_callback_add(bg, EVAS_CALLBACK_MOUSE_MOVE, _mouse_handler, + square); + + create_ship(&client_ship, canvas, 2); + create_ship(&server_ship, canvas, 1); + + conn = e_dbus_bus_get(DBUS_BUS_SESSION); + + e_dbus_request_name(conn, BUS_NAME_SERVER, DBUS_NAME_FLAG_DO_NOT_QUEUE, + _cb_dbus_request_name_server, NULL); + + evas_object_show(win); + + elm_run(); + elm_shutdown(); + /*e_dbus_interface_unregister(conn, PATH_NAME_CLIENT); + e_dbus_interface_unregister(conn, PATH_NAME_SERVER);*/ + obj_unregister(); + e_dbus_shutdown(); + + return 0; +} +ELM_MAIN() diff --git a/src/bin/test.c b/src/bin/test.c new file mode 100644 index 0000000..3b298c1 --- /dev/null +++ b/src/bin/test.c @@ -0,0 +1,118 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef _WIN32 +# define DBUS_API_SUBJECT_TO_CHANGE +#endif + +#include +#include + +#include + +#include "E_DBus.h" + +void +copy_message(DBusMessageIter *from, DBusMessageIter *to) +{ + int type; + printf(" copy message\n"); + while((type = dbus_message_iter_get_arg_type(from)) != DBUS_TYPE_INVALID) + { + printf(" copy type: %c\n", type); + if (dbus_type_is_basic(type)) + { + /* XXX is int64 big enough to hold all basic types? */ + dbus_int64_t val; + dbus_message_iter_get_basic(from, &val); + dbus_message_iter_append_basic(to, type, &val); + } + else if (dbus_type_is_container(type)) + { + int subtype; + + subtype = dbus_message_iter_get_element_type(from); + if (type == DBUS_TYPE_ARRAY && dbus_type_is_fixed(subtype)) + { + int n; + void *val; + dbus_message_iter_get_fixed_array(from, &val, &n); + dbus_message_iter_append_fixed_array(to, subtype, val, n); + } + else + { + DBusMessageIter fsub, tsub; + char *sig; + dbus_message_iter_recurse(from, &fsub); + sig = dbus_message_iter_get_signature(&fsub); + if (dbus_message_iter_open_container(to, type, sig, &tsub)) + { + copy_message(&fsub, &tsub); + dbus_message_iter_close_container(to, &tsub); + } + else + { + printf("ERR: container open failed\n"); + } + } + } + dbus_message_iter_next(from); + } +} + +DBusMessage * +cb_repeat(E_DBus_Object *obj __UNUSED__, DBusMessage *msg) +{ + DBusMessage *reply; + DBusMessageIter from, to; + + printf("\n\nREPEAT\n--------\n"); + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init(msg, &from); + dbus_message_iter_init_append(reply, &to); + + copy_message(&from, &to); + return reply; +} + +void +cb_request_name(void *data __UNUSED__, DBusMessage *msg __UNUSED__, DBusError *err __UNUSED__) +{ + // XXX check that this actually succeeded and handle errors... + printf("request name\n"); +} + +int +_setup(E_DBus_Connection *conn) +{ + E_DBus_Object *repeater; + E_DBus_Interface *iface; + e_dbus_request_name(conn, "org.e.Repeater", 0, cb_request_name, NULL); + repeater = e_dbus_object_add(conn, "/org/e/Repeater", NULL); + iface = e_dbus_interface_new("org.e.Repeater"); + e_dbus_interface_method_add(iface, "Repeat", NULL, NULL, cb_repeat); + e_dbus_object_interface_attach(repeater, iface); + return 1; +} + +int +main () +{ + E_DBus_Connection *conn; + ecore_init(); + e_dbus_init(); + + conn = e_dbus_bus_get(DBUS_BUS_SESSION); + + if (conn) + { + if (_setup(conn)) ecore_main_loop_begin(); + e_dbus_connection_close(conn); + } + + e_dbus_shutdown(); + ecore_shutdown(); + + return 0; +} diff --git a/src/bin/test_client.c b/src/bin/test_client.c new file mode 100644 index 0000000..48aaf71 --- /dev/null +++ b/src/bin/test_client.c @@ -0,0 +1,75 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include + +#define NUM_LOOPS 10000 + +static dbus_uint32_t msg_num = 0; + +void +cb_reply(void *data __UNUSED__, DBusMessage *reply, DBusError *error) +{ + dbus_uint32_t val; + if (dbus_error_is_set(error)) + { + printf("Error: %s - %s\n", error->name, error->message); + return; + } + + dbus_message_get_args(reply, error, DBUS_TYPE_UINT32, &val, DBUS_TYPE_INVALID); + printf("Received: %d\n", val); + if (val == NUM_LOOPS - 1) ecore_main_loop_quit(); +} + +Eina_Bool +send_message(void *data) +{ + + DBusMessage *msg; + E_DBus_Connection *conn; + + conn = data; + + msg = dbus_message_new_method_call( + "org.e.Repeater", + "/org/e/Repeater", + "org.e.Repeater", + "Repeat" + ); + + dbus_message_append_args(msg, DBUS_TYPE_UINT32, &msg_num, DBUS_TYPE_INVALID); + msg_num++; + e_dbus_message_send(conn, msg, cb_reply, -1, NULL); + dbus_message_unref(msg); + printf("Sent: %d\n", msg_num); + return ECORE_CALLBACK_RENEW; +} + +int +main() +{ + E_DBus_Connection *conn; + int ret = 0; + ecore_init(); + e_dbus_init(); + + conn = e_dbus_bus_get(DBUS_BUS_SESSION); + if (conn) + ecore_timer_add(0.0, send_message, conn); + else + { + printf("Error: could not connect to session bus.\n"); + ret = 1; + } + + ecore_main_loop_begin(); + + e_dbus_shutdown(); + ecore_shutdown(); + return ret; +} diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am new file mode 100644 index 0000000..f4ccefc --- /dev/null +++ b/src/lib/Makefile.am @@ -0,0 +1,27 @@ +MAINTAINERCLEANFILES = Makefile.in + +SUBDIRS = dbus + +if BUILD_EBLUEZ +SUBDIRS += bluez +endif + +if BUILD_ECONNMAN0_7X +SUBDIRS += connman0_7x +endif + +if BUILD_EHAL +SUBDIRS += hal +endif + +if BUILD_ENOTIFY +SUBDIRS += notification +endif + +if BUILD_EOFONO +SUBDIRS += ofono +endif + +if BUILD_EUKIT +SUBDIRS += ukit +endif diff --git a/src/lib/bluez/E_Bluez.h b/src/lib/bluez/E_Bluez.h new file mode 100644 index 0000000..094fd91 --- /dev/null +++ b/src/lib/bluez/E_Bluez.h @@ -0,0 +1,157 @@ +#ifndef E_BLUEZ_H +#define E_BLUEZ_H + +#include +#include + +#include +#include +#include + +/** + * @defgroup EBluez_Group EBluez + * + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Ecore Events */ +extern int E_BLUEZ_EVENT_MANAGER_IN; +extern int E_BLUEZ_EVENT_MANAGER_OUT; +extern int E_BLUEZ_EVENT_ELEMENT_ADD; +extern int E_BLUEZ_EVENT_ELEMENT_DEL; +extern int E_BLUEZ_EVENT_ELEMENT_UPDATED; +extern int E_BLUEZ_EVENT_DEVICE_FOUND; +// TODO: extern int E_BLUEZ_EVENT_DEVICE_DISAPPEARED; + +typedef struct _E_Bluez_Element E_Bluez_Element; +typedef struct _E_Bluez_Array E_Bluez_Array; +typedef struct _E_Bluez_Device_Found E_Bluez_Device_Found; + +struct _E_Bluez_Element +{ + const char *path; + const char *interface; + E_DBus_Signal_Handler *signal_handler; + E_DBus_Signal_Handler *device_found_handler; + Eina_Inlist *props; + + /* private */ + struct + { + Eina_Inlist *properties_get; + Eina_Inlist *property_set; + Eina_Inlist *agent_register; + Eina_Inlist *agent_unregister; + Eina_Inlist *start_discovery; + Eina_Inlist *stop_discovery; + Eina_Inlist *create_paired_device; + } _pending; + struct + { + Ecore_Idler *changed; + } _idler; + Eina_Inlist *_listeners; + int _references; +}; + +struct _E_Bluez_Array +{ + int type; + Eina_Array *array; +}; + +struct _E_Bluez_Device_Found +{ + E_Bluez_Element *adapter; + const char *name; + E_Bluez_Array *array; +}; + +/* General Public API */ +EAPI unsigned int e_bluez_system_init(E_DBus_Connection *edbus_conn) EINA_ARG_NONNULL(1); +EAPI unsigned int e_bluez_system_shutdown(void); + +/* Manager Methods */ +EAPI E_Bluez_Element * e_bluez_manager_get(void) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_manager_default_adapter(E_DBus_Method_Return_Cb cb, void *data) EINA_WARN_UNUSED_RESULT; + +/* Adapter Methods */ +EAPI E_Bluez_Element * e_bluez_adapter_get(const char *path) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_bluez_adapter_agent_register(E_Bluez_Element *element, const char *object_path, const char *capability, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_adapter_agent_unregister(E_Bluez_Element *element, const char *object_path, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_adapter_address_get(const E_Bluez_Element *element, const char **address) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_adapter_name_get(const E_Bluez_Element *element, const char **name) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_adapter_name_set(E_Bluez_Element *element, const char *name, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1,2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_adapter_powered_get(const E_Bluez_Element *element, Eina_Bool *powered) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_adapter_powered_set(E_Bluez_Element *profile, Eina_Bool powered, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_adapter_discoverable_get(const E_Bluez_Element *element, Eina_Bool *discoverable) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_adapter_discoverable_set(E_Bluez_Element *profile, Eina_Bool discoverable, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_adapter_discoverable_timeout_get(const E_Bluez_Element *element, unsigned int *timeout) EINA_ARG_NONNULL(1,2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_adapter_discoverable_timeout_set(E_Bluez_Element *element, unsigned int timeout, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_adapter_discovering_get(const E_Bluez_Element *element, Eina_Bool *discovering) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_adapter_start_discovery(E_Bluez_Element *element, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_adapter_stop_discovery(E_Bluez_Element *element, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_adapter_create_paired_device(E_Bluez_Element *element, const char *object_path, const char *capability, const char *device, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1, 2, 4) EINA_WARN_UNUSED_RESULT; + +/* Device Found Methods */ +EAPI void e_bluez_devicefound_free(E_Bluez_Device_Found *device) EINA_ARG_NONNULL(1); +EAPI const char * e_bluez_devicefound_alias_get(const E_Bluez_Device_Found *device) EINA_ARG_NONNULL(1); + +/* Devices Methods */ +EAPI E_Bluez_Element * e_bluez_device_get(const char *path) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_device_name_get(const E_Bluez_Element *element, const char **name) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_device_alias_get(const E_Bluez_Element *element, const char **alias) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_device_paired_get(const E_Bluez_Element *element, Eina_Bool *paired) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +/* Low-Level API: + * + * Should just be used to work around problems until proper solution + * is made into e_bluez. + */ +EAPI Eina_Bool e_bluez_manager_sync_elements(void); + +EAPI Eina_Bool e_bluez_elements_get_all(unsigned int *count, E_Bluez_Element ***p_elements) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_elements_get_all_type(const char *type, unsigned int *count, E_Bluez_Element ***p_elements) EINA_ARG_NONNULL(1, 2, 3) EINA_WARN_UNUSED_RESULT; +EAPI E_Bluez_Element * e_bluez_element_get(const char *path); + +EAPI void e_bluez_element_listener_add(E_Bluez_Element *element, void (*cb)(void *data, const E_Bluez_Element *element), const void *data, void (*free_data)(void *data)) EINA_ARG_NONNULL(1, 2); +EAPI void e_bluez_element_listener_del(E_Bluez_Element *element, void (*cb)(void *data, const E_Bluez_Element *element), const void *data) EINA_ARG_NONNULL(1, 2); + +EAPI int e_bluez_element_ref(E_Bluez_Element *element) EINA_ARG_NONNULL(1); +EAPI int e_bluez_element_unref(E_Bluez_Element *element) EINA_ARG_NONNULL(1); + +EAPI void e_bluez_element_print(FILE *fp, const E_Bluez_Element *element) EINA_ARG_NONNULL(1, 2); +EAPI void e_bluez_element_array_print(FILE *fp, E_Bluez_Array *array); + +EAPI Eina_Bool e_bluez_element_properties_sync(E_Bluez_Element *element) EINA_ARG_NONNULL(1); +EAPI Eina_Bool e_bluez_element_properties_sync_full(E_Bluez_Element *element, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1); + +EAPI Eina_Bool e_bluez_element_property_set(E_Bluez_Element *element, const char *prop, int type, const void *value) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_element_property_set_full(E_Bluez_Element *element, const char *prop, int type, const void *value, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_element_property_dict_set_full(E_Bluez_Element *element, const char *prop, const char *key, int type, const void *value, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1, 2, 3) EINA_WARN_UNUSED_RESULT; + +EAPI void e_bluez_element_properties_list(const E_Bluez_Element *element, Eina_Bool (*cb)(void *data, const E_Bluez_Element *element, const char *name, int type, const void *value), const void *data) EINA_ARG_NONNULL(1, 2); + +EAPI Eina_Bool e_bluez_element_property_type_get_stringshared(const E_Bluez_Element *element, const char *name, int *type) EINA_ARG_NONNULL(1, 2, 3) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_element_property_type_get(const E_Bluez_Element *element, const char *name, int *type) EINA_ARG_NONNULL(1, 2, 3) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_element_property_dict_get_stringshared(const E_Bluez_Element *element, const char *dict_name, const char *key_name, int *type, void *value) EINA_ARG_NONNULL(1, 2, 4) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_element_property_get_stringshared(const E_Bluez_Element *element, const char *name, int *type, void *value) EINA_ARG_NONNULL(1, 2, 4) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_element_property_get(const E_Bluez_Element *element, const char *name, int *type, void *value) EINA_ARG_NONNULL(1, 2, 4) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_bluez_element_is_adapter(const E_Bluez_Element *element) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_bluez_element_is_device(const E_Bluez_Element *element) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* E_BLUEZ_H */ diff --git a/src/lib/bluez/Makefile.am b/src/lib/bluez/Makefile.am new file mode 100644 index 0000000..8f27943 --- /dev/null +++ b/src/lib/bluez/Makefile.am @@ -0,0 +1,24 @@ +MAINTAINERCLEANFILES = Makefile.in + +AM_CPPFLAGS = \ +-I $(top_srcdir)/src/lib/dbus \ +@EDBUS_CFLAGS@ + +lib_LTLIBRARIES = libebluez.la +includes_HEADERS = E_Bluez.h +includesdir = $(includedir)/e_dbus-@VMAJ@ + +libebluez_la_SOURCES = \ +e_bluez.c \ +e_bluez_element.c \ +e_bluez_manager.c \ +e_bluez_adapter.c \ +e_bluez_device.c \ +e_bluez_devicefound.c + +libebluez_la_LIBADD = \ +@EDBUS_LIBS@ $(top_builddir)/src/lib/dbus/libedbus.la + +libebluez_la_LDFLAGS = -version-info @version_info@ @release_info@ + +EXTRA_DIST = e_bluez_private.h diff --git a/src/lib/bluez/e_bluez.c b/src/lib/bluez/e_bluez.c new file mode 100644 index 0000000..a49793e --- /dev/null +++ b/src/lib/bluez/e_bluez.c @@ -0,0 +1,390 @@ +#include "e_bluez_private.h" +#include +#include + +static E_DBus_Signal_Handler *cb_name_owner_changed = NULL; +static DBusPendingCall *pending_get_name_owner = NULL; +static unsigned int init_count = 0; +static char *unique_name = NULL; + +static const char bus_name[] = "org.bluez"; + +E_DBus_Connection *e_bluez_conn = NULL; + +EAPI int E_BLUEZ_EVENT_MANAGER_IN = 0; +EAPI int E_BLUEZ_EVENT_MANAGER_OUT = 0; +EAPI int E_BLUEZ_EVENT_ELEMENT_ADD = 0; +EAPI int E_BLUEZ_EVENT_ELEMENT_DEL = 0; +EAPI int E_BLUEZ_EVENT_ELEMENT_UPDATED = 0; +EAPI int E_BLUEZ_EVENT_DEVICE_FOUND = 0; + +const char *e_bluez_iface_manager = NULL; +const char *e_bluez_iface_adapter = NULL; +const char *e_bluez_iface_device = NULL; +const char *e_bluez_prop_address = NULL; +const char *e_bluez_prop_name = NULL; +const char *e_bluez_prop_alias = NULL; +const char *e_bluez_prop_class = NULL; +const char *e_bluez_prop_icon = NULL; +const char *e_bluez_prop_paired = NULL; +const char *e_bluez_prop_trusted = NULL; +const char *e_bluez_prop_connected = NULL; +const char *e_bluez_prop_uuids = NULL; +const char *e_bluez_prop_powered = NULL; +const char *e_bluez_prop_discoverable = NULL; +const char *e_bluez_prop_pairable = NULL; +const char *e_bluez_prop_discoverabletimeout = NULL; +const char *e_bluez_prop_pairabletimeout = NULL; +const char *e_bluez_prop_discovering = NULL; +const char *e_bluez_prop_devices = NULL; + +int _e_dbus_bluez_log_dom = -1; + +const char * +e_bluez_system_bus_name_get(void) +{ + return unique_name ? unique_name : bus_name; +} + +/*********************************************************************** +* Manager +***********************************************************************/ + +/* + * FIXME: Do we really need to call Manager.GetProperties()? + */ + +/** + * Synchronize elements with server. + * + * This will call Manager.GetProperties() on server, retrieve properties + * and some element paths and then request their properties. + * + * This call will add events E_BLUEZ_EVENT_ELEMENT_ADD and + * E_BLUEZ_EVENT_ELEMENT_UPDATED to the main loop. + * + * This will not remove stale elements. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_manager_sync_elements(void) +{ + E_Bluez_Element *manager; + + if (!unique_name) + return EINA_FALSE; + + manager = e_bluez_element_register(manager_path, e_bluez_iface_manager); + if (manager) + e_bluez_element_properties_sync(manager); + else + return EINA_FALSE; + + DBG("sync_manager: %s (%s)", unique_name, bus_name); + + return EINA_TRUE; +} + +static void +_e_bluez_system_name_owner_exit(void) +{ + e_bluez_manager_clear_elements(); + ecore_event_add(E_BLUEZ_EVENT_MANAGER_OUT, NULL, NULL, NULL); + + free(unique_name); + unique_name = NULL; +} + +static void +_e_bluez_system_name_owner_enter(const char *uid) +{ + DBG("enter bluez at %s (old was %s)", uid, unique_name); + if (unique_name && strcmp(unique_name, uid) == 0) + { + DBG("same unique_name for bluez, ignore."); + return; + } + + if (unique_name) + _e_bluez_system_name_owner_exit(); + + unique_name = strdup(uid); + + ecore_event_add(E_BLUEZ_EVENT_MANAGER_IN, NULL, NULL, NULL); + e_bluez_manager_sync_elements(); +} + +static void +_e_bluez_system_name_owner_changed(void *data __UNUSED__, DBusMessage *msg) +{ + DBusError err; + const char *name, *from, *to; + + dbus_error_init(&err); + if (!dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &from, + DBUS_TYPE_STRING, &to, + DBUS_TYPE_INVALID)) + { + ERR("could not get NameOwnerChanged arguments: %s: %s", + err.name, err.message); + dbus_error_free(&err); + return; + } + + if (strcmp(name, bus_name) != 0) + return; + + DBG("NameOwnerChanged from=[%s] to=[%s]", from, to); + + if (from[0] == '\0' && to[0] != '\0') + { + _e_bluez_system_name_owner_enter(to); + } + else if (from[0] != '\0' && to[0] == '\0') + { + DBG("exit bluez at %s", from); + if (strcmp(unique_name, from) != 0) + DBG("%s was not the known name %s, ignored.", from, unique_name); + else + _e_bluez_system_name_owner_exit(); + } + else + { + DBG("unknow change from %s to %s", from, to); + } +} + +static void +_e_bluez_get_name_owner(void *data __UNUSED__, DBusMessage *msg, DBusError *err) +{ + DBusMessageIter itr; + int t; + const char *uid; + + pending_get_name_owner = NULL; + + if (!_dbus_callback_check_and_init(msg, &itr, err)) + return; + + t = dbus_message_iter_get_arg_type(&itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_STRING)) + return; + + dbus_message_iter_get_basic(&itr, &uid); + if (!uid) + { + ERR("no name owner!"); + return; + } + + _e_bluez_system_name_owner_enter(uid); + return; +} + +/** + * Initialize E BlueZ (E_Bluez) system. + * + * This will connect and watch org.bluez.Manager and Element + * events and translate to Ecore main loop events, also provide a + * proxy for method invocation on server. + * + * Interesting events are: + * - E_BLUEZ_EVENT_MANAGER_IN: issued when bluez is avaiable. + * - E_BLUEZ_EVENT_MANAGER_OUT: issued when bluez connection is lost. + * - E_BLUEZ_EVENT_ELEMENT_ADD: element was added. + * - E_BLUEZ_EVENT_ELEMENT_DEL: element was deleted. + * - E_BLUEZ_EVENT_ELEMENT_UPDATED: element was updated (properties + * or state changed). + * - E_BLUEZ_EVENT_DEVICE_FOUND: a device was found, raised after calling + * Adapter.StartDiscorvery() + * + * Manager IN/OUT events do not provide any event information, just + * tells you that system is usable or not. After manager is out, all + * elements will be removed, so after this event do not use the system anymore. + * + * Element events will give you an element object. After DEL event callback + * returns, that element will not be valid anymore. + */ +unsigned int +e_bluez_system_init(E_DBus_Connection *edbus_conn) +{ + init_count++; + + if (init_count > 1) + return init_count; + + _e_dbus_bluez_log_dom = eina_log_domain_register + ("e_dbus_bluez", EINA_LOG_DEFAULT_COLOR); + + if(_e_dbus_bluez_log_dom < 0) + { + EINA_LOG_ERR + ("impossible to create a log domain for edbus_bluez module"); + return -1; + } + + if (E_BLUEZ_EVENT_MANAGER_IN == 0) + E_BLUEZ_EVENT_MANAGER_IN = ecore_event_type_new(); + + if (E_BLUEZ_EVENT_MANAGER_OUT == 0) + E_BLUEZ_EVENT_MANAGER_OUT = ecore_event_type_new(); + + if (E_BLUEZ_EVENT_ELEMENT_ADD == 0) + E_BLUEZ_EVENT_ELEMENT_ADD = ecore_event_type_new(); + + if (E_BLUEZ_EVENT_ELEMENT_DEL == 0) + E_BLUEZ_EVENT_ELEMENT_DEL = ecore_event_type_new(); + + if (E_BLUEZ_EVENT_ELEMENT_UPDATED == 0) + E_BLUEZ_EVENT_ELEMENT_UPDATED = ecore_event_type_new(); + + if (E_BLUEZ_EVENT_DEVICE_FOUND == 0) + E_BLUEZ_EVENT_DEVICE_FOUND = ecore_event_type_new(); + + if (!e_bluez_iface_manager) + e_bluez_iface_manager = eina_stringshare_add("org.bluez.Manager"); + + if (!e_bluez_iface_adapter) + e_bluez_iface_adapter = eina_stringshare_add("org.bluez.Adapter"); + + if (!e_bluez_iface_device) + e_bluez_iface_device = eina_stringshare_add("org.bluez.Device"); + + if (!e_bluez_prop_address) + e_bluez_prop_address = eina_stringshare_add("Address"); + + if (!e_bluez_prop_name) + e_bluez_prop_name = eina_stringshare_add("Name"); + + if (!e_bluez_prop_alias) + e_bluez_prop_alias = eina_stringshare_add("Alias"); + + if (!e_bluez_prop_class) + e_bluez_prop_class = eina_stringshare_add("Class"); + + if (!e_bluez_prop_icon) + e_bluez_prop_icon = eina_stringshare_add("Icon"); + + if (!e_bluez_prop_paired) + e_bluez_prop_paired = eina_stringshare_add("Paired"); + + if (!e_bluez_prop_trusted) + e_bluez_prop_trusted = eina_stringshare_add("Trusted"); + + if (!e_bluez_prop_connected) + e_bluez_prop_connected = eina_stringshare_add("Connected"); + + if (!e_bluez_prop_uuids) + e_bluez_prop_uuids = eina_stringshare_add("UUIDs"); + + if (!e_bluez_prop_powered) + e_bluez_prop_powered = eina_stringshare_add("Powered"); + + if (!e_bluez_prop_discoverable) + e_bluez_prop_discoverable = eina_stringshare_add("Discoverable"); + + if (!e_bluez_prop_pairable) + e_bluez_prop_pairable = eina_stringshare_add("Pairable"); + + if (!e_bluez_prop_discoverabletimeout) + e_bluez_prop_discoverabletimeout = eina_stringshare_add("DiscoverableTimeout"); + + if (!e_bluez_prop_pairabletimeout) + e_bluez_prop_pairabletimeout = eina_stringshare_add("PairableTimeout"); + + if (!e_bluez_prop_discovering) + e_bluez_prop_discovering = eina_stringshare_add("Discovering"); + + if (!e_bluez_prop_devices) + e_bluez_prop_devices = eina_stringshare_add("Devices"); + + e_bluez_conn = edbus_conn; + cb_name_owner_changed = e_dbus_signal_handler_add + (e_bluez_conn, E_DBUS_FDO_BUS, E_DBUS_FDO_PATH, E_DBUS_FDO_INTERFACE, "NameOwnerChanged", + _e_bluez_system_name_owner_changed, NULL); + + if (pending_get_name_owner) + dbus_pending_call_cancel(pending_get_name_owner); + + pending_get_name_owner = e_dbus_get_name_owner + (e_bluez_conn, bus_name, _e_bluez_get_name_owner, NULL); + + e_bluez_elements_init(); + + return init_count; +} + +static inline void +_stringshare_del(const char **str) +{ + if (!*str) + return; + + eina_stringshare_del(*str); + *str = NULL; +} + +/** + * Shutdown bluez system. + * + * When count drops to 0 resources will be released and no calls should be + * made anymore. + */ +unsigned int +e_bluez_system_shutdown(void) +{ + if (init_count == 0) + { + ERR("bluez system already shut down."); + return 0; + } + + init_count--; + if (init_count > 0) + return init_count; + + _stringshare_del(&e_bluez_iface_manager); + _stringshare_del(&e_bluez_iface_adapter); + _stringshare_del(&e_bluez_iface_device); + _stringshare_del(&e_bluez_prop_address); + _stringshare_del(&e_bluez_prop_name); + _stringshare_del(&e_bluez_prop_alias); + _stringshare_del(&e_bluez_prop_class); + _stringshare_del(&e_bluez_prop_icon); + _stringshare_del(&e_bluez_prop_paired); + _stringshare_del(&e_bluez_prop_trusted); + _stringshare_del(&e_bluez_prop_connected); + _stringshare_del(&e_bluez_prop_uuids); + _stringshare_del(&e_bluez_prop_powered); + _stringshare_del(&e_bluez_prop_discoverable); + _stringshare_del(&e_bluez_prop_pairable); + _stringshare_del(&e_bluez_prop_discoverabletimeout); + _stringshare_del(&e_bluez_prop_pairabletimeout); + _stringshare_del(&e_bluez_prop_discovering); + _stringshare_del(&e_bluez_prop_devices); + + if (pending_get_name_owner) + { + dbus_pending_call_cancel(pending_get_name_owner); + pending_get_name_owner = NULL; + } + + if (cb_name_owner_changed) + { + e_dbus_signal_handler_del(e_bluez_conn, cb_name_owner_changed); + cb_name_owner_changed = NULL; + } + + if (unique_name) + _e_bluez_system_name_owner_exit(); + + e_bluez_elements_shutdown(); + eina_log_domain_unregister(_e_dbus_bluez_log_dom); + e_bluez_conn = NULL; + + return init_count; +} + diff --git a/src/lib/bluez/e_bluez_adapter.c b/src/lib/bluez/e_bluez_adapter.c new file mode 100644 index 0000000..698cac4 --- /dev/null +++ b/src/lib/bluez/e_bluez_adapter.c @@ -0,0 +1,443 @@ +#include "e_bluez_private.h" + +E_Bluez_Element * +e_bluez_adapter_get(const char *path) +{ + E_Bluez_Element *adapter; + + EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL); + + adapter = e_bluez_element_get(path); + if (!adapter) + return NULL; + + if (!e_bluez_element_is_adapter(adapter)) + { + WRN("path '%s' is not a adapter!", path); + return NULL; + } + + return adapter; +} + +static void +_device_found_callback(void *data, DBusMessage *msg) +{ + E_Bluez_Element *element = (E_Bluez_Element *)data; + E_Bluez_Device_Found *device; + DBusMessageIter itr; + int t; + char *name = NULL; + void *value = NULL; + + DBG("Device found %s", element->path); + + if (!_dbus_callback_check_and_init(msg, &itr, NULL)) + return; + + device = calloc(sizeof(E_Bluez_Device_Found), 1); + if (!device) + { + ERR("No memory to alocate E_Bluez_Device_Found"); + return; + } + + t = dbus_message_iter_get_arg_type(&itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_STRING)) + { + ERR("missing device name in DeviceFound"); + return; + } + + dbus_message_iter_get_basic(&itr, &name); + + dbus_message_iter_next(&itr); + t = dbus_message_iter_get_arg_type(&itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_ARRAY)) + { + ERR("missing array in DeviceFound"); + return; + } + + value = e_bluez_element_iter_get_array(&itr, name); + + if (!value) + return; + + device->name = eina_stringshare_add(name); + device->adapter = element; + device->array = value; + + ecore_event_add(E_BLUEZ_EVENT_DEVICE_FOUND, device, NULL, NULL); +} + +/** + * Register new agent for handling user requests. + * + * Call method RegisterAgent(object) on server in order to + * register new agent for handling user requests. + * + * @param element adapter's element + * @param object_path object to be registered. + * @param capability input/output agent capabilities + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_adapter_agent_register(E_Bluez_Element *element, const char *object_path, const char *capability, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "RegisterAgent"; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(object_path, EINA_FALSE); + + return e_bluez_element_call_with_path_and_string + (element, name, object_path, capability, NULL, + &element->_pending.agent_register, cb, data); +} + +/** + * Unregister an existing agent. + * + * Call method UnregisterAgent(object) on server in order to + * unregister an existing agent. + * + * @param element adapter's element + * @param object_path agent to be unregistered. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_adapter_agent_unregister(E_Bluez_Element *element, const char *object_path, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "UnregisterAgent"; + + EINA_SAFETY_ON_NULL_RETURN_VAL(object_path, EINA_FALSE); + + return e_bluez_element_call_with_path + (element, name, object_path, NULL, + &element->_pending.agent_unregister, cb, data); +} + +/** + * Get property "Address" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param address where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_adapter_address_get(const E_Bluez_Element *element, const char **address) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINA_FALSE); + + return e_bluez_element_property_get_stringshared + (element, e_bluez_prop_address, NULL, address); +} + +/** + * Get property "Name" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param name where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_adapter_name_get(const E_Bluez_Element *element, const char **name) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); + + return e_bluez_element_property_get_stringshared + (element, e_bluez_prop_name, NULL, name); +} + +/** + * Call method SetProperty("Name", name) at the given element on server. + * + * This is a server call, not local, so it may fail and in that case + * no property is updated locally. If the value was set the event + * E_BLUEZ_EVENT_ELEMENT_UPDATED will be added to main loop. + * + * @param name value to set. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * @see e_bluez_adapter_name_get() + */ +Eina_Bool +e_bluez_adapter_name_set(E_Bluez_Element *element, const char *name, E_DBus_Method_Return_Cb cb, const void *data) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + return e_bluez_element_property_set_full + (element, e_bluez_prop_name, DBUS_TYPE_STRING, name, cb, data); +} + +/** + * Get property "Powered" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param powered where to store the property value, must be a pointer + * to booleans (Eina_Bool *). + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * @see e_bluez_adapter_powered_set() + */ +Eina_Bool +e_bluez_adapter_powered_get(const E_Bluez_Element *element, Eina_Bool *powered) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(powered, EINA_FALSE); + + return e_bluez_element_property_get_stringshared + (element, e_bluez_prop_powered, NULL, powered); +} + +/** + * Call method SetProperty("Powered", powered) at the given element on server. + * + * + * @param powered value to set. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_adapter_powered_set(E_Bluez_Element *element, Eina_Bool powered, E_DBus_Method_Return_Cb cb, const void *data) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + return e_bluez_element_property_set_full + (element, e_bluez_prop_powered, DBUS_TYPE_BOOLEAN, + &powered, cb, data); +} + +/** + * Get property "Discoverable" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param discoverable where to store the property value, must be a pointer + * to booleans (Eina_Bool *). + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * @see e_bluez_adapter_discoverable_set() + */ +Eina_Bool +e_bluez_adapter_discoverable_get(const E_Bluez_Element *element, Eina_Bool *discoverable) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(discoverable, EINA_FALSE); + + return e_bluez_element_property_get_stringshared + (element, e_bluez_prop_discoverable, NULL, discoverable); +} + +/** + * Call method SetProperty("Discoverable", discoverable) at the given + * element on server. + * + * @param discoverable value to set. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_adapter_discoverable_set(E_Bluez_Element *element, Eina_Bool discoverable, E_DBus_Method_Return_Cb cb, const void *data) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + return e_bluez_element_property_set_full + (element, e_bluez_prop_discoverable, DBUS_TYPE_BOOLEAN, + &discoverable, cb, data); +} + +/** + * Get property "DiscoverableTimeout" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param adapter path to get property. + * @param discoverable timeout where to store the property value, must be a pointer + * to uint32 (unsigned int *). + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * @see e_bluez_adapter_discoverable_timeout_set() + */ +Eina_Bool +e_bluez_adapter_discoverable_timeout_get(const E_Bluez_Element *element, unsigned int *timeout) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(timeout, EINA_FALSE); + return e_bluez_element_property_get_stringshared + (element, e_bluez_prop_discoverabletimeout, NULL, timeout); +} + +/** + * Call method SetProperty("DiscoverableTimeout", timeout) at the + * given element on server. + * + * This is a server call, not local, so it may fail and in that case + * no property is updated locally. If the value was set the event + * E_BLUEZ_EVENT_ELEMENT_UPDATED will be added to main loop. + * + * @param adapter path to set property. + * @param discoverable timeout value to set. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * @see e_bluez_adapter_discoverable_timeout_get() + */ +Eina_Bool +e_bluez_adapter_discoverable_timeout_set(E_Bluez_Element *element, unsigned int timeout, E_DBus_Method_Return_Cb cb, const void *data) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + return e_bluez_element_property_set_full + (element, e_bluez_prop_discoverabletimeout, DBUS_TYPE_UINT32, + &timeout, cb, data); +} + +/** + * Get property "Discovering" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param discovering where to store the property value, must be a pointer + * to booleans (Eina_Bool *). + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * @see e_bluez_adapter_discovering_set() + */ +Eina_Bool +e_bluez_adapter_discovering_get(const E_Bluez_Element *element, Eina_Bool *discovering) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(discovering, EINA_FALSE); + + return e_bluez_element_property_get_stringshared + (element, e_bluez_prop_discovering, NULL, discovering); +} + +/** + * Start Discovery of Bluetooth Devices + * + * call StartDiscovery() + * + * @param element the adapter's element. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_adapter_start_discovery(E_Bluez_Element *element, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "StartDiscovery"; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + + element->device_found_handler = + e_dbus_signal_handler_add + (e_bluez_conn, e_bluez_system_bus_name_get(), + element->path, element->interface, "DeviceFound", + _device_found_callback, element); + + return e_bluez_element_call_full + (element, name, NULL, &element->_pending.start_discovery, cb, data); +} + +/** + * Stop Discovery of Bluetooth Devices + * + * call StopDiscovery() + * + * @param element the adapter's element. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_adapter_stop_discovery(E_Bluez_Element *element, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "StopDiscovery"; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + + return e_bluez_element_call_full + (element, name, NULL, &element->_pending.stop_discovery, cb, data); +} + +/** + * Create a new Paired Device. + * + * Call method CreatePairedDevice() + * + * @param element adapter's element + * @param object_path object to be registered. + * @param capability input/output agent capabilities + * @param device device to pair with + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_adapter_create_paired_device(E_Bluez_Element *element, const char *object_path, const char *capability, const char *device, E_DBus_Method_Return_Cb cb, const void *data) +{ + DBusMessageIter itr; + DBusMessage *msg; + + const char name[] = "CreatePairedDevice"; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(object_path, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(device, EINA_FALSE); + + msg = dbus_message_new_method_call + (e_bluez_system_bus_name_get(), element->path, element->interface, + name); + + if (!msg) + return EINA_FALSE; + + dbus_message_iter_init_append(msg, &itr); + dbus_message_iter_append_basic(&itr, DBUS_TYPE_STRING, &device); + dbus_message_iter_append_basic(&itr, DBUS_TYPE_OBJECT_PATH, &object_path); + dbus_message_iter_append_basic(&itr, DBUS_TYPE_STRING, &capability); + + return e_bluez_element_message_send + (element, name, NULL, msg, + &element->_pending.create_paired_device, cb, data); +} + diff --git a/src/lib/bluez/e_bluez_device.c b/src/lib/bluez/e_bluez_device.c new file mode 100644 index 0000000..b75c74a --- /dev/null +++ b/src/lib/bluez/e_bluez_device.c @@ -0,0 +1,92 @@ +#include "e_bluez_private.h" + +E_Bluez_Element * +e_bluez_device_get(const char *path) +{ + E_Bluez_Element *device; + + EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL); + + device = e_bluez_element_get(path); + if (!device) + return NULL; + + if (!e_bluez_element_is_device(device)) + { + WRN("path '%s' is not a device!", path); + return NULL; + } + + return device; +} + +/** + * Get property "Name" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param address where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_device_name_get(const E_Bluez_Element *element, const char **name) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); + + return e_bluez_element_property_get_stringshared + (element, e_bluez_prop_name, NULL, name); +} + +/** + * Get property "Alias" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param address where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_device_alias_get(const E_Bluez_Element *element, const char **alias) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(alias, EINA_FALSE); + + return e_bluez_element_property_get_stringshared + (element, e_bluez_prop_alias, NULL, alias); +} + +/** + * Get property "Paired" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param powered where to store the property value, must be a pointer + * to boolean (Eina_Bool *). + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_device_paired_get(const E_Bluez_Element *element, Eina_Bool *paired) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(paired, EINA_FALSE); + + return e_bluez_element_property_get_stringshared + (element, e_bluez_prop_paired, NULL, paired); +} + diff --git a/src/lib/bluez/e_bluez_devicefound.c b/src/lib/bluez/e_bluez_devicefound.c new file mode 100644 index 0000000..619bc20 --- /dev/null +++ b/src/lib/bluez/e_bluez_devicefound.c @@ -0,0 +1,37 @@ +#include "e_bluez_private.h" + +/** + * Free a E_Bluez_Device_Found struct + * + * @param device the struct to be freed + */ +void +e_bluez_devicefound_free(E_Bluez_Device_Found *device) +{ + EINA_SAFETY_ON_NULL_RETURN(device); + + eina_stringshare_del(device->name); + e_bluez_element_array_free(device->array, NULL); +} + +/** + * Return the pointer to the stringshared alias for the given found device. + * + * @return stringshared pointer, or @c NULL if unknown. + */ +const char * +e_bluez_devicefound_alias_get(const E_Bluez_Device_Found *device) +{ + E_Bluez_Element_Dict_Entry *entry; + + EINA_SAFETY_ON_NULL_RETURN_VAL(device, NULL); + + entry = e_bluez_element_array_dict_find_stringshared + (device->array, e_bluez_prop_alias); + + if ((entry) && (entry->type == DBUS_TYPE_STRING)) + return entry->value.str; + + return NULL; +} + diff --git a/src/lib/bluez/e_bluez_element.c b/src/lib/bluez/e_bluez_element.c new file mode 100644 index 0000000..c68983f --- /dev/null +++ b/src/lib/bluez/e_bluez_element.c @@ -0,0 +1,2404 @@ +#include "e_bluez_private.h" +#include +#include +#include + +static Eina_Hash *elements = NULL; + +typedef struct _E_Bluez_Element_Pending E_Bluez_Element_Pending; +typedef struct _E_Bluez_Element_Call_Data E_Bluez_Element_Call_Data; +typedef struct _E_Bluez_Element_Property E_Bluez_Element_Property; +typedef struct _E_Bluez_Element_Listener E_Bluez_Element_Listener; + +struct _E_Bluez_Element_Pending +{ + EINA_INLIST; + DBusPendingCall *pending; + void *data; + E_DBus_Method_Return_Cb user_cb; + void *user_data; +}; + +struct _E_Bluez_Element_Call_Data +{ + E_Bluez_Element *element; + E_DBus_Method_Return_Cb cb; + E_Bluez_Element_Pending *pending; + Eina_Inlist **p_list; +}; + +struct _E_Bluez_Element_Property +{ + EINA_INLIST; + const char *name; + int type; + union { + Eina_Bool boolean; + const char *str; + unsigned short u16; + unsigned int u32; + unsigned char byte; + const char *path; + void *variant; + E_Bluez_Array *array; + } value; +}; + +struct _E_Bluez_Element_Listener +{ + EINA_INLIST; + void (*cb)(void *data, const E_Bluez_Element *element); + void *data; + void (*free_data)(void *data); +}; + +static void +_e_bluez_element_event_no_free(void *data __UNUSED__, void *ev) +{ + E_Bluez_Element *element = ev; + e_bluez_element_unref(element); +} + +void +e_bluez_element_event_add(int event_type, E_Bluez_Element *element) +{ + e_bluez_element_ref(element); + ecore_event_add + (event_type, element, _e_bluez_element_event_no_free, element); +} + +static void +e_bluez_element_call_dispatch_and_free(void *d, DBusMessage *msg, DBusError *err) +{ + E_Bluez_Element_Call_Data *data = d; + E_Bluez_Element_Pending *pending; + + pending = data->pending; + pending->pending = NULL; + + if (data->cb) + data->cb(data->element, msg, err); + + if (pending->user_cb) + pending->user_cb(pending->user_data, msg, err); + + pending->data = NULL; + *data->p_list = eina_inlist_remove(*data->p_list, EINA_INLIST_GET(pending)); + free(pending); + free(data); +} + +static void +e_bluez_element_pending_cancel_and_free(Eina_Inlist **pending) +{ + while (*pending) + { + E_Bluez_Element_Pending *p = (E_Bluez_Element_Pending *)*pending; + DBusError err; + + dbus_pending_call_cancel(p->pending); + + dbus_error_init(&err); + dbus_set_error(&err, "Canceled", "Pending method call was canceled."); + e_bluez_element_call_dispatch_and_free(p->data, NULL, &err); + dbus_error_free(&err); + } +} + +void +e_bluez_element_listener_add(E_Bluez_Element *element, void (*cb)(void *data, const E_Bluez_Element *element), const void *data, void (*free_data)(void *data)) +{ + E_Bluez_Element_Listener *l; + + if (!element) + { + ERR("safety check failed: element == NULL"); + goto error; + } + + if (!cb) + { + ERR("safety check failed: cb == NULL"); + goto error; + } + + l = malloc(sizeof(*l)); + if (!l) + { + ERR("could not allocate E_Bluez_Element_Listener"); + goto error; + } + + l->cb = cb; + l->data = (void *)data; + l->free_data = free_data; + + element->_listeners = eina_inlist_append + (element->_listeners, EINA_INLIST_GET(l)); + + return; + +error: + if (free_data) + free_data((void *)data); +} + +void +e_bluez_element_listener_del(E_Bluez_Element *element, void (*cb)(void *data, const E_Bluez_Element *element), const void *data) +{ + E_Bluez_Element_Listener *l; + + EINA_SAFETY_ON_NULL_RETURN(element); + EINA_SAFETY_ON_NULL_RETURN(cb); + + EINA_INLIST_FOREACH(element->_listeners, l) + if ((l->cb == cb) && (l->data == data)) + { + element->_listeners = eina_inlist_remove + (element->_listeners, EINA_INLIST_GET(l)); + if (l->free_data) + l->free_data(l->data); + + free(l); + return; + } +} + +static void +_e_bluez_element_listeners_call_do(E_Bluez_Element *element) +{ + E_Bluez_Element_Listener *l, **shadow; + unsigned int i, count; + + /* NB: iterate on a copy in order to allow listeners to be deleted + * from callbacks. number of listeners should be small, so the + * following should do fine. + */ + count = eina_inlist_count(element->_listeners); + if (count < 1) + goto end; + + shadow = alloca(sizeof(*shadow) * count); + if (!shadow) + goto end; + + i = 0; + EINA_INLIST_FOREACH(element->_listeners, l) + shadow[i++] = l; + + for (i = 0; i < count; i++) + shadow[i]->cb(shadow[i]->data, element); + +end: + e_bluez_element_event_add(E_BLUEZ_EVENT_ELEMENT_UPDATED, element); +} + +static Eina_Bool +_e_bluez_element_listeners_call_idler(void *data) +{ + E_Bluez_Element *element = data; + _e_bluez_element_listeners_call_do(element); + element->_idler.changed = NULL; + return ECORE_CALLBACK_CANCEL; +} + +static void +_e_bluez_element_listeners_call(E_Bluez_Element *element) +{ + if (element->_idler.changed) + return; + + element->_idler.changed = ecore_idler_add + (_e_bluez_element_listeners_call_idler, element); +} + +/*********************************************************************** +* Property +***********************************************************************/ + +static void +_e_bluez_element_dict_entry_free(E_Bluez_Element_Dict_Entry *entry) +{ + switch (entry->type) + { + case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_BYTE: + case DBUS_TYPE_INT16: + case DBUS_TYPE_UINT16: + case DBUS_TYPE_UINT32: + break; + + case DBUS_TYPE_OBJECT_PATH: + eina_stringshare_del(entry->value.path); + break; + + case DBUS_TYPE_STRING: + eina_stringshare_del(entry->value.str); + break; + + default: + ERR("don't know how to free dict entry '%s' of type %c (%d)", + entry->name, entry->type, entry->type); + } + + eina_stringshare_del(entry->name); + free(entry); +} + +static E_Bluez_Element_Dict_Entry * +_e_bluez_element_dict_entry_new(DBusMessageIter *itr) +{ + E_Bluez_Element_Dict_Entry *entry; + DBusMessageIter e_itr, v_itr; + int t; + const char *key = NULL; + void *value = NULL; + + dbus_message_iter_recurse(itr, &e_itr); + + t = dbus_message_iter_get_arg_type(&e_itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_STRING)) + { + ERR("invalid format for dict entry. first type not a string: %c (%d)", + t, t); + return NULL; + } + + dbus_message_iter_get_basic(&e_itr, &key); + if (!key || !key[0]) + { + ERR("invalid format for dict entry. no key."); + return NULL; + } + + dbus_message_iter_next(&e_itr); + t = dbus_message_iter_get_arg_type(&e_itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_VARIANT)) + { + ERR("invalid format for dict entry '%s'. " + "second type not a variant: %c (%d)", + key, t, t); + return NULL; + } + + dbus_message_iter_recurse(&e_itr, &v_itr); + + t = dbus_message_iter_get_arg_type(&v_itr); + if ((t == DBUS_TYPE_INVALID) || (t == DBUS_TYPE_ARRAY)) + { + ERR("invalid type for dict value for entry '%s': %c (%d)", + key, t, t); + return NULL; + } + + entry = calloc(1, sizeof(*entry)); + if (!entry) + { + ERR("could not allocate memory for dict entry."); + return NULL; + } + + dbus_message_iter_get_basic(&v_itr, &value); + switch (t) + { + case DBUS_TYPE_BOOLEAN: + entry->value.boolean = (Eina_Bool)(long)value; + break; + + case DBUS_TYPE_BYTE: + entry->value.byte = (unsigned char)(long)value; + break; + + case DBUS_TYPE_INT16: + entry->value.i16 = (short)(long)value; + break; + + case DBUS_TYPE_UINT16: + entry->value.u16 = (unsigned short)(long)value; + break; + + case DBUS_TYPE_UINT32: + entry->value.u32 = (unsigned int)(long)value; + break; + + case DBUS_TYPE_STRING: + entry->value.str = eina_stringshare_add(value); + break; + + case DBUS_TYPE_OBJECT_PATH: + entry->value.path = eina_stringshare_add(value); + break; + + default: + ERR("don't know how to create dict entry '%s' for of type %c (%d)", + key, t, t); + free(entry); + return NULL; + } + + entry->name = eina_stringshare_add(key); + entry->type = t; + return entry; +} + +E_Bluez_Element_Dict_Entry * +e_bluez_element_array_dict_find_stringshared(const E_Bluez_Array *array, const char *key) +{ + E_Bluez_Element_Dict_Entry *entry; + Eina_Array_Iterator iterator; + unsigned int i; + + EINA_ARRAY_ITER_NEXT(array->array, i, entry, iterator) + if (entry->name == key) + return entry; + + return NULL; +} + +void +e_bluez_element_array_free(E_Bluez_Array *array, E_Bluez_Array *new __UNUSED__) +{ + Eina_Array_Iterator iterator; + unsigned int i; + void *item; + + if (!array) + return; + + switch (array->type) + { + case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_BYTE: + case DBUS_TYPE_INT16: + case DBUS_TYPE_UINT16: + case DBUS_TYPE_UINT32: + break; + + case DBUS_TYPE_OBJECT_PATH: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + eina_stringshare_del(item); + break; + + case DBUS_TYPE_STRING: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + eina_stringshare_del(item); + break; + + case DBUS_TYPE_DICT_ENTRY: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + _e_bluez_element_dict_entry_free(item); + break; + + default: + ERR("don't know how to free array of values of type %c (%d)", + array->type, array->type); + break; + } + eina_array_free(array->array); + free(array); +} + +static void +_e_bluez_element_property_value_free(E_Bluez_Element_Property *property) +{ + switch (property->type) + { + case 0: + return; + + case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_BYTE: + case DBUS_TYPE_UINT16: + case DBUS_TYPE_UINT32: + break; + + case DBUS_TYPE_STRING: + eina_stringshare_del(property->value.str); + break; + + case DBUS_TYPE_OBJECT_PATH: + eina_stringshare_del(property->value.path); + break; + + case DBUS_TYPE_ARRAY: + e_bluez_element_array_free(property->value.array, NULL); + break; + + default: + ERR("don't know how to free value of property type %c (%d)", + property->type, property->type); + } +} + +static const char * +_e_bluez_element_get_interface(const char *key) +{ + const char *interface = NULL, *tail; + char head; + + head = key[0]; + tail = key + 1; + + switch (head) + { + case 'A': + if (strcmp(tail, "dapters") == 0) + interface = e_bluez_iface_adapter; + + break; + + case 'D': + if (strcmp(tail, "evices") == 0) + interface = e_bluez_iface_device; + + break; + + default: + break; + } + + if (!interface) + ERR("failed to find interface for property \"%s\"", key); + + return interface; +} + +static void +_e_bluez_element_item_register(const char *key, const char *item) +{ + E_Bluez_Element *element; + const char *interface; + + interface = _e_bluez_element_get_interface(key); + if (!interface) + return; + + element = e_bluez_element_register(item, interface); + if ((element) && (!e_bluez_element_properties_sync(element))) + WRN("could not get properties of %s", element->path); +} + +/* Match 2 arrays to find which are new and which are old elements + * For new elements, register them under prop_name property + * For old elements, unregister them, sending proper DEL event + */ +static void +_e_bluez_element_array_match(E_Bluez_Array *old, E_Bluez_Array *new, const char *prop_name) +{ + Eina_List *deleted = NULL; + Eina_Array_Iterator iter_old, iter_new; + unsigned int i_old = 0, i_new = 0; + void *item_old, *item_new; + Eina_List *l; + void *data; + + if (!old) + return; + + if (old->type != DBUS_TYPE_OBJECT_PATH) + return; + + if ((!new) || (!new->array) || eina_array_count(new->array) == 0) + { + if ((!old) || (!old->array) || eina_array_count(old->array) == 0) + { + return; + } + else + { + iter_old = old->array->data; + goto out_remove_remaining; + } + } + + iter_new = new->array->data; + item_new = *iter_new; + EINA_ARRAY_ITER_NEXT(old->array, i_old, item_old, iter_old) + { + if (item_old == item_new) + { + i_new++; + if (i_new >= eina_array_count(new->array)) + { + i_old++; + break; + } + + iter_new++; + item_new = *iter_new; + } + else + { + deleted = eina_list_append(deleted, item_old); + } + } + + for(; i_new < eina_array_count(new->array); iter_new++, i_new++) + { + Eina_Bool found = EINA_FALSE; + item_new = *iter_new; + if (!item_new) + break; + + EINA_LIST_FOREACH(deleted, l, data) + { + if (data == item_new) + { + deleted = eina_list_remove_list(deleted, l); + found = EINA_TRUE; + break; + } + } + if (!found) + { + _e_bluez_element_item_register(prop_name, item_new); + DBG("Add element %s\n", (const char *)item_new); + } + } + + /* everybody after i_old on old->array + everybody from deleted list + will be removed + */ + EINA_LIST_FREE(deleted, data) + { + E_Bluez_Element *e = e_bluez_element_get(data); + if (e) + e_bluez_element_unregister(e); + + DBG("Delete element %s\n", (const char *)data); + } + +out_remove_remaining: + for(; i_old < eina_array_count(old->array); iter_old++, i_old++) + { + E_Bluez_Element *e; + item_old = *iter_old; + if (!item_old) + break; + + e = e_bluez_element_get(item_old); + if (e) + e_bluez_element_unregister(e); + + DBG("Delete element %s\n", (const char *)item_old); + } +} + +static Eina_Bool +_e_bluez_element_property_update(E_Bluez_Element_Property *property, int type, void *data) +{ + Eina_Bool changed = EINA_FALSE; + + if ((type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) && data) + data = (char *)eina_stringshare_add(data); + + if (property->type != type) + { + if (property->type) + DBG("property type changed from '%c' to '%c'", + property->type, type); + + _e_bluez_element_property_value_free(property); + memset(&property->value, 0, sizeof(property->value)); + property->type = type; + changed = EINA_TRUE; + } + + switch (type) + { + case DBUS_TYPE_BOOLEAN: + if (changed || property->value.boolean != (Eina_Bool)(long)data) + { + property->value.boolean = (Eina_Bool)(long)data; + changed = EINA_TRUE; + } + + break; + + case DBUS_TYPE_BYTE: + if (changed || property->value.byte != (unsigned char)(long)data) + { + property->value.byte = (unsigned char)(long)data; + changed = EINA_TRUE; + } + + break; + + case DBUS_TYPE_UINT16: + if (changed || property->value.u16 != (unsigned short)(long)data) + { + property->value.u16 = (unsigned short)(long)data; + changed = EINA_TRUE; + } + + break; + + case DBUS_TYPE_UINT32: + if (changed || property->value.u32 != (unsigned int)(long)data) + { + property->value.u32 = (unsigned int)(long)data; + changed = EINA_TRUE; + } + + break; + + case DBUS_TYPE_STRING: + if (changed) + { + property->value.str = data; + } + else + { + if (property->value.str) + eina_stringshare_del(property->value.str); + + if (property->value.str != data) + { + property->value.str = data; + changed = EINA_TRUE; + } + } + + break; + + case DBUS_TYPE_OBJECT_PATH: + if (changed) + { + property->value.path = data; + } + else + { + if (property->value.path) + eina_stringshare_del(property->value.path); + + if (property->value.path != data) + { + property->value.path = data; + changed = EINA_TRUE; + } + } + + break; + + case DBUS_TYPE_ARRAY: + if (!changed) + if (property->value.array) + { + _e_bluez_element_array_match(property->value.array, data, property->name); + e_bluez_element_array_free(property->value.array, data); + } + + property->value.array = data; + changed = EINA_TRUE; + break; + + default: + ERR("don't know how to update property type %c (%d)", type, type); + } + + return changed; +} + +static E_Bluez_Element_Property * +_e_bluez_element_property_new(const char *name, int type, void *data) +{ + E_Bluez_Element_Property *property; + + property = calloc(1, sizeof(*property)); + if (!property) + { + eina_stringshare_del(name); + ERR("could not allocate property: %s", strerror(errno)); + return NULL; + } + + property->name = name; + _e_bluez_element_property_update(property, type, data); + return property; +} + +static void +_e_bluez_element_property_free(E_Bluez_Element_Property *property) +{ + _e_bluez_element_property_value_free(property); + eina_stringshare_del(property->name); + free(property); +} + +/*********************************************************************** +* Element +***********************************************************************/ +unsigned char * +e_bluez_element_bytes_array_get_stringshared(const E_Bluez_Element *element, const char *property, unsigned int *count) +{ + Eina_Array_Iterator iterator; + E_Bluez_Array *array; + unsigned char *ret, *p; + unsigned int i; + void *item; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(property, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(count, NULL); + + *count = 0; + + if (!e_bluez_element_property_get_stringshared + (element, property, NULL, &array)) + return NULL; + + if ((!array) || (!(array->array))) + return NULL; + + *count = eina_array_count(array->array); + ret = malloc(*count * sizeof(unsigned char)); + if (!ret) + { + ERR("could not allocate return array of %d bytes: %s", + *count, strerror(errno)); + return NULL; + } + + p = ret; + + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + { + *p = (unsigned char)(long)item; + p++; + } + return ret; +} + +Eina_Bool +e_bluez_element_objects_array_get_stringshared(const E_Bluez_Element *element, const char *property, unsigned int *count, E_Bluez_Element ***p_elements) +{ + E_Bluez_Element **ret, **p; + Eina_Array_Iterator iterator; + E_Bluez_Array *array; + unsigned int i; + int type; + void *item; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(property, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(p_elements, EINA_FALSE); + + *count = 0; + *p_elements = NULL; + + if (!e_bluez_element_property_get_stringshared + (element, property, &type, &array)) + return EINA_FALSE; + + if (type != DBUS_TYPE_ARRAY) + { + ERR("property %s is not an array!", property); + return EINA_FALSE; + } + + if ((!array) || (!array->array) || (array->type == DBUS_TYPE_INVALID)) + return EINA_FALSE; + + if (array->type != DBUS_TYPE_OBJECT_PATH) + { + ERR("property %s is not an array of object paths!", property); + return EINA_FALSE; + } + + *count = eina_array_count(array->array); + ret = malloc(*count * sizeof(E_Bluez_Element *)); + if (!ret) + { + ERR("could not allocate return array of %d elements: %s", + *count, strerror(errno)); + *count = 0; + return EINA_FALSE; + } + + p = ret; + + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + { + E_Bluez_Element *e = e_bluez_element_get(item); + if (!e) + continue; + + *p = e; + p++; + } + *count = p - ret; + *p_elements = ret; + return EINA_TRUE; +} + +/* strings are just pointers (references), no strdup or stringshare_add/ref */ +Eina_Bool +e_bluez_element_strings_array_get_stringshared(const E_Bluez_Element *element, const char *property, unsigned int *count, const char ***strings) +{ + const char **ret, **p; + Eina_Array_Iterator iterator; + E_Bluez_Array *array; + unsigned int i; + int type; + void *item; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(property, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(strings, EINA_FALSE); + + *count = 0; + *strings = NULL; + + if (!e_bluez_element_property_get_stringshared + (element, property, &type, &array)) + return EINA_FALSE; + + if (type != DBUS_TYPE_ARRAY) + { + ERR("property %s is not an array!", property); + return EINA_FALSE; + } + + if ((!array) || (!array->array) || (array->type == DBUS_TYPE_INVALID)) + return EINA_FALSE; + + if (array->type != DBUS_TYPE_STRING) + { + ERR("property %s is not an array of strings!", property); + return EINA_FALSE; + } + + *count = eina_array_count(array->array); + ret = malloc(*count * sizeof(char *)); + if (!ret) + { + ERR("could not allocate return array of %d strings: %s", + *count, strerror(errno)); + *count = 0; + return EINA_FALSE; + } + + p = ret; + + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + { + if (!item) + continue; + + *p = item; + p++; + } + *count = p - ret; + *strings = ret; + return EINA_TRUE; +} + +void +e_bluez_element_array_print(FILE *fp, E_Bluez_Array *array) +{ + Eina_Array_Iterator iterator; + unsigned int i; + void *item; + + if (!array) + return; + + switch (array->type) + { + case DBUS_TYPE_OBJECT_PATH: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + fprintf(fp, "\"%s\", ", (const char *)item); + break; + + case DBUS_TYPE_STRING: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + fprintf(fp, "\"%s\", ", (const char *)item); + break; + + case DBUS_TYPE_BYTE: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + fprintf(fp, "%#02hhx (\"%c\"), ", (unsigned char)(long)item, + (unsigned char)(long)item); + break; + + case DBUS_TYPE_UINT16: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + fprintf(fp, "%#04hx (%hu), ", (unsigned short)(long)item, + (unsigned short)(long)item); + break; + + case DBUS_TYPE_UINT32: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + fprintf(fp, "%#08x (%u), ", (unsigned int)(long)item, + (unsigned int)(long)item); + break; + + case DBUS_TYPE_DICT_ENTRY: + fputs("{ ", fp); + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + { + E_Bluez_Element_Dict_Entry *entry = item; + fprintf(fp, "%s: ", entry->name); + switch (entry->type) + { + case DBUS_TYPE_OBJECT_PATH: + fprintf(fp, "\"%s\", ", entry->value.path); + break; + + case DBUS_TYPE_STRING: + fprintf(fp, "\"%s\", ", entry->value.str); + break; + + case DBUS_TYPE_BOOLEAN: + fprintf(fp, "%hhu, ", + entry->value.boolean); + break; + + case DBUS_TYPE_BYTE: + fprintf(fp, "%#02hhx (\"%c\"), ", + entry->value.byte, entry->value.byte); + break; + + case DBUS_TYPE_INT16: + fprintf(fp, "%#04hx (%hi), ", + entry->value.i16, entry->value.i16); + break; + + case DBUS_TYPE_UINT16: + fprintf(fp, "%#04hx (%hu), ", + entry->value.u16, entry->value.u16); + break; + + case DBUS_TYPE_UINT32: + fprintf(fp, "%#08x (%u), ", + entry->value.u32, entry->value.u32); + break; + + default: + fprintf(fp, "", entry->type); + } + } + fputs("}", fp); + break; + + default: + fprintf(fp, "", array->type); + } +} + +/** + * Print element to file descriptor. + */ +void +e_bluez_element_print(FILE *fp, const E_Bluez_Element *element) +{ + const E_Bluez_Element_Property *p; + + EINA_SAFETY_ON_NULL_RETURN(fp); + if (!element) + { + fputs("Error: no element to print\n", fp); + return; + } + + fprintf(fp, + "Element %p: %s [%s]\n" + "\tProperties:\n", + element, element->path, element->interface); + + EINA_INLIST_FOREACH(element->props, p) + { + fprintf(fp, "\t\t%s (%c) = ", p->name, p->type); + + switch (p->type) + { + case DBUS_TYPE_STRING: + fprintf(fp, "\"%s\"", p->value.str); + break; + + case DBUS_TYPE_OBJECT_PATH: + fprintf(fp, "\"%s\"", p->value.path); + break; + + case DBUS_TYPE_BOOLEAN: + fprintf(fp, "%hhu", p->value.boolean); + break; + + case DBUS_TYPE_BYTE: + fprintf(fp, "%#02hhx (%d), ", p->value.byte, p->value.byte); + break; + + case DBUS_TYPE_UINT16: + fprintf(fp, "%hu", p->value.u16); + break; + + case DBUS_TYPE_UINT32: + fprintf(fp, "%u", p->value.u32); + break; + + case DBUS_TYPE_ARRAY: + e_bluez_element_array_print(fp, p->value.array); + break; + + default: + fputs("don't know how to print type", fp); + } + + fputc('\n', fp); + } +} + +static E_Bluez_Element * +e_bluez_element_new(const char *path, const char *interface) +{ + E_Bluez_Element *element; + + element = calloc(1, sizeof(*element)); + if (!element) + { + ERR("could not allocate element: %s", strerror(errno)); + return NULL; + } + + element->path = eina_stringshare_add(path); + element->interface = eina_stringshare_ref(interface); + element->_references = 1; + + return element; +} + +static void +e_bluez_element_extra_properties_free(E_Bluez_Element *element) +{ + while (element->props) + { + E_Bluez_Element_Property *prop; + prop = (E_Bluez_Element_Property *)element->props; + element->props = element->props->next; + _e_bluez_element_property_free(prop); + } +} + +static void +e_bluez_element_free(E_Bluez_Element *element) +{ + if (element->_idler.changed) + ecore_idler_del(element->_idler.changed); + + while (element->_listeners) + { + E_Bluez_Element_Listener *l = (void *)element->_listeners; + element->_listeners = eina_inlist_remove + (element->_listeners, element->_listeners); + + if (l->free_data) + l->free_data(l->data); + + free(l); + } + + e_bluez_element_pending_cancel_and_free(&element->_pending.properties_get); + e_bluez_element_pending_cancel_and_free(&element->_pending.property_set); + e_bluez_element_pending_cancel_and_free(&element->_pending.agent_register); + e_bluez_element_pending_cancel_and_free(&element->_pending.agent_unregister); + + e_bluez_element_extra_properties_free(element); + eina_stringshare_del(element->interface); + eina_stringshare_del(element->path); + free(element); +} + +/** + * Add reference to element. + */ +int +e_bluez_element_ref(E_Bluez_Element *element) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, 0); + return ++element->_references; +} + +/** + * Remove reference from element. + * + * If reference count drops to 0 element will be freed. + */ +int +e_bluez_element_unref(E_Bluez_Element *element) +{ + int i; + EINA_SAFETY_ON_NULL_RETURN_VAL(element, 0); + + i = --element->_references; + if (i == 0) + e_bluez_element_free(element); + else if (i < 0) + ERR("element %p references %d < 0", element, i); + + return i; +} + +/** + * Send message with callbacks set to work with bluez elements. + * + * If this call fails (returns @c EINA_FALSE), pending callbacks will not be called, + * not even with error messages. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE on failure. + */ +Eina_Bool +e_bluez_element_message_send(E_Bluez_Element *element, const char *method_name, E_DBus_Method_Return_Cb cb, DBusMessage *msg, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data) +{ + E_Bluez_Element_Call_Data *data; + E_Bluez_Element_Pending *p; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(method_name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(pending, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(msg, EINA_FALSE); + + data = malloc(sizeof(*data)); + if (!data) + { + ERR("could not alloc e_bluez_element_call_data: %s", + strerror(errno)); + dbus_message_unref(msg); + return EINA_FALSE; + } + + p = malloc(sizeof(*p)); + if (!p) + { + ERR("could not alloc E_Bluez_Element_Pending: %s", + strerror(errno)); + free(data); + dbus_message_unref(msg); + return EINA_FALSE; + } + + data->element = element; + data->cb = cb; + data->pending = p; + data->p_list = pending; + p->user_cb = user_cb; + p->user_data = (void *)user_data; + p->data = data; + p->pending = e_dbus_message_send + (e_bluez_conn, msg, e_bluez_element_call_dispatch_and_free, -1, data); + dbus_message_unref(msg); + + if (p->pending) + { + *pending = eina_inlist_append(*pending, EINA_INLIST_GET(p)); + return EINA_TRUE; + } + + ERR("failed to call %s (obj=%s, path=%s, iface=%s)", + method_name, e_bluez_system_bus_name_get(), + element->path, element->interface); + free(data); + free(p); + return EINA_FALSE; +} + +Eina_Bool +e_bluez_element_call_full(E_Bluez_Element *element, const char *method_name, E_DBus_Method_Return_Cb cb, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data) +{ + DBusMessage *msg; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(method_name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(pending, EINA_FALSE); + + msg = dbus_message_new_method_call + (e_bluez_system_bus_name_get(), element->path, element->interface, + method_name); + + return e_bluez_element_message_send + (element, method_name, cb, msg, pending, user_cb, user_data); +} + +static Eina_Bool +_e_bluez_element_property_value_add(E_Bluez_Element *element, const char *name, int type, void *value) +{ + E_Bluez_Element_Property *p; + + name = eina_stringshare_add(name); + EINA_INLIST_FOREACH(element->props, p) + { + if (p->name == name) + { + eina_stringshare_del(name); + return _e_bluez_element_property_update(p, type, value); + } + } + + p = _e_bluez_element_property_new(name, type, value); + if (!p) + { + ERR("could not create property %s (%c)", name, type); + return EINA_FALSE; + } + + element->props = eina_inlist_append(element->props, EINA_INLIST_GET(p)); + return EINA_TRUE; +} + +E_Bluez_Array * +e_bluez_element_iter_get_array(DBusMessageIter *itr, const char *key) +{ + E_Bluez_Array *array; + DBusMessageIter e_itr; + + array = malloc(sizeof(E_Bluez_Array)); + if (!array) + { + ERR("could not create new e_bluez array."); + return NULL; + } + + array->array = eina_array_new(16); + if (!(array->array)) + { + ERR("could not create new eina array."); + free(array); + return NULL; + } + + dbus_message_iter_recurse(itr, &e_itr); + array->type = dbus_message_iter_get_arg_type(&e_itr); + if (array->type == DBUS_TYPE_INVALID) + { + DBG("array %s is of type 'invalid' (empty?)", key); + eina_array_free(array->array); + free(array); + return NULL; + } + + do + { + switch (array->type) + { + case DBUS_TYPE_OBJECT_PATH: + { + const char *path; + + dbus_message_iter_get_basic(&e_itr, &path); + path = eina_stringshare_add(path); + eina_array_push(array->array, path); + _e_bluez_element_item_register(key, path); + } + break; + + case DBUS_TYPE_STRING: + { + const char *str; + + dbus_message_iter_get_basic(&e_itr, &str); + str = eina_stringshare_add(str); + eina_array_push(array->array, str); + } + break; + + case DBUS_TYPE_BYTE: + { + unsigned char byte; + dbus_message_iter_get_basic(&e_itr, &byte); + eina_array_push(array->array, (void *)(long)byte); + } + break; + + case DBUS_TYPE_DICT_ENTRY: + { + E_Bluez_Element_Dict_Entry *entry; + entry = _e_bluez_element_dict_entry_new(&e_itr); + if (entry) + eina_array_push(array->array, entry); + } + break; + + default: + ERR("don't know how to build array '%s' of type %c (%d)", + key, array->type, array->type); + eina_array_free(array->array); + free(array); + return NULL; + } + } + while (dbus_message_iter_next(&e_itr)); + return array; +} + +static void +_e_bluez_element_get_properties_callback(void *user_data, DBusMessage *msg, DBusError *err) +{ + E_Bluez_Element *element = user_data; + DBusMessageIter itr, s_itr; + int t, changed; + + DBG("get_properties msg=%p", msg); + + if (!_dbus_callback_check_and_init(msg, &itr, err)) + return; + + t = dbus_message_iter_get_arg_type(&itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_ARRAY)) + return; + + changed = 0; + dbus_message_iter_recurse(&itr, &s_itr); + do + { + DBusMessageIter e_itr, v_itr; + const char *key; + void *value = NULL; + int r; + + t = dbus_message_iter_get_arg_type(&s_itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_DICT_ENTRY)) + continue; + + dbus_message_iter_recurse(&s_itr, &e_itr); + + t = dbus_message_iter_get_arg_type(&e_itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_STRING)) + continue; + + dbus_message_iter_get_basic(&e_itr, &key); + dbus_message_iter_next(&e_itr); + t = dbus_message_iter_get_arg_type(&e_itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_VARIANT)) + continue; + + dbus_message_iter_recurse(&e_itr, &v_itr); + t = dbus_message_iter_get_arg_type(&v_itr); + if (t == DBUS_TYPE_ARRAY) + { + value = e_bluez_element_iter_get_array(&v_itr, key); + } + else if (t != DBUS_TYPE_INVALID) + { + dbus_message_iter_get_basic(&v_itr, &value); + } + else + { + ERR("property has invalid type %s", key); + continue; + } + + r = _e_bluez_element_property_value_add(element, key, t, value); + if (r < 0) + { + ERR("failed to add property value %s (%c)", key, t); + } + else if (r == 1) + { + INF("property value changed %s (%c)", key, t); + changed = 1; + } + } + while (dbus_message_iter_next(&s_itr)); + + if (changed) + _e_bluez_element_listeners_call(element); +} + +/** + * Sync element properties with server. + * + * Call method GetProperties() at the given element on server in order to sync + * them. + * + * @param element to call method on server. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_element_properties_sync_full(E_Bluez_Element *element, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "GetProperties"; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + return e_bluez_element_call_full + (element, name, _e_bluez_element_get_properties_callback, + &element->_pending.properties_get, cb, data); +} + +/** + * Sync element properties with server, simple version. + * + * Call method GetProperties() at the given element on server in order to sync + * them. This is the simple version and there is no check of server reply + * for errors. + * + * @param element to call method on server. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_element_properties_sync(E_Bluez_Element *element) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + return e_bluez_element_properties_sync_full(element, NULL, NULL); +} + +/** + * Call method SetProperty(prop, {key: value}) at the given element on server. + * + * This is a server call, not local, so it may fail and in that case + * no property is updated locally. If the value was set the event + * E_BLUEZ_EVENT_ELEMENT_UPDATED will be added to main loop. + * + * @param element to call method on server. + * @param prop property name. + * @param key dict key name. + * @param type DBus type to use for value. + * @param value pointer to value, just like regular DBus, see + * dbus_message_iter_append_basic(). + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_element_property_dict_set_full(E_Bluez_Element *element, const char *prop, const char *key, int type, const void *value, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "SetProperty"; + DBusMessage *msg; + DBusMessageIter itr, variant, dict, entry; + char typestr[32]; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(prop, EINA_FALSE); + + msg = dbus_message_new_method_call + (e_bluez_system_bus_name_get(), element->path, element->interface, name); + + if (!msg) + return EINA_FALSE; + + dbus_message_iter_init_append(msg, &itr); + dbus_message_iter_append_basic(&itr, DBUS_TYPE_STRING, &prop); + + if ((size_t)snprintf(typestr, sizeof(typestr), + (DBUS_TYPE_ARRAY_AS_STRING + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + "%c" + DBUS_DICT_ENTRY_END_CHAR_AS_STRING), + type) >= sizeof(typestr)) + { + ERR("sizeof(typestr) is too small!"); + return EINA_FALSE; + } + + if (dbus_message_iter_open_container(&itr, DBUS_TYPE_VARIANT, typestr, &variant)) + { + snprintf(typestr, sizeof(typestr), + (DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + "%c" + DBUS_DICT_ENTRY_END_CHAR_AS_STRING), + type); + + if (dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, typestr, &dict)) + { + if (dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry)) + { + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + + if ((type == DBUS_TYPE_STRING) || (type == DBUS_TYPE_OBJECT_PATH)) + dbus_message_iter_append_basic(&entry, type, &value); + else + dbus_message_iter_append_basic(&entry, type, value); + + dbus_message_iter_close_container(&dict, &entry); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + dbus_message_iter_close_container(&variant, &dict); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + dbus_message_iter_close_container(&itr, &variant); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + + return e_bluez_element_message_send + (element, name, NULL, msg, &element->_pending.property_set, cb, data); +} + +/** + * Call method SetProperty(prop, value) at the given element on server. + * + * This is a server call, not local, so it may fail and in that case + * no property is updated locally. If the value was set the event + * E_BLUEZ_EVENT_ELEMENT_UPDATED will be added to main loop. + * + * @param element to call method on server. + * @param prop property name. + * @param type DBus type to use for value. + * @param value pointer to value, just like regular DBus, see + * dbus_message_iter_append_basic(). + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_element_property_set_full(E_Bluez_Element *element, const char *prop, int type, const void *value, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "SetProperty"; + char typestr[2]; + DBusMessage *msg; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(prop, EINA_FALSE); + + msg = dbus_message_new_method_call + (e_bluez_system_bus_name_get(), element->path, element->interface, name); + + if (!msg) + return EINA_FALSE; + + DBusMessageIter itr, v; + dbus_message_iter_init_append(msg, &itr); + dbus_message_iter_append_basic(&itr, DBUS_TYPE_STRING, &prop); + + typestr[0] = type; + typestr[1] = '\0'; + if (dbus_message_iter_open_container(&itr, DBUS_TYPE_VARIANT, typestr, &v)) + { + if ((type == DBUS_TYPE_STRING) || (type == DBUS_TYPE_OBJECT_PATH)) + { + dbus_message_iter_append_basic(&v, type, &value); + } + else if (type == DBUS_TYPE_BOOLEAN) + { + unsigned int b = *(char *)value; + dbus_message_iter_append_basic(&v, type, &b); + } + else + { + dbus_message_iter_append_basic(&v, type, value); + } + dbus_message_iter_close_container(&itr, &v); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + + return e_bluez_element_message_send + (element, name, NULL, msg, &element->_pending.property_set, cb, data); +} + +/** + * Call method SetProperty(prop, value) at the given element on server. + * + * This is the simple version and there is no check of server reply + * for errors. + * + * @param element to call method on server. + * @param prop property name. + * @param type DBus type to use for value. + * @param value pointer to value, just like regular DBus, see + * dbus_message_iter_append_basic(). + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_element_property_set(E_Bluez_Element *element, const char *prop, int type, const void *value) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(prop, EINA_FALSE); + return e_bluez_element_property_set_full + (element, prop, type, value, NULL, NULL); +} + +Eina_Bool +e_bluez_element_call_with_path(E_Bluez_Element *element, const char *method_name, const char *string, E_DBus_Method_Return_Cb cb, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data) +{ + DBusMessageIter itr; + DBusMessage *msg; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(method_name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(string, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(pending, EINA_FALSE); + + msg = dbus_message_new_method_call + (e_bluez_system_bus_name_get(), element->path, element->interface, + method_name); + + if (!msg) + return EINA_FALSE; + + dbus_message_iter_init_append(msg, &itr); + dbus_message_iter_append_basic(&itr, DBUS_TYPE_OBJECT_PATH, &string); + + return e_bluez_element_message_send + (element, method_name, cb, msg, pending, user_cb, user_data); +} + +Eina_Bool +e_bluez_element_call_with_string(E_Bluez_Element *element, const char *method_name, const char *string, E_DBus_Method_Return_Cb cb, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data) +{ + DBusMessageIter itr; + DBusMessage *msg; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(method_name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(string, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(pending, EINA_FALSE); + + msg = dbus_message_new_method_call + (e_bluez_system_bus_name_get(), element->path, element->interface, + method_name); + + if (!msg) + return EINA_FALSE; + + dbus_message_iter_init_append(msg, &itr); + dbus_message_iter_append_basic(&itr, DBUS_TYPE_STRING, &string); + + return e_bluez_element_message_send + (element, method_name, cb, msg, pending, user_cb, user_data); +} + +Eina_Bool +e_bluez_element_call_with_path_and_string(E_Bluez_Element *element, const char *method_name, const char *path, const char *string, E_DBus_Method_Return_Cb cb, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data) +{ + DBusMessageIter itr; + DBusMessage *msg; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(method_name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(path, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(string, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(pending, EINA_FALSE); + + msg = dbus_message_new_method_call + (e_bluez_system_bus_name_get(), element->path, element->interface, + method_name); + + if (!msg) + return EINA_FALSE; + + dbus_message_iter_init_append(msg, &itr); + dbus_message_iter_append_basic(&itr, DBUS_TYPE_OBJECT_PATH, &path); + dbus_message_iter_append_basic(&itr, DBUS_TYPE_STRING, &string); + + return e_bluez_element_message_send + (element, method_name, cb, msg, pending, user_cb, user_data); +} + +/** + * Get property type. + * + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param element which element to get the property + * @param name property name, must be previously stringshared + * @param type will contain the value type. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_element_property_type_get_stringshared(const E_Bluez_Element *element, const char *name, int *type) +{ + const E_Bluez_Element_Property *p; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(type, EINA_FALSE); + + EINA_INLIST_FOREACH(element->props, p) + { + if (p->name == name) + { + *type = p->type; + return EINA_TRUE; + } + } + + WRN("element %s (%p) has no property with name \"%s\".", + element->path, element, name); + return EINA_FALSE; +} + +/** + * Get property type. + * + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param element which element to get the property + * @param name property name + * @param type will contain the value type. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_element_property_type_get(const E_Bluez_Element *element, const char *name, int *type) +{ + Eina_Bool ret; + name = eina_stringshare_add(name); + ret = e_bluez_element_property_type_get_stringshared(element, name, type); + eina_stringshare_del(name); + return ret; +} + +void +e_bluez_element_list_properties(const E_Bluez_Element *element, Eina_Bool (*cb)(void *data, const E_Bluez_Element *element, const char *name, int type, const void *value), const void *data) +{ + const E_Bluez_Element_Property *p; + + EINA_SAFETY_ON_NULL_RETURN(element); + EINA_SAFETY_ON_NULL_RETURN(cb); + + EINA_INLIST_FOREACH(element->props, p) + { + const void *value = NULL; + + switch (p->type) + { + case DBUS_TYPE_STRING: + value = &p->value.str; + break; + + case DBUS_TYPE_OBJECT_PATH: + value = &p->value.path; + break; + + case DBUS_TYPE_BOOLEAN: + value = (void *)(unsigned long)p->value.boolean; + break; + + case DBUS_TYPE_UINT16: + value = &p->value.u16; + break; + + case DBUS_TYPE_UINT32: + value = &p->value.u32; + break; + + default: + ERR("unsupported type %c", p->type); + } + + if (!cb((void *)data, element, p->name, p->type, value)) + return; + } +} + +/** + * Get dict value given its key inside a dict property. + * + * This will look into properties for one of type dict that contains + * the given key, to find the property. If no property is found then + * @c EINA_FALSE is returned. + * + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param element which element to get the property + * @param dict_name property name, must be previously stringshared + * @param key key inside dict, must be previously stringshared + * @param type if provided it will contain the value type. + * @param value where to store the property value, must be a pointer to the + * exact type, (Eina_Bool *) for booleans, (char **) for strings, and so on. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_element_property_dict_get_stringshared(const E_Bluez_Element *element, const char *dict_name, const char *key, int *type, void *value) +{ + const E_Bluez_Element_Property *p; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(dict_name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(value, EINA_FALSE); + + EINA_INLIST_FOREACH(element->props, p) + { + E_Bluez_Element_Dict_Entry *entry; + E_Bluez_Array *array; + + if (p->name != dict_name) + continue; + + if (p->type != DBUS_TYPE_ARRAY) + { + WRN("element %s (%p) has property \"%s\" is not an array: %c (%d)", + element->path, element, dict_name, p->type, p->type); + return EINA_FALSE; + } + + array = p->value.array; + if ((!array) || (array->type != DBUS_TYPE_DICT_ENTRY)) + { + int t = array ? array->type : DBUS_TYPE_INVALID; + WRN("element %s (%p) has property \"%s\" is not a dict: %c (%d)", + element->path, element, dict_name, t, t); + return EINA_FALSE; + } + + entry = e_bluez_element_array_dict_find_stringshared(array, key); + if (!entry) + { + WRN("element %s (%p) has no dict property with name \"%s\" with " + "key \"%s\".", + element->path, element, dict_name, key); + return EINA_FALSE; + } + + if (type) + *type = entry->type; + + switch (entry->type) + { + case DBUS_TYPE_BOOLEAN: + *(Eina_Bool *)value = entry->value.boolean; + return EINA_TRUE; + + case DBUS_TYPE_BYTE: + *(unsigned char *)value = entry->value.byte; + return EINA_TRUE; + + case DBUS_TYPE_INT16: + *(short *)value = entry->value.i16; + return EINA_TRUE; + + case DBUS_TYPE_UINT16: + *(unsigned short *)value = entry->value.u16; + return EINA_TRUE; + + case DBUS_TYPE_UINT32: + *(unsigned int *)value = entry->value.u32; + return EINA_TRUE; + + case DBUS_TYPE_STRING: + *(const char **)value = entry->value.str; + return EINA_TRUE; + + case DBUS_TYPE_OBJECT_PATH: + *(const char **)value = entry->value.path; + return EINA_TRUE; + + default: + ERR("don't know how to get property %s, key %s type %c (%d)", + dict_name, key, entry->type, entry->type); + return EINA_FALSE; + } + } + + WRN("element %s (%p) has no property with name \"%s\".", + element->path, element, dict_name); + return EINA_FALSE; +} + +/** + * Get property value given its name. + * + * This will look into properties, to find the property. + * If no property is found then @c EINA_FALSE is returned. + * + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param element which element to get the property + * @param name property name, must be previously stringshared + * @param type if provided it will contain the value type. + * @param value where to store the property value, must be a pointer to the + * exact type, (Eina_Bool *) for booleans, (char **) for strings, and so on. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_element_property_get_stringshared(const E_Bluez_Element *element, const char *name, int *type, void *value) +{ + const E_Bluez_Element_Property *p; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(value, EINA_FALSE); + + EINA_INLIST_FOREACH(element->props, p) + { + if (p->name != name) + continue; + + if (type) + *type = p->type; + + switch (p->type) + { + case DBUS_TYPE_BOOLEAN: + *(Eina_Bool *)value = p->value.boolean; + return EINA_TRUE; + + case DBUS_TYPE_BYTE: + *(unsigned char *)value = p->value.byte; + return EINA_TRUE; + + case DBUS_TYPE_UINT16: + *(unsigned short *)value = p->value.u16; + return EINA_TRUE; + + case DBUS_TYPE_UINT32: + *(unsigned int *)value = p->value.u32; + return EINA_TRUE; + + case DBUS_TYPE_STRING: + *(const char **)value = p->value.str; + return EINA_TRUE; + + case DBUS_TYPE_OBJECT_PATH: + *(const char **)value = p->value.path; + return EINA_TRUE; + + case DBUS_TYPE_ARRAY: + *(E_Bluez_Array **)value = p->value.array; + return EINA_TRUE; + + default: + ERR("don't know how to get property type %c (%d)", + p->type, p->type); + return EINA_FALSE; + } + } + + WRN("element %s (%p) has no property with name \"%s\".", + element->path, element, name); + return EINA_FALSE; +} + +/** + * Get property value given its name. + * + * This will look into properties, to find the property. + * If no property is found then @c EINA_FALSE is returned. + * + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param element which element to get the property + * @param name property name + * @param type if provided it will contain the value type. + * @param value where to store the property value, must be a pointer to the + * exact type, (Eina_Bool *) for booleans, (char **) for strings, and so on. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_element_property_get(const E_Bluez_Element *element, const char *name, int *type, void *value) +{ + Eina_Bool ret; + name = eina_stringshare_add(name); + ret = e_bluez_element_property_get_stringshared + (element, name, type, value); + eina_stringshare_del(name); + return ret; +} + +struct e_bluez_elements_for_each_data +{ + Eina_Hash_Foreach cb; + void *data; +}; + +static Eina_Bool +_e_bluez_elements_for_each(Eina_Hash *hash __UNUSED__, const char *key, void *data, void *fdata) +{ + struct e_bluez_elements_for_each_data *each_data = fdata; + + each_data->cb(elements, key, data, each_data->data); + return EINA_TRUE; +} + +/** + * Call the given function for each existing element. + * + * @param cb function to call for each element. It will get as parameters, + * in order: the element pointer and the given @a user_data. + * @param user_data data to give to @a cb for each element. + */ +void +e_bluez_elements_for_each(Eina_Hash_Foreach cb, const void *user_data) +{ + struct e_bluez_elements_for_each_data data = {cb, (void *)user_data}; + + EINA_SAFETY_ON_NULL_RETURN(cb); + + eina_hash_foreach(elements, (Eina_Hash_Foreach)_e_bluez_elements_for_each, + &data); +} + +static Eina_Bool +_e_bluez_elements_get_allocate(unsigned int *count, E_Bluez_Element ***p_elements) +{ + *count = eina_hash_population(elements); + if (*count == 0) + { + *p_elements = NULL; + return EINA_TRUE; + } + + *p_elements = malloc(*count * sizeof(E_Bluez_Element *)); + if (!*p_elements) + { + ERR("could not allocate return array of %d elements: %s", + *count, strerror(errno)); + *count = 0; + return EINA_FALSE; + } + + return EINA_TRUE; +} + +static Eina_Bool +_e_bluez_elements_get_all(Eina_Hash *hash __UNUSED__, const char *key __UNUSED__, void *data, void *fdata) +{ + E_Bluez_Element *element = data; + E_Bluez_Element ***p_ret = fdata; + + **p_ret = element; + (*p_ret)++; + return EINA_TRUE; +} + +/** + * Get all known elements. + * + * No reference is added to these elements, since there are no threads + * in the system, you are free to add references yourself right after + * the return of this call without race condition, elements by the + * system (ie: elementRemoved signal)could only be touched on the next + * main loop iteration. + * + * @param count return the number of elements in array. + * @param p_elements array with all elements, these are not referenced + * and in no particular order, just set if return is 1. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_elements_get_all(unsigned int *count, E_Bluez_Element ***p_elements) +{ + E_Bluez_Element **p; + + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(p_elements, EINA_FALSE); + + if (!_e_bluez_elements_get_allocate(count, p_elements)) + return EINA_FALSE; + + p = *p_elements; + eina_hash_foreach(elements, (Eina_Hash_Foreach)_e_bluez_elements_get_all, + &p); + return EINA_TRUE; +} + +struct e_bluez_elements_get_all_str_data +{ + E_Bluez_Element **elements; + int count; + const char *str; +}; + +static Eina_Bool +_e_bluez_elements_get_all_type(Eina_Hash *hash __UNUSED__, const char *key __UNUSED__, void *e, void *user_data) +{ + struct e_bluez_elements_get_all_str_data *data = user_data; + E_Bluez_Element *element = e; + + if ((data->str) && (element->interface != data->str)) + return EINA_TRUE; + + data->elements[data->count] = element; + data->count++; + return EINA_TRUE; +} + +/** + * Get all known elements of type. + * + * No reference is added to these elements, since there are no threads + * in the system, you are free to add references yourself right after + * the return of this call without race condition, elements by the + * system (ie: ElementRemoved signal) could only be touched on the next + * main loop iteration. + * + * @param type type to filter, or NULL to get all. + * @param count return the number of elements in array. + * @param p_elements array with all elements, these are not referenced + * and in no particular order, just set if return is 1. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * + * @see e_bluez_elements_get_all() + */ +Eina_Bool +e_bluez_elements_get_all_type(const char *type, unsigned int *count, E_Bluez_Element ***p_elements) +{ + struct e_bluez_elements_get_all_str_data data; + + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(p_elements, EINA_FALSE); + + if (!_e_bluez_elements_get_allocate(count, p_elements)) + return EINA_FALSE; + + data.elements = *p_elements; + data.count = 0; + data.str = eina_stringshare_add(type); + eina_hash_foreach(elements, + (Eina_Hash_Foreach)_e_bluez_elements_get_all_type, + &data); + + eina_stringshare_del(data.str); + *count = data.count; + return EINA_TRUE; +} + +/** + * Get the element registered at given path. + * + * @param path the path to query for registered object. + * + * @return element pointer if found, NULL otherwise. No references are added. + */ +E_Bluez_Element * +e_bluez_element_get(const char *path) +{ + E_Bluez_Element *element; + + EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL); + element = eina_hash_find(elements, path); + + return element; +} + +static void +_e_bluez_element_property_changed_callback(void *data, DBusMessage *msg) +{ + E_Bluez_Element *element = (E_Bluez_Element *)data; + DBusMessageIter itr, v_itr; + int t, r, changed = 0; + const char *name = NULL; + void *value = NULL; + + DBG("Property changed in element %s", element->path); + + if (!_dbus_callback_check_and_init(msg, &itr, NULL)) + return; + + t = dbus_message_iter_get_arg_type(&itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_STRING)) + { + ERR("missing name in property changed signal"); + return; + } + + dbus_message_iter_get_basic(&itr, &name); + + dbus_message_iter_next(&itr); + t = dbus_message_iter_get_arg_type(&itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_VARIANT)) + { + ERR("missing value in property changed signal"); + return; + } + + dbus_message_iter_recurse(&itr, &v_itr); + t = dbus_message_iter_get_arg_type(&v_itr); + + if (t == DBUS_TYPE_ARRAY) + { + value = e_bluez_element_iter_get_array(&v_itr, name); + } + else if (t != DBUS_TYPE_INVALID) + { + dbus_message_iter_get_basic(&v_itr, &value); + } + else + { + ERR("property has invalid type %s", name); + return; + } + + r = _e_bluez_element_property_value_add(element, name, t, value); + if (r < 0) + { + ERR("failed to add property value %s (%c)", name, t); + } + else if (r == 1) + { + INF("property value changed %s (%c)", name, t); + changed = 1; + } + + if (changed) + _e_bluez_element_listeners_call(element); +} + +/** + * Register the given path, possible creating and element and return it. + * + * This will check if path is already registered, in that case the + * exiting element is returned. If it was not registered yet, a new + * element is created, registered and returned. + * + * This call will not add extra references to the object. + * + * @param path the path to register the element + * + * @return the registered object, no references are added. + */ +E_Bluez_Element * +e_bluez_element_register(const char *path, const char *interface) +{ + E_Bluez_Element *element; + + EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(interface, NULL); + + element = eina_hash_find(elements, path); + if (element) + return element; + + element = e_bluez_element_new(path, interface); + if (!element) + return NULL; + + if (!eina_hash_add(elements, element->path, element)) + { + ERR("could not add element %s to hash, delete it.", path); + e_bluez_element_free(element); + return NULL; + } + + element->signal_handler = + e_dbus_signal_handler_add + (e_bluez_conn, e_bluez_system_bus_name_get(), + element->path, element->interface, "PropertyChanged", + _e_bluez_element_property_changed_callback, element); + + e_bluez_element_event_add(E_BLUEZ_EVENT_ELEMENT_ADD, element); + + return element; +} + +static void +_e_bluez_element_event_unregister_and_free(void *data __UNUSED__, void *ev) +{ + E_Bluez_Element *element = ev; + e_bluez_element_unref(element); +} + +static void +_e_bluez_element_unregister_internal(E_Bluez_Element *element) +{ + if (element->signal_handler) + { + e_dbus_signal_handler_del(e_bluez_conn, element->signal_handler); + element->signal_handler = NULL; + } + + ecore_event_add(E_BLUEZ_EVENT_ELEMENT_DEL, element, + _e_bluez_element_event_unregister_and_free, NULL); +} + +/** + * Forget about the given element. + * + * This will remove the element from the pool of known objects, then + * add an E_BLUEZ_EVENT_ELEMENT_DEL and after that will unreference it, + * possible freeing it. + * + * @param element element to forget about. Its reference will be removed. + */ +void +e_bluez_element_unregister(E_Bluez_Element *element) +{ + if (!element) + return; + + if (elements) + eina_hash_del_by_key(elements, element->path); +} + +/** + * Remove all known elements. + * + * This will remove all known elements but will NOT add any + * E_BLUEZ_EVENT_ELEMENT_DEL to main loop. + * + * This is just useful to make sure next e_bluez_manager_sync_elements() + * will not leave any stale elements. This is unlikely to happen, as + * E_Bluez is supposed to catch all required events to avoid stale elements. + */ +void +e_bluez_manager_clear_elements(void) +{ + e_bluez_elements_shutdown(); + e_bluez_elements_init(); +} + +/** + * Creates elements hash. + * + * This has no init counter since its already guarded by other code. + * @internal + */ +void +e_bluez_elements_init(void) +{ + EINA_SAFETY_ON_FALSE_RETURN(!elements); + elements = + eina_hash_string_superfast_new(EINA_FREE_CB + (_e_bluez_element_unregister_internal)); +} + +void +e_bluez_elements_shutdown(void) +{ + EINA_SAFETY_ON_FALSE_RETURN(!!elements); + eina_hash_free(elements); + elements = NULL; +} + +static inline Eina_Bool +_e_bluez_element_is(const E_Bluez_Element *element, const char *interface) +{ + return element->interface == interface; +} + +Eina_Bool +e_bluez_element_is_adapter(const E_Bluez_Element *element) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + return _e_bluez_element_is(element, e_bluez_iface_adapter); +} + +Eina_Bool +e_bluez_element_is_device(const E_Bluez_Element *element) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + return _e_bluez_element_is(element, e_bluez_iface_device); +} + diff --git a/src/lib/bluez/e_bluez_manager.c b/src/lib/bluez/e_bluez_manager.c new file mode 100644 index 0000000..1ab1eb5 --- /dev/null +++ b/src/lib/bluez/e_bluez_manager.c @@ -0,0 +1,34 @@ +#include "e_bluez_private.h" + +/** + * Get the element manager. + * + * @return element pointer if found, NULL otherwise. + */ +E_Bluez_Element * +e_bluez_manager_get(void) +{ + return e_bluez_element_get(manager_path); +} + +/** + * + * Call method DefaultAdapter() on Manager. + * TODO: Soon this method will be replaced by ANY adapter inside BlueZ, so we + * won't need t to call it anymore. + * + * @param cb function to call when server replies or some error happens. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_bluez_manager_default_adapter(E_DBus_Method_Return_Cb cb, void *data) +{ + E_Bluez_Element *element = e_bluez_element_get(manager_path); + const char name[] = "DefaultAdapter"; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + return e_bluez_element_call_full + (element, name, NULL, &element->_pending.properties_get, cb, data); +} + diff --git a/src/lib/bluez/e_bluez_private.h b/src/lib/bluez/e_bluez_private.h new file mode 100644 index 0000000..6e6b863 --- /dev/null +++ b/src/lib/bluez/e_bluez_private.h @@ -0,0 +1,143 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_ALLOCA_H +# include +#elif defined __GNUC__ +# define alloca __builtin_alloca +#elif defined _AIX +# define alloca __alloca +#elif defined _MSC_VER +# include +# define alloca _alloca +#else +# include +# ifdef __cplusplus +extern "C" +# endif +void * alloca (size_t); +#endif + +#include + +#include +#include + +#include "E_Bluez.h" + +static const char manager_path[] = "/"; + +extern const char *e_bluez_iface_manager; +extern const char *e_bluez_iface_adapter; +extern const char *e_bluez_iface_device; +extern const char *e_bluez_prop_address; +extern const char *e_bluez_prop_name; +extern const char *e_bluez_prop_alias; +extern const char *e_bluez_prop_class; +extern const char *e_bluez_prop_icon; +extern const char *e_bluez_prop_paired; +extern const char *e_bluez_prop_trusted; +extern const char *e_bluez_prop_connected; +extern const char *e_bluez_prop_uuids; +extern const char *e_bluez_prop_powered; +extern const char *e_bluez_prop_discoverable; +extern const char *e_bluez_prop_pairable; +extern const char *e_bluez_prop_discoverabletimeout; +extern const char *e_bluez_prop_pairabletimeout; +extern const char *e_bluez_prop_discovering; +extern const char *e_bluez_prop_devices; + +extern int _e_dbus_bluez_log_dom; + +typedef struct _E_Bluez_Element_Dict_Entry E_Bluez_Element_Dict_Entry; + +struct _E_Bluez_Element_Dict_Entry +{ + const char *name; + int type; + union { + Eina_Bool boolean; + const char *str; + short i16; + unsigned short u16; + unsigned int u32; + unsigned char byte; + const char *path; + } value; +}; + +#ifndef EINA_LOG_DEFAULT_COLOR +#define EINA_LOG_DEFAULT_COLOR EINA_COLOR_CYAN +#endif + +#undef DBG +#undef INF +#undef WRN +#undef ERR + +#define DBG(...) EINA_LOG_DOM_DBG(_e_dbus_bluez_log_dom, __VA_ARGS__) +#define INF(...) EINA_LOG_DOM_INFO(_e_dbus_bluez_log_dom, __VA_ARGS__) +#define WRN(...) EINA_LOG_DOM_WARN(_e_dbus_bluez_log_dom, __VA_ARGS__) +#define ERR(...) EINA_LOG_DOM_ERR(_e_dbus_bluez_log_dom, __VA_ARGS__) + +static inline Eina_Bool +_dbus_callback_check_and_init(DBusMessage *msg, DBusMessageIter *itr, DBusError *err) +{ + if (!msg) + { + if (err) + ERR("an error was reported by server: " + "name=\"%s\", message=\"%s\"", + err->name, err->message); + else + ERR("callback without message arguments!"); + + return EINA_FALSE; + } + + if (!dbus_message_iter_init(msg, itr)) + { + ERR("could not init iterator."); + return EINA_FALSE; + } + + return EINA_TRUE; +} + +static inline Eina_Bool +__dbus_iter_type_check(int type, int expected, const char *expected_name) +{ + if (type == expected) + return EINA_TRUE; + + ERR("expected type %s (%c) but got %c instead!", + expected_name, expected, type); + + return EINA_FALSE; +} + +#define _dbus_iter_type_check(t, e) __dbus_iter_type_check(t, e, # e) + +extern E_DBus_Connection *e_bluez_conn; + +const char * e_bluez_system_bus_name_get(void); + +void e_bluez_manager_clear_elements(void); + +void e_bluez_elements_init(void); +void e_bluez_elements_shutdown(void); + +E_Bluez_Element * e_bluez_element_register(const char *path, const char *interface); +void e_bluez_element_unregister(E_Bluez_Element *element); + +Eina_Bool e_bluez_element_message_send(E_Bluez_Element *element, const char *method_name, E_DBus_Method_Return_Cb cb, DBusMessage *msg, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data); +E_Bluez_Array * e_bluez_element_iter_get_array(DBusMessageIter *itr, const char *key); +void e_bluez_element_event_add(int event_type, E_Bluez_Element *element); +E_Bluez_Element_Dict_Entry * e_bluez_element_array_dict_find_stringshared(const E_Bluez_Array *array, const char *key); +void e_bluez_element_array_free(E_Bluez_Array *array, E_Bluez_Array *new __UNUSED__); + +Eina_Bool e_bluez_element_call_full(E_Bluez_Element *element, const char *method_name, E_DBus_Method_Return_Cb cb, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data); +Eina_Bool e_bluez_element_call_with_path(E_Bluez_Element *element, const char *method_name, const char *string, E_DBus_Method_Return_Cb cb, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data); +Eina_Bool e_bluez_element_call_with_string(E_Bluez_Element *element, const char *method_name, const char *string, E_DBus_Method_Return_Cb cb, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data); +Eina_Bool e_bluez_element_call_with_path_and_string(E_Bluez_Element *element, const char *method_name, const char *path, const char *string, E_DBus_Method_Return_Cb cb, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data); diff --git a/src/lib/connman0_7x/E_Connman.h b/src/lib/connman0_7x/E_Connman.h new file mode 100644 index 0000000..06e879a --- /dev/null +++ b/src/lib/connman0_7x/E_Connman.h @@ -0,0 +1,251 @@ +#ifndef E_CONNMAN_H +#define E_CONNMAN_H + +#include +#include + +#include +#include +#include + +/** + * @defgroup EConnman_Group EConnman + * + * Currently supporting upstream API version 0.75 and later. + * + * @note this API is subject to changed based on upstream connman changes, + * then it is required to acknowledge this by defining: + * @code + * #define E_CONNMAN_I_KNOW_THIS_API_IS_SUBJECT_TO_CHANGE 1 + * @endcode + * + * @{ + */ +#ifndef E_CONNMAN_I_KNOW_THIS_API_IS_SUBJECT_TO_CHANGE +#error "E_Connman.h is an unstable API linked to upstream connman project" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Ecore Events */ +extern int E_CONNMAN_EVENT_MANAGER_IN; +extern int E_CONNMAN_EVENT_MANAGER_OUT; +extern int E_CONNMAN_EVENT_ELEMENT_ADD; +extern int E_CONNMAN_EVENT_ELEMENT_DEL; +extern int E_CONNMAN_EVENT_ELEMENT_UPDATED; + +typedef struct _E_Connman_Element E_Connman_Element; + +struct _E_Connman_Element +{ + const char *path; + const char *interface; + E_DBus_Signal_Handler *signal_handler; + Eina_Inlist *props; + + /* private */ + struct + { + Eina_Inlist *properties_get; + Eina_Inlist *property_set; + Eina_Inlist *agent_register; + Eina_Inlist *agent_unregister; + Eina_Inlist *request_scan; + Eina_Inlist *technology_enable; + Eina_Inlist *technology_disable; + Eina_Inlist *profile_remove; + Eina_Inlist *service_connect; + Eina_Inlist *service_disconnect; + Eina_Inlist *service_remove; + Eina_Inlist *service_move_before; + Eina_Inlist *service_move_after; + Eina_Inlist *service_clear_property; + } _pending; + struct + { + Ecore_Idler *changed; + } _idler; + Eina_Inlist *_listeners; + int _references; +}; + +/* General Public API */ +EAPI unsigned int e_connman_system_init(E_DBus_Connection *edbus_conn) EINA_ARG_NONNULL(1); +EAPI unsigned int e_connman_system_shutdown(void); + +/* Manager Methods */ +EAPI E_Connman_Element * e_connman_manager_get(void) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_manager_agent_register(const char *object_path, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_manager_agent_unregister(const char *object_path, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_manager_state_get(const char **state) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_manager_offline_mode_get(Eina_Bool *offline) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_manager_offline_mode_set(Eina_Bool offline, E_DBus_Method_Return_Cb cb, const void *data) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_manager_profiles_get(unsigned int *count, E_Connman_Element ***p_elements) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_manager_services_get(unsigned int *count, E_Connman_Element ***p_elements) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_manager_technologies_get(unsigned int *count, E_Connman_Element ***p_elements) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_manager_request_scan(const char *type, E_DBus_Method_Return_Cb cb, const void *data) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_manager_technology_default_get(const char **type) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_manager_technology_enable(const char *type, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_manager_technology_disable(const char *type, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_manager_technologies_available_get(unsigned int *count, const char ***strings) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_manager_technologies_enabled_get(unsigned int *count, const char ***strings) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_manager_technologies_connected_get(unsigned int *count, const char ***strings) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_manager_profile_remove(const E_Connman_Element *profile, E_DBus_Method_Return_Cb cb, const void *data) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_manager_profile_active_get(E_Connman_Element **profile) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_manager_profile_active_set(const E_Connman_Element *profile, E_DBus_Method_Return_Cb cb, const void *data) EINA_WARN_UNUSED_RESULT; + +// TODO: profile_create() +// TODO: service_connect() (actually creates and connect) +// TODO: signal E_CONNMAN_EVENT_MANAGER_STATE_CHANGED + +/* Profile Methods */ +EAPI E_Connman_Element * e_connman_profile_get(const char *path) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_profile_name_get(const E_Connman_Element *profile, const char **name) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_profile_name_set(E_Connman_Element *profile, const char *name, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_profile_offline_mode_get(const E_Connman_Element *profile, Eina_Bool *offline) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_profile_offline_mode_set(E_Connman_Element *profile, Eina_Bool offline, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_profile_services_get(const E_Connman_Element *profile, unsigned int *count, E_Connman_Element ***p_elements) EINA_ARG_NONNULL(1, 2, 3) EINA_WARN_UNUSED_RESULT; + +/* Services Methods */ +EAPI E_Connman_Element * e_connman_service_get(const char *path) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_service_clear_property(E_Connman_Element *service, const char *property, E_DBus_Method_Return_Cb cb, const void *data); + +EAPI Eina_Bool e_connman_service_remove(E_Connman_Element *service, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_service_connect(E_Connman_Element *service, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_disconnect(E_Connman_Element *service, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_service_move_before(E_Connman_Element *service, const char *object_path, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_move_after(E_Connman_Element *service, const char *object_path, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_service_state_get(const E_Connman_Element *service, const char **state) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_error_get(const E_Connman_Element *service, const char **error) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_name_get(const E_Connman_Element *service, const char **name) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_type_get(const E_Connman_Element *service, const char **type) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_security_get(const E_Connman_Element *service, unsigned int *count, const char ***security) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_service_passphrase_get(const E_Connman_Element *service, const char **passphrase) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_passphrase_set(E_Connman_Element *service, const char *passphrase, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_service_strength_get(const E_Connman_Element *service, unsigned char *strength) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_service_favorite_get(const E_Connman_Element *service, Eina_Bool *favorite) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_immutable_get(const E_Connman_Element *service, Eina_Bool *immutable) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_service_auto_connect_get(const E_Connman_Element *service, Eina_Bool *auto_connect) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_auto_connect_set(E_Connman_Element *service, Eina_Bool auto_connect, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_service_passphrase_required_get(const E_Connman_Element *service, Eina_Bool *passphrase_required) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_login_required_get(const E_Connman_Element *service, Eina_Bool *login_required) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_service_roaming_get(const E_Connman_Element *service, Eina_Bool *roaming) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_nameservers_get(const E_Connman_Element *service, unsigned int *count, const char ***nameserver) EINA_ARG_NONNULL(1, 2, 3) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_nameservers_configuration_get(const E_Connman_Element *service, unsigned int *count, const char ***nameservers) EINA_ARG_NONNULL(1, 2, 3) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_nameservers_configuration_set(E_Connman_Element *service, unsigned int count, const char **nameservers, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1, 2, 3, 4, 5) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_domains_get(const E_Connman_Element *service, unsigned int *count, const char ***domains) EINA_ARG_NONNULL(1, 2, 3) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_service_ipv4_method_get(const E_Connman_Element *service, const char **method) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_ipv4_address_get(const E_Connman_Element *service, const char **address) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_ipv4_gateway_get(const E_Connman_Element *service, const char **gateway) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_ipv4_netmask_get(const E_Connman_Element *service, const char **netmask) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_service_ipv4_configuration_method_get(const E_Connman_Element *service, const char **method) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_ipv4_configuration_address_get(const E_Connman_Element *service, const char **address) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_ipv4_configuration_gateway_get(const E_Connman_Element *service, const char **gateway) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_ipv4_configuration_netmask_get(const E_Connman_Element *service, const char **netmask) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_service_proxy_method_get(const E_Connman_Element *service, const char **method) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_proxy_url_get(const E_Connman_Element *service, const char **url) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_proxy_servers_get(const E_Connman_Element *service, unsigned int *count, const char ***servers) EINA_ARG_NONNULL(1, 2, 3) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_proxy_excludes_get(const E_Connman_Element *service, unsigned int *count, const char ***excludes) EINA_ARG_NONNULL(1, 2, 3) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_service_proxy_configuration_method_get(const E_Connman_Element *service, const char **method) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_proxy_configuration_url_get(const E_Connman_Element *service, const char **url) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_proxy_configuration_servers_get(const E_Connman_Element *service, unsigned int *count, const char ***servers) EINA_ARG_NONNULL(1, 2, 3) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_proxy_configuration_excludes_get(const E_Connman_Element *service, unsigned int *count, const char ***excludes) EINA_ARG_NONNULL(1, 2, 3) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_service_ethernet_method_get(const E_Connman_Element *service, const char **method) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_ethernet_interface_get(const E_Connman_Element *service, const char **iface) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_ethernet_address_get(const E_Connman_Element *service, const char **address) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_ethernet_mtu_get(const E_Connman_Element *service, unsigned short *mtu) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_ethernet_speed_get(const E_Connman_Element *service, unsigned short *speed) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_ethernet_duplex_get(const E_Connman_Element *service, const char **duplex) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +/* Methods to configure IPv4 service */ +EAPI Eina_Bool e_connman_service_ipv4_configure_dhcp(E_Connman_Element *service, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_service_ipv4_configure_manual(E_Connman_Element *service, const char *address, const char *netmask, const char *gateway, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +/* Technologies methods */ +EAPI E_Connman_Element * e_connman_technology_get(const char *path) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_technology_state_get(const E_Connman_Element *technology, const char **state) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_technology_name_get(const E_Connman_Element *technology, const char **state) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_technology_type_get(const E_Connman_Element *technology, const char **state) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +/* Low-Level API: + * + * Should just be used to work around problems until proper solution + * is made into e_connman. + */ +EAPI Eina_Bool e_connman_manager_sync_elements(void); + +EAPI Eina_Bool e_connman_elements_get_all(unsigned int *count, E_Connman_Element ***p_elements) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_elements_get_all_type(const char *type, unsigned int *count, E_Connman_Element ***p_elements) EINA_ARG_NONNULL(1, 2, 3) EINA_WARN_UNUSED_RESULT; +EAPI E_Connman_Element * e_connman_element_get(const char *path); + +EAPI void e_connman_element_listener_add(E_Connman_Element *element, void (*cb)(void *data, const E_Connman_Element *element), const void *data, void (*free_data)(void *data)) EINA_ARG_NONNULL(1, 2); +EAPI void e_connman_element_listener_del(E_Connman_Element *element, void (*cb)(void *data, const E_Connman_Element *element), const void *data) EINA_ARG_NONNULL(1, 2); + +EAPI int e_connman_element_ref(E_Connman_Element *element) EINA_ARG_NONNULL(1); +EAPI int e_connman_element_unref(E_Connman_Element *element) EINA_ARG_NONNULL(1); + +EAPI void e_connman_element_print(FILE *fp, const E_Connman_Element *element) EINA_ARG_NONNULL(1, 2); + +EAPI Eina_Bool e_connman_element_properties_sync(E_Connman_Element *element) EINA_ARG_NONNULL(1); +EAPI Eina_Bool e_connman_element_properties_sync_full(E_Connman_Element *element, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1); +EAPI void e_connman_element_properties_list(const E_Connman_Element *element, Eina_Bool (*cb)(void *data, const E_Connman_Element *element, const char *name, int type, const void *value), const void *data) EINA_ARG_NONNULL(1, 2); + +EAPI Eina_Bool e_connman_element_property_set(E_Connman_Element *element, const char *prop, int type, const void *value) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_element_property_set_full(E_Connman_Element *element, const char *prop, int type, const void *value, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_element_property_array_set_full(E_Connman_Element *element, const char *prop, int type, unsigned int count, const void * const *values, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_element_property_dict_set_full(E_Connman_Element *element, const char *prop, const char *key, int type, const void *value, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1, 2, 3) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_element_property_type_get_stringshared(const E_Connman_Element *element, const char *name, int *type) EINA_ARG_NONNULL(1, 2, 3) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_element_property_type_get(const E_Connman_Element *element, const char *name, int *type) EINA_ARG_NONNULL(1, 2, 3) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_element_property_dict_get_stringshared(const E_Connman_Element *element, const char *dict_name, const char *key_name, int *type, void *value) EINA_ARG_NONNULL(1, 2, 4) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_element_property_dict_strings_array_get_stringshared(const E_Connman_Element *element, const char *dict_name, const char *key, unsigned int *count, const char ***strings) EINA_ARG_NONNULL(1, 2, 3, 4) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_element_property_get_stringshared(const E_Connman_Element *element, const char *name, int *type, void *value) EINA_ARG_NONNULL(1, 2, 4) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_element_property_get(const E_Connman_Element *element, const char *name, int *type, void *value) EINA_ARG_NONNULL(1, 2, 4) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_connman_element_is_manager(const E_Connman_Element *element) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_element_is_profile(const E_Connman_Element *element) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_element_is_service(const E_Connman_Element *element) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_connman_element_is_technology(const E_Connman_Element *element) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* E_CONNMAN_H */ diff --git a/src/lib/connman0_7x/Makefile.am b/src/lib/connman0_7x/Makefile.am new file mode 100644 index 0000000..1d645c2 --- /dev/null +++ b/src/lib/connman0_7x/Makefile.am @@ -0,0 +1,24 @@ +MAINTAINERCLEANFILES = Makefile.in + +AM_CPPFLAGS = \ +-I $(top_srcdir)/src/lib/dbus \ +@EDBUS_CFLAGS@ + +lib_LTLIBRARIES = libeconnman0_7x.la +includes_HEADERS = E_Connman.h +includesdir = $(includedir)/e_dbus-@VMAJ@/connman0_7x + +libeconnman0_7x_la_SOURCES = \ +e_connman.c \ +e_connman_element.c \ +e_connman_manager.c \ +e_connman_profile.c \ +e_connman_service.c \ +e_connman_technology.c + +libeconnman0_7x_la_LIBADD = \ +@EDBUS_LIBS@ $(top_builddir)/src/lib/dbus/libedbus.la + +libeconnman0_7x_la_LDFLAGS = -version-info @version_info@ @release_info@ + +EXTRA_DIST = e_connman_private.h diff --git a/src/lib/connman0_7x/e_connman.c b/src/lib/connman0_7x/e_connman.c new file mode 100644 index 0000000..2302bcd --- /dev/null +++ b/src/lib/connman0_7x/e_connman.c @@ -0,0 +1,439 @@ +#include "e_connman_private.h" +#include +#include + +static E_DBus_Signal_Handler *cb_name_owner_changed = NULL; +static DBusPendingCall *pending_get_name_owner = NULL; +static unsigned int init_count = 0; +static char *unique_name = NULL; + +static const char bus_name[] = "net.connman"; + +E_DBus_Connection *e_connman_conn = NULL; + +EAPI int E_CONNMAN_EVENT_MANAGER_IN = 0; +EAPI int E_CONNMAN_EVENT_MANAGER_OUT = 0; +EAPI int E_CONNMAN_EVENT_ELEMENT_ADD = 0; +EAPI int E_CONNMAN_EVENT_ELEMENT_DEL = 0; +EAPI int E_CONNMAN_EVENT_ELEMENT_UPDATED = 0; + +const char *e_connman_iface_manager = NULL; +const char *e_connman_iface_profile = NULL; +const char *e_connman_iface_service = NULL; +const char *e_connman_iface_connection = NULL; +const char *e_connman_iface_technology = NULL; + +const char *e_connman_prop_ipv4 = NULL; +const char *e_connman_prop_ipv4_configuration = NULL; +const char *e_connman_prop_ethernet = NULL; +const char *e_connman_prop_interface = NULL; +const char *e_connman_prop_speed = NULL; +const char *e_connman_prop_duplex = NULL; +const char *e_connman_prop_method = NULL; +const char *e_connman_prop_address = NULL; +const char *e_connman_prop_gateway = NULL; +const char *e_connman_prop_netmask = NULL; +const char *e_connman_prop_mtu = NULL; +const char *e_connman_prop_name = NULL; +const char *e_connman_prop_offline_mode = NULL; +const char *e_connman_prop_profiles = NULL; +const char *e_connman_prop_profile_active = NULL; +const char *e_connman_prop_services = NULL; +const char *e_connman_prop_technologies = NULL; +const char *e_connman_prop_state = NULL; +const char *e_connman_prop_strength = NULL; +const char *e_connman_prop_type = NULL; +const char *e_connman_prop_error = NULL; +const char *e_connman_prop_security = NULL; +const char *e_connman_prop_passphrase = NULL; +const char *e_connman_prop_passphrase_required = NULL; +const char *e_connman_prop_login_required = NULL; +const char *e_connman_prop_favorite = NULL; +const char *e_connman_prop_immutable = NULL; +const char *e_connman_prop_auto_connect = NULL; +const char *e_connman_prop_roaming = NULL; +const char *e_connman_prop_technology_default = NULL; +const char *e_connman_prop_technologies_available = NULL; +const char *e_connman_prop_technologies_enabled = NULL; +const char *e_connman_prop_technologies_connected = NULL; +const char *e_connman_prop_nameservers = NULL; +const char *e_connman_prop_nameservers_configuration = NULL; +const char *e_connman_prop_domains = NULL; +const char *e_connman_prop_domains_configuration = NULL; +const char *e_connman_prop_proxy = NULL; +const char *e_connman_prop_proxy_configuration = NULL; +const char *e_connman_prop_url = NULL; +const char *e_connman_prop_servers = NULL; +const char *e_connman_prop_excludes = NULL; + +int _e_dbus_connman_log_dom = -1; + +const char * +e_connman_system_bus_name_get(void) +{ + return unique_name ? unique_name : bus_name; +} + +/*********************************************************************** +* Manager +***********************************************************************/ + +/** + * Synchronize elements with server. + * + * This will call Manager.GetProperties() on server, retrieve properties + * and some element paths and then request their properties. + * + * This call will add events E_CONNMAN_EVENT_ELEMENT_ADD and + * E_CONNMAN_EVENT_ELEMENT_UPDATED to the main loop. + * + * This will not remove stale elements. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_manager_sync_elements(void) +{ + E_Connman_Element *manager; + + if (!unique_name) + return EINA_FALSE; + + manager = e_connman_element_register(manager_path, e_connman_iface_manager); + if (manager) + e_connman_element_properties_sync(manager); + else + return EINA_FALSE; + + DBG("sync_manager: %s (%s)", unique_name, bus_name); + + return EINA_TRUE; +} + +static void +_e_connman_system_name_owner_exit(void) +{ + e_connman_manager_clear_elements(); + ecore_event_add(E_CONNMAN_EVENT_MANAGER_OUT, NULL, NULL, NULL); + + free(unique_name); + unique_name = NULL; +} + +static void +_e_connman_system_name_owner_enter(const char *uid) +{ + DBG("enter connman at %s (old was %s)", uid, unique_name); + if (unique_name && strcmp(unique_name, uid) == 0) + { + DBG("same unique_name for connman, ignore."); + return; + } + + if (unique_name) + _e_connman_system_name_owner_exit(); + + unique_name = strdup(uid); + + ecore_event_add(E_CONNMAN_EVENT_MANAGER_IN, NULL, NULL, NULL); + e_connman_manager_sync_elements(); +} + +static void +_e_connman_system_name_owner_changed(void *data __UNUSED__, DBusMessage *msg) +{ + DBusError err; + const char *name, *from, *to; + + dbus_error_init(&err); + if (!dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &from, + DBUS_TYPE_STRING, &to, + DBUS_TYPE_INVALID)) + { + ERR("could not get NameOwnerChanged arguments: %s: %s", + err.name, err.message); + dbus_error_free(&err); + return; + } + + if (strcmp(name, bus_name) != 0) + return; + + DBG("NameOwnerChanged from=[%s] to=[%s]", from, to); + + if (from[0] == '\0' && to[0] != '\0') + { + _e_connman_system_name_owner_enter(to); + } + else if (from[0] != '\0' && to[0] == '\0') + { + DBG("exit connman at %s", from); + if (strcmp(unique_name, from) != 0) + DBG("%s was not the known name %s, ignored.", from, unique_name); + else + _e_connman_system_name_owner_exit(); + } + else + { + DBG("unknow change from %s to %s", from, to); + } +} + +static void +_e_connman_get_name_owner(void *data __UNUSED__, DBusMessage *msg, DBusError *err) +{ + DBusMessageIter itr; + int t; + const char *uid; + + pending_get_name_owner = NULL; + + if (!_dbus_callback_check_and_init(msg, &itr, err)) + return; + + t = dbus_message_iter_get_arg_type(&itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_STRING)) + return; + + dbus_message_iter_get_basic(&itr, &uid); + if (!uid) + { + ERR("no name owner!"); + return; + } + + _e_connman_system_name_owner_enter(uid); + return; +} + +/** + * Initialize E Connection Manager (E_Connman) system. + * + * This will connect and watch net.connman.Manager and Element + * events and translate to Ecore main loop events, also provide a + * proxy for method invocation on server. + * + * Interesting events are: + * - E_CONNMAN_EVENT_MANAGER_IN: issued when connman is avaiable. + * - E_CONNMAN_EVENT_MANAGER_OUT: issued when connman connection is lost. + * - E_CONNMAN_EVENT_ELEMENT_ADD: element was added. + * - E_CONNMAN_EVENT_ELEMENT_DEL: element was deleted. + * - E_CONNMAN_EVENT_ELEMENT_UPDATED: element was updated (properties + * or state changed). + * + * Manager IN/OUT events do not provide any event information, just + * tells you that system is usable or not. After manager is out, all + * elements will be removed, so after this event do not use the system anymore. + * + * Element events will give you an element object. After DEL event callback + * returns, that element will not be valid anymore. + */ +unsigned int +e_connman_system_init(E_DBus_Connection *edbus_conn) +{ + init_count++; + + if (init_count > 1) + return init_count; + + _e_dbus_connman_log_dom = eina_log_domain_register + ("e_dbus_connman", EINA_LOG_DEFAULT_COLOR); + + if (_e_dbus_connman_log_dom < 0) + { + EINA_LOG_ERR + ("impossible to create a log domain for edbus_connman module"); + return -1; + } + + if (E_CONNMAN_EVENT_MANAGER_IN == 0) + E_CONNMAN_EVENT_MANAGER_IN = ecore_event_type_new(); + + if (E_CONNMAN_EVENT_MANAGER_OUT == 0) + E_CONNMAN_EVENT_MANAGER_OUT = ecore_event_type_new(); + + if (E_CONNMAN_EVENT_ELEMENT_ADD == 0) + E_CONNMAN_EVENT_ELEMENT_ADD = ecore_event_type_new(); + + if (E_CONNMAN_EVENT_ELEMENT_DEL == 0) + E_CONNMAN_EVENT_ELEMENT_DEL = ecore_event_type_new(); + + if (E_CONNMAN_EVENT_ELEMENT_UPDATED == 0) + E_CONNMAN_EVENT_ELEMENT_UPDATED = ecore_event_type_new(); + +#define ADD_STRINGSHARE(name, s) \ + if (!name) \ + name = eina_stringshare_add(s) + + ADD_STRINGSHARE(e_connman_iface_manager, "net.connman.Manager"); + ADD_STRINGSHARE(e_connman_iface_profile, "net.connman.Profile"); + ADD_STRINGSHARE(e_connman_iface_service, "net.connman.Service"); + ADD_STRINGSHARE(e_connman_iface_connection, "net.connman.Connection"); + ADD_STRINGSHARE(e_connman_iface_technology, "net.connman.Technology"); + ADD_STRINGSHARE(e_connman_prop_ipv4, "IPv4"); + ADD_STRINGSHARE(e_connman_prop_ipv4_configuration, "IPv4.Configuration"); + ADD_STRINGSHARE(e_connman_prop_ethernet, "Ethernet"); + ADD_STRINGSHARE(e_connman_prop_interface, "Interface"); + ADD_STRINGSHARE(e_connman_prop_speed, "Speed"); + ADD_STRINGSHARE(e_connman_prop_duplex, "Duplex"); + ADD_STRINGSHARE(e_connman_prop_method, "Method"); + ADD_STRINGSHARE(e_connman_prop_address, "Address"); + ADD_STRINGSHARE(e_connman_prop_gateway, "Gateway"); + ADD_STRINGSHARE(e_connman_prop_netmask, "Netmask"); + ADD_STRINGSHARE(e_connman_prop_mtu, "MTU"); + ADD_STRINGSHARE(e_connman_prop_name, "Name"); + ADD_STRINGSHARE(e_connman_prop_offline_mode, "OfflineMode"); + ADD_STRINGSHARE(e_connman_prop_profiles, "Profiles"); + ADD_STRINGSHARE(e_connman_prop_profile_active, "ActiveProfile"); + ADD_STRINGSHARE(e_connman_prop_services, "Services"); + ADD_STRINGSHARE(e_connman_prop_technologies, "Technologies"); + ADD_STRINGSHARE(e_connman_prop_state, "State"); + ADD_STRINGSHARE(e_connman_prop_strength, "Strength"); + ADD_STRINGSHARE(e_connman_prop_type, "Type"); + ADD_STRINGSHARE(e_connman_prop_error, "Error"); + ADD_STRINGSHARE(e_connman_prop_security, "Security"); + ADD_STRINGSHARE(e_connman_prop_passphrase, "Passphrase"); + ADD_STRINGSHARE(e_connman_prop_passphrase_required, "PassphraseRequired"); + ADD_STRINGSHARE(e_connman_prop_login_required, "LoginRequired"); + ADD_STRINGSHARE(e_connman_prop_favorite, "Favorite"); + ADD_STRINGSHARE(e_connman_prop_immutable, "Immutable"); + ADD_STRINGSHARE(e_connman_prop_auto_connect, "AutoConnect"); + ADD_STRINGSHARE(e_connman_prop_roaming, "Roaming"); + ADD_STRINGSHARE(e_connman_prop_technology_default, "DefaultTechnology"); + ADD_STRINGSHARE(e_connman_prop_technologies_available, + "AvailableTechnologies"); + ADD_STRINGSHARE(e_connman_prop_technologies_enabled, "EnabledTechnologies"); + ADD_STRINGSHARE(e_connman_prop_technologies_connected, + "ConnectedTechnologies"); + ADD_STRINGSHARE(e_connman_prop_nameservers, "Nameservers"); + ADD_STRINGSHARE(e_connman_prop_nameservers_configuration, + "Nameservers.Configuration"); + ADD_STRINGSHARE(e_connman_prop_domains, "Domains"); + ADD_STRINGSHARE(e_connman_prop_domains_configuration, + "Domains.Configuration"); + ADD_STRINGSHARE(e_connman_prop_proxy, "Proxy"); + ADD_STRINGSHARE(e_connman_prop_proxy_configuration, "Proxy.Configuration"); + ADD_STRINGSHARE(e_connman_prop_url, "URL"); + ADD_STRINGSHARE(e_connman_prop_servers, "Servers"); + ADD_STRINGSHARE(e_connman_prop_excludes, "Excludes"); + +#undef ADD_STRINGSHARE + + e_connman_conn = edbus_conn; + cb_name_owner_changed = e_dbus_signal_handler_add + (e_connman_conn, E_DBUS_FDO_BUS, E_DBUS_FDO_PATH, E_DBUS_FDO_INTERFACE, "NameOwnerChanged", + _e_connman_system_name_owner_changed, NULL); + + if (pending_get_name_owner) + dbus_pending_call_cancel(pending_get_name_owner); + + pending_get_name_owner = e_dbus_get_name_owner + (e_connman_conn, bus_name, _e_connman_get_name_owner, NULL); + + e_connman_elements_init(); + + return init_count; +} + +static inline void +_stringshare_del(const char **str) +{ + if (!*str) + return; + + eina_stringshare_del(*str); + *str = NULL; +} + +/** + * Shutdown connman system. + * + * When count drops to 0 resources will be released and no calls should be + * made anymore. + */ +unsigned int +e_connman_system_shutdown(void) +{ + if (init_count == 0) + { + ERR("connman system already shut down."); + return 0; + } + + init_count--; + if (init_count > 0) + return init_count; + + _stringshare_del(&e_connman_iface_manager); + _stringshare_del(&e_connman_iface_profile); + _stringshare_del(&e_connman_iface_service); + _stringshare_del(&e_connman_iface_connection); + _stringshare_del(&e_connman_iface_technology); + + _stringshare_del(&e_connman_prop_ipv4); + _stringshare_del(&e_connman_prop_ipv4_configuration); + _stringshare_del(&e_connman_prop_ethernet); + _stringshare_del(&e_connman_prop_interface); + _stringshare_del(&e_connman_prop_speed); + _stringshare_del(&e_connman_prop_duplex); + _stringshare_del(&e_connman_prop_method); + _stringshare_del(&e_connman_prop_address); + _stringshare_del(&e_connman_prop_gateway); + _stringshare_del(&e_connman_prop_netmask); + _stringshare_del(&e_connman_prop_mtu); + _stringshare_del(&e_connman_prop_name); + _stringshare_del(&e_connman_prop_offline_mode); + _stringshare_del(&e_connman_prop_profiles); + _stringshare_del(&e_connman_prop_profile_active); + _stringshare_del(&e_connman_prop_services); + _stringshare_del(&e_connman_prop_technologies); + _stringshare_del(&e_connman_prop_state); + _stringshare_del(&e_connman_prop_strength); + _stringshare_del(&e_connman_prop_type); + _stringshare_del(&e_connman_prop_error); + _stringshare_del(&e_connman_prop_security); + _stringshare_del(&e_connman_prop_passphrase); + _stringshare_del(&e_connman_prop_passphrase_required); + _stringshare_del(&e_connman_prop_login_required); + _stringshare_del(&e_connman_prop_favorite); + _stringshare_del(&e_connman_prop_immutable); + _stringshare_del(&e_connman_prop_auto_connect); + _stringshare_del(&e_connman_prop_roaming); + _stringshare_del(&e_connman_prop_technology_default); + _stringshare_del(&e_connman_prop_technologies_available); + _stringshare_del(&e_connman_prop_technologies_enabled); + _stringshare_del(&e_connman_prop_technologies_connected); + _stringshare_del(&e_connman_prop_nameservers); + _stringshare_del(&e_connman_prop_nameservers_configuration); + _stringshare_del(&e_connman_prop_domains); + _stringshare_del(&e_connman_prop_domains_configuration); + _stringshare_del(&e_connman_prop_proxy); + _stringshare_del(&e_connman_prop_proxy_configuration); + _stringshare_del(&e_connman_prop_url); + _stringshare_del(&e_connman_prop_servers); + _stringshare_del(&e_connman_prop_excludes); + + if (pending_get_name_owner) + { + dbus_pending_call_cancel(pending_get_name_owner); + pending_get_name_owner = NULL; + } + + if (cb_name_owner_changed) + { + e_dbus_signal_handler_del(e_connman_conn, cb_name_owner_changed); + cb_name_owner_changed = NULL; + } + + if (unique_name) + _e_connman_system_name_owner_exit(); + + e_connman_elements_shutdown(); + eina_log_domain_unregister(_e_dbus_connman_log_dom); + e_connman_conn = NULL; + + return init_count; +} + diff --git a/src/lib/connman0_7x/e_connman_element.c b/src/lib/connman0_7x/e_connman_element.c new file mode 100644 index 0000000..336c0c7 --- /dev/null +++ b/src/lib/connman0_7x/e_connman_element.c @@ -0,0 +1,2504 @@ +#include "e_connman_private.h" +#include +#include +#include + +static Eina_Hash *elements = NULL; + +typedef struct _E_Connman_Element_Pending E_Connman_Element_Pending; +typedef struct _E_Connman_Element_Call_Data E_Connman_Element_Call_Data; +typedef struct _E_Connman_Element_Property E_Connman_Element_Property; +typedef struct _E_Connman_Element_Listener E_Connman_Element_Listener; +typedef struct _E_Connman_Element_Dict_Entry E_Connman_Element_Dict_Entry; + +struct _E_Connman_Element_Pending +{ + EINA_INLIST; + DBusPendingCall *pending; + void *data; + E_DBus_Method_Return_Cb user_cb; + void *user_data; +}; + +struct _E_Connman_Element_Call_Data +{ + E_Connman_Element *element; + E_DBus_Method_Return_Cb cb; + E_Connman_Element_Pending *pending; + Eina_Inlist **p_list; +}; + +struct _E_Connman_Element_Property +{ + EINA_INLIST; + const char *name; + int type; + union { + Eina_Bool boolean; + const char *str; + unsigned short u16; + unsigned int u32; + unsigned char byte; + const char *path; + void *variant; + E_Connman_Array *array; + } value; +}; + +struct _E_Connman_Element_Dict_Entry +{ + const char *name; + int type; + union { + Eina_Bool boolean; + const char *str; + unsigned short u16; + unsigned int u32; + unsigned char byte; + const char *path; + E_Connman_Array *array; + } value; +}; + +struct _E_Connman_Element_Listener +{ + EINA_INLIST; + void (*cb)(void *data, const E_Connman_Element *element); + void *data; + void (*free_data)(void *data); +}; + +static void +_e_connman_element_event_no_free(void *data __UNUSED__, void *ev) +{ + E_Connman_Element *element = ev; + e_connman_element_unref(element); +} + +static void +e_connman_element_event_add(int event_type, E_Connman_Element *element) +{ + e_connman_element_ref(element); + ecore_event_add + (event_type, element, _e_connman_element_event_no_free, element); +} + +static void +e_connman_element_call_dispatch_and_free(void *d, DBusMessage *msg, DBusError *err) +{ + E_Connman_Element_Call_Data *data = d; + E_Connman_Element_Pending *pending; + + pending = data->pending; + pending->pending = NULL; + + if (data->cb) + data->cb(data->element, msg, err); + + if (pending->user_cb) + pending->user_cb(pending->user_data, msg, err); + + pending->data = NULL; + *data->p_list = eina_inlist_remove(*data->p_list, EINA_INLIST_GET(pending)); + free(pending); + free(data); +} + +static void +e_connman_element_pending_cancel_and_free(Eina_Inlist **pending) +{ + while (*pending) + { + E_Connman_Element_Pending *p = (E_Connman_Element_Pending *)*pending; + DBusError err; + + dbus_pending_call_cancel(p->pending); + + dbus_error_init(&err); + dbus_set_error(&err, "Canceled", "Pending method call was canceled."); + e_connman_element_call_dispatch_and_free(p->data, NULL, &err); + dbus_error_free(&err); + } +} + +void +e_connman_element_listener_add(E_Connman_Element *element, void (*cb)(void *data, const E_Connman_Element *element), const void *data, void (*free_data)(void *data)) +{ + E_Connman_Element_Listener *l; + EINA_SAFETY_ON_FALSE_GOTO(element, error); + EINA_SAFETY_ON_FALSE_GOTO(cb, error); + + l = malloc(sizeof(*l)); + if (!l) + { + ERR("could not allocate E_Connman_Element_Listener"); + goto error; + } + + l->cb = cb; + l->data = (void *)data; + l->free_data = free_data; + + element->_listeners = eina_inlist_append + (element->_listeners, EINA_INLIST_GET(l)); + + return; + +error: + if (free_data) + free_data((void *)data); +} + +void +e_connman_element_listener_del(E_Connman_Element *element, void (*cb)(void *data, const E_Connman_Element *element), const void *data) +{ + E_Connman_Element_Listener *l; + + EINA_SAFETY_ON_NULL_RETURN(element); + EINA_SAFETY_ON_NULL_RETURN(cb); + + EINA_INLIST_FOREACH(element->_listeners, l) + if ((l->cb == cb) && (l->data == data)) + { + element->_listeners = eina_inlist_remove + (element->_listeners, EINA_INLIST_GET(l)); + if (l->free_data) + l->free_data(l->data); + + free(l); + return; + } +} + +static void +_e_connman_element_listeners_call_do(E_Connman_Element *element) +{ + E_Connman_Element_Listener *l; + Eina_Inlist *x; + + /* NB: iterate on a copy in order to allow listeners to be deleted + * from callbacks. number of listeners should be small, so the + * following should do fine. + */ + if (eina_inlist_count(element->_listeners) < 1) goto end; + + EINA_INLIST_FOREACH_SAFE(element->_listeners, x, l) + l->cb(l->data, element); + +end: + e_connman_element_event_add(E_CONNMAN_EVENT_ELEMENT_UPDATED, element); +} + +static Eina_Bool +_e_connman_element_listeners_call_idler(void *data) +{ + E_Connman_Element *element = data; + _e_connman_element_listeners_call_do(element); + element->_idler.changed = NULL; + return ECORE_CALLBACK_CANCEL; +} + +static void +_e_connman_element_listeners_call(E_Connman_Element *element) +{ + if (element->_idler.changed) + return; + + element->_idler.changed = ecore_idler_add + (_e_connman_element_listeners_call_idler, element); +} + +/*********************************************************************** +* Property +***********************************************************************/ + +static void +_e_connman_element_dict_entry_free(E_Connman_Element_Dict_Entry *entry) +{ + switch (entry->type) + { + case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_BYTE: + case DBUS_TYPE_UINT16: + case DBUS_TYPE_UINT32: + break; + + case DBUS_TYPE_OBJECT_PATH: + eina_stringshare_del(entry->value.path); + break; + + case DBUS_TYPE_STRING: + eina_stringshare_del(entry->value.str); + break; + + default: + ERR("don't know how to free dict entry '%s' of type %c (%d)", + entry->name, entry->type, entry->type); + } + + eina_stringshare_del(entry->name); + free(entry); +} + +static E_Connman_Element_Dict_Entry * +_e_connman_element_dict_entry_new(DBusMessageIter *itr) +{ + E_Connman_Element_Dict_Entry *entry; + DBusMessageIter e_itr, v_itr; + int t; + const char *key = NULL; + void *value = NULL; + + dbus_message_iter_recurse(itr, &e_itr); + + t = dbus_message_iter_get_arg_type(&e_itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_STRING)) + { + ERR("invalid format for dict entry. first type not a string: %c (%d)", + t, t); + return NULL; + } + + dbus_message_iter_get_basic(&e_itr, &key); + if (!key || !key[0]) + { + ERR("invalid format for dict entry. no key."); + return NULL; + } + + dbus_message_iter_next(&e_itr); + t = dbus_message_iter_get_arg_type(&e_itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_VARIANT)) + { + ERR("invalid format for dict entry '%s'. " + "second type not a variant: %c (%d)", + key, t, t); + return NULL; + } + + dbus_message_iter_recurse(&e_itr, &v_itr); + + t = dbus_message_iter_get_arg_type(&v_itr); + if ((t == DBUS_TYPE_INVALID) || (t == DBUS_TYPE_ARRAY)) + { + ERR("invalid type for dict value for entry '%s': %c (%d)", + key, t, t); + return NULL; + } + + entry = calloc(1, sizeof(*entry)); + if (!entry) + { + ERR("could not allocate memory for dict entry."); + return NULL; + } + + dbus_message_iter_get_basic(&v_itr, &value); + switch (t) + { + case DBUS_TYPE_BOOLEAN: + entry->value.boolean = (Eina_Bool)(long)value; + break; + + case DBUS_TYPE_BYTE: + entry->value.byte = (unsigned char)(long)value; + break; + + case DBUS_TYPE_UINT16: + entry->value.u16 = (unsigned short)(long)value; + break; + + case DBUS_TYPE_UINT32: + entry->value.u32 = (unsigned int)(long)value; + break; + + case DBUS_TYPE_STRING: + entry->value.str = eina_stringshare_add(value); + break; + + case DBUS_TYPE_OBJECT_PATH: + entry->value.path = eina_stringshare_add(value); + break; + + default: + ERR("don't know how to create dict entry '%s' for of type %c (%d)", + key, t, t); + free(entry); + return NULL; + } + + entry->name = eina_stringshare_add(key); + entry->type = t; + return entry; +} + +static E_Connman_Element_Dict_Entry * +_e_connman_element_array_dict_find_stringshared(const E_Connman_Array *array, const char *key) +{ + E_Connman_Element_Dict_Entry *entry; + Eina_Array_Iterator iterator; + unsigned int i; + + EINA_ARRAY_ITER_NEXT(array->array, i, entry, iterator) + if (entry->name == key) + return entry; + + return NULL; +} + +static void +_e_connman_element_array_free(E_Connman_Array *array, E_Connman_Array *new __UNUSED__) +{ + Eina_Array_Iterator iterator; + unsigned int i; + void *item; + + if (!array) + return; + + switch (array->type) + { + case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_BYTE: + case DBUS_TYPE_UINT16: + case DBUS_TYPE_UINT32: + break; + + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + eina_stringshare_del(item); + break; + + case DBUS_TYPE_DICT_ENTRY: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + _e_connman_element_dict_entry_free(item); + break; + + default: + ERR("don't know how to free array of values of type %c (%d)", + array->type, array->type); + break; + } + eina_array_free(array->array); + free(array); +} + +static void +_e_connman_element_property_value_free(E_Connman_Element_Property *property) +{ + switch (property->type) + { + case 0: + return; + + case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_BYTE: + case DBUS_TYPE_UINT16: + case DBUS_TYPE_UINT32: + break; + + case DBUS_TYPE_STRING: + eina_stringshare_del(property->value.str); + break; + + case DBUS_TYPE_OBJECT_PATH: + eina_stringshare_del(property->value.path); + break; + + case DBUS_TYPE_ARRAY: + _e_connman_element_array_free(property->value.array, NULL); + break; + + default: + ERR("don't know how to free value of property type %c (%d)", + property->type, property->type); + } +} + +static const char * +_e_connman_element_get_interface(const char *key) +{ + const char *interface = NULL, *tail; + char head; + + head = key[0]; + tail = key + 1; + + switch (head) + { + case 'P': + if (strcmp(tail, "rofiles") == 0) + interface = e_connman_iface_profile; + + break; + + case 'S': + if (strcmp(tail, "ervices") == 0) + interface = e_connman_iface_service; + + break; + + case 'T': + if (strcmp(tail, "echnologies") == 0) + interface = e_connman_iface_technology; + + break; + + default: + break; + } + + if (!interface) + ERR("failed to find interface for property \"%s\"", key); + + return interface; +} + +static void +_e_connman_element_item_register(const char *key, const char *item) +{ + E_Connman_Element *element; + const char *interface; + + interface = _e_connman_element_get_interface(key); + if (!interface) + return; + + element = e_connman_element_register(item, interface); + if ((element) && (!e_connman_element_properties_sync(element))) + WRN("could not get properties of %s", element->path); +} + +/* Match 2 arrays to find which are new and which are old elements + * For new elements, register them under prop_name property + * For old elements, unregister them, sending proper DEL event + */ +static void +_e_connman_element_array_match(E_Connman_Array *old, E_Connman_Array *new, const char *prop_name) +{ + Eina_List *deleted = NULL; + Eina_Array_Iterator iter_old, iter_new; + unsigned int i_old = 0, i_new = 0; + void *item_old, *item_new; + Eina_List *l; + void *data; + + if (!old) + return; + + if (old->type != DBUS_TYPE_OBJECT_PATH) + return; + + if ((!new) || (!new->array) || eina_array_count(new->array) == 0) + { + if ((!old) || (!old->array) || eina_array_count(old->array) == 0) + { + return; + } + else + { + iter_old = old->array->data; + goto out_remove_remaining; + } + } + + iter_new = new->array->data; + item_new = *iter_new; + EINA_ARRAY_ITER_NEXT(old->array, i_old, item_old, iter_old) + { + if (item_old == item_new) + { + i_new++; + if (i_new >= eina_array_count(new->array)) + { + i_old++; + break; + } + + iter_new++; + item_new = *iter_new; + } + else + { + deleted = eina_list_append(deleted, item_old); + } + } + + for(; i_new < eina_array_count(new->array); iter_new++, i_new++) + { + Eina_Bool found = EINA_FALSE; + item_new = *iter_new; + if (!item_new) + break; + + EINA_LIST_FOREACH(deleted, l, data) + { + if (data == item_new) + { + deleted = eina_list_remove_list(deleted, l); + found = EINA_TRUE; + break; + } + } + if (!found) + { + _e_connman_element_item_register(prop_name, item_new); + DBG("Add element %s\n", (const char *)item_new); + } + } + + /* everybody after i_old on old->array + everybody from deleted list + will be removed + */ + EINA_LIST_FREE(deleted, data) + { + E_Connman_Element *e = e_connman_element_get(data); + if (e) + e_connman_element_unregister(e); + + DBG("Delete element %s\n", (const char *)data); + } + +out_remove_remaining: + for(; i_old < eina_array_count(old->array); iter_old++, i_old++) + { + E_Connman_Element *e; + item_old = *iter_old; + if (!item_old) + break; + + e = e_connman_element_get(item_old); + if (e) + e_connman_element_unregister(e); + + DBG("Delete element %s\n", (const char *)item_old); + } +} + +static Eina_Bool +_e_connman_element_property_update(E_Connman_Element_Property *property, int type, void *data) +{ + Eina_Bool changed = EINA_FALSE; + + if ((type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) && data) + data = (char *)eina_stringshare_add(data); + + if (property->type != type) + { + if (property->type) + DBG("property type changed from '%c' to '%c'", + property->type, type); + + _e_connman_element_property_value_free(property); + memset(&property->value, 0, sizeof(property->value)); + property->type = type; + changed = EINA_TRUE; + } + + switch (type) + { + case DBUS_TYPE_BOOLEAN: + if (changed || property->value.boolean != (Eina_Bool)(long)data) + { + property->value.boolean = (Eina_Bool)(long)data; + changed = EINA_TRUE; + } + + break; + + case DBUS_TYPE_BYTE: + if (changed || property->value.byte != (unsigned char)(long)data) + { + property->value.byte = (unsigned char)(long)data; + changed = EINA_TRUE; + } + + break; + + case DBUS_TYPE_UINT16: + if (changed || property->value.u16 != (unsigned short)(long)data) + { + property->value.u16 = (unsigned short)(long)data; + changed = EINA_TRUE; + } + + break; + + case DBUS_TYPE_UINT32: + if (changed || property->value.u32 != (unsigned int)(long)data) + { + property->value.u32 = (unsigned int)(long)data; + changed = EINA_TRUE; + } + + break; + + case DBUS_TYPE_STRING: + if (changed) + { + property->value.str = data; + } + else + { + if (property->value.str) + eina_stringshare_del(property->value.str); + + if (property->value.str != data) + { + property->value.str = data; + changed = EINA_TRUE; + } + } + + break; + + case DBUS_TYPE_OBJECT_PATH: + if (changed) + { + property->value.path = data; + } + else + { + if (property->value.path) + eina_stringshare_del(property->value.path); + + if (property->value.path != data) + { + property->value.path = data; + changed = EINA_TRUE; + } + } + + break; + + case DBUS_TYPE_ARRAY: + if (!changed) + if (property->value.array) + { + _e_connman_element_array_match(property->value.array, data, property->name); + _e_connman_element_array_free(property->value.array, data); + } + + property->value.array = data; + changed = EINA_TRUE; + break; + + default: + ERR("don't know how to update property type %c (%d)", type, type); + } + + return changed; +} + +static E_Connman_Element_Property * +_e_connman_element_property_new(const char *name, int type, void *data) +{ + E_Connman_Element_Property *property; + + property = calloc(1, sizeof(*property)); + if (!property) + { + eina_stringshare_del(name); + ERR("could not allocate property: %s", strerror(errno)); + return NULL; + } + + property->name = name; + _e_connman_element_property_update(property, type, data); + return property; +} + +static void +_e_connman_element_property_free(E_Connman_Element_Property *property) +{ + _e_connman_element_property_value_free(property); + eina_stringshare_del(property->name); + free(property); +} + +/*********************************************************************** +* Element +***********************************************************************/ +unsigned char * +e_connman_element_bytes_array_get_stringshared(const E_Connman_Element *element, const char *property, unsigned int *count) +{ + Eina_Array_Iterator iterator; + E_Connman_Array *array; + unsigned char *ret, *p; + unsigned int i; + void *item; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(property, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(count, NULL); + + *count = 0; + + if (!e_connman_element_property_get_stringshared + (element, property, NULL, &array)) + return NULL; + + if ((!array) || (!(array->array))) + return NULL; + + *count = eina_array_count(array->array); + ret = malloc(*count * sizeof(unsigned char)); + if (!ret) + { + ERR("could not allocate return array of %d bytes: %s", + *count, strerror(errno)); + return NULL; + } + + p = ret; + + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + { + *p = (unsigned char)(long)item; + p++; + } + return ret; +} + +Eina_Bool +e_connman_element_objects_array_get_stringshared(const E_Connman_Element *element, const char *property, unsigned int *count, E_Connman_Element ***p_elements) +{ + E_Connman_Element **ret, **p; + Eina_Array_Iterator iterator; + E_Connman_Array *array; + unsigned int i; + int type; + void *item; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(property, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(p_elements, EINA_FALSE); + + *count = 0; + *p_elements = NULL; + + if (!e_connman_element_property_get_stringshared + (element, property, &type, &array)) + return EINA_FALSE; + + if (type != DBUS_TYPE_ARRAY) + { + ERR("property %s is not an array!", property); + return EINA_FALSE; + } + + if ((!array) || (!array->array) || (array->type == DBUS_TYPE_INVALID)) + return EINA_FALSE; + + if (array->type != DBUS_TYPE_OBJECT_PATH) + { + ERR("property %s is not an array of object paths!", property); + return EINA_FALSE; + } + + *count = eina_array_count(array->array); + ret = malloc(*count * sizeof(E_Connman_Element *)); + if (!ret) + { + ERR("could not allocate return array of %d elements: %s", + *count, strerror(errno)); + *count = 0; + return EINA_FALSE; + } + + p = ret; + + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + { + E_Connman_Element *e = e_connman_element_get(item); + if (!e) + continue; + + *p = e; + p++; + } + *count = p - ret; + *p_elements = ret; + return EINA_TRUE; +} + +/* array and strings are just pointers (references), + * no malloc, strdup or stringshare_add/ref + */ +Eina_Bool +e_connman_element_strings_array_get_stringshared(const E_Connman_Element *element, const char *property, unsigned int *count, const char ***strings) +{ + E_Connman_Array *array; + int type; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(property, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(strings, EINA_FALSE); + + *count = 0; + *strings = NULL; + + if (!e_connman_element_property_get_stringshared + (element, property, &type, &array)) + return EINA_FALSE; + + if (type != DBUS_TYPE_ARRAY) + { + ERR("property %s is not an array!", property); + return EINA_FALSE; + } + + if ((!array) || (!array->array) || (array->type == DBUS_TYPE_INVALID)) + return EINA_FALSE; + + if (array->type != DBUS_TYPE_STRING) + { + ERR("property %s is not an array of strings!", property); + return EINA_FALSE; + } + + *count = array->array->count; + *strings = (const char **)array->array->data; + return EINA_TRUE; +} + +static void +_e_connman_element_array_print(FILE *fp, E_Connman_Array *array) +{ + Eina_Array_Iterator iterator; + unsigned int i; + void *item; + + if (!array) + return; + + switch (array->type) + { + case DBUS_TYPE_OBJECT_PATH: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + fprintf(fp, "\"%s\", ", (const char *)item); + break; + + case DBUS_TYPE_STRING: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + fprintf(fp, "\"%s\", ", (const char *)item); + break; + + case DBUS_TYPE_BYTE: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + fprintf(fp, "%#02hhx (\"%c\"), ", (unsigned char)(long)item, + (unsigned char)(long)item); + break; + + case DBUS_TYPE_UINT16: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + fprintf(fp, "%#04hx (%hu), ", (unsigned short)(long)item, + (unsigned short)(long)item); + break; + + case DBUS_TYPE_UINT32: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + fprintf(fp, "%#08x (%u), ", (unsigned int)(long)item, + (unsigned int)(long)item); + break; + + case DBUS_TYPE_DICT_ENTRY: + fputs("{ ", fp); + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + { + E_Connman_Element_Dict_Entry *entry = item; + fprintf(fp, "%s: ", entry->name); + switch (entry->type) + { + case DBUS_TYPE_OBJECT_PATH: + fprintf(fp, "\"%s\", ", entry->value.path); + break; + + case DBUS_TYPE_STRING: + fprintf(fp, "\"%s\", ", entry->value.str); + break; + + case DBUS_TYPE_BYTE: + fprintf(fp, "%#02hhx (\"%c\"), ", + entry->value.byte, entry->value.byte); + break; + + case DBUS_TYPE_UINT16: + fprintf(fp, "%#04hx (%hu), ", + entry->value.u16, entry->value.u16); + break; + + case DBUS_TYPE_UINT32: + fprintf(fp, "%#08x (%u), ", + entry->value.u32, entry->value.u32); + break; + + default: + fprintf(fp, "", entry->type); + } + } + fputs("}", fp); + break; + + default: + fprintf(fp, "", array->type); + } +} + +/** + * Print element to file descriptor. + */ +void +e_connman_element_print(FILE *fp, const E_Connman_Element *element) +{ + const E_Connman_Element_Property *p; + + EINA_SAFETY_ON_NULL_RETURN(fp); + if (!element) + { + fputs("Error: no element to print\n", fp); + return; + } + + fprintf(fp, + "Element %p: %s [%s]\n" + "\tProperties:\n", + element, element->path, element->interface); + + EINA_INLIST_FOREACH(element->props, p) + { + fprintf(fp, "\t\t%s (%c) = ", p->name, p->type); + + switch (p->type) + { + case DBUS_TYPE_STRING: + fprintf(fp, "\"%s\"", p->value.str); + break; + + case DBUS_TYPE_OBJECT_PATH: + fprintf(fp, "\"%s\"", p->value.path); + break; + + case DBUS_TYPE_BOOLEAN: + fprintf(fp, "%hhu", p->value.boolean); + break; + + case DBUS_TYPE_BYTE: + fprintf(fp, "%#02hhx (%d), ", p->value.byte, p->value.byte); + break; + + case DBUS_TYPE_UINT16: + fprintf(fp, "%hu", p->value.u16); + break; + + case DBUS_TYPE_UINT32: + fprintf(fp, "%u", p->value.u32); + break; + + case DBUS_TYPE_ARRAY: + _e_connman_element_array_print(fp, p->value.array); + break; + + default: + fputs("don't know how to print type", fp); + } + + fputc('\n', fp); + } +} + +static E_Connman_Element * +e_connman_element_new(const char *path, const char *interface) +{ + E_Connman_Element *element; + + element = calloc(1, sizeof(*element)); + if (!element) + { + ERR("could not allocate element: %s", strerror(errno)); + return NULL; + } + + element->path = eina_stringshare_add(path); + element->interface = eina_stringshare_ref(interface); + element->_references = 1; + + return element; +} + +static void +e_connman_element_extra_properties_free(E_Connman_Element *element) +{ + while (element->props) + { + E_Connman_Element_Property *prop; + prop = (E_Connman_Element_Property *)element->props; + element->props = element->props->next; + _e_connman_element_property_free(prop); + } +} + +static void +e_connman_element_free(E_Connman_Element *element) +{ + if (element->_idler.changed) + ecore_idler_del(element->_idler.changed); + + while (element->_listeners) + { + E_Connman_Element_Listener *l = (void *)element->_listeners; + element->_listeners = eina_inlist_remove + (element->_listeners, element->_listeners); + + if (l->free_data) + l->free_data(l->data); + + free(l); + } + + e_connman_element_pending_cancel_and_free(&element->_pending.properties_get); + e_connman_element_pending_cancel_and_free(&element->_pending.property_set); + e_connman_element_pending_cancel_and_free(&element->_pending.agent_register); + e_connman_element_pending_cancel_and_free(&element->_pending.agent_unregister); + e_connman_element_pending_cancel_and_free(&element->_pending.request_scan); + e_connman_element_pending_cancel_and_free(&element->_pending.technology_enable); + e_connman_element_pending_cancel_and_free(&element->_pending.technology_disable); + e_connman_element_pending_cancel_and_free(&element->_pending.profile_remove); + e_connman_element_pending_cancel_and_free(&element->_pending.service_connect); + e_connman_element_pending_cancel_and_free(&element->_pending.service_disconnect); + e_connman_element_pending_cancel_and_free(&element->_pending.service_remove); + e_connman_element_pending_cancel_and_free(&element->_pending.service_move_before); + e_connman_element_pending_cancel_and_free(&element->_pending.service_move_after); + e_connman_element_pending_cancel_and_free(&element->_pending.service_clear_property); + + e_connman_element_extra_properties_free(element); + eina_stringshare_del(element->interface); + eina_stringshare_del(element->path); + free(element); +} + +/** + * Add reference to element. + */ +int +e_connman_element_ref(E_Connman_Element *element) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, 0); + return ++element->_references; +} + +/** + * Remove reference from element. + * + * If reference count drops to 0 element will be freed. + */ +int +e_connman_element_unref(E_Connman_Element *element) +{ + int i; + EINA_SAFETY_ON_NULL_RETURN_VAL(element, 0); + + i = --element->_references; + if (i == 0) + e_connman_element_free(element); + else if (i < 0) + ERR("element %p references %d < 0", element, i); + + return i; +} + +/** + * Send message with callbacks set to work with connman elements. + * + * If this call fails (returns @c EINA_FALSE), pending callbacks will not be called, + * not even with error messages. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE on failure. + */ +Eina_Bool +e_connman_element_message_send(E_Connman_Element *element, const char *method_name, E_DBus_Method_Return_Cb cb, DBusMessage *msg, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data) +{ + E_Connman_Element_Call_Data *data; + E_Connman_Element_Pending *p; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(method_name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(pending, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(msg, EINA_FALSE); + + data = malloc(sizeof(*data)); + if (!data) + { + ERR("could not alloc e_connman_element_call_data: %s", + strerror(errno)); + dbus_message_unref(msg); + return EINA_FALSE; + } + + p = malloc(sizeof(*p)); + if (!p) + { + ERR("could not alloc E_Connman_Element_Pending: %s", + strerror(errno)); + free(data); + dbus_message_unref(msg); + return EINA_FALSE; + } + + data->element = element; + data->cb = cb; + data->pending = p; + data->p_list = pending; + p->user_cb = user_cb; + p->user_data = (void *)user_data; + p->data = data; + p->pending = e_dbus_message_send + (e_connman_conn, msg, e_connman_element_call_dispatch_and_free, -1, data); + dbus_message_unref(msg); + + if (p->pending) + { + *pending = eina_inlist_append(*pending, EINA_INLIST_GET(p)); + return EINA_TRUE; + } + + ERR("failed to call %s (obj=%s, path=%s, iface=%s)", + method_name, e_connman_system_bus_name_get(), + element->path, element->interface); + free(data); + free(p); + return EINA_FALSE; +} + +Eina_Bool +e_connman_element_call_full(E_Connman_Element *element, const char *method_name, E_DBus_Method_Return_Cb cb, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data) +{ + DBusMessage *msg; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(method_name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(pending, EINA_FALSE); + + msg = dbus_message_new_method_call + (e_connman_system_bus_name_get(), element->path, element->interface, + method_name); + + return e_connman_element_message_send + (element, method_name, cb, msg, pending, user_cb, user_data); +} + +static Eina_Bool +_e_connman_element_property_value_add(E_Connman_Element *element, const char *name, int type, void *value) +{ + E_Connman_Element_Property *p; + + name = eina_stringshare_add(name); + EINA_INLIST_FOREACH(element->props, p) + { + if (p->name == name) + { + eina_stringshare_del(name); + return _e_connman_element_property_update(p, type, value); + } + } + + p = _e_connman_element_property_new(name, type, value); + if (!p) + { + ERR("could not create property %s (%c)", name, type); + return EINA_FALSE; + } + + element->props = eina_inlist_append(element->props, EINA_INLIST_GET(p)); + return EINA_TRUE; +} + +static E_Connman_Array * +_e_connman_element_iter_get_array(DBusMessageIter *itr, const char *key) +{ + E_Connman_Array *array; + DBusMessageIter e_itr; + + array = malloc(sizeof(E_Connman_Array)); + if (!array) + { + ERR("could not create new e_connman array."); + return NULL; + } + + array->array = eina_array_new(16); + if (!(array->array)) + { + ERR("could not create new eina array."); + free(array); + return NULL; + } + + dbus_message_iter_recurse(itr, &e_itr); + array->type = dbus_message_iter_get_arg_type(&e_itr); + if (array->type == DBUS_TYPE_INVALID) + { + DBG("array %s is of type 'invalid' (empty?)", key); + eina_array_free(array->array); + free(array); + return NULL; + } + + do + { + switch (array->type) + { + case DBUS_TYPE_OBJECT_PATH: + { + const char *path; + + dbus_message_iter_get_basic(&e_itr, &path); + path = eina_stringshare_add(path); + eina_array_push(array->array, path); + _e_connman_element_item_register(key, path); + } + break; + + case DBUS_TYPE_STRING: + { + const char *str; + + dbus_message_iter_get_basic(&e_itr, &str); + str = eina_stringshare_add(str); + eina_array_push(array->array, str); + } + break; + + case DBUS_TYPE_BYTE: + { + unsigned char byte; + dbus_message_iter_get_basic(&e_itr, &byte); + eina_array_push(array->array, (void *)(long)byte); + } + break; + + case DBUS_TYPE_DICT_ENTRY: + { + E_Connman_Element_Dict_Entry *entry; + entry = _e_connman_element_dict_entry_new(&e_itr); + if (entry) + eina_array_push(array->array, entry); + } + break; + + default: + ERR("don't know how to build array '%s' of type %c (%d)", + key, array->type, array->type); + eina_array_free(array->array); + free(array); + return NULL; + } + } + while (dbus_message_iter_next(&e_itr)); + return array; +} + +static void +_e_connman_element_get_properties_callback(void *user_data, DBusMessage *msg, DBusError *err) +{ + E_Connman_Element *element = user_data; + DBusMessageIter itr, s_itr; + int t, changed; + + DBG("get_properties msg=%p", msg); + + if (!_dbus_callback_check_and_init(msg, &itr, err)) + return; + + t = dbus_message_iter_get_arg_type(&itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_ARRAY)) + return; + + changed = 0; + dbus_message_iter_recurse(&itr, &s_itr); + do + { + DBusMessageIter e_itr, v_itr; + const char *key; + void *value = NULL; + int r; + + t = dbus_message_iter_get_arg_type(&s_itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_DICT_ENTRY)) + continue; + + dbus_message_iter_recurse(&s_itr, &e_itr); + + t = dbus_message_iter_get_arg_type(&e_itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_STRING)) + continue; + + dbus_message_iter_get_basic(&e_itr, &key); + dbus_message_iter_next(&e_itr); + t = dbus_message_iter_get_arg_type(&e_itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_VARIANT)) + continue; + + dbus_message_iter_recurse(&e_itr, &v_itr); + t = dbus_message_iter_get_arg_type(&v_itr); + if (t == DBUS_TYPE_ARRAY) + { + value = _e_connman_element_iter_get_array(&v_itr, key); + } + else if (t != DBUS_TYPE_INVALID) + { + dbus_message_iter_get_basic(&v_itr, &value); + } + else + { + ERR("property has invalid type %s", key); + continue; + } + + r = _e_connman_element_property_value_add(element, key, t, value); + if (r < 0) + { + ERR("failed to add property value %s (%c)", key, t); + } + else if (r == 1) + { + INF("property value changed %s (%c)", key, t); + changed = 1; + } + } + while (dbus_message_iter_next(&s_itr)); + + if (changed) + _e_connman_element_listeners_call(element); +} + +/** + * Sync element properties with server. + * + * Call method GetProperties() at the given element on server in order to sync + * them. + * + * @param element to call method on server. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_element_sync_properties_full(E_Connman_Element *element, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "GetProperties"; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + return e_connman_element_call_full + (element, name, _e_connman_element_get_properties_callback, + &element->_pending.properties_get, cb, data); +} + +/** + * Sync element properties with server, simple version. + * + * Call method GetProperties() at the given element on server in order to sync + * them. This is the simple version and there is no check of server reply + * for errors. + * + * @param element to call method on server. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_element_properties_sync(E_Connman_Element *element) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + return e_connman_element_sync_properties_full(element, NULL, NULL); +} + +/** + * Call method SetProperty(prop, {key: value}) at the given element on server. + * + * This is a server call, not local, so it may fail and in that case + * no property is updated locally. If the value was set the event + * E_CONNMAN_EVENT_ELEMENT_UPDATED will be added to main loop. + * + * @param element to call method on server. + * @param prop property name. + * @param key dict key name. + * @param type DBus type to use for value. + * @param value pointer to value, just like regular DBus, see + * dbus_message_iter_append_basic(). + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_element_property_dict_set_full(E_Connman_Element *element, const char *prop, const char *key, int type, const void *value, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "SetProperty"; + DBusMessage *msg; + DBusMessageIter itr, variant, dict, entry; + char typestr[32]; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(prop, EINA_FALSE); + + msg = dbus_message_new_method_call + (e_connman_system_bus_name_get(), element->path, element->interface, name); + + if (!msg) + return EINA_FALSE; + + dbus_message_iter_init_append(msg, &itr); + dbus_message_iter_append_basic(&itr, DBUS_TYPE_STRING, &prop); + + if ((size_t)snprintf(typestr, sizeof(typestr), + (DBUS_TYPE_ARRAY_AS_STRING + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + "%c" + DBUS_DICT_ENTRY_END_CHAR_AS_STRING), + type) >= sizeof(typestr)) + { + ERR("sizeof(typestr) is too small!"); + return EINA_FALSE; + } + + if (dbus_message_iter_open_container(&itr, DBUS_TYPE_VARIANT, typestr, &variant)) + { + snprintf(typestr, sizeof(typestr), + (DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + "%c" + DBUS_DICT_ENTRY_END_CHAR_AS_STRING), + type); + if (dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, typestr, &dict)) + { + if (dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry)) + { + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + + if ((type == DBUS_TYPE_STRING) || (type == DBUS_TYPE_OBJECT_PATH)) + dbus_message_iter_append_basic(&entry, type, &value); + else + dbus_message_iter_append_basic(&entry, type, value); + + dbus_message_iter_close_container(&dict, &entry); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + dbus_message_iter_close_container(&variant, &dict); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + dbus_message_iter_close_container(&itr, &variant); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + + return e_connman_element_message_send + (element, name, NULL, msg, &element->_pending.property_set, cb, data); +} + +/** + * Call method SetProperty(prop, value) at the given element on server. + * + * This is a server call, not local, so it may fail and in that case + * no property is updated locally. If the value was set the event + * E_CONNMAN_EVENT_ELEMENT_UPDATED will be added to main loop. + * + * @param element to call method on server. + * @param prop property name. + * @param type DBus type to use for value. + * @param value pointer to value, just like regular DBus, see + * dbus_message_iter_append_basic(). + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_element_property_set_full(E_Connman_Element *element, const char *prop, int type, const void *value, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "SetProperty"; + char typestr[2]; + DBusMessage *msg; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(prop, EINA_FALSE); + + msg = dbus_message_new_method_call + (e_connman_system_bus_name_get(), element->path, element->interface, name); + + if (!msg) + return EINA_FALSE; + + DBusMessageIter itr, v; + dbus_message_iter_init_append(msg, &itr); + dbus_message_iter_append_basic(&itr, DBUS_TYPE_STRING, &prop); + + typestr[0] = type; + typestr[1] = '\0'; + if (dbus_message_iter_open_container(&itr, DBUS_TYPE_VARIANT, typestr, &v)) + { + if ((type == DBUS_TYPE_STRING) || (type == DBUS_TYPE_OBJECT_PATH)) + { + dbus_message_iter_append_basic(&v, type, &value); + } + else if (type == DBUS_TYPE_BOOLEAN) + { + unsigned int b = *(char *)value; + dbus_message_iter_append_basic(&v, type, &b); + } + else + { + dbus_message_iter_append_basic(&v, type, value); + } + + dbus_message_iter_close_container(&itr, &v); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + + return e_connman_element_message_send + (element, name, NULL, msg, &element->_pending.property_set, cb, data); +} + +/** + * Call method SetProperty(prop, value) at the given element on server, when + * value is an array. + * + * This is a server call, not local, so it may fail and in that case + * no property is updated locally. If the value was set the event + * E_CONNMAN_EVENT_ELEMENT_UPDATED will be added to main loop. + * + * @param element to call method on server. + * @param prop property name. + * @param type DBus type to use for value. + * @param value pointer to value, just like regular DBus, see + * dbus_message_iter_append_basic(). + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_element_property_array_set_full(E_Connman_Element *element, const char *prop, int type, unsigned int count, const void * const *values, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "SetProperty"; + char type_sig[2] = { type, '\0'}; + char array_sig[3] = { DBUS_TYPE_ARRAY, type, '\0' }; + DBusMessage *msg; + DBusMessageIter itr, variant, array; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(prop, EINA_FALSE); + + msg = dbus_message_new_method_call + (e_connman_system_bus_name_get(), element->path, element->interface, name); + + if (!msg) + return EINA_FALSE; + + dbus_message_iter_init_append(msg, &itr); + dbus_message_iter_append_basic(&itr, DBUS_TYPE_STRING, &prop); + + if (dbus_message_iter_open_container(&itr, DBUS_TYPE_VARIANT, array_sig, &variant)) + { + if (dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, type_sig, &array)) + { + if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) + { + unsigned int i; + for (i = 0; i < count; i++) + { + const void *entry = values[i]; + dbus_message_iter_append_basic(&array, type, &entry); + } + } + else + { + unsigned int i; + for (i = 0; i < count; i++) + { + const void *entry = values[i]; + dbus_message_iter_append_basic(&array, type, entry); + } + } + + dbus_message_iter_close_container(&variant, &array); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + dbus_message_iter_close_container(&itr, &variant); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + + return e_connman_element_message_send(element, name, NULL, msg, + &element->_pending.property_set, + cb, data); +} + +/** + * Call method SetProperty(prop, value) at the given element on server. + * + * This is the simple version and there is no check of server reply + * for errors. + * + * @param element to call method on server. + * @param prop property name. + * @param type DBus type to use for value. + * @param value pointer to value, just like regular DBus, see + * dbus_message_iter_append_basic(). + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_element_property_set(E_Connman_Element *element, const char *prop, int type, const void *value) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(prop, EINA_FALSE); + return e_connman_element_property_set_full + (element, prop, type, value, NULL, NULL); +} + +Eina_Bool +e_connman_element_call_with_path(E_Connman_Element *element, const char *method_name, const char *string, E_DBus_Method_Return_Cb cb, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data) +{ + DBusMessageIter itr; + DBusMessage *msg; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(method_name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(string, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(pending, EINA_FALSE); + + msg = dbus_message_new_method_call + (e_connman_system_bus_name_get(), element->path, element->interface, + method_name); + + if (!msg) + return EINA_FALSE; + + dbus_message_iter_init_append(msg, &itr); + dbus_message_iter_append_basic(&itr, DBUS_TYPE_OBJECT_PATH, &string); + + return e_connman_element_message_send + (element, method_name, cb, msg, pending, user_cb, user_data); +} + +Eina_Bool +e_connman_element_call_with_string(E_Connman_Element *element, const char *method_name, const char *string, E_DBus_Method_Return_Cb cb, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data) +{ + DBusMessageIter itr; + DBusMessage *msg; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(method_name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(string, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(pending, EINA_FALSE); + + msg = dbus_message_new_method_call + (e_connman_system_bus_name_get(), element->path, element->interface, + method_name); + + if (!msg) + return EINA_FALSE; + + dbus_message_iter_init_append(msg, &itr); + dbus_message_iter_append_basic(&itr, DBUS_TYPE_STRING, &string); + + return e_connman_element_message_send + (element, method_name, cb, msg, pending, user_cb, user_data); +} + +/** + * Get property type. + * + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param element which element to get the property + * @param name property name, must be previously stringshared + * @param type will contain the value type. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_element_property_type_get_stringshared(const E_Connman_Element *element, const char *name, int *type) +{ + const E_Connman_Element_Property *p; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(type, EINA_FALSE); + + EINA_INLIST_FOREACH(element->props, p) + { + if (p->name == name) + { + *type = p->type; + return EINA_TRUE; + } + } + + WRN("element %s (%p) has no property with name \"%s\".", + element->path, element, name); + return EINA_FALSE; +} + +/** + * Get property type. + * + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param element which element to get the property + * @param name property name + * @param type will contain the value type. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_element_property_type_get(const E_Connman_Element *element, const char *name, int *type) +{ + Eina_Bool ret; + name = eina_stringshare_add(name); + ret = e_connman_element_property_type_get_stringshared(element, name, type); + eina_stringshare_del(name); + return ret; +} + +void +e_connman_element_list_properties(const E_Connman_Element *element, Eina_Bool (*cb)(void *data, const E_Connman_Element *element, const char *name, int type, const void *value), const void *data) +{ + const E_Connman_Element_Property *p; + + EINA_SAFETY_ON_NULL_RETURN(element); + EINA_SAFETY_ON_NULL_RETURN(cb); + + EINA_INLIST_FOREACH(element->props, p) + { + const void *value = NULL; + + switch (p->type) + { + case DBUS_TYPE_STRING: + value = &p->value.str; + break; + + case DBUS_TYPE_OBJECT_PATH: + value = &p->value.path; + break; + + case DBUS_TYPE_BOOLEAN: + value = (void *)&p->value.boolean; + break; + + case DBUS_TYPE_UINT16: + value = &p->value.u16; + break; + + case DBUS_TYPE_UINT32: + value = &p->value.u32; + break; + + default: + ERR("unsupported type %c", p->type); + } + + if (!cb((void *)data, element, p->name, p->type, value)) + return; + } +} + +/** + * Get dict value given its key inside a dict property. + * + * This will look into properties for one of type dict that contains + * the given key, to find the property. If no property is found then + * @c EINA_FALSE is returned. + * + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param element which element to get the property + * @param dict_name property name, must be previously stringshared + * @param key key inside dict, must be previously stringshared + * @param type if provided it will contain the value type. + * @param value where to store the property value, must be a pointer to the + * exact type, (Eina_Bool *) for booleans, (char **) for strings, and so on. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_element_property_dict_get_stringshared(const E_Connman_Element *element, const char *dict_name, const char *key, int *type, void *value) +{ + const E_Connman_Element_Property *p; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(dict_name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(value, EINA_FALSE); + + EINA_INLIST_FOREACH(element->props, p) + { + E_Connman_Element_Dict_Entry *entry; + E_Connman_Array *array; + + if (p->name != dict_name) + continue; + + if (p->type != DBUS_TYPE_ARRAY) + { + WRN("element %s (%p) has property \"%s\" is not an array: %c (%d)", + element->path, element, dict_name, p->type, p->type); + return EINA_FALSE; + } + + array = p->value.array; + if ((!array) || (array->type != DBUS_TYPE_DICT_ENTRY)) + { + int t = array ? array->type : DBUS_TYPE_INVALID; + WRN("element %s (%p) has property \"%s\" is not a dict: %c (%d)", + element->path, element, dict_name, t, t); + return EINA_FALSE; + } + + entry = _e_connman_element_array_dict_find_stringshared(array, key); + if (!entry) + { + WRN("element %s (%p) has no dict property with name \"%s\" with " + "key \"%s\".", + element->path, element, dict_name, key); + return EINA_FALSE; + } + + if (type) + *type = entry->type; + + switch (entry->type) + { + case DBUS_TYPE_BOOLEAN: + *(Eina_Bool *)value = entry->value.boolean; + return EINA_TRUE; + + case DBUS_TYPE_BYTE: + *(unsigned char *)value = entry->value.byte; + return EINA_TRUE; + + case DBUS_TYPE_UINT16: + *(unsigned short *)value = entry->value.u16; + return EINA_TRUE; + + case DBUS_TYPE_UINT32: + *(unsigned int *)value = entry->value.u32; + return EINA_TRUE; + + case DBUS_TYPE_STRING: + *(const char **)value = entry->value.str; + return EINA_TRUE; + + case DBUS_TYPE_OBJECT_PATH: + *(const char **)value = entry->value.path; + return EINA_TRUE; + + case DBUS_TYPE_ARRAY: + *(E_Connman_Array **)value = entry->value.array; + return EINA_TRUE; + + default: + ERR("don't know how to get property %s, key %s type %c (%d)", + dict_name, key, entry->type, entry->type); + return EINA_FALSE; + } + } + + DBG("element %s (%p) has no property with name \"%s\".", + element->path, element, dict_name); + return EINA_FALSE; +} + +Eina_Bool +e_connman_element_property_dict_strings_array_get_stringshared(const E_Connman_Element *element, const char *dict_name, const char *key, unsigned int *count, const char ***strings) +{ + const char **ret, **p; + Eina_Array_Iterator iterator; + E_Connman_Array *array; + unsigned int i; + int type; + void *item; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(dict_name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(strings, EINA_FALSE); + + *count = 0; + *strings = NULL; + + if (!e_connman_element_property_dict_get_stringshared(element, dict_name, + key, &type, &array)) + return EINA_FALSE; + + if (type != DBUS_TYPE_ARRAY) + { + ERR("property %s.%s is not an array!", dict_name, key); + return EINA_FALSE; + } + + if ((!array) || (!array->array) || (array->type == DBUS_TYPE_INVALID)) + return EINA_FALSE; + + if (array->type != DBUS_TYPE_STRING) + { + ERR("property %s.%s is not an array of strings!", dict_name, key); + return EINA_FALSE; + } + + *count = eina_array_count(array->array); + ret = malloc(*count * sizeof(char *)); + if (!ret) + { + ERR("could not allocate return array of %d strings: %s", + *count, strerror(errno)); + *count = 0; + return EINA_FALSE; + } + + p = ret; + + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + { + if (!item) + continue; + + *p = item; + p++; + } + *count = p - ret; + *strings = ret; + return EINA_TRUE; +} + +/** + * Get property value given its name. + * + * This will look into properties, to find the property. + * If no property is found then @c EINA_FALSE is returned. + * + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param element which element to get the property + * @param name property name, must be previously stringshared + * @param type if provided it will contain the value type. + * @param value where to store the property value, must be a pointer to the + * exact type, (Eina_Bool *) for booleans, (char **) for strings, and so on. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_element_property_get_stringshared(const E_Connman_Element *element, const char *name, int *type, void *value) +{ + const E_Connman_Element_Property *p; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(value, EINA_FALSE); + + EINA_INLIST_FOREACH(element->props, p) + { + if (p->name != name) + continue; + + if (type) + *type = p->type; + + switch (p->type) + { + case DBUS_TYPE_BOOLEAN: + *(Eina_Bool *)value = p->value.boolean; + return EINA_TRUE; + + case DBUS_TYPE_BYTE: + *(unsigned char *)value = p->value.byte; + return EINA_TRUE; + + case DBUS_TYPE_UINT16: + *(unsigned short *)value = p->value.u16; + return EINA_TRUE; + + case DBUS_TYPE_UINT32: + *(unsigned int *)value = p->value.u32; + return EINA_TRUE; + + case DBUS_TYPE_STRING: + *(const char **)value = p->value.str; + return EINA_TRUE; + + case DBUS_TYPE_OBJECT_PATH: + *(const char **)value = p->value.path; + return EINA_TRUE; + + case DBUS_TYPE_ARRAY: + *(E_Connman_Array **)value = p->value.array; + return EINA_TRUE; + + default: + ERR("don't know how to get property type %c (%d)", + p->type, p->type); + return EINA_FALSE; + } + } + + WRN("element %s (%p) has no property with name \"%s\".", + element->path, element, name); + return EINA_FALSE; +} + +/** + * Get property value given its name. + * + * This will look into properties, to find the property. + * If no property is found then @c EINA_FALSE is returned. + * + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param element which element to get the property + * @param name property name + * @param type if provided it will contain the value type. + * @param value where to store the property value, must be a pointer to the + * exact type, (Eina_Bool *) for booleans, (char **) for strings, and so on. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_element_property_get(const E_Connman_Element *element, const char *name, int *type, void *value) +{ + Eina_Bool ret; + name = eina_stringshare_add(name); + ret = e_connman_element_property_get_stringshared + (element, name, type, value); + eina_stringshare_del(name); + return ret; +} + +struct e_connman_elements_for_each_data +{ + Eina_Hash_Foreach cb; + void *data; +}; + +static Eina_Bool +_e_connman_elements_for_each(Eina_Hash *hash __UNUSED__, const char *key, void *data, void *fdata) +{ + struct e_connman_elements_for_each_data *each_data = fdata; + + each_data->cb(elements, key, data, each_data->data); + return EINA_TRUE; +} + +/** + * Call the given function for each existing element. + * + * @param cb function to call for each element. It will get as parameters, + * in order: the element pointer and the given @a user_data. + * @param user_data data to give to @a cb for each element. + */ +void +e_connman_elements_for_each(Eina_Hash_Foreach cb, const void *user_data) +{ + struct e_connman_elements_for_each_data data = {cb, (void *)user_data}; + + EINA_SAFETY_ON_NULL_RETURN(cb); + + eina_hash_foreach(elements, (Eina_Hash_Foreach)_e_connman_elements_for_each, + &data); +} + +static Eina_Bool +_e_connman_elements_get_allocate(unsigned int *count, E_Connman_Element ***p_elements) +{ + *count = eina_hash_population(elements); + if (*count == 0) + { + *p_elements = NULL; + return EINA_TRUE; + } + + *p_elements = malloc(*count * sizeof(E_Connman_Element *)); + if (!*p_elements) + { + ERR("could not allocate return array of %d elements: %s", + *count, strerror(errno)); + *count = 0; + return EINA_FALSE; + } + + return EINA_TRUE; +} + +static Eina_Bool +_e_connman_elements_get_all(Eina_Hash *hash __UNUSED__, const char *key __UNUSED__, void *data, void *fdata) +{ + E_Connman_Element *element = data; + E_Connman_Element ***p_ret = fdata; + + **p_ret = element; + (*p_ret)++; + return EINA_TRUE; +} + +/** + * Get all known elements. + * + * No reference is added to these elements, since there are no threads + * in the system, you are free to add references yourself right after + * the return of this call without race condition, elements by the + * system (ie: elementRemoved signal)could only be touched on the next + * main loop iteration. + * + * @param count return the number of elements in array. + * @param p_elements array with all elements, these are not referenced + * and in no particular order, just set if return is @c EINA_TRUE. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_elements_get_all(unsigned int *count, E_Connman_Element ***p_elements) +{ + E_Connman_Element **p; + + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(p_elements, EINA_FALSE); + + if (!_e_connman_elements_get_allocate(count, p_elements)) + return EINA_FALSE; + + p = *p_elements; + eina_hash_foreach(elements, (Eina_Hash_Foreach)_e_connman_elements_get_all, + &p); + return EINA_TRUE; +} + +struct e_connman_elements_get_all_str_data +{ + E_Connman_Element **elements; + int count; + const char *str; +}; + +static Eina_Bool +_e_connman_elements_get_all_type(Eina_Hash *hash __UNUSED__, const char *key __UNUSED__, void *e, void *user_data) +{ + struct e_connman_elements_get_all_str_data *data = user_data; + E_Connman_Element *element = e; + + if ((data->str) && (element->interface != data->str)) + return EINA_TRUE; + + data->elements[data->count] = element; + data->count++; + return EINA_TRUE; +} + +/** + * Get all known elements of type. + * + * No reference is added to these elements, since there are no threads + * in the system, you are free to add references yourself right after + * the return of this call without race condition, elements by the + * system (ie: ElementRemoved signal) could only be touched on the next + * main loop iteration. + * + * @param type type to filter, or NULL to get all. + * @param count return the number of elements in array. + * @param p_elements array with all elements, these are not referenced + * and in no particular order, just set if return is @c EINA_TRUE. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * + * @see e_connman_elements_get_all() + */ +Eina_Bool +e_connman_elements_get_all_type(const char *type, unsigned int *count, E_Connman_Element ***p_elements) +{ + struct e_connman_elements_get_all_str_data data; + + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(p_elements, EINA_FALSE); + + if (!_e_connman_elements_get_allocate(count, p_elements)) + return EINA_FALSE; + + data.elements = *p_elements; + data.count = 0; + data.str = eina_stringshare_add(type); + eina_hash_foreach(elements, + (Eina_Hash_Foreach)_e_connman_elements_get_all_type, + &data); + + eina_stringshare_del(data.str); + *count = data.count; + return EINA_TRUE; +} + +/** + * Get the element registered at given path. + * + * @param path the path to query for registered object. + * + * @return element pointer if found, NULL otherwise. No references are added. + */ +E_Connman_Element * +e_connman_element_get(const char *path) +{ + E_Connman_Element *element; + + EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL); + element = eina_hash_find(elements, path); + + return element; +} + +static void +_e_connman_element_property_changed_callback(void *data, DBusMessage *msg) +{ + E_Connman_Element *element = (E_Connman_Element *)data; + DBusMessageIter itr, v_itr; + int t, r, changed = 0; + const char *name = NULL; + void *value = NULL; + + DBG("Property changed in element %s", element->path); + + if (!_dbus_callback_check_and_init(msg, &itr, NULL)) + return; + + t = dbus_message_iter_get_arg_type(&itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_STRING)) + { + ERR("missing name in property changed signal"); + return; + } + + dbus_message_iter_get_basic(&itr, &name); + + dbus_message_iter_next(&itr); + t = dbus_message_iter_get_arg_type(&itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_VARIANT)) + { + ERR("missing value in property changed signal"); + return; + } + + dbus_message_iter_recurse(&itr, &v_itr); + t = dbus_message_iter_get_arg_type(&v_itr); + + if (t == DBUS_TYPE_ARRAY) + { + value = _e_connman_element_iter_get_array(&v_itr, name); + } + else if (t != DBUS_TYPE_INVALID) + { + dbus_message_iter_get_basic(&v_itr, &value); + } + else + { + ERR("property has invalid type %s", name); + return; + } + + r = _e_connman_element_property_value_add(element, name, t, value); + if (r < 0) + { + ERR("failed to add property value %s (%c)", name, t); + } + else if (r == 1) + { + INF("property value changed %s (%c)", name, t); + changed = 1; + } + + if (changed) + _e_connman_element_listeners_call(element); +} + +/** + * Register the given path, possible creating and element and return it. + * + * This will check if path is already registered, in that case the + * exiting element is returned. If it was not registered yet, a new + * element is created, registered and returned. + * + * This call will not add extra references to the object. + * + * @param path the path to register the element + * + * @return the registered object, no references are added. + */ +E_Connman_Element * +e_connman_element_register(const char *path, const char *interface) +{ + E_Connman_Element *element; + + EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(interface, NULL); + + element = eina_hash_find(elements, path); + if (element) + return element; + + element = e_connman_element_new(path, interface); + if (!element) + return NULL; + + if (!eina_hash_add(elements, element->path, element)) + { + ERR("could not add element %s to hash, delete it.", path); + e_connman_element_free(element); + return NULL; + } + + element->signal_handler = + e_dbus_signal_handler_add + (e_connman_conn, e_connman_system_bus_name_get(), + element->path, element->interface, "PropertyChanged", + _e_connman_element_property_changed_callback, element); + + e_connman_element_event_add(E_CONNMAN_EVENT_ELEMENT_ADD, element); + + return element; +} + +static void +_e_connman_element_event_unregister_and_free(void *data __UNUSED__, void *ev) +{ + E_Connman_Element *element = ev; + e_connman_element_unref(element); +} + +static void +_e_connman_element_unregister_internal(E_Connman_Element *element) +{ + if (element->signal_handler) + { + e_dbus_signal_handler_del(e_connman_conn, element->signal_handler); + element->signal_handler = NULL; + } + + ecore_event_add(E_CONNMAN_EVENT_ELEMENT_DEL, element, + _e_connman_element_event_unregister_and_free, NULL); +} + +/** + * Forget about the given element. + * + * This will remove the element from the pool of known objects, then + * add an E_CONNMAN_EVENT_ELEMENT_DEL and after that will unreference it, + * possible freeing it. + * + * @param element element to forget about. Its reference will be removed. + */ +void +e_connman_element_unregister(E_Connman_Element *element) +{ + if (!element) + return; + + if (elements) + eina_hash_del_by_key(elements, element->path); +} + +/** + * Remove all known elements. + * + * This will remove all known elements but will NOT add any + * E_CONNMAN_EVENT_ELEMENT_DEL to main loop. + * + * This is just useful to make sure next e_connman_manager_sync_elements() + * will not leave any stale elements. This is unlikely to happen, as + * E_Connman is supposed to catch all required events to avoid stale elements. + */ +void +e_connman_manager_clear_elements(void) +{ + e_connman_elements_shutdown(); + e_connman_elements_init(); +} + +/** + * Creates elements hash. + * + * This has no init counter since its already guarded by other code. + * @internal + */ +void +e_connman_elements_init(void) +{ + EINA_SAFETY_ON_FALSE_RETURN(!elements); + elements = + eina_hash_string_superfast_new(EINA_FREE_CB + (_e_connman_element_unregister_internal)); +} + +void +e_connman_elements_shutdown(void) +{ + EINA_SAFETY_ON_FALSE_RETURN(!!elements); + eina_hash_free(elements); + elements = NULL; +} + +static inline Eina_Bool +_e_connman_element_is(const E_Connman_Element *element, const char *interface) +{ + return element->interface == interface; +} + +Eina_Bool +e_connman_element_is_manager(const E_Connman_Element *element) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + return _e_connman_element_is(element, e_connman_iface_manager); +} + +Eina_Bool +e_connman_element_is_profile(const E_Connman_Element *element) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + return _e_connman_element_is(element, e_connman_iface_profile); +} + +Eina_Bool +e_connman_element_is_service(const E_Connman_Element *element) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + return _e_connman_element_is(element, e_connman_iface_service); +} + +Eina_Bool +e_connman_element_is_technology(const E_Connman_Element *element) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + return _e_connman_element_is(element, e_connman_iface_technology); +} + diff --git a/src/lib/connman0_7x/e_connman_manager.c b/src/lib/connman0_7x/e_connman_manager.c new file mode 100644 index 0000000..f6cc5ed --- /dev/null +++ b/src/lib/connman0_7x/e_connman_manager.c @@ -0,0 +1,590 @@ +#include "e_connman_private.h" + +/** + * Get the element manager. + * + * @return element pointer if found, NULL otherwise. + */ +E_Connman_Element * +e_connman_manager_get(void) +{ + return e_connman_element_get(manager_path); +} + +/** + * Register new agent for handling user requests. + * + * Call method RegisterAgent(object) on server in order to + * register new agent for handling user requests. + * + * @param object_path object to be registered. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_manager_agent_register(const char *object_path, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "RegisterAgent"; + E_Connman_Element *element; + + EINA_SAFETY_ON_NULL_RETURN_VAL(object_path, EINA_FALSE); + + element = e_connman_manager_get(); + if (!element) + return EINA_FALSE; + + return e_connman_element_call_with_path + (element, name, object_path, NULL, + &element->_pending.agent_register, cb, data); +} + +/** + * Unregister an existing agent. + * + * Call method UnregisterAgent(object) on server in order to + * unregister an existing agent. + * + * @param object_path agent to be unregistered. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_manager_agent_unregister(const char *object_path, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "UnregisterAgent"; + E_Connman_Element *element; + + EINA_SAFETY_ON_NULL_RETURN_VAL(object_path, EINA_FALSE); + + element = e_connman_manager_get(); + if (!element) + return EINA_FALSE; + + return e_connman_element_call_with_path + (element, name, object_path, NULL, + &element->_pending.agent_unregister, cb, data); +} + +/** + * Get property "State" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * The global connection state of a system. Possible + * values are "online" if at least one connection exists + * and "offline" if no service is connected. + * + * In certain situations the state might change to + * the value "connected". This can only be seen if + * previously no connection was present. + * + * @param state where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_manager_state_get(const char **state) +{ + E_Connman_Element *element; + + EINA_SAFETY_ON_NULL_RETURN_VAL(state, EINA_FALSE); + + element = e_connman_manager_get(); + if (!element) + return EINA_FALSE; + + return e_connman_element_property_get_stringshared + (element, e_connman_prop_state, NULL, state); +} + +/** + * Get property "OfflineMode" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * The offline mode indicates the global setting for switching all radios on or + * off. Changing offline mode to true results in powering down all devices that + * use radio technology. When leaving offline mode the individual policy of each + * technology decides to switch the radio back on or not. + * + * During offline mode, it is still possible to switch certain technologies + * manually back on. For example the limited usage of WiFi or Bluetooth + * technologies might be allowed in some situations. + * + * @param offline where to store the property value, must be a pointer + * to Eina_Bool (Eina_Bool *). + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * @see e_connman_manager_offline_mode_set() + */ +Eina_Bool +e_connman_manager_offline_mode_get(Eina_Bool *offline) +{ + E_Connman_Element *element; + + EINA_SAFETY_ON_NULL_RETURN_VAL(offline, EINA_FALSE); + + element = e_connman_manager_get(); + if (!element) + return EINA_FALSE; + + return e_connman_element_property_get_stringshared + (element, e_connman_prop_offline_mode, NULL, offline); +} + +/** + * Call method SetProperty("OfflineMode", offline) at the given element on server. + * + * This is a server call, not local, so it may fail and in that case + * no property is updated locally. If the value was set the event + * E_CONNMAN_EVENT_ELEMENT_UPDATED will be added to main loop. + * + * The offline mode indicates the global setting for switching all radios on or + * off. Changing offline mode to true results in powering down all devices that + * use radio technology. When leaving offline mode the individual policy of each + * technology decides to switch the radio back on or not. + * + * During offline mode, it is still possible to switch certain technologies + * manually back on. For example the limited usage of WiFi or Bluetooth + * technologies might be allowed in some situations. + * + * @param offline value to set. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * @see e_connman_manager_offline_mode_get() + */ +Eina_Bool +e_connman_manager_offline_mode_set(Eina_Bool offline, E_DBus_Method_Return_Cb cb, const void *data) +{ + E_Connman_Element *element = e_connman_manager_get(); + if (!element) + return EINA_FALSE; + + return e_connman_element_property_set_full + (element, e_connman_prop_offline_mode, DBUS_TYPE_BOOLEAN, + &offline, cb, data); +} + +/** + * Get array of profile elements. + * + * @param count return the number of elements in array. + * @param p_elements array with all elements, these are not referenced + * and in no particular order, just set if return is @c EINA_TRUE. The + * array itself is allocated using malloc() and should be freed + * after usage is done. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_manager_profiles_get(unsigned int *count, E_Connman_Element ***p_elements) +{ + E_Connman_Element *element; + + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(p_elements, EINA_FALSE); + + element = e_connman_manager_get(); + if (!element) + return EINA_FALSE; + + return e_connman_element_objects_array_get_stringshared + (element, e_connman_prop_profiles, count, p_elements); +} + +/** + * Get array of services elements. + * + * List of service object paths. The list is sorted + * internally to have the service with the default + * route always first and then the favorite services + * followed by scan results. + * + * This list represents the available services for the + * current selected profile. If the profile gets changed + * then this list will be updated. + * + * The same list is available via the profile object + * itself. It is just provided here for convenience of + * applications only dealing with the current active + * profile. + * + * @param count return the number of elements in array. + * @param p_elements array with all elements, these are not referenced + * and in no particular order, just set if return is @c EINA_TRUE. The + * array itself is allocated using malloc() and should be freed + * after usage is done. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_manager_services_get(unsigned int *count, E_Connman_Element ***p_elements) +{ + E_Connman_Element *element; + + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(p_elements, EINA_FALSE); + + element = e_connman_manager_get(); + if (!element) + return EINA_FALSE; + + return e_connman_element_objects_array_get_stringshared + (element, e_connman_prop_services, count, p_elements); +} + +/** + * Get array of technology elements. + * + * @param count return the number of elements in array. + * @param p_elements array with all elements, these are not referenced + * and in no particular order, just set if return is @c EINA_TRUE. The + * array itself is allocated using malloc() and should be freed + * after usage is done. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_manager_technologies_get(unsigned int *count, E_Connman_Element ***p_elements) +{ + E_Connman_Element *element; + + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(p_elements, EINA_FALSE); + + element = e_connman_manager_get(); + if (!element) + return EINA_FALSE; + + return e_connman_element_objects_array_get_stringshared + (element, e_connman_prop_technologies, count, p_elements); +} + +/** + * Request to trigger a scan for given technology. + * + * Call method RequestScan(type) on server in order to find new services for + * such technology type. + * + * The empty string for type means all technolgies. + * + * @param type technology type to scan. Empty or NULL for all technologies. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_manager_request_scan(const char *type, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "RequestScan"; + E_Connman_Element *element; + + if (!type) + type = ""; + + element = e_connman_manager_get(); + if (!element) + return EINA_FALSE; + + return e_connman_element_call_with_string + (element, name, type, NULL, + &element->_pending.request_scan, cb, data); +} + +/** + * Enable specified type of technology. + * + * Call method EnableTechnology(type) on server. + * + * @param type technology type to enable. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_manager_technology_enable(const char *type, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "EnableTechnology"; + E_Connman_Element *element; + + EINA_SAFETY_ON_NULL_RETURN_VAL(type, EINA_FALSE); + + element = e_connman_manager_get(); + if (!element) + return EINA_FALSE; + + return e_connman_element_call_with_string + (element, name, type, NULL, + &element->_pending.technology_enable, cb, data); +} + +/** + * Disable specified type of technology. + * + * Call method DisableTechnology(type) on server. + * + * @param type technology type to disable. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_manager_technology_disable(const char *type, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "DisableTechnology"; + E_Connman_Element *element; + + EINA_SAFETY_ON_NULL_RETURN_VAL(type, EINA_FALSE); + + element = e_connman_manager_get(); + if (!element) + return EINA_FALSE; + + return e_connman_element_call_with_string + (element, name, type, NULL, + &element->_pending.technology_disable, cb, data); +} + +/** + * Get property "DefaultTechnology" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * The current connected technology which holds the default route. + * + * @param type where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_manager_technology_default_get(const char **type) +{ + E_Connman_Element *element; + + EINA_SAFETY_ON_NULL_RETURN_VAL(type, EINA_FALSE); + + element = e_connman_manager_get(); + if (!element) + return EINA_FALSE; + + return e_connman_element_property_get_stringshared + (element, e_connman_prop_technology_default, NULL, type); +} + +/** + * Remove specified profile. + * + * Call method RemoveProfile(profile) on server. + * + * It is not possible to remove the current active profile. To remove + * the active profile a different one must be selected via + * ActiveProfile property first. + * + * At minimum one profile must be available all the time. + * + * @param profile element to remove, must be of type profile. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_manager_profile_remove(const E_Connman_Element *profile, E_DBus_Method_Return_Cb cb, const void *data) +{ + E_Connman_Element *element; + const char name[] = "RemoveProfile"; + + EINA_SAFETY_ON_NULL_RETURN_VAL(profile, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(profile->path, EINA_FALSE); + + if (!e_connman_element_is_profile(profile)) + return EINA_FALSE; + + element = e_connman_manager_get(); + if (!element) + return EINA_FALSE; + + return e_connman_element_call_with_path + (element, name, profile->path, NULL, + &element->_pending.profile_remove, cb, data); +} + +/** + * Get property "ActiveProfile" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param element where to store the element, just changed if return is @c EINA_TRUE + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * + * @see e_connman_manager_profile_active_set() + */ +Eina_Bool +e_connman_manager_profile_active_get(E_Connman_Element **profile) +{ + E_Connman_Element *element; + char *profile_path; + + EINA_SAFETY_ON_NULL_RETURN_VAL(profile, EINA_FALSE); + + element = e_connman_manager_get(); + if (!element) + return EINA_FALSE; + + if (!e_connman_element_property_get_stringshared + (element, e_connman_prop_profile_active, NULL, &profile_path)) + return EINA_FALSE; + + *profile = e_connman_element_get(profile_path); + return EINA_TRUE; +} + +/** + * Call method SetProperty("ActiveProfile", profile) at the given + * element on server. + * + * This is a server call, not local, so it may fail and in that case + * no property is updated locally. If the value was set the event + * E_CONNMAN_EVENT_ELEMENT_UPDATED will be added to main loop. + * + * @param profile object to set. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * @see e_connman_manager_profile_active_get() + */ +Eina_Bool +e_connman_manager_profile_active_set(const E_Connman_Element *profile, E_DBus_Method_Return_Cb cb, const void *data) +{ + E_Connman_Element *element; + + EINA_SAFETY_ON_NULL_RETURN_VAL(profile, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(profile->path, EINA_FALSE); + + if (!e_connman_element_is_profile(profile)) + return EINA_FALSE; + + element = e_connman_manager_get(); + if (!element) + return EINA_FALSE; + + return e_connman_element_property_set_full + (element, e_connman_prop_profile_active, DBUS_TYPE_OBJECT_PATH, + profile->path, cb, data); +} + +/** + * Get array of strings representing the available technologies. + * + * @param count return the number of elements in array. + * @param p_strings array with pointers to internal strings. These + * strings are not copied in any way, and they are granted to + * be eina_stringshare instances, so one can use + * eina_stringshare_ref() if he wants to save memory and cpu to + * get an extra reference. The array itself is allocated using + * malloc() and should be freed after usage is done. This + * pointer is just set if return is @c EINA_TRUE. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_manager_technologies_available_get(unsigned int *count, const char ***p_strings) +{ + E_Connman_Element *element; + + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(p_strings, EINA_FALSE); + + element = e_connman_manager_get(); + if (!element) + return EINA_FALSE; + + return e_connman_element_strings_array_get_stringshared + (element, e_connman_prop_technologies_available, count, p_strings); +} + +/** + * Get array of strings representing the enabled technologies. + * + * @param count return the number of elements in array. + * @param p_strings array with pointers to internal strings. These + * strings are not copied in any way, and they are granted to + * be eina_stringshare instances, so one can use + * eina_stringshare_ref() if he wants to save memory and cpu to + * get an extra reference. The array itself is allocated using + * malloc() and should be freed after usage is done. This + * pointer is just set if return is @c EINA_TRUE. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_manager_technologies_enabled_get(unsigned int *count, const char ***p_strings) +{ + E_Connman_Element *element; + + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(p_strings, EINA_FALSE); + + element = e_connman_manager_get(); + if (!element) + return EINA_FALSE; + + return e_connman_element_strings_array_get_stringshared + (element, e_connman_prop_technologies_enabled, count, p_strings); +} + +/** + * Get array of strings representing the connected technologies. + * + * @param count return the number of elements in array. + * @param p_strings array with pointers to internal strings. These + * strings are not copied in any way, and they are granted to + * be eina_stringshare instances, so one can use + * eina_stringshare_ref() if he wants to save memory and cpu to + * get an extra reference. The array itself is allocated using + * malloc() and should be freed after usage is done. This + * pointer is just set if return is @c EINA_TRUE. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_manager_technologies_connected_get(unsigned int *count, const char ***p_strings) +{ + E_Connman_Element *element; + + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(p_strings, EINA_FALSE); + + element = e_connman_manager_get(); + if (!element) + return EINA_FALSE; + + return e_connman_element_strings_array_get_stringshared + (element, e_connman_prop_technologies_connected, count, p_strings); +} + diff --git a/src/lib/connman0_7x/e_connman_private.h b/src/lib/connman0_7x/e_connman_private.h new file mode 100644 index 0000000..d6f98f9 --- /dev/null +++ b/src/lib/connman0_7x/e_connman_private.h @@ -0,0 +1,174 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_ALLOCA_H +# include +#elif defined __GNUC__ +# define alloca __builtin_alloca +#elif defined _AIX +# define alloca __alloca +#elif defined _MSC_VER +# include +# define alloca _alloca +#else +# include +# ifdef __cplusplus +extern "C" +# endif +void * alloca (size_t); +#endif + +#include + +#include +#include + +#define E_CONNMAN_I_KNOW_THIS_API_IS_SUBJECT_TO_CHANGE 1 +#include "E_Connman.h" + +typedef struct _E_Connman_Array E_Connman_Array; +struct _E_Connman_Array +{ + int type; + Eina_Array *array; +}; + +static const char manager_path[] = "/"; + +extern const char *e_connman_iface_manager; +extern const char *e_connman_iface_profile; +extern const char *e_connman_iface_service; +extern const char *e_connman_iface_connection; +extern const char *e_connman_iface_technology; + +extern const char *e_connman_prop_ipv4; +extern const char *e_connman_prop_ipv4_configuration; +extern const char *e_connman_prop_ethernet; +extern const char *e_connman_prop_interface; +extern const char *e_connman_prop_speed; +extern const char *e_connman_prop_duplex; +extern const char *e_connman_prop_method; +extern const char *e_connman_prop_address; +extern const char *e_connman_prop_gateway; +extern const char *e_connman_prop_netmask; +extern const char *e_connman_prop_mtu; +extern const char *e_connman_prop_name; +extern const char *e_connman_prop_offline_mode; +extern const char *e_connman_prop_profiles; +extern const char *e_connman_prop_profile_active; +extern const char *e_connman_prop_services; +extern const char *e_connman_prop_technologies; +extern const char *e_connman_prop_state; +extern const char *e_connman_prop_strength; +extern const char *e_connman_prop_type; +extern const char *e_connman_prop_error; +extern const char *e_connman_prop_security; +extern const char *e_connman_prop_passphrase; +extern const char *e_connman_prop_passphrase_required; +extern const char *e_connman_prop_favorite; +extern const char *e_connman_prop_immutable; +extern const char *e_connman_prop_auto_connect; +extern const char *e_connman_prop_roaming; +extern const char *e_connman_prop_technology_default; +extern const char *e_connman_prop_technologies_available; +extern const char *e_connman_prop_technologies_enabled; +extern const char *e_connman_prop_technologies_connected; +extern const char *e_connman_prop_login_required; +extern const char *e_connman_prop_nameservers; +extern const char *e_connman_prop_nameservers_configuration; +extern const char *e_connman_prop_domains; +extern const char *e_connman_prop_domains_configuration; +extern const char *e_connman_prop_proxy; +extern const char *e_connman_prop_proxy_configuration; +extern const char *e_connman_prop_url; +extern const char *e_connman_prop_servers; +extern const char *e_connman_prop_excludes; + +extern int _e_dbus_connman_log_dom; + +#ifndef EINA_LOG_DEFAULT_COLOR +#define EINA_LOG_DEFAULT_COLOR EINA_COLOR_CYAN +#endif + +#undef DBG +#undef INF +#undef WRN +#undef ERR + +#define DBG(...) EINA_LOG_DOM_DBG(_e_dbus_connman_log_dom, __VA_ARGS__) +#define INF(...) EINA_LOG_DOM_INFO(_e_dbus_connman_log_dom, __VA_ARGS__) +#define WRN(...) EINA_LOG_DOM_WARN(_e_dbus_connman_log_dom, __VA_ARGS__) +#define ERR(...) EINA_LOG_DOM_ERR(_e_dbus_connman_log_dom, __VA_ARGS__) + +static inline Eina_Bool +_dbus_callback_check_and_init(DBusMessage *msg, DBusMessageIter *itr, DBusError *err) +{ + if (!msg) + { + if (err && (err->name[0] == 'C')) + return EINA_FALSE; + if (err) + { + /* dont keep reporting the same err again and again */ + static char perr[256] = {0}; + + if (!(!strncmp(perr, err->name, sizeof(perr) - 1))) + { + ERR("an error was reported by server: " + "name=\"%s\", message=\"%s\"", + err->name, err->message); + strncpy(perr, err->name, sizeof(perr) - 1); + perr[sizeof(perr) - 1] = 0; + } + } + else + ERR("callback without message arguments!"); + + return EINA_FALSE; + } + + if (!dbus_message_iter_init(msg, itr)) + { + ERR("could not init iterator."); + return EINA_FALSE; + } + + return EINA_TRUE; +} + +static inline Eina_Bool +__dbus_iter_type_check(int type, int expected, const char *expected_name) +{ + if (type == expected) + return EINA_TRUE; + + ERR("expected type %s (%c) but got %c instead!", + expected_name, expected, type); + + return EINA_FALSE; +} + +#define _dbus_iter_type_check(t, e) __dbus_iter_type_check(t, e, # e) + +extern E_DBus_Connection *e_connman_conn; + +const char * e_connman_system_bus_name_get(void); + +void e_connman_manager_clear_elements(void); + +void e_connman_elements_init(void); +void e_connman_elements_shutdown(void); + +E_Connman_Element * e_connman_element_register(const char *path, const char *interface); +void e_connman_element_unregister(E_Connman_Element *element); + +Eina_Bool e_connman_element_objects_array_get_stringshared(const E_Connman_Element *element, const char *property, unsigned int *count, E_Connman_Element ***elements); +Eina_Bool e_connman_element_strings_array_get_stringshared(const E_Connman_Element *element, const char *property, unsigned int *count, const char ***strings); +unsigned char * e_connman_element_bytes_array_get_stringshared(const E_Connman_Element *element, const char *property, unsigned int *count); + +Eina_Bool e_connman_element_message_send(E_Connman_Element *element, const char *method_name, E_DBus_Method_Return_Cb cb, DBusMessage *msg, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data); + +Eina_Bool e_connman_element_call_full(E_Connman_Element *element, const char *method_name, E_DBus_Method_Return_Cb cb, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data); +Eina_Bool e_connman_element_call_with_path(E_Connman_Element *element, const char *method_name, const char *string, E_DBus_Method_Return_Cb cb, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data); +Eina_Bool e_connman_element_call_with_string(E_Connman_Element *element, const char *method_name, const char *string, E_DBus_Method_Return_Cb cb, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data); diff --git a/src/lib/connman0_7x/e_connman_profile.c b/src/lib/connman0_7x/e_connman_profile.c new file mode 100644 index 0000000..70c0dcf --- /dev/null +++ b/src/lib/connman0_7x/e_connman_profile.c @@ -0,0 +1,143 @@ +#include "e_connman_private.h" + +E_Connman_Element * +e_connman_profile_get(const char *path) +{ + E_Connman_Element *profile; + + EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL); + + profile = e_connman_element_get(path); + if (!profile) + return NULL; + + if (!e_connman_element_is_profile(profile)) + { + WRN("path '%s' is not a profile!", path); + return NULL; + } + + return profile; +} + +/** + * Get property "Name" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * The profile name, if set with e_connman_profile_name_set() + * + * @param profile path to get property. + * @param name where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * @see e_connman_profile_name_set() + */ +Eina_Bool +e_connman_profile_name_get(const E_Connman_Element *profile, const char **name) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(profile, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); + return e_connman_element_property_get_stringshared + (profile, e_connman_prop_name, NULL, name); +} + +/** + * Call method SetProperty("Name", name) at the given element on server. + * + * This is a server call, not local, so it may fail and in that case + * no property is updated locally. If the value was set the event + * E_CONNMAN_EVENT_ELEMENT_UPDATED will be added to main loop. + * + * @param name value to set. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * @see e_connman_profile_name_get() + */ +Eina_Bool +e_connman_profile_name_set(E_Connman_Element *profile, const char *name, E_DBus_Method_Return_Cb cb, const void *data) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(profile, EINA_FALSE); + return e_connman_element_property_set_full + (profile, e_connman_prop_name, DBUS_TYPE_STRING, name, cb, data); +} + +/** + * Get property "OfflineMode" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * The offline mode indicates the global setting for switching all radios on or + * off. Changing offline mode to true results in powering down all devices that + * use radio technology. + * + * @param offline where to store the property value, must be a pointer + * to Eina_Bool (Eina_Bool *). + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * @see e_connman_profile_offline_mode_set() + */ +Eina_Bool +e_connman_profile_offline_mode_get(const E_Connman_Element *profile, Eina_Bool *offline) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(profile, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(offline, EINA_FALSE); + return e_connman_element_property_get_stringshared + (profile, e_connman_prop_offline_mode, NULL, offline); +} + +/** + * Call method SetProperty("OfflineMode", offline) at the given element on server. + * + * This is a server call, not local, so it may fail and in that case + * no property is updated locally. If the value was set the event + * E_CONNMAN_EVENT_ELEMENT_UPDATED will be added to main loop. + * + * The offline mode indicates the global setting for switching all radios on or + * off. Changing offline mode to true results in powering down all devices that + * use radio technology. + * + * @param offline value to set. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * @see e_connman_profile_offline_mode_get() + */ +Eina_Bool +e_connman_profile_offline_mode_set(E_Connman_Element *profile, Eina_Bool offline, E_DBus_Method_Return_Cb cb, const void *data) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(profile, EINA_FALSE); + return e_connman_element_property_set_full + (profile, e_connman_prop_offline_mode, DBUS_TYPE_BOOLEAN, + &offline, cb, data); +} + +/** + * Get array of service elements. + * + * @param count return the number of elements in array. + * @param p_elements array with all elements, these are not referenced + * and in no particular order, just set if return is @c EINA_TRUE. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_profile_services_get(const E_Connman_Element *profile, unsigned int *count, E_Connman_Element ***p_elements) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(profile, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(p_elements, EINA_FALSE); + return e_connman_element_objects_array_get_stringshared + (profile, e_connman_prop_services, count, p_elements); +} + diff --git a/src/lib/connman0_7x/e_connman_service.c b/src/lib/connman0_7x/e_connman_service.c new file mode 100644 index 0000000..0c6c320 --- /dev/null +++ b/src/lib/connman0_7x/e_connman_service.c @@ -0,0 +1,1646 @@ +#include "e_connman_private.h" + +E_Connman_Element * +e_connman_service_get(const char *path) +{ + E_Connman_Element *service; + + EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL); + + service = e_connman_element_get(path); + if (!service) + return NULL; + + if (!e_connman_element_is_service(service)) + { + WRN("path '%s' is not a service!", path); + return NULL; + } + + return service; +} + +/** + * Connect this service. + * + * Connect this service. It will attempt to connect + * WiFi, WiMAX or Bluetooth services. + * + * For Ethernet devices this method can only be used + * if it has previously been disconnected. Otherwise + * the plugging of a cable will trigger connecting + * automatically. If no cable is plugged in this method + * will fail. + * + * @param service path to call method on server. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_connect(E_Connman_Element *service, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "Connect"; + + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + return e_connman_element_call_full + (service, name, NULL, &service->_pending.service_connect, cb, data); +} + +/** + * Disconnect this service. + * + * Disconnect this service. If the service is not + * connected an error message will be generated. + * + * On Ethernet devices this will disconnect the IP + * details from the service. It will not magically + * unplug the cable. When no cable is plugged in this + * method will fail. + * + * This method can also be used to abort a previous + * connectiong attempt via the Connect method. + * + * @param service path to call method on server. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_disconnect(E_Connman_Element *service, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "Disconnect"; + + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + return e_connman_element_call_full + (service, name, NULL, &service->_pending.service_disconnect, cb, data); +} + +/** + * Remove this service. + * + * A successfully connected service with Favorite=true + * can be removed this way. If it is connected, it will + * be automatically disconnected first. + * + * If the service requires a passphrase it will be + * cleared and forgotten when removing. + * + * This is similar to setting the Favorite property + * to false, but that is currently not supported. + * + * In the case a connection attempt failed and the + * service is in the State=failure, this method can + * also be used to reset the service. + * + * Calling this method on Ethernet devices will cause + * an error message. It is not possible to remove these + * kind of devices. + * + * @param service path to call method on server. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_remove(E_Connman_Element *service, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "Remove"; + + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + return e_connman_element_call_full + (service, name, NULL, &service->_pending.service_remove, cb, data); +} + +/** + * Clears the value of the specified property. + * + * + * @param service path to call method on server. + * @param property to be cleared. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_clear_property(E_Connman_Element *service, const char *property, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "ClearProperty"; + + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(property, EINA_FALSE); + return e_connman_element_call_with_string + (service, name, property, NULL, &service->_pending.service_clear_property, + cb, data); +} + +/** + * Move service before in favorites list. + * + * Call method MoveBefore(object service) at the given service on server. + * + * If a service has been used before, this allows a + * reorder of the favorite services. + * + * The target service object must be part of this + * profile. Moving between profiles is not supported. + * + * @param service path to call method on server. + * @param object_path object service. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_move_before(E_Connman_Element *service, const char *object_path, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "MoveBefore"; + + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(object_path, EINA_FALSE); + return e_connman_element_call_with_path + (service, name, object_path, NULL, + &service->_pending.service_move_before, cb, data); +} + +/** + * Move service after in favorites list. + * + * Call method MoveAfter(object service) at the given service on server. + * + * If a service has been used before, this allows a + * reorder of the favorite services. + * + * The target service object must be part of this + * profile. Moving between profiles is not supported. + * + * @param service path to call method on server. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_move_after(E_Connman_Element *service, const char *object_path, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "MoveAfter"; + + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(object_path, EINA_FALSE); + return e_connman_element_call_with_path + (service, name, object_path, NULL, + &service->_pending.service_move_after, cb, data); +} + +/** + * Get property "State" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * The service state information. + * + * Valid states are "idle", "failure", "association", + * "configuration", "ready", "login" and "online". + * + * @param service path to get property. + * @param state where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_state_get(const E_Connman_Element *service, const char **state) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(state, EINA_FALSE); + return e_connman_element_property_get_stringshared + (service, e_connman_prop_state, NULL, state); +} + +/** + * Get property "Error" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * The service error status details. + * + * When error occur during connection or disconnection + * the detailed information are represented in this + * property to help the user interface to present the + * user with alternate options. + * + * This property is only valid when the service is in + * the "failure" state. Otherwise it might be empty or + * not present at all. + * + * Current defined error code is "dhcp-failed". + * + * @param service path to get property. + * @param error where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_error_get(const E_Connman_Element *service, const char **error) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(error, EINA_FALSE); + return e_connman_element_property_get_stringshared + (service, e_connman_prop_error, NULL, error); +} + +/** + * Get property "Name" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * The service name (for example "Wireless" etc.) + * + * This name can be used for directly displaying it in + * the application. It has pure informational purpose. + * + * For Ethernet devices and hidden WiFi networks it is + * not guaranteed that this property is present. + * + * @param service path to get property. + * @param name where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_name_get(const E_Connman_Element *service, const char **name) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); + return e_connman_element_property_get_stringshared + (service, e_connman_prop_name, NULL, name); +} + +/** + * Get property "Type" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * The service type (for example "ethernet", "wifi" etc.) + * + * This information should only be used to determine + * advanced properties or showing the correct icon + * to the user. + * + * @param service path to get property. + * @param type where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_type_get(const E_Connman_Element *service, const char **type) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(type, EINA_FALSE); + return e_connman_element_property_get_stringshared + (service, e_connman_prop_type, NULL, type); +} + +/** + * Get property "Security" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * If the service type is WiFi, then this property is + * present and contains the list of security method or key + * management setting. + * + * Possible values are "none", "wep", "wpa", "rsn", "psk", "ieee8021x" and "wps" + * + * This property might be only present for WiFi + * services. + * + * @param service path to get property. + * @param count where to return the number of elements in @a security + * @param security where to store the property value, must be a pointer + * to array of strings, it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_security_get(const E_Connman_Element *service, unsigned int *count, const char ***security) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(security, EINA_FALSE); + + return e_connman_element_strings_array_get_stringshared + (service, e_connman_prop_security, count, security); +} + +/** + * Get property "Passphrase" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * If the service type is WiFi, then this property + * can be used to store a passphrase. + * + * No PropertyChanged signals will be send for this + * property. The PassphraseRequired property should + * be monitored instead. + * + * This property might also not always be included + * since it is protected by a different security policy. + * + * @param service path to get property. + * @param passphrase where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * @see e_connman_service_passphrase_set() + */ +Eina_Bool +e_connman_service_passphrase_get(const E_Connman_Element *service, const char **passphrase) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(passphrase, EINA_FALSE); + return e_connman_element_property_get_stringshared + (service, e_connman_prop_passphrase, NULL, passphrase); +} + +/** + * Set property "Passphrase" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * If the service type is WiFi, then this property + * can be used to store a passphrase. + * + * No PropertyChanged signals will be send for this + * property. The PassphraseRequired property should + * be monitored instead. + * + * This property might also not always be included + * since it is protected by a different security policy. + * + * @param service path to get property. + * @param passphrase value to set. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * @see e_connman_service_passphrase_get() + */ +Eina_Bool +e_connman_service_passphrase_set(E_Connman_Element *service, const char *passphrase, E_DBus_Method_Return_Cb cb, const void *data) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + return e_connman_element_property_set_full + (service, e_connman_prop_passphrase, DBUS_TYPE_STRING, + passphrase, cb, data); +} + +/** + * Get property "PassphraseRequired" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * If the service type is WiFi, then this property + * indicates if a passphrase is required. + * + * If a passphrase has been set already or if no + * passphrase is needed, then this property will + * be set to false. + * + * @param service path to get property. + * @param passphrase_required where to store the property value, must be a + * pointer to Eina_Bool (Eina_Bool *). + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_passphrase_required_get(const E_Connman_Element *service, Eina_Bool *passphrase_required) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(passphrase_required, EINA_FALSE); + return e_connman_element_property_get_stringshared + (service, e_connman_prop_passphrase_required, NULL, passphrase_required); +} + +/** + * Get property "LoginRequired" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * Indicates that a manual configuration must be done to login the + * user, likely access an website using a browser. + * + * If a login has been set already or if no + * login is needed, then this property will + * be set to false. + * + * @param service path to get property. + * @param login_required where to store the property value, must be a + * pointer to Eina_Bool (Eina_Bool *). + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * + * @since 1.1.0 + */ +Eina_Bool +e_connman_service_login_required_get(const E_Connman_Element *service, Eina_Bool *login_required) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(login_required, EINA_FALSE); + return e_connman_element_property_get_stringshared + (service, e_connman_prop_login_required, NULL, login_required); +} + +/** + * Get property "Strength" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * Indicates the signal strength of the service. This + * is a normalized value between 0 and 100. + * + * This property will not be present for Ethernet + * devices. + * + * @param service path to get property. + * @param strength where to store the property value, must be a pointer + * to byte (unsigned char*). + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_strength_get(const E_Connman_Element *service, unsigned char *strength) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(strength, EINA_FALSE); + return e_connman_element_property_get_stringshared + (service, e_connman_prop_strength, NULL, strength); +} + +/** + * Get property "Favorite" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * Will be true if a cable is plugged in or the user + * selected and successfully connected to this service. + * + * This value is automatically changed and to revert + * it back to false the Remove() method needs to be + * used. + * + * @param service path to get property. + * @param favorite where to store the property value, must be a + * pointer to Eina_Bool (Eina_Bool *). + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_favorite_get(const E_Connman_Element *service, Eina_Bool *favorite) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(favorite, EINA_FALSE); + return e_connman_element_property_get_stringshared + (service, e_connman_prop_favorite, NULL, favorite); +} + +/** + * Get property "Immutable" value. + * + * This value will be set to true if the service is configured + * externally via a configuration file. + * + * The only valid operation are e_connman_service_connect() and + * e_connman_service_disconnect(). The e_connman_service_remove() + * method will result in an error. + + * @param service path to get property. + * @param immutable where to store the property value, must be a + * pointer to Eina_Bool (Eina_Bool *). + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_immutable_get(const E_Connman_Element *service, Eina_Bool *immutable) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(immutable, EINA_FALSE); + return e_connman_element_property_get_stringshared + (service, e_connman_prop_immutable, NULL, immutable); +} + +/** + * Get property "AutoConnect" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * If set to true, this service will auto-connect + * when not other connection is available. + * + * For favorite services it is possible to change + * this value to prevent or permit automatic + * connection attempts. + * + * @param service path to get property. + * @param auto_connect where to store the property value, must be a + * pointer to Eina_Bool (Eina_Bool *). + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * @see e_connman_service_auto_connect_set() + */ +Eina_Bool +e_connman_service_auto_connect_get(const E_Connman_Element *service, Eina_Bool *auto_connect) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(auto_connect, EINA_FALSE); + return e_connman_element_property_get_stringshared + (service, e_connman_prop_auto_connect, NULL, auto_connect); +} + +/** + * Set property "AutoConnect" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * If set to true, this service will auto-connect + * when not other connection is available. + * + * For favorite services it is possible to change + * this value to prevent or permit automatic + * connection attempts. + * + * @param service path to get property. + * @param service_favorite where to store the property value, must be a + * pointer to Eina_Bool (Eina_Bool *). + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * @see e_connman_service_auto_connect_get() + */ +Eina_Bool +e_connman_service_auto_connect_set(E_Connman_Element *service, Eina_Bool auto_connect, E_DBus_Method_Return_Cb cb, const void *data) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + return e_connman_element_property_set_full + (service, e_connman_prop_auto_connect, DBUS_TYPE_BOOLEAN, &auto_connect, cb, data); +} + +/** + * Get property "Roaming" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * This property indicates if this service is roaming. + * + * In the case of Cellular services this normally + * indicates connections to a foreign provider when + * traveling abroad. + * + * @param service path to get property. + * @param roaming where to store the property value, must be a + * pointer to Eina_Bool (Eina_Bool *). + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_roaming_get(const E_Connman_Element *service, Eina_Bool *roaming) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(roaming, EINA_FALSE); + return e_connman_element_property_get_stringshared + (service, e_connman_prop_roaming, NULL, roaming); +} + +/** + * Get property "Nameservers" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * The list of currently active nameservers for this service. If the server is + * not in READY or ONLINE state than this list will be empty. + * + * Global nameservers are automatically added to this list. + * + * The array represents a sorted list of the current nameservers. The first one + * has the highest priority and is used by default. + * + * When using DHCP this array represents the nameservers provided by the + * network. In case of manual settings, the ones from Nameservers.Configuration + * are used. + * + * @param service path to get property. + * @param count return the number of elements in array. + * @param nameservers array with pointers to internal strings. These + * strings are not copied in any way, and they are granted to + * be eina_stringshare instances, so one can use + * eina_stringshare_ref() if he wants to save memory and cpu to + * get an extra reference. The array itself is also NOT + * allocated or copied, do not modify it. This pointer is just + * set if return is @c EINA_TRUE. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_nameservers_get(const E_Connman_Element *service, unsigned int *count, const char ***nameservers) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(nameservers, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + return e_connman_element_strings_array_get_stringshared + (service, e_connman_prop_nameservers, count, nameservers); +} + +/** + * Get property "Nameservers.Configuration" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * The list of currently active nameservers for this service. If the server is + * not in READY or ONLINE state than this list will be empty. + * + * Unlike Nameservers, this is the user-set value, rather than the + * actual value. + * + * @param service path to get property. + * @param count return the number of elements in array. + * @param nameservers array with pointers to internal strings. These + * strings are not copied in any way, and they are granted to + * be eina_stringshare instances, so one can use + * eina_stringshare_ref() if he wants to save memory and cpu to + * get an extra reference. The array itself is also NOT + * allocated or copied, do not modify it. This pointer is just + * set if return is @c EINA_TRUE. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_nameservers_configuration_get(const E_Connman_Element *service, unsigned int *count, const char ***nameservers) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(nameservers, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + return e_connman_element_strings_array_get_stringshared + (service, e_connman_prop_nameservers_configuration, + count, nameservers); +} + +/** + * Set property "Nameservers.Configuration" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * Unlike Nameservers, this is the user-set value, rather than the actual value. + * It allows user to override the default setting. When using manual + * configuration and no global nameservers are configured, then it is useful to + * configure this setting as well. + * + * This list is sorted by priority and the first entry represents the nameserver + * with the highest priority. + * + * Changes to the domain name servers can be done at any time. It will not cause + * a disconnect of the service. However there might be small window where name + * resolution might fail. + * + * @param service path to set property. + * @param nameservers sorted list of the current nameservers. The first one has + * the highest priority and is used by default. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * @see e_connman_service_nameservers_configuration_get() + */ +Eina_Bool +e_connman_service_nameservers_configuration_set(E_Connman_Element *service, unsigned int count, const char **nameservers, E_DBus_Method_Return_Cb cb, const void *data) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(nameservers, EINA_FALSE); + return e_connman_element_property_array_set_full + (service, e_connman_prop_nameservers_configuration, + DBUS_TYPE_STRING, count, + (const void * const *)nameservers, cb, data); +} + +/** + * Get property "Domains" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * The list of currently active domains for this service. If the server is + * not in READY or ONLINE state than this list will be empty. + * + * The list of currently used search domains. + * + * @param service path to get property. + * @param count return the number of elements in array. + * @param domains array with pointers to internal strings. These + * strings are not copied in any way, and they are granted to + * be eina_stringshare instances, so one can use + * eina_stringshare_ref() if he wants to save memory and cpu to + * get an extra reference. The array itself is also NOT + * allocated or copied, do not modify it. This pointer is just + * set if return is @c EINA_TRUE. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_domains_get(const E_Connman_Element *service, unsigned int *count, const char ***domains) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(domains, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + return e_connman_element_strings_array_get_stringshared + (service, e_connman_prop_domains, count, domains); +} + +/** + * Get property "Domains.Configuration" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * The list of currently active domains for this service. If the server is + * not in READY or ONLINE state than this list will be empty. + * + * Unlike Domains, this is the user-set value, rather than the + * actual value. + * + * @param service path to get property. + * @param count return the number of elements in array. + * @param domains array with pointers to internal strings. These + * strings are not copied in any way, and they are granted to + * be eina_stringshare instances, so one can use + * eina_stringshare_ref() if he wants to save memory and cpu to + * get an extra reference. The array itself is also NOT + * allocated or copied, do not modify it. This pointer is just + * set if return is @c EINA_TRUE. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_domains_configuration_get(const E_Connman_Element *service, unsigned int *count, const char ***domains) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(domains, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + return e_connman_element_strings_array_get_stringshared + (service, e_connman_prop_domains_configuration, count, domains); +} + +/** + * Set property "Domains.Configuration" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * Unlike Domains, this is the user-set value, rather than the actual value. + * It allows user to override the default setting. When using manual + * configuration and no global domains are configured, then it is useful to + * configure this setting as well. + * + * This list is sorted by priority and the first entry represents the nameserver + * with the highest priority. + * + * Changes to the domain name servers can be done at any time. It will not cause + * a disconnect of the service. However there might be small window where name + * resolution might fail. + * + * @param service path to set property. + * @param count number of elements in @a domain. + * @param domains sorted list of the current domains. The first one has + * the highest priority and is used by default. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * @see e_connman_service_domains_configuration_get() + */ +Eina_Bool +e_connman_service_domains_configuration_set(E_Connman_Element *service, unsigned int count, const char **domains, E_DBus_Method_Return_Cb cb, const void *data) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(domains, EINA_FALSE); + return e_connman_element_property_array_set_full + (service, e_connman_prop_domains_configuration, + DBUS_TYPE_STRING, count, + (const void * const *)domains, cb, data); +} + +/** + * Get property "IPv4.Method" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * The IPv4 method in use. Possible values here are "dhcp" and + * "static". + * + * @param service path to get property. + * @param method where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_ipv4_method_get(const E_Connman_Element *service, const char **method) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(method, EINA_FALSE); + return e_connman_element_property_dict_get_stringshared + (service, e_connman_prop_ipv4, e_connman_prop_method, NULL, method); +} + +/** + * Get property "IPv4.Address" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * The current IPv4 address. + * + * @param service path to get property. + * @param address where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_ipv4_address_get(const E_Connman_Element *service, const char **address) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINA_FALSE); + return e_connman_element_property_dict_get_stringshared + (service, e_connman_prop_ipv4, e_connman_prop_address, NULL, address); +} + +/** + * Get property "IPv4.Gateway" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * The current IPv4 gateway. + * + * @param service path to get property. + * @param gateway where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_ipv4_gateway_get(const E_Connman_Element *service, const char **gateway) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(gateway, EINA_FALSE); + return e_connman_element_property_dict_get_stringshared + (service, e_connman_prop_ipv4, e_connman_prop_gateway, NULL, gateway); +} + +/** + * Get property "IPv4.Netmask" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * The current IPv4 netmask. + * + * @param service path to get property. + * @param netmask where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_ipv4_netmask_get(const E_Connman_Element *service, const char **netmask) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(netmask, EINA_FALSE); + return e_connman_element_property_dict_get_stringshared + (service, e_connman_prop_ipv4, e_connman_prop_netmask, NULL, netmask); +} + +/** + * Get property "IPv4.Configuration.Method" value. + * + * Unlike IPv4.Method, this is the user-set value, rather than the + * actual value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * The IPv4 configured method. Possible values here are "dhcp" and + * "static". + * + * @param service path to get property. + * @param method where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_ipv4_configuration_method_get(const E_Connman_Element *service, const char **method) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(method, EINA_FALSE); + return e_connman_element_property_dict_get_stringshared + (service, e_connman_prop_ipv4_configuration, e_connman_prop_method, + NULL, method); +} + +/** + * Get property "IPv4.Configuration.Address" value. + * + * Unlike IPv4.Address, this is the user-set value, rather than the + * actual value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * The current configured IPv4 address. + * + * @param service path to get property. + * @param address where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_ipv4_configuration_address_get(const E_Connman_Element *service, const char **address) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINA_FALSE); + return e_connman_element_property_dict_get_stringshared + (service, e_connman_prop_ipv4_configuration, e_connman_prop_address, + NULL, address); +} + +/** + * Get property "IPv4.Configuration.Gateway" value. + * + * Unlike IPv4.Gateway, this is the user-set value, rather than the + * actual value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * The current configured IPv4 gateway. + * + * @param service path to get property. + * @param gateway where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_ipv4_configuration_gateway_get(const E_Connman_Element *service, const char **gateway) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(gateway, EINA_FALSE); + return e_connman_element_property_dict_get_stringshared + (service, e_connman_prop_ipv4_configuration, e_connman_prop_gateway, + NULL, gateway); +} + +/** + * Get property "IPv4.Configuration.Netmask" value. + * + * Unlike IPv4.Netmask, this is the user-set value, rather than the + * actual value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * The current configured IPv4 netmask. + * + * @param service path to get property. + * @param netmask where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_ipv4_configuration_netmask_get(const E_Connman_Element *service, const char **netmask) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(netmask, EINA_FALSE); + return e_connman_element_property_dict_get_stringshared + (service, e_connman_prop_ipv4_configuration, e_connman_prop_netmask, + NULL, netmask); +} + +/** + * Set IPv4 to connect automatically using DHCP. + * + * @param service path to set. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_ipv4_configure_dhcp(E_Connman_Element *service, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char method[] = "dhcp"; + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + return e_connman_element_property_dict_set_full + (service, e_connman_prop_ipv4_configuration, e_connman_prop_method, + DBUS_TYPE_STRING, method, cb, data); +} + +/** + * Set IPv4 to connect using manually set parameters. + * + * @param service path to set. + * @param address IPv4 address. + * @param netmask IPv4 netmask, or @c NULL for "/32". + * @param gateway IPv4 gateway address. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_ipv4_configure_manual(E_Connman_Element *service, const char *address, const char *netmask, const char *gateway, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "SetProperty"; + const char *method = "manual"; /* not method[] as gcc screws it with dbus */ + DBusMessage *msg; + DBusMessageIter itr, variant, dict, entry; + + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINA_FALSE); + + msg = dbus_message_new_method_call + (e_connman_system_bus_name_get(), service->path, service->interface, name); + + if (!msg) + return 0; + + dbus_message_iter_init_append(msg, &itr); + dbus_message_iter_append_basic + (&itr, DBUS_TYPE_STRING, &e_connman_prop_ipv4_configuration); + + if (dbus_message_iter_open_container + (&itr, DBUS_TYPE_VARIANT, + (DBUS_TYPE_ARRAY_AS_STRING + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING), + &variant)) + { + if (dbus_message_iter_open_container + (&variant, DBUS_TYPE_ARRAY, + (DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING), + &dict)) + { + if (dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry)) + { + dbus_message_iter_append_basic + (&entry, DBUS_TYPE_STRING, &e_connman_prop_method); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &method); + dbus_message_iter_close_container(&dict, &entry); + } + if (dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry)) + { + dbus_message_iter_append_basic + (&entry, DBUS_TYPE_STRING, &e_connman_prop_address); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &address); + dbus_message_iter_close_container(&dict, &entry); + } + if (netmask) + { + if (dbus_message_iter_open_container + (&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry)) + { + dbus_message_iter_append_basic + (&entry, DBUS_TYPE_STRING, &e_connman_prop_netmask); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &netmask); + dbus_message_iter_close_container(&dict, &entry); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + } + + if (gateway) + { + if (dbus_message_iter_open_container + (&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry)) + { + dbus_message_iter_append_basic + (&entry, DBUS_TYPE_STRING, &e_connman_prop_gateway); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &gateway); + dbus_message_iter_close_container(&dict, &entry); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + } + dbus_message_iter_close_container(&variant, &dict); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + dbus_message_iter_close_container(&itr, &variant); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + + return e_connman_element_message_send + (service, name, NULL, msg, &service->_pending.property_set, cb, data); +} + +/** + * Get property "Proxy.Method" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * Possible values are "direct", "auto" and "manual". + * + * @param service path to get property. + * @param method where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_proxy_method_get(const E_Connman_Element *service, const char **method) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(method, EINA_FALSE); + return e_connman_element_property_dict_get_stringshared + (service, e_connman_prop_proxy, e_connman_prop_method, NULL, method); +} + +/** + * Get property "Proxy.URL" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * Automatic proxy configuration URL. Used by "auto" method. + * + * @param service path to get property. + * @param url where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_proxy_url_get(const E_Connman_Element *service, const char **url) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(url, EINA_FALSE); + return e_connman_element_property_dict_get_stringshared + (service, e_connman_prop_proxy, e_connman_prop_url, NULL, url); +} + +/** + * Get property "Proxy.Servers" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * List of proxy URIs. The URI without a protocol will be interpreted as the + * generic proxy URI. All others will target a specific protocol and only once. + * Example of generic proxy server entry would be like this: + * "server.example.com:911". + * + * Used when "manual" method is set. + * + * @param service path to get property. + * @param count return the number of elements in array. + * @param servers array with pointers to internal strings. These + * strings are not copied in any way, and they are granted to + * be eina_stringshare instances, so one can use + * eina_stringshare_ref() if he wants to save memory and cpu to + * get an extra reference. The array itself is also NOT + * allocated or copied, do not modify it. This pointer is just + * set if return is @c EINA_TRUE. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_proxy_servers_get(const E_Connman_Element *service, unsigned int *count, const char ***servers) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(servers, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + return e_connman_element_property_dict_strings_array_get_stringshared + (service, e_connman_prop_proxy, e_connman_prop_servers, count, servers); +} + +/** + * Get property "Proxy.Excludes" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * List of hosts which can be accessed directly. + * + * Used when "manual" method is set. + * + * @param service path to get property. + * @param count return the number of elements in array. + * @param excludes array with pointers to internal strings. These + * strings are not copied in any way, and they are granted to + * be eina_stringshare instances, so one can use + * eina_stringshare_ref() if he wants to save memory and cpu to + * get an extra reference. The array itself is also NOT + * allocated or copied, do not modify it. This pointer is just + * set if return is @c EINA_TRUE. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_proxy_excludes_get(const E_Connman_Element *service, unsigned int *count, const char ***excludes) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(excludes, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + return e_connman_element_property_dict_strings_array_get_stringshared + (service, e_connman_prop_proxy, e_connman_prop_excludes, count, excludes); +} + +/** + * Get property "Proxy.Configuration.Method" value. + * + * Unlike Proxy.Configuration.Method, this is the user-set value, rather than + * the actual value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * Possible values are "direct", "auto" and "manual". + * + * @param service path to get property. + * @param method where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_proxy_configuration_method_get(const E_Connman_Element *service, const char **method) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(method, EINA_FALSE); + return e_connman_element_property_dict_get_stringshared + (service, e_connman_prop_proxy_configuration, e_connman_prop_method, NULL, method); +} + +/** + * Get property "Proxy.Configuration.URL" value. + * + * Unlike Proxy.URL, this is the user-set value, rather than the + * actual value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * Automatic proxy configuration URL. Used by "auto" method. + * + * @param service path to get property. + * @param url where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_proxy_configuration_url_get(const E_Connman_Element *service, const char **url) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(url, EINA_FALSE); + return e_connman_element_property_dict_get_stringshared + (service, e_connman_prop_proxy_configuration, e_connman_prop_url, NULL, url); +} + +/** + * Get property "Proxy.Configuration.Servers" value. + * + * Unlike Proxy.Servers, this is the user-set value, rather than the + * actual value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * List of proxy URIs. The URI without a protocol will be interpreted as the + * generic proxy URI. All others will target a specific protocol and only once. + * Example of generic proxy server entry would be like this: + * "server.example.com:911". + * + * Used when "manual" method is set. + * + * @param service path to get property. + * @param count return the number of elements in array. + * @param servers array with pointers to internal strings. These + * strings are not copied in any way, and they are granted to + * be eina_stringshare instances, so one can use + * eina_stringshare_ref() if he wants to save memory and cpu to + * get an extra reference. The array itself is also NOT + * allocated or copied, do not modify it. This pointer is just + * set if return is @c EINA_TRUE. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_proxy_configuration_servers_get(const E_Connman_Element *service, unsigned int *count, const char ***servers) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(servers, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + return e_connman_element_property_dict_strings_array_get_stringshared + (service, e_connman_prop_proxy_configuration, e_connman_prop_servers, count, servers); +} + +/** + * Get property "Proxy.Configuration.Excludes" value. + * + * Unlike Proxy.Excludes, this is the user-set value, rather than the + * actual value. + + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * List of hosts which can be accessed directly. + * + * Used when "manual" method is set. + * + * @param service path to get property. + * @param count return the number of elements in array. + * @param excludes array with pointers to internal strings. These + * strings are not copied in any way, and they are granted to + * be eina_stringshare instances, so one can use + * eina_stringshare_ref() if he wants to save memory and cpu to + * get an extra reference. The array itself is also NOT + * allocated or copied, do not modify it. This pointer is just + * set if return is @c EINA_TRUE. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_proxy_configuration_excludes_get(const E_Connman_Element *service, unsigned int *count, const char ***excludes) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(excludes, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + return e_connman_element_property_dict_strings_array_get_stringshared + (service, e_connman_prop_proxy_configuration, e_connman_prop_excludes, count, excludes); +} + +/** + * Get property "Ethernet.Interface" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * Interface name (for example eth0). + * + * @param service path to get property. + * @param iface where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_ethernet_interface_get(const E_Connman_Element *service, const char **iface) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(iface, EINA_FALSE); + return e_connman_element_property_dict_get_stringshared + (service, e_connman_prop_ethernet, e_connman_prop_interface, NULL, iface); +} + +/** + * Get property "Ethernet.Method" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * The Ethernet configuration method. Possible values here "auto" and "manual". + * + * @param service path to get property. + * @param method where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_ethernet_method_get(const E_Connman_Element *service, const char **method) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(method, EINA_FALSE); + return e_connman_element_property_dict_get_stringshared + (service, e_connman_prop_ethernet, e_connman_prop_method, NULL, method); +} + +/** + * Get property "Ethernet.Speed" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * Selected speed of the line. This information might not always be available. + * + * @param service path to get property. + * @param speed where to store the property value. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_ethernet_speed_get(const E_Connman_Element *service, unsigned short *speed) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(speed, EINA_FALSE); + return e_connman_element_property_dict_get_stringshared + (service, e_connman_prop_ethernet, e_connman_prop_speed, NULL, speed); +} + +/** + * Get property "Ethernet.Address" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * The current configured Ethernet address (mac-address). + * + * @param service path to get property. + * @param address where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_ethernet_address_get(const E_Connman_Element *service, const char **address) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINA_FALSE); + return e_connman_element_property_dict_get_stringshared + (service, e_connman_prop_ethernet, e_connman_prop_address, NULL, address); +} + +/** + * Get property "Ethernet.Duplex" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * Selected duplex settings of the line. Possible values are "half" and "full". + * This information might not always be available. + * + * @param service path to get property. + * @param duplex where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_ethernet_duplex_get(const E_Connman_Element *service, const char **duplex) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(duplex, EINA_FALSE); + return e_connman_element_property_dict_get_stringshared + (service, e_connman_prop_ethernet, e_connman_prop_duplex, NULL, duplex); +} + +/** + * Get property "Ethernet.MTU" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * The current configured Ethernet MTU. + * + * @param service path to get property. + * @param gateway where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_service_ethernet_mtu_get(const E_Connman_Element *service, unsigned short *mtu) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(service, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(mtu, EINA_FALSE); + return e_connman_element_property_dict_get_stringshared + (service, e_connman_prop_ethernet, e_connman_prop_mtu, NULL, mtu); +} diff --git a/src/lib/connman0_7x/e_connman_technology.c b/src/lib/connman0_7x/e_connman_technology.c new file mode 100644 index 0000000..62d4e78 --- /dev/null +++ b/src/lib/connman0_7x/e_connman_technology.c @@ -0,0 +1,106 @@ +#include "e_connman_private.h" + +E_Connman_Element * +e_connman_technology_get(const char *path) +{ + E_Connman_Element *technology; + + EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL); + + technology = e_connman_element_get(path); + if (!technology) + return NULL; + + if (!e_connman_element_is_technology(technology)) + { + WRN("path '%s' is not a technology!", path); + return NULL; + } + + return technology; +} + +/** + * Get property "Name" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * Name of this technology. + * + * @param technology path to get property + * @param name where to store the property value, must be a pointer + * to string (const char *), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_technology_name_get(const E_Connman_Element *technology, const char **name) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(technology, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); + return e_connman_element_property_get_stringshared + (technology, e_connman_prop_name, NULL, name); +} + +/** + * Get property "Type" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * The technology type (for example "ethernet" etc.) + * + * This information should only be used to determine + * advanced properties or showing the correct icon + * to the user. + * + * @param technology path to get property. + * @param type where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_technology_type_get(const E_Connman_Element *technology, const char **type) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(technology, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(type, EINA_FALSE); + return e_connman_element_property_get_stringshared + (technology, e_connman_prop_type, NULL, type); +} + +/** + * Get property "State" value. + * + * If this property isn't found then @c EINA_FALSE is returned. + * If @c EINA_FALSE is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * The technology state information. + * + * Valid states are "offline", "available", "enabled", + * and "connected". + * + * @param technology path to get property. + * @param state where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_connman_technology_state_get(const E_Connman_Element *technology, const char **state) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(technology, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(state, EINA_FALSE); + return e_connman_element_property_get_stringshared + (technology, e_connman_prop_state, NULL, state); +} diff --git a/src/lib/dbus/E_DBus.h b/src/lib/dbus/E_DBus.h new file mode 100644 index 0000000..bd00f44 --- /dev/null +++ b/src/lib/dbus/E_DBus.h @@ -0,0 +1,434 @@ +#ifndef E_DBUS_H +#define E_DBUS_H + +#define DBUS_API_SUBJECT_TO_CHANGE + +#ifdef _WIN32 +# ifdef interface +# undef interface +# endif +#endif + +#ifdef _WIN32 +# ifdef interface +# undef interface +# endif +# define DBUS_API_SUBJECT_TO_CHANGE +#endif + +#include +#include + +#ifdef EAPI +# undef EAPI +#endif + +#ifdef _WIN32 +# ifdef EFL_EDBUS_BUILD +# ifdef DLL_EXPORT +# define EAPI __declspec(dllexport) +# else +# define EAPI +# endif /* ! DLL_EXPORT */ +# else +# define EAPI __declspec(dllimport) +# endif /* ! EFL_EDBUS_BUILD */ +#else +# ifdef __GNUC__ +# if __GNUC__ >= 4 +# define EAPI __attribute__ ((visibility("default"))) +# else +# define EAPI +# endif +# else +# define EAPI +# endif +#endif + +/** + * @mainpage EDbus + * + * @section edbus_intro_sec Introduction + * + * EDbus is a wrapper around the + * dbus + * library, which is a message bus system. It also implements a set of + * specifications using dbus as interprocess communication. + * + * @section edbus_modules_sec Modules + * + * @li @ref EDbus_Group Wrapper around the dbus library, which + * implementent an inter-process communication (IPC) system for + * software applications to communicate with one another. + * @li @ref EBluez_Group Implementation of the BlueZ specifications, for wireless + * communications with Bleutooth devices. + * @li @ref EConnman_Group Implementation of the connman specifications, which + * manages internet connections running the Linux operating system. + * @li @ref EHal_Group Implementation of the HAL + * specifications, which is a (software) layer between the hardware + * devices of a computer and the softwares that run on that + * computer (Hardware Abstraction Layer). HAL is deprecated, in favor + * of DeviceKit. + * @li @ref ENotify_Group To de described. + * @li @ref EOfono_Group Implementation of the ofono specifications, which is an + * interface for mobile telephony applications. + * @li @ref EUkit_Group Implementation of the DeviceKit + * specifications, which is, like HAL, an Hardware Abstraction + * Layer. DeviceKit is a replacement of the deprecated HAL system. It + * has two submodules: UDisks, which manipulate storage devices, and + * UPower, which manage power devices. + */ + +/** + * @defgroup EDbus_Group EDbus + * + * @{ + */ + +#define E_DBUS_FDO_BUS "org.freedesktop.DBus" +#define E_DBUS_FDO_PATH "/org/freedesktop/DBus" +#define E_DBUS_FDO_INTERFACE E_DBUS_FDO_BUS +#define E_DBUS_FDO_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties" + +#ifdef __cplusplus +extern "C" { +#endif + +#define E_DBUS_VERSION_MAJOR 1 +#define E_DBUS_VERSION_MINOR 7 + + typedef struct _E_DBus_Version + { + int major; + int minor; + int micro; + int revision; + } E_DBus_Version; + + EAPI extern E_DBus_Version *e_dbus_version; + + EAPI extern int E_DBUS_DOMAIN_GLOBAL; + EAPI extern int E_DBUS_EVENT_SIGNAL; + + typedef struct E_DBus_Connection E_DBus_Connection; + typedef struct E_DBus_Object E_DBus_Object; + typedef struct E_DBus_Interface E_DBus_Interface; + typedef struct E_DBus_Signal_Handler E_DBus_Signal_Handler; + + typedef DBusMessage *(* E_DBus_Method_Cb)(E_DBus_Object *obj, DBusMessage *message); + typedef void (*E_DBus_Method_Return_Cb) (void *data, DBusMessage *msg, DBusError *error); + typedef void (*E_DBus_Signal_Cb) (void *data, DBusMessage *msg); + + typedef void (*E_DBus_Object_Property_Get_Cb) (E_DBus_Object *obj, const char *property, int *type, void **value); + typedef int (*E_DBus_Object_Property_Set_Cb) (E_DBus_Object *obj, const char *property, int type, void *value); + +/** + * A callback function for a DBus call + * @param user_data the data passed in to the method call + * @param event_data a struct containing the return data. + */ + typedef void (*E_DBus_Callback_Func) (void *user_data, void *method_return, DBusError *error); + typedef void *(*E_DBus_Unmarshal_Func) (DBusMessage *msg, DBusError *err); + typedef void (*E_DBus_Free_Func) (void *data); + + typedef struct E_DBus_Callback E_DBus_Callback; + + +/** + * @brief Initialize e_dbus + */ +EAPI int e_dbus_init(void); + +/** + * Shutdown e_dbus. + */ +EAPI int e_dbus_shutdown(void); + +/* setting up the connection */ + +/** + * Retrieve a connection to the bus and integrate it with the ecore main loop. + * @param type the type of bus to connect to, e.g. DBUS_BUS_SYSTEM or DBUS_BUS_SESSION + */ +EAPI E_DBus_Connection *e_dbus_bus_get(DBusBusType type); + + EAPI void e_dbus_connection_ref(E_DBus_Connection *conn); + +/** + * Integrate a DBus connection with the ecore main loop + * + * @param conn - a dbus connection + */ +EAPI E_DBus_Connection *e_dbus_connection_setup(DBusConnection *conn); + +/** + * Close out a connection retrieved with e_dbus_bus_get() + * @param conn the connection to close + */ +EAPI void e_dbus_connection_close(E_DBus_Connection *conn); + +/* receiving method calls */ + EAPI E_DBus_Interface *e_dbus_interface_new(const char *interface); + EAPI void e_dbus_interface_ref(E_DBus_Interface *iface); + EAPI void e_dbus_interface_unref(E_DBus_Interface *iface); + EAPI void e_dbus_object_interface_attach(E_DBus_Object *obj, E_DBus_Interface *iface); + EAPI void e_dbus_object_interface_detach(E_DBus_Object *obj, E_DBus_Interface *iface); + +/** + * Add a method to an object + * + * @param iface the E_DBus_Interface to which this method belongs + * @param member the name of the method + * @param signature an optional message signature. if provided, then messages + * with invalid signatures will be automatically rejected + * (an Error response will be sent) and introspection data + * will be available. + * + * @return 1 if successful, 0 if failed (e.g. no memory) + */ +EAPI int e_dbus_interface_method_add(E_DBus_Interface *iface, const char *member, const char *signature, const char *reply_signature, E_DBus_Method_Cb func); + +/** + * Add a signal to an object + * + * @param iface the E_DBus_Interface to which this signal belongs + * @param name the name of the signal + * @param signature an optional message signature. + * + * @return 1 if successful, 0 if failed (e.g. no memory) + */ +EAPI int e_dbus_interface_signal_add(E_DBus_Interface *iface, const char *name, const char *signature); + +/** + * Add a dbus object. + * + * @param conn the connection on with the object should listen + * @param object_path a unique string identifying an object (e.g. org/enlightenment/WindowManager + * @param data custom data to set on the object (retrievable via + * e_dbus_object_data_get()) + */ +EAPI E_DBus_Object *e_dbus_object_add(E_DBus_Connection *conn, const char *object_path, void *data); + +/** + * Free a dbus object + * + * @param obj the object to free + */ +EAPI void e_dbus_object_free(E_DBus_Object *obj); + +/** + * @brief Fetch the data pointer for a dbus object + * @param obj the dbus object + */ +EAPI void *e_dbus_object_data_get(E_DBus_Object *obj); + +/** + * @brief Get the dbus connection of a dbus object + * @param obj the dbus object + */ +EAPI E_DBus_Connection *e_dbus_object_conn_get(E_DBus_Object *obj); + +/** + * @brief Get the path of a dbus object + * @param obj the dbus object + */ +EAPI const char *e_dbus_object_path_get(E_DBus_Object *obj); + +/** + * @brief Get the interfaces of a dbus object + * @param obj the dbus object + */ +EAPI const Eina_List *e_dbus_object_interfaces_get(E_DBus_Object *obj); + +/** + * @brief Sets the callback to fetch properties from an object + * @param obj the object + * @param func the callback + */ +EAPI void e_dbus_object_property_get_cb_set(E_DBus_Object *obj, E_DBus_Object_Property_Get_Cb func); + +/** + * @brief Sets the callback to set properties on an object + * @param obj the object + * @param func the callback + */ +EAPI void e_dbus_object_property_set_cb_set(E_DBus_Object *obj, E_DBus_Object_Property_Set_Cb func); + + +/* sending method calls */ + +/** + * @brief Send a DBus message with callbacks + * @param conn The DBus connection + * @param msg The message to send + * @param cb_return A callback function for returns (only used if @a msg is a method-call) + * @param timeout A timeout in milliseconds, after which a synthetic error will be generated + * @param data custom data to pass in to the callback + * @return a DBusPendingCall that can be used to cancel the current call + */ +EAPI DBusPendingCall *e_dbus_message_send(E_DBus_Connection *conn, DBusMessage *msg, E_DBus_Method_Return_Cb cb_return, int timeout, void *data); + + EAPI DBusPendingCall *e_dbus_method_call_send(E_DBus_Connection *conn, DBusMessage *msg, E_DBus_Unmarshal_Func unmarshal_func, E_DBus_Callback_Func cb_func, E_DBus_Free_Func free_func, int timeout, void *data); + + +/* signal receiving */ + +/** + * Add a signal handler + * + * @param conn the dbus connection + * @param sender name of the signal's sender + * @param path the object path of the signal's sender + * @param interface the signal's interface + * @param member the signal's name + * @param cb_signal a callback to call when the signal is received + * @param data custom data to pass in to the callback + */ +EAPI E_DBus_Signal_Handler *e_dbus_signal_handler_add(E_DBus_Connection *conn, const char *sender, const char *path, const char *interface, const char *member, E_DBus_Signal_Cb cb_signal, void *data); + +/** + * Delete a signal handler + * + * @param conn the dbus connection + * @param sh the handler to delete + */ +EAPI void e_dbus_signal_handler_del(E_DBus_Connection *conn, E_DBus_Signal_Handler *sh); + +/* standard dbus method calls */ + + EAPI DBusPendingCall *e_dbus_request_name(E_DBus_Connection *conn, const char *name, + unsigned int flags, + E_DBus_Method_Return_Cb cb_return, + const void *data); + EAPI DBusPendingCall *e_dbus_release_name(E_DBus_Connection *conn, const char *name, + E_DBus_Method_Return_Cb cb_return, + const void *data); + + EAPI DBusPendingCall *e_dbus_get_name_owner(E_DBus_Connection *conn, const char *name, + E_DBus_Method_Return_Cb cb_return, + const void *data); + EAPI DBusPendingCall *e_dbus_list_names(E_DBus_Connection *conn, + E_DBus_Method_Return_Cb cb_return, + const void *data); + EAPI DBusPendingCall *e_dbus_list_activatable_names(E_DBus_Connection *conn, + E_DBus_Method_Return_Cb cb_return, + const void *data); + EAPI DBusPendingCall *e_dbus_name_has_owner(E_DBus_Connection *conn, const char *name, + E_DBus_Method_Return_Cb cb_return, + const void *data); + EAPI DBusPendingCall *e_dbus_start_service_by_name(E_DBus_Connection *conn, const char *name, unsigned int flags, + E_DBus_Method_Return_Cb cb_return, + const void *data); + +/* standard methods calls on objects */ + +/** + * Calls the Introspect method on a given bus and object path. + * @param conn The dbus connection to use + * @param bus The bus to call the method on + * @param object_path The path of the bus to call on + * @param cb_return The callback to call on reply from dbus + * @param data The data to associate with the callback + * @return A pending dbus call + */ +EAPI DBusPendingCall *e_dbus_introspect(E_DBus_Connection *conn, const char *bus, + const char *object_path, E_DBus_Method_Return_Cb cb_return, const void *data); + +/** + * Ping the dbus peer + * + * @param conn the dbus connection + * @param destination the bus name that the object is on + * @param path the object path + * @param cb_return a callback for a successful return + * @param data data to pass to the callbacks + */ +EAPI DBusPendingCall *e_dbus_peer_ping(E_DBus_Connection *conn, const char *destination, + const char *path, E_DBus_Method_Return_Cb cb_return, + const void *data); + +/** + * Get the UUID of the peer + * + * @param conn the dbus connection + * @param destination the bus name that the object is on + * @param path the object path + * @param cb_return a callback for a successful return + * @param data data to pass to the callbacks + */ +EAPI DBusPendingCall *e_dbus_peer_get_machine_id(E_DBus_Connection *conn, + const char *destination, const char *path, + E_DBus_Method_Return_Cb cb_return, + const void *data); + EAPI DBusPendingCall *e_dbus_properties_get_all(E_DBus_Connection *conn, const char *destination, + const char *path, const char *interface, + E_DBus_Method_Return_Cb cb_return, + const void *data); + +/** + * Get the value of a property on an object + * + * @param conn the dbus connection + * @param destination the bus name that the object is on + * @param path the object path + * @param interface the interface name of the property + * @param property the name of the property + * @param cb_return a callback for a successful return + * @param data data to pass to the callbacks + */ +EAPI DBusPendingCall *e_dbus_properties_get(E_DBus_Connection *conn, const char *destination, + const char *path, const char *interface, + const char *property, + E_DBus_Method_Return_Cb cb_return, + const void *data); + +/** + * Set the value of a property on an object + * + * @param conn the dbus connection + * @param destination the bus name that the object is on + * @param path the object path + * @param interface the interface name of the property + * @param property the name of the property + * @param value_type the type of the property's value + * @param value a pointer to the value + * @param cb_return a callback for a successful return + * @param data data to pass to the callbacks + */ +EAPI DBusPendingCall *e_dbus_properties_set(E_DBus_Connection *conn, const char *destination, + const char *path, const char *interface, + const char *property, int value_type, + const void *value, E_DBus_Method_Return_Cb cb_return, + const void *data); + + +/** + * @brief Create a callback structure + * @param cb_func the callback function + * @param user_data data to pass to the callback + */ +EAPI E_DBus_Callback *e_dbus_callback_new(E_DBus_Callback_Func cb_func, E_DBus_Unmarshal_Func unmarshal_func, E_DBus_Free_Func free_func, void *user_data); + +/** + * @brief Free a callback structure + * @param callback the callback to free + */ +EAPI void e_dbus_callback_free(E_DBus_Callback *callback); + EAPI void e_dbus_callback_call(E_DBus_Callback *cb, void *data, DBusError *error); + EAPI void *e_dbus_callback_unmarshal(E_DBus_Callback *cb, DBusMessage *msg, DBusError *err); + EAPI void e_dbus_callback_return_free(E_DBus_Callback *callback, void *data); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif diff --git a/src/lib/dbus/Makefile.am b/src/lib/dbus/Makefile.am new file mode 100644 index 0000000..e838d03 --- /dev/null +++ b/src/lib/dbus/Makefile.am @@ -0,0 +1,26 @@ +MAINTAINERCLEANFILES = Makefile.in + +AM_CPPFLAGS = \ +@EFL_EDBUS_BUILD@ \ +@DBUS_VERSION_CFLAGS@ \ +@EDBUS_CFLAGS@ + + +lib_LTLIBRARIES = libedbus.la +includes_HEADERS = E_DBus.h +includesdir = $(includedir)/e_dbus-@VMAJ@ + +libedbus_la_SOURCES = \ +e_dbus.c \ +e_dbus_message.c \ +e_dbus_methods.c \ +e_dbus_interfaces.c \ +e_dbus_object.c \ +e_dbus_util.c \ +e_dbus_signal.c + + +libedbus_la_LIBADD = @EDBUS_LIBS@ +libedbus_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -version-info @version_info@ @release_info@ + +EXTRA_DIST = e_dbus_private.h diff --git a/src/lib/dbus/e_dbus.c b/src/lib/dbus/e_dbus.c new file mode 100644 index 0000000..5f0ade6 --- /dev/null +++ b/src/lib/dbus/e_dbus.c @@ -0,0 +1,620 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include "e_dbus_private.h" + +static E_DBus_Version _version = { VMAJ, VMIN, VMIC, VREV }; +EAPI E_DBus_Version *e_dbus_version = &_version; + +#define NUM_BUS_TYPES 3 + +/* + * TODO: + * listen for disconnected signal and clean up? + */ +int _e_dbus_log_dom = -1; +static int connection_slot = -1; + +static int _edbus_init_count = 0; +static int close_connection = 0; +EAPI int E_DBUS_EVENT_SIGNAL = 0; + +static E_DBus_Connection *shared_connections[2] = {NULL, NULL}; + +typedef struct E_DBus_Handler_Data E_DBus_Handler_Data; +typedef struct E_DBus_Timeout_Data E_DBus_Timeout_Data; + + +struct E_DBus_Handler_Data +{ + int fd; + Ecore_Fd_Handler *fd_handler; + E_DBus_Connection *cd; + DBusWatch *watch; + int enabled; +}; + +struct E_DBus_Timeout_Data +{ + Ecore_Timer *handler; + DBusTimeout *timeout; + E_DBus_Connection *cd; + int interval; +}; + +static Eina_Bool e_dbus_idler(void *data); + +static void +e_dbus_fd_handler_del(E_DBus_Handler_Data *hd) +{ + if (!hd->fd_handler) return; + + DBG("handler disabled"); + hd->cd->fd_handlers = eina_list_remove(hd->cd->fd_handlers, hd->fd_handler); + ecore_main_fd_handler_del(hd->fd_handler); + hd->fd_handler = NULL; +} + +static Eina_Bool +e_dbus_fd_handler(void *data, Ecore_Fd_Handler *fd_handler) +{ + E_DBus_Handler_Data *hd; + unsigned int condition = 0; + + DBG("fd handler (%p)!", fd_handler); + + hd = data; + + if (!hd->enabled) + { + e_dbus_fd_handler_del(hd); + return ECORE_CALLBACK_CANCEL; + } + if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ)) condition |= DBUS_WATCH_READABLE; + if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_WRITE)) condition |= DBUS_WATCH_WRITABLE; + if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_ERROR)) condition |= DBUS_WATCH_ERROR; + DBG("fdh || READ: %d, WRITE: %d", + (condition & DBUS_WATCH_READABLE) == DBUS_WATCH_READABLE, + (condition & DBUS_WATCH_WRITABLE) == DBUS_WATCH_WRITABLE); + + if (condition & DBUS_WATCH_ERROR) DBG("DBUS watch error"); + dbus_watch_handle(hd->watch, condition); + hd = NULL; + + return ECORE_CALLBACK_RENEW; +} + +static void +e_dbus_fd_handler_add(E_DBus_Handler_Data *hd) +{ + unsigned int dflags; + Ecore_Fd_Handler_Flags eflags; + Eina_List *l; + Ecore_Fd_Handler *fdh; + + if (hd->fd_handler) return; + dflags = dbus_watch_get_flags(hd->watch); + eflags = ECORE_FD_ERROR; + if (dflags & DBUS_WATCH_READABLE) eflags |= ECORE_FD_READ; + if (dflags & DBUS_WATCH_WRITABLE) eflags |= ECORE_FD_WRITE; + + EINA_LIST_FOREACH(hd->cd->fd_handlers, l, fdh) + { + if (ecore_main_fd_handler_fd_get(fdh) == hd->fd) return; + } + + DBG("fd handler add (%d)", hd->fd); + hd->fd_handler = ecore_main_fd_handler_add(hd->fd, + eflags, + e_dbus_fd_handler, + hd, + NULL, + NULL); + + hd->cd->fd_handlers = eina_list_append(hd->cd->fd_handlers, hd->fd_handler); +} + + +static void +e_dbus_handler_data_free(void *data) +{ + E_DBus_Handler_Data *hd = data; + + DBG("e_dbus_handler_data_free"); + if (hd->fd_handler) + { + hd->cd->fd_handlers = eina_list_remove(hd->cd->fd_handlers, hd->fd_handler); + ecore_main_fd_handler_del(hd->fd_handler); + } + free(hd); +} + +static void +e_dbus_connection_data_watch_add(E_DBus_Connection *cd, DBusWatch *watch) +{ + E_DBus_Handler_Data *hd; + + hd = calloc(1, sizeof(E_DBus_Handler_Data)); + dbus_watch_set_data(watch, hd, e_dbus_handler_data_free); + hd->cd = cd; + hd->watch = watch; + + hd->enabled = dbus_watch_get_enabled(watch); +#if (DBUS_VERSION_MAJOR == 1 && DBUS_VERSION_MINOR == 1 && DBUS_VERSION_MICRO>= 1) || (DBUS_VERSION_MAJOR == 1 && DBUS_VERSION_MINOR > 1) || (DBUS_VERSION_MAJOR > 1) + hd->fd = dbus_watch_get_unix_fd(hd->watch); +#else + hd->fd = dbus_watch_get_fd(hd->watch); +#endif + DBG("watch add (enabled: %d)", hd->enabled); + if (hd->enabled) e_dbus_fd_handler_add(hd); +} + +static E_DBus_Connection * +e_dbus_connection_new(DBusConnection *conn) +{ + E_DBus_Connection *cd; + const char *conn_name; + + cd = calloc(1, sizeof(E_DBus_Connection)); + if (!cd) return NULL; + + cd->conn = conn; + conn_name = dbus_bus_get_unique_name(conn); + if (conn_name) + { + DBG("Connected! Name: %s", conn_name); + cd->conn_name = strdup(conn_name); + } + else + DBG("Not connected"); + + cd->shared_type = (unsigned int)-1; + cd->fd_handlers = NULL; + cd->timeouts = NULL; + + return cd; +} + +static void +e_dbus_connection_free(void *data) +{ + E_DBus_Connection *cd = data; + Ecore_Fd_Handler *fd_handler; + Ecore_Timer *timer; + DBG("e_dbus_connection free!"); + + EINA_LIST_FREE(cd->fd_handlers, fd_handler) + ecore_main_fd_handler_del(fd_handler); + + EINA_LIST_FREE(cd->timeouts, timer) + ecore_timer_del(timer); + + if (cd->shared_type != (unsigned int)-1) + shared_connections[cd->shared_type] = NULL; + + e_dbus_signal_handlers_free_all(cd); + + if (cd->conn_name) free(cd->conn_name); + + if (cd->idler) ecore_idler_del(cd->idler); + + free(cd); +} + +static void +cb_dispatch_status(DBusConnection *conn __UNUSED__, DBusDispatchStatus new_status, void *data) +{ + E_DBus_Connection *cd; + + DBG("dispatch status: %d!", new_status); + cd = data; + + if (new_status == DBUS_DISPATCH_DATA_REMAINS && !cd->idler) + cd->idler = ecore_idler_add(e_dbus_idler, cd); + else if (new_status != DBUS_DISPATCH_DATA_REMAINS && cd->idler) + { + ecore_idler_del(cd->idler); + cd->idler = NULL; + } +} + +static Eina_Bool +e_dbus_timeout_handler(void *data) +{ + E_DBus_Timeout_Data *td; + + td = data; + + if (!dbus_timeout_get_enabled(td->timeout)) + { + DBG("timeout_handler (not enabled, ending)"); + td->handler = NULL; + return ECORE_CALLBACK_CANCEL; + } + + DBG("timeout handler!"); + dbus_timeout_handle(td->timeout); + return ECORE_CALLBACK_CANCEL; +} + +static void +e_dbus_timeout_data_free(void *timeout_data) +{ + E_DBus_Timeout_Data *td = timeout_data; + DBG("e_dbus_timeout_data_free"); + if (td->handler) ecore_timer_del(td->handler); + free(td); +} + +static dbus_bool_t +cb_timeout_add(DBusTimeout *timeout, void *data) +{ + E_DBus_Connection *cd; + E_DBus_Timeout_Data *td; + + cd = data; + DBG("timeout add!"); + td = calloc(1, sizeof(E_DBus_Timeout_Data)); + td->cd = cd; + dbus_timeout_set_data(timeout, (void *)td, e_dbus_timeout_data_free); + + td->interval = dbus_timeout_get_interval(timeout); + td->timeout = timeout; + + if (dbus_timeout_get_enabled(timeout)) td->handler = ecore_timer_add(td->interval, e_dbus_timeout_handler, td); + td->cd->timeouts = eina_list_append(td->cd->timeouts, td->handler); + + return true; +} + +static void +cb_timeout_del(DBusTimeout *timeout, void *data __UNUSED__) +{ + E_DBus_Timeout_Data *td; + DBG("timeout del!"); + + td = (E_DBus_Timeout_Data *)dbus_timeout_get_data(timeout); + + if (td->handler) + { + td->cd->timeouts = eina_list_remove(td->cd->timeouts, td->handler); + ecore_timer_del(td->handler); + td->handler = NULL; + } + + /* Note: timeout data gets freed when the timeout itself is freed by dbus */ +} + +static void +cb_timeout_toggle(DBusTimeout *timeout, void *data __UNUSED__) +{ + E_DBus_Timeout_Data *td; + DBG("timeout toggle!"); + + td = (E_DBus_Timeout_Data *)dbus_timeout_get_data(timeout); + + if (dbus_timeout_get_enabled(td->timeout)) + { + td->interval = dbus_timeout_get_interval(timeout); + td->handler = ecore_timer_add(td->interval, e_dbus_timeout_handler, td); + } + else + { + ecore_timer_del(td->handler); + td->handler = NULL; + } + + +} + +static dbus_bool_t +cb_watch_add(DBusWatch *watch, void *data) +{ + E_DBus_Connection *cd; + cd = data; + + DBG("cb_watch_add"); + e_dbus_connection_data_watch_add(cd, watch); + + return true; +} + +static void +cb_watch_del(DBusWatch *watch, void *data __UNUSED__) +{ + E_DBus_Handler_Data *hd; + + DBG("cb_watch_del"); + hd = (E_DBus_Handler_Data *)dbus_watch_get_data(watch); + e_dbus_fd_handler_del(hd); +} + +static void +cb_watch_toggle(DBusWatch *watch, void *data __UNUSED__) +{ + E_DBus_Handler_Data *hd; + + DBG("cb_watch_toggle"); + hd = dbus_watch_get_data(watch); + + if (!hd) return; + + hd->enabled = dbus_watch_get_enabled(watch); + + INFO("watch %p is %sabled", hd, hd->enabled ? "en" : "dis"); + if (hd->enabled) e_dbus_fd_handler_add(hd); + else e_dbus_fd_handler_del(hd); +} + +static void +e_dbus_message_free(void *data __UNUSED__, void *message) +{ + dbus_message_unref(message); +} + +static DBusHandlerResult +e_dbus_filter(DBusConnection *conn __UNUSED__, DBusMessage *message, void *user_data) +{ + E_DBus_Connection *cd = user_data; + DBG("-----------------"); + DBG("Message!"); + + DBG("type: %s", dbus_message_type_to_string(dbus_message_get_type(message))); + DBG("path: %s", dbus_message_get_path(message)); + DBG("interface: %s", dbus_message_get_interface(message)); + DBG("member: %s", dbus_message_get_member(message)); + DBG("sender: %s", dbus_message_get_sender(message)); + + switch (dbus_message_get_type(message)) + { + case DBUS_MESSAGE_TYPE_METHOD_CALL: + DBG("signature: %s", dbus_message_get_signature(message)); + break; + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + DBG("reply serial %d", dbus_message_get_reply_serial(message)); + break; + case DBUS_MESSAGE_TYPE_ERROR: + DBG("error: %s", dbus_message_get_error_name(message)); + break; + case DBUS_MESSAGE_TYPE_SIGNAL: + dbus_message_ref(message); + if (cd->signal_dispatcher) cd->signal_dispatcher(cd, message); + ecore_event_add(E_DBUS_EVENT_SIGNAL, message, e_dbus_message_free, NULL); + break; + default: + break; + } + DBG("-----------------"); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +int e_dbus_idler_active = 0; + +static Eina_Bool +e_dbus_idler(void *data) +{ + E_DBus_Connection *cd; + cd = data; + + if (DBUS_DISPATCH_COMPLETE == dbus_connection_get_dispatch_status(cd->conn)) + { + DBG("done dispatching!"); + cd->idler = NULL; + return ECORE_CALLBACK_CANCEL; + } + e_dbus_idler_active++; + dbus_connection_ref(cd->conn); + DBG("dispatch()"); + dbus_connection_dispatch(cd->conn); + dbus_connection_unref(cd->conn); + e_dbus_idler_active--; + e_dbus_signal_handlers_clean(cd); + if (!e_dbus_idler_active && close_connection) + { + do + { + e_dbus_connection_close(cd); + } while (--close_connection); + } + return ECORE_CALLBACK_RENEW; +} + +EAPI E_DBus_Connection * +e_dbus_bus_get(DBusBusType type) +{ + DBusError err; + E_DBus_Connection *econn; + DBusConnection *conn; + + /* each app only needs a single connection to either bus */ + if (type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION) + { + if (shared_connections[type]) + { + e_dbus_connection_ref(shared_connections[type]); + return shared_connections[type]; + } + } + + dbus_error_init(&err); + + conn = dbus_bus_get_private(type, &err); + if (dbus_error_is_set(&err)) + { + ERR("Error connecting to bus: %s", err.message); + dbus_error_free(&err); + return NULL; + } + + econn = e_dbus_connection_setup(conn); + if (!econn) + { + ERR("Error setting up dbus connection."); + dbus_connection_close(conn); + dbus_connection_unref(conn); + return NULL; + } + + if (type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION) + { + econn->shared_type = type; + shared_connections[type] = econn; + } + dbus_error_free(&err); + e_dbus_connection_ref(econn); + return econn; +} + +EAPI E_DBus_Connection * +e_dbus_connection_setup(DBusConnection *conn) +{ + E_DBus_Connection *cd; + + cd = e_dbus_connection_new(conn); + if (!cd) return NULL; + + /* connection_setup */ + dbus_connection_set_exit_on_disconnect(cd->conn, EINA_FALSE); + dbus_connection_allocate_data_slot(&connection_slot); + + dbus_connection_set_data(cd->conn, connection_slot, (void *)cd, e_dbus_connection_free); + dbus_connection_set_watch_functions(cd->conn, + cb_watch_add, + cb_watch_del, + cb_watch_toggle, + cd, + NULL); + + dbus_connection_set_timeout_functions(cd->conn, + cb_timeout_add, + cb_timeout_del, + cb_timeout_toggle, + cd, + NULL); + + dbus_connection_set_dispatch_status_function(cd->conn, cb_dispatch_status, cd, NULL); + dbus_connection_add_filter(cd->conn, e_dbus_filter, cd, NULL); + + cb_dispatch_status(cd->conn, dbus_connection_get_dispatch_status(cd->conn), cd); + + return cd; +} + + +EAPI void +e_dbus_connection_close(E_DBus_Connection *conn) +{ + if (!conn) return; + DBG("e_dbus_connection_close"); + + if (e_dbus_idler_active) + { + close_connection++; + return; + } + if (--(conn->refcount) != 0) return; + + dbus_connection_free_data_slot(&connection_slot); + dbus_connection_remove_filter(conn->conn, e_dbus_filter, conn); + dbus_connection_set_watch_functions (conn->conn, + NULL, + NULL, + NULL, + NULL, NULL); + + dbus_connection_set_timeout_functions (conn->conn, + NULL, + NULL, + NULL, + NULL, NULL); + + dbus_connection_set_dispatch_status_function (conn->conn, NULL, NULL, NULL); + + /* Idler functin must be cancelled when dbus connection is unreferenced */ + if (conn->idler) + { + ecore_idler_del(conn->idler); + conn->idler = NULL; + } + + dbus_connection_close(conn->conn); + dbus_connection_unref(conn->conn); + + // Note: the E_DBus_Connection gets freed when the dbus_connection is cleaned up by the previous unref +} + +EAPI void +e_dbus_connection_ref(E_DBus_Connection *conn) +{ + EINA_SAFETY_ON_NULL_RETURN(conn); + conn->refcount++; +} + +DBusConnection * +e_dbus_connection_dbus_connection_get(E_DBus_Connection *conn) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + return conn->conn; +} + +EAPI int +e_dbus_init(void) +{ + if (++_edbus_init_count != 1) + return _edbus_init_count; + + if (!eina_init()) + { + fprintf(stderr,"E-dbus: Enable to initialize eina\n"); + return --_edbus_init_count; + } + + _e_dbus_log_dom = eina_log_domain_register("e_dbus", E_DBUS_COLOR_DEFAULT); + if (_e_dbus_log_dom < 0) + { + EINA_LOG_ERR("Unable to create an 'e_dbus' log domain"); + eina_shutdown(); + return --_edbus_init_count; + } + if (!ecore_init()) + { + ERR("E-dbus: Unable to initialize ecore"); + eina_log_domain_unregister(_e_dbus_log_dom); + _e_dbus_log_dom = -1; + eina_shutdown(); + return --_edbus_init_count; + } + + + E_DBUS_EVENT_SIGNAL = ecore_event_type_new(); + e_dbus_object_init(); + + return _edbus_init_count; +} + +EAPI int +e_dbus_shutdown(void) +{ + if (_edbus_init_count <= 0) + { + EINA_LOG_ERR("Init count not greater than 0 in shutdown."); + return 0; + } + if (--_edbus_init_count) + return _edbus_init_count; + + e_dbus_object_shutdown(); + ecore_shutdown(); + eina_log_domain_unregister(_e_dbus_log_dom); + _e_dbus_log_dom = -1; + eina_shutdown(); + + return _edbus_init_count; +} diff --git a/src/lib/dbus/e_dbus_interfaces.c b/src/lib/dbus/e_dbus_interfaces.c new file mode 100644 index 0000000..161a180 --- /dev/null +++ b/src/lib/dbus/e_dbus_interfaces.c @@ -0,0 +1,176 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "e_dbus_private.h" + +/* + * This file contains wrappers around the standard interfaces that + * objects on the bus should implement. + */ + +static inline DBusPendingCall * +_dbus_peer_call(E_DBus_Connection *conn, const char *method_name, const char *destination, const char *path, E_DBus_Method_Return_Cb cb_return, const void *data) +{ + DBusMessage *msg; + DBusPendingCall *ret; + + msg = dbus_message_new_method_call + (destination, path, "org.freedesktop.DBus.Peer", method_name); + if (!msg) + { + ERR("E-dbus Error: failed to create message for method call: %s() at " + "\"%s\" at \"%s\"", + method_name, destination, path); + return NULL; + } + + ret = e_dbus_message_send(conn, msg, cb_return, -1, (void *)data); + dbus_message_unref(msg); + + if (!ret) + ERR("E-dbus Error: could not %s() \"%s\" at \"%s\".", + method_name, destination, path); + + return ret; +} + +EAPI DBusPendingCall * +e_dbus_peer_ping(E_DBus_Connection *conn, const char *destination, const char *path, E_DBus_Method_Return_Cb cb_return, const void *data) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + return _dbus_peer_call(conn, "Ping", destination, path, cb_return, data); +} + +EAPI DBusPendingCall * +e_dbus_peer_get_machine_id(E_DBus_Connection *conn, const char *destination, const char *path, E_DBus_Method_Return_Cb cb_return, const void *data) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + return _dbus_peer_call(conn, "GetMachineId", destination, path, cb_return, data); +} + +static inline DBusMessage * +_dbus_message_property_method_call(E_DBus_Connection *conn, const char *method_name, const char *destination, const char *path, const char *interface, const char *property) +{ + DBusMessage *msg; + + if (!conn) + { + ERR("E-dbus Error: no connection for call of %s", method_name); + return NULL; + } + + msg = dbus_message_new_method_call + (destination, path, E_DBUS_FDO_INTERFACE_PROPERTIES, method_name); + if (!msg) + { + ERR("E-dbus Error: failed to create message for method call: %s() at " + "\"%s\" at \"%s\"", + method_name, destination, path); + return NULL; + } + + if (property) + { + dbus_message_append_args(msg, DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID); + } + else + { + dbus_message_append_args(msg, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID); + } + + return msg; +} + +EAPI DBusPendingCall * +e_dbus_properties_get_all(E_DBus_Connection *conn, const char *destination, const char *path, const char *interface, E_DBus_Method_Return_Cb cb_return, const void *data) +{ + DBusMessage *msg; + DBusPendingCall *ret; + + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + msg = _dbus_message_property_method_call + (conn, "GetAll", destination, path, interface, NULL); + if (!msg) + return NULL; + ret = e_dbus_message_send(conn, msg, cb_return, -1, (void *)data); + dbus_message_unref(msg); + + if (!ret) + ERR("ERROR: failed to call GetAll() at \"%s\" at \"%s\"", + destination, path); + + return ret; +} + +EAPI DBusPendingCall * +e_dbus_properties_get(E_DBus_Connection *conn, const char *destination, const char *path, const char *interface, const char *property, E_DBus_Method_Return_Cb cb_return, const void *data) +{ + DBusMessage *msg; + DBusPendingCall *ret; + + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + msg = _dbus_message_property_method_call + (conn, "Get", destination, path, interface, property); + if (!msg) + return NULL; + ret = e_dbus_message_send(conn, msg, cb_return, -1, (void *)data); + dbus_message_unref(msg); + + if (!ret) + ERR("E-dbus Error: failed to call Get() at \"%s\" at \"%s\"", + destination, path); + + return ret; +} + +EAPI DBusPendingCall * +e_dbus_properties_set(E_DBus_Connection *conn, const char *destination, const char *path, const char *interface, const char *property, int value_type, const void *value, E_DBus_Method_Return_Cb cb_return, const void *data) +{ + DBusMessage *msg; + DBusMessageIter iter, sub; + DBusError err; + DBusPendingCall *ret; + char sig[2]; + + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + if (!dbus_type_is_basic(value_type)) + { + if (cb_return) + { + dbus_error_init(&err); + dbus_set_error(&err, "org.enlightenment.DBus.InvalidType", "Only basic types may be set using e_dbus_properties_set()"); + cb_return((void *)data, NULL, &err); + + } + return NULL; + } + + msg = _dbus_message_property_method_call + (conn, "Set", destination, path, interface, property); + if (!msg) + return NULL; + + dbus_message_iter_init_append(msg, &iter); + sig[0] = value_type; + sig[1] = 0; + if (dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, sig, &sub)) + { + dbus_message_iter_append_basic(&sub, value_type, value); + dbus_message_iter_close_container(&iter, &sub); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + + ret = e_dbus_message_send(conn, msg, cb_return, -1, (void *)data); + dbus_message_unref(msg); + + if (!ret) + ERR("E-dbus Error: failed to call Set() at \"%s\" at \"%s\"", + destination, path); + + return ret; +} diff --git a/src/lib/dbus/e_dbus_message.c b/src/lib/dbus/e_dbus_message.c new file mode 100644 index 0000000..d535ced --- /dev/null +++ b/src/lib/dbus/e_dbus_message.c @@ -0,0 +1,122 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "e_dbus_private.h" + +typedef struct E_DBus_Pending_Call_Data E_DBus_Pending_Call_Data; +struct E_DBus_Pending_Call_Data +{ + E_DBus_Method_Return_Cb cb_return; + void *data; +}; + +static void +cb_pending(DBusPendingCall *pending, void *user_data) +{ + DBusMessage *msg; + DBusError err; + E_DBus_Pending_Call_Data *data = user_data; + + if (!dbus_pending_call_get_completed(pending)) + { + INFO("E-dbus: NOT COMPLETED"); + free(data); + dbus_pending_call_unref(pending); + return; + } + + dbus_error_init(&err); + msg = dbus_pending_call_steal_reply(pending); + if (!msg) + { + if (data->cb_return) + { + dbus_set_error(&err, "org.enlightenment.DBus.NoReply", "There was no reply to this method call."); + data->cb_return(data->data, NULL, &err); + dbus_error_free(&err); + } + return; + } + + if (dbus_set_error_from_message(&err, msg)) + { + if (data->cb_return) + data->cb_return(data->data, NULL, &err); + dbus_error_free(&err); + } + else + { + if (data->cb_return) + data->cb_return(data->data, msg, &err); + } + + dbus_message_unref(msg); + dbus_pending_call_unref(pending); +} + + +EAPI DBusPendingCall * +e_dbus_message_send(E_DBus_Connection *conn, DBusMessage *msg, E_DBus_Method_Return_Cb cb_return, int timeout, void *data) +{ + DBusPendingCall *pending; + + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + if (!dbus_connection_send_with_reply(conn->conn, msg, &pending, timeout)) + return NULL; + + if (cb_return && pending) + { + E_DBus_Pending_Call_Data *pdata; + + pdata = malloc(sizeof(E_DBus_Pending_Call_Data)); + pdata->cb_return = cb_return; + pdata->data = data; + + if (!dbus_pending_call_set_notify(pending, cb_pending, pdata, free)) + { + free(pdata); + dbus_message_unref(msg); + dbus_pending_call_cancel(pending); + return NULL; + } + } + + return pending; +} + +static void +cb_method_call(void *data, DBusMessage *msg, DBusError *err) +{ + E_DBus_Callback *cb = data; + void *method_return = NULL; + DBusError new_err; + if (!cb) return; + + dbus_error_init(&new_err); + if (!dbus_error_is_set(err)) + method_return = e_dbus_callback_unmarshal(cb, msg, &new_err); + else + dbus_move_error(err, &new_err); + + e_dbus_callback_call(cb, method_return, &new_err); + e_dbus_callback_return_free(cb, method_return); + + if (dbus_error_is_set(&new_err)) + dbus_error_free(&new_err); + + e_dbus_callback_free(cb); +} + +EAPI DBusPendingCall * +e_dbus_method_call_send(E_DBus_Connection *conn, DBusMessage *msg, E_DBus_Unmarshal_Func unmarshal_func, E_DBus_Callback_Func cb_func, E_DBus_Free_Func free_func, int timeout, void *data) +{ + E_DBus_Callback *cb; + + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + cb = e_dbus_callback_new(cb_func, unmarshal_func, free_func, data); + return e_dbus_message_send(conn, msg, cb_method_call, timeout, cb); +} diff --git a/src/lib/dbus/e_dbus_methods.c b/src/lib/dbus/e_dbus_methods.c new file mode 100644 index 0000000..cb1003d --- /dev/null +++ b/src/lib/dbus/e_dbus_methods.c @@ -0,0 +1,175 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "e_dbus_private.h" + +static inline DBusMessage * +_dbus_message_method_call(const char *method_name) +{ + DBusMessage *msg; + + msg = dbus_message_new_method_call + (E_DBUS_FDO_BUS, E_DBUS_FDO_PATH, E_DBUS_FDO_INTERFACE, + method_name); + if (!msg) + ERR("E-dbus Error: failed to create message for method call: %s", + method_name); + return msg; +} + +static inline DBusPendingCall * +_dbus_call__void(E_DBus_Connection *conn, const const char *method_name, E_DBus_Method_Return_Cb cb_return, const void *data) +{ + DBusMessage *msg; + DBusPendingCall *ret; + + if (!conn) + { + ERR("E.dbus Error: no connection for call of %s", method_name); + return NULL; + } + + msg = _dbus_message_method_call(method_name); + if (!msg) + return NULL; + ret = e_dbus_message_send(conn, msg, cb_return, -1, (void *)data); + dbus_message_unref(msg); + + if (!ret) + ERR("E-dbus Error: failed to call %s()", method_name); + + return ret; +} + +static inline DBusPendingCall * +_dbus_call__str(E_DBus_Connection *conn, const const char *method_name, const char *str, E_DBus_Method_Return_Cb cb_return, const void *data) +{ + DBusMessage *msg; + DBusPendingCall *ret; + + if (!conn) + { + ERR("E-dbus Error: no connection for call of %s", method_name); + return NULL; + } + + msg = _dbus_message_method_call(method_name); + if (!msg) + return NULL; + dbus_message_append_args(msg, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID); + ret = e_dbus_message_send(conn, msg, cb_return, -1, (void *)data); + dbus_message_unref(msg); + + if (!ret) + ERR("E-dbus Error: failed to call %s(\"%s\")", method_name, str); + + return ret; +} + +EAPI DBusPendingCall * +e_dbus_introspect(E_DBus_Connection *conn, const char *bus, const char *object_path, E_DBus_Method_Return_Cb cb_return, const void *data) +{ + DBusPendingCall *ret; + DBusMessage *msg; + + if (!conn) + { + ERR("E-dbus Error: no connection for use with introspection"); + return NULL; + } + + msg = dbus_message_new_method_call + (bus, object_path, "org.freedesktop.DBus.Introspectable", "Introspect"); + if (!msg) + return NULL; + ret = e_dbus_message_send(conn, msg, cb_return, -1, (void *)data); + dbus_message_unref(msg); + return ret; +} + +EAPI DBusPendingCall * +e_dbus_request_name(E_DBus_Connection *conn, const char *name, unsigned int flags, E_DBus_Method_Return_Cb cb_return, const void *data) +{ + DBusPendingCall *ret; + DBusMessage *msg; + dbus_uint32_t u_flags; + + if (!conn) + { + ERR("E-dbus Error: no connection for call of RequestName"); + return NULL; + } + + u_flags = flags; + + msg = _dbus_message_method_call("RequestName"); + if (!msg) + return NULL; + dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, DBUS_TYPE_UINT32, &u_flags, DBUS_TYPE_INVALID); + ret = e_dbus_message_send(conn, msg, cb_return, -1, (void *)data); + dbus_message_unref(msg); + return ret; +} + +EAPI DBusPendingCall * +e_dbus_release_name(E_DBus_Connection *conn, const char *name, E_DBus_Method_Return_Cb cb_return, const void *data) +{ + return _dbus_call__str(conn, "ReleaseName", name, cb_return, data); +} + + +EAPI DBusPendingCall * +e_dbus_get_name_owner(E_DBus_Connection *conn, const char *name, E_DBus_Method_Return_Cb cb_return, const void *data) +{ + return _dbus_call__str(conn, "GetNameOwner", name, cb_return, data); +} + +EAPI DBusPendingCall * +e_dbus_list_names(E_DBus_Connection *conn, E_DBus_Method_Return_Cb cb_return, const void *data) +{ + return _dbus_call__void(conn, "ListNames", cb_return, data); +} + + +EAPI DBusPendingCall * +e_dbus_list_activatable_names(E_DBus_Connection *conn, E_DBus_Method_Return_Cb cb_return, const void *data) +{ + return _dbus_call__void(conn, "ListActivatableNames", cb_return, data); +} + +EAPI DBusPendingCall * +e_dbus_name_has_owner(E_DBus_Connection *conn, const char *name, E_DBus_Method_Return_Cb cb_return, const void *data) +{ + return _dbus_call__str(conn, "NameHasOwner", name, cb_return, data); +} + + +EAPI DBusPendingCall * +e_dbus_start_service_by_name(E_DBus_Connection *conn, const char *name, unsigned int flags, E_DBus_Method_Return_Cb cb_return, const void *data) +{ + const char method_name[] = "StartServiceByName"; + DBusMessage *msg; + DBusPendingCall *ret; + + if (!conn) + { + ERR("ERROR: no connection for call of %s", method_name); + return NULL; + } + + msg = _dbus_message_method_call(method_name); + if (!msg) + return NULL; + dbus_message_append_args(msg, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_UINT32, &flags, + DBUS_TYPE_INVALID); + ret = e_dbus_message_send(conn, msg, cb_return, -1, (void *)data); + dbus_message_unref(msg); + + if (!ret) + ERR("E-dbus Error: failed to call %s(\"%s\")", method_name, name); + + return ret; +} diff --git a/src/lib/dbus/e_dbus_object.c b/src/lib/dbus/e_dbus_object.c new file mode 100644 index 0000000..d649c3b --- /dev/null +++ b/src/lib/dbus/e_dbus_object.c @@ -0,0 +1,713 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "e_dbus_private.h" + +static E_DBus_Interface *introspectable_interface = NULL; +static E_DBus_Interface *properties_interface = NULL; + +typedef struct E_DBus_Method E_DBus_Method; +typedef struct E_DBus_Signal E_DBus_Signal; + +Eina_Strbuf * e_dbus_object_introspect(E_DBus_Object *obj); + +static void e_dbus_object_unregister(DBusConnection *conn, void *user_data); +static DBusHandlerResult e_dbus_object_handler(DBusConnection *conn, DBusMessage *message, void *user_data); + +static void e_dbus_interface_free(E_DBus_Interface *iface); + +static E_DBus_Method *e_dbus_method_new(const char *member, const char *signature, const char *reply_signature, E_DBus_Method_Cb func); +static void e_dbus_object_method_free(E_DBus_Method *m); + +static E_DBus_Signal *e_dbus_signal_new(const char *name, const char *signature); +static void e_dbus_object_signal_free(E_DBus_Signal *s); + +static void _introspect_indent_append(Eina_Strbuf *buf, int level); +static void _introspect_interface_append(Eina_Strbuf *buf, E_DBus_Interface *iface, int level); +static void _introspect_method_append(Eina_Strbuf *buf, E_DBus_Method *method, int level); +static void _introspect_signal_append(Eina_Strbuf *buf, E_DBus_Signal *signal, int level); +static void _introspect_arg_append(Eina_Strbuf *buf, const char *type, const char *direction, int level); + + +//static Eina_List *standard_methods = NULL; + + +static DBusObjectPathVTable vtable = { + e_dbus_object_unregister, + e_dbus_object_handler, + NULL, + NULL, + NULL, + NULL +}; + +struct E_DBus_Object +{ + E_DBus_Connection *conn; + char *path; + Eina_List *interfaces; + char *introspection_data; + int introspection_dirty; + + E_DBus_Object_Property_Get_Cb cb_property_get; + E_DBus_Object_Property_Set_Cb cb_property_set; + + void *data; +}; + +struct E_DBus_Interface +{ + char *name; + Eina_List *methods; + Eina_List *signals; + int refcount; +}; + +struct E_DBus_Method +{ + char *member; + char *signature; + char *reply_signature; + E_DBus_Method_Cb func; +}; + +struct E_DBus_Signal +{ + char *name; + char *signature; +}; + +static DBusMessage * +cb_introspect(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessage *ret; + Eina_Strbuf *buf; + + if (obj->introspection_dirty || !obj->introspection_data) + { + buf = e_dbus_object_introspect(obj); + if (!buf) + { + ret = dbus_message_new_error(msg, "org.enlightenment.NotIntrospectable", "This object does not provide introspection data"); + return ret; + } + + if (obj->introspection_data) free(obj->introspection_data); + obj->introspection_data = strdup(eina_strbuf_string_get(buf)); + eina_strbuf_free(buf); + } + //printf("XML: \n\n%s\n\n", obj->introspection_data); + ret = dbus_message_new_method_return(msg); + dbus_message_append_args(ret, DBUS_TYPE_STRING, &(obj->introspection_data), DBUS_TYPE_INVALID); + + return ret; +} + +static DBusMessage * +cb_properties_get(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessage *reply; + DBusMessageIter iter, sub; + DBusError err; + int type; + void *value; + char *property, *interface; + + dbus_error_init(&err); + dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, + &property, DBUS_TYPE_INVALID); + + if (dbus_error_is_set(&err)) + { + return dbus_message_new_error(msg, err.name, err.message); + } + + /* + * FIXME: there's no way to pass interface the interface here - this shall be + * fixed by another callback function, since fixing it here would break the + * API. + */ + obj->cb_property_get(obj, property, &type, &value); + if (type == DBUS_TYPE_INVALID) + { + return dbus_message_new_error_printf(msg, "org.enlightenment.DBus.InvalidProperty", "The property '%s' does not exist on this object.", property); + } + + if (dbus_type_is_basic(type)) + { + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + if (dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, e_dbus_basic_type_as_string(type), &sub)) + { + dbus_message_iter_append_basic(&sub, type, &value); + dbus_message_iter_close_container(&iter, &sub); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + return reply; + } + else + { + return dbus_message_new_error(msg, "org.enlightenment.DBus.UnsupportedType", "E_DBus currently only supports properties of a basic type."); + } +} + +static DBusMessage * +cb_properties_set(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter, sub; + int type; + void *value; + char *property, *interface; + + dbus_message_iter_init(msg, &iter); + dbus_message_iter_get_basic(&iter, &interface); + dbus_message_iter_next(&iter); + dbus_message_iter_get_basic(&iter, &property); + dbus_message_iter_next(&iter); + dbus_message_iter_recurse(&iter, &sub); + type = dbus_message_iter_get_arg_type(&sub); + if (dbus_type_is_basic(type)) + { + dbus_message_iter_get_basic(&sub, &value); + + /* + * FIXME: there's no way to pass interface the interface here - this shall + * be fixed by another callback function, since fixing it here would break + * the API. + */ + if (obj->cb_property_set(obj, property, type, value)) + { + return dbus_message_new_method_return(msg); + } + else + { + return dbus_message_new_error_printf(msg, "org.enlightenment.DBus.InvalidProperty", "The property '%s' does not exist on this object.", property); + } + } + else + { + return dbus_message_new_error(msg, "org.enlightenment.DBus.UnsupportedType", "E_DBus currently only supports properties of a basic type."); + } + +} + +int +e_dbus_object_init(void) +{ + introspectable_interface = e_dbus_interface_new("org.freedesktop.DBus.Introspectable"); + properties_interface = e_dbus_interface_new(E_DBUS_FDO_INTERFACE_PROPERTIES); + if (!introspectable_interface || !properties_interface) + { + if (introspectable_interface) e_dbus_interface_unref(introspectable_interface); + introspectable_interface = NULL; + if (properties_interface) e_dbus_interface_unref(properties_interface); + properties_interface = NULL; + return 0; + } + + e_dbus_interface_method_add(introspectable_interface, "Introspect", "", "s", cb_introspect); + e_dbus_interface_method_add(properties_interface, "Get", "ss", "v", cb_properties_get); + e_dbus_interface_method_add(properties_interface, "Set", "ssv", "", cb_properties_set); + return 1; +} + +void +e_dbus_object_shutdown(void) +{ + e_dbus_interface_unref(introspectable_interface); + introspectable_interface = NULL; + + e_dbus_interface_unref(properties_interface); + properties_interface = NULL; +} + +EAPI E_DBus_Object * +e_dbus_object_add(E_DBus_Connection *conn, const char *object_path, void *data) +{ + E_DBus_Object *obj; + + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(object_path, NULL); + + obj = calloc(1, sizeof(E_DBus_Object)); + if (!obj) return NULL; + + if (!dbus_connection_register_object_path(conn->conn, object_path, &vtable, obj)) + { + free(obj); + return NULL; + } + + obj->conn = conn; + e_dbus_connection_ref(conn); + obj->path = strdup(object_path); + obj->data = data; + obj->interfaces = NULL; + + e_dbus_object_interface_attach(obj, introspectable_interface); + + return obj; +} + +EAPI void +e_dbus_object_free(E_DBus_Object *obj) +{ + E_DBus_Interface *iface; + + if (!obj) return; + + DBG("e_dbus_object_free (%s)", obj->path); + dbus_connection_unregister_object_path(obj->conn->conn, obj->path); + e_dbus_connection_close(obj->conn); + + if (obj->path) free(obj->path); + EINA_LIST_FREE(obj->interfaces, iface) + e_dbus_interface_unref(iface); + + if (obj->introspection_data) free(obj->introspection_data); + + free(obj); +} + +EAPI void * +e_dbus_object_data_get(E_DBus_Object *obj) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL); + return obj->data; +} + +EAPI E_DBus_Connection * +e_dbus_object_conn_get(E_DBus_Object *obj) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL); + return obj->conn; +} + +EAPI const char * +e_dbus_object_path_get(E_DBus_Object *obj) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL); + return obj->path; +} + +EAPI const Eina_List * +e_dbus_object_interfaces_get(E_DBus_Object *obj) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL); + return obj->interfaces; +} + +EAPI void +e_dbus_object_property_get_cb_set(E_DBus_Object *obj, E_DBus_Object_Property_Get_Cb func) +{ + EINA_SAFETY_ON_NULL_RETURN(obj); + if (obj->cb_property_get == NULL && obj->cb_property_set == NULL) + e_dbus_object_interface_attach(obj, properties_interface); + obj->cb_property_get = func; +} + +EAPI void +e_dbus_object_property_set_cb_set(E_DBus_Object *obj, E_DBus_Object_Property_Set_Cb func) +{ + EINA_SAFETY_ON_NULL_RETURN(obj); + if (obj->cb_property_get == NULL && obj->cb_property_set == NULL) + e_dbus_object_interface_attach(obj, properties_interface); + obj->cb_property_set = func; +} + +EAPI void +e_dbus_object_interface_attach(E_DBus_Object *obj, E_DBus_Interface *iface) +{ + E_DBus_Interface *iface_added; + Eina_List *l; + EINA_SAFETY_ON_NULL_RETURN(obj); + EINA_SAFETY_ON_NULL_RETURN(iface); + + EINA_LIST_FOREACH(obj->interfaces, l, iface_added) + { + if (strcmp(iface->name, iface_added->name) == 0) + { + ERR("This object(%s) already have this interface name(%s) attached", + obj->path, iface->name); + return; + } + } + + e_dbus_interface_ref(iface); + obj->interfaces = eina_list_append(obj->interfaces, iface); + obj->introspection_dirty = 1; + DBG("e_dbus_object_interface_attach (%s, %s) ", obj->path, iface->name); +} + +EAPI void +e_dbus_object_interface_detach(E_DBus_Object *obj, E_DBus_Interface *iface) +{ + E_DBus_Interface *found; + + EINA_SAFETY_ON_NULL_RETURN(obj); + EINA_SAFETY_ON_NULL_RETURN(iface); + DBG("e_dbus_object_interface_detach (%s, %s) ", obj->path, iface->name); + found = eina_list_data_find(obj->interfaces, iface); + if (!found) return; + + obj->interfaces = eina_list_remove(obj->interfaces, iface); + obj->introspection_dirty = 1; + e_dbus_interface_unref(iface); +} + +EAPI void +e_dbus_interface_ref(E_DBus_Interface *iface) +{ + EINA_SAFETY_ON_NULL_RETURN(iface); + iface->refcount++; + DBG("e_dbus_interface_ref (%s) = %d", iface->name, iface->refcount); +} + +EAPI void +e_dbus_interface_unref(E_DBus_Interface *iface) +{ + EINA_SAFETY_ON_NULL_RETURN(iface); + DBG("e_dbus_interface_unref (%s) = %d", iface->name, iface->refcount - 1); + if (--(iface->refcount) == 0) + e_dbus_interface_free(iface); +} + +static void +e_dbus_interface_free(E_DBus_Interface *iface) +{ + E_DBus_Method *m; + E_DBus_Signal *s; + + if (iface->name) free(iface->name); + EINA_LIST_FREE(iface->methods, m) + e_dbus_object_method_free(m); + EINA_LIST_FREE(iface->signals, s) + e_dbus_object_signal_free(s); + free(iface); +} + + +EAPI int +e_dbus_interface_method_add(E_DBus_Interface *iface, const char *member, const char *signature, const char *reply_signature, E_DBus_Method_Cb func) +{ + E_DBus_Method *m; + + EINA_SAFETY_ON_NULL_RETURN_VAL(iface, 0); + m = e_dbus_method_new(member, signature, reply_signature, func); + DBG("E-dbus: Add method %s: %p", member, m); + if (!m) return 0; + + iface->methods = eina_list_append(iface->methods, m); + return 1; +} + +EAPI int +e_dbus_interface_signal_add(E_DBus_Interface *iface, const char *name, const char *signature) +{ + E_DBus_Signal *s; + + EINA_SAFETY_ON_NULL_RETURN_VAL(iface, 0); + s = e_dbus_signal_new(name, signature); + DBG("E-dbus: Add signal %s: %p", name, s); + if (!s) return 0; + + iface->signals = eina_list_append(iface->signals, s); + return 1; +} + +EAPI E_DBus_Interface * +e_dbus_interface_new(const char *interface) +{ + E_DBus_Interface *iface; + + if (!interface) return NULL; + + iface = calloc(1, sizeof(E_DBus_Interface)); + if (!iface) return NULL; + + iface->refcount = 1; + iface->name = strdup(interface); + iface->methods = NULL; + iface->signals = NULL; + + return iface; +} + +static E_DBus_Method * +e_dbus_method_new(const char *member, const char *signature, const char *reply_signature, E_DBus_Method_Cb func) +{ + E_DBus_Method *m; + + if (!member || !func) return NULL; + + if (signature && !dbus_signature_validate(signature, NULL)) return NULL; + if (reply_signature && !dbus_signature_validate(reply_signature, NULL)) return NULL; + m = calloc(1, sizeof(E_DBus_Method)); + if (!m) return NULL; + + m->member = strdup(member); + if (signature) + m->signature = strdup(signature); + if (reply_signature) + m->reply_signature = strdup(reply_signature); + m->func = func; + + return m; +} + +static void +e_dbus_object_method_free(E_DBus_Method *m) +{ + if (!m) return; + if (m->member) free(m->member); + if (m->signature) free(m->signature); + if (m->reply_signature) free(m->reply_signature); + + free(m); +} + +static E_DBus_Signal * +e_dbus_signal_new(const char *name, const char *signature) +{ + E_DBus_Signal *s; + + if (!name) return NULL; + + if (signature && !dbus_signature_validate(signature, NULL)) return NULL; + s = calloc(1, sizeof(E_DBus_Signal)); + if (!s) return NULL; + + s->name = strdup(name); + if (signature) + s->signature = strdup(signature); + + return s; +} + +static void +e_dbus_object_signal_free(E_DBus_Signal *s) +{ + if (!s) return; + if (s->name) free(s->name); + if (s->signature) free(s->signature); + free(s); +} + +static E_DBus_Method * +e_dbus_object_method_find(E_DBus_Object *obj, const char *interface, const char *member) +{ + E_DBus_Method *m; + E_DBus_Interface *iface; + Eina_List *l, *ll; + + if (!obj || !member) return NULL; + + EINA_LIST_FOREACH(obj->interfaces, l, iface) + { + if (strcmp(interface, iface->name)) continue; + EINA_LIST_FOREACH(iface->methods, ll, m) + { + if (!strcmp(member, m->member)) + return m; + } + } + return NULL; +} + +static DBusHandlerResult +e_dbus_object_handler(DBusConnection *conn, DBusMessage *message, void *user_data) +{ + E_DBus_Object *obj; + E_DBus_Method *m; + DBusMessage *reply; + dbus_uint32_t serial; + + obj = user_data; + if (!obj) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + m = e_dbus_object_method_find(obj, dbus_message_get_interface(message), dbus_message_get_member(message)); + + /* XXX should this send an 'invalid method' error instead? */ + if (!m) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (m->signature && !dbus_message_has_signature(message, m->signature)) + reply = dbus_message_new_error_printf(message, "org.enlightenment.InvalidSignature", "Expected signature: %s", m->signature); + else + reply = m->func(obj, message); + + /* user can choose reply later */ + if (!reply) + return DBUS_HANDLER_RESULT_HANDLED; + + dbus_connection_send(conn, reply, &serial); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static void +e_dbus_object_unregister(DBusConnection *conn __UNUSED__, void *user_data __UNUSED__) +{ + /* free up the object struct? */ +} + +Eina_Strbuf * +e_dbus_object_introspect(E_DBus_Object *obj) +{ + Eina_Strbuf *buf; + int level = 0; + E_DBus_Interface *iface; + Eina_List *l; + + buf = eina_strbuf_new(); + + /* Doctype */ + eina_strbuf_append(buf, "\n"); + + eina_strbuf_append(buf, "path); + eina_strbuf_append(buf, "\">\n"); + level++; + + EINA_LIST_FOREACH(obj->interfaces, l, iface) + _introspect_interface_append(buf, iface, level); + + eina_strbuf_append(buf, "\n"); + return buf; +} + +static void +_introspect_indent_append(Eina_Strbuf *buf, int level) +{ + /* XXX optimize this? */ + int i = level * 2; + while (i-- > 0) + eina_strbuf_append_char(buf, ' '); +} +static void +_introspect_interface_append(Eina_Strbuf *buf, E_DBus_Interface *iface, int level) +{ + E_DBus_Method *m; + E_DBus_Signal *s; + Eina_List *l; + + _introspect_indent_append(buf, level); + eina_strbuf_append(buf, "name); + eina_strbuf_append(buf, "\">\n"); + level++; + + DBG("introspect iface: %s", iface->name); + EINA_LIST_FOREACH(iface->methods, l, m) + _introspect_method_append(buf, m, level); + EINA_LIST_FOREACH(iface->signals, l, s) + _introspect_signal_append(buf, s, level); + + level--; + _introspect_indent_append(buf, level); + eina_strbuf_append(buf, "\n"); +} +static void +_introspect_method_append(Eina_Strbuf *buf, E_DBus_Method *method, int level) +{ + DBusSignatureIter iter; + char *type; + + _introspect_indent_append(buf, level); + DBG("introspect method: %s\n", method->member); + eina_strbuf_append(buf, "member); + eina_strbuf_append(buf, "\">\n"); + level++; + + /* append args */ + if (method->signature && + method->signature[0] && + dbus_signature_validate(method->signature, NULL)) + { + dbus_signature_iter_init(&iter, method->signature); + while ((type = dbus_signature_iter_get_signature(&iter))) + { + _introspect_arg_append(buf, type, "in", level); + + dbus_free(type); + if (!dbus_signature_iter_next(&iter)) break; + } + } + + /* append reply args */ + if (method->reply_signature && + method->reply_signature[0] && + dbus_signature_validate(method->reply_signature, NULL)) + { + dbus_signature_iter_init(&iter, method->reply_signature); + while ((type = dbus_signature_iter_get_signature(&iter))) + { + _introspect_arg_append(buf, type, "out", level); + + dbus_free(type); + if (!dbus_signature_iter_next(&iter)) break; + } + } + + level--; + _introspect_indent_append(buf, level); + eina_strbuf_append(buf, "\n"); +} + +static void +_introspect_signal_append(Eina_Strbuf *buf, E_DBus_Signal *s, int level) +{ + DBusSignatureIter iter; + char *type; + + _introspect_indent_append(buf, level); + DBG("introspect signal: %s", s->name); + eina_strbuf_append(buf, "name); + eina_strbuf_append(buf, "\">\n"); + level++; + + /* append args */ + if (s->signature && + s->signature[0] && + dbus_signature_validate(s->signature, NULL)) + { + dbus_signature_iter_init(&iter, s->signature); + while ((type = dbus_signature_iter_get_signature(&iter))) + { + _introspect_arg_append(buf, type, NULL, level); + + dbus_free(type); + if (!dbus_signature_iter_next(&iter)) break; + } + } + + level--; + _introspect_indent_append(buf, level); + eina_strbuf_append(buf, "\n"); +} + +static void +_introspect_arg_append(Eina_Strbuf *buf, const char *type, const char *direction, int level) +{ + _introspect_indent_append(buf, level); + eina_strbuf_append(buf, "\n"); +} + diff --git a/src/lib/dbus/e_dbus_private.h b/src/lib/dbus/e_dbus_private.h new file mode 100644 index 0000000..93fce49 --- /dev/null +++ b/src/lib/dbus/e_dbus_private.h @@ -0,0 +1,53 @@ +#ifndef E_DBUS_PRIVATE_H +#define E_DBUS_PRIVATE_H + +#include + +#include "E_DBus.h" + +#ifndef E_DBUS_COLOR_DEFAULT +#define E_DBUS_COLOR_DEFAULT EINA_COLOR_CYAN +#endif +EAPI extern int _e_dbus_log_dom; +#define DBG(...) EINA_LOG_DOM_DBG(_e_dbus_log_dom, __VA_ARGS__) +#define INFO(...) EINA_LOG_DOM_INFO(_e_dbus_log_dom, __VA_ARGS__) +#define WARN(...) EINA_LOG_DOM_WARN(_e_dbus_log_dom, __VA_ARGS__) +#define ERR(...) EINA_LOG_DOM_ERR(_e_dbus_log_dom, __VA_ARGS__) + + +struct E_DBus_Connection +{ + DBusBusType shared_type; + DBusConnection *conn; + char *conn_name; + + Eina_List *fd_handlers; + Eina_List *timeouts; + Eina_List *signal_handlers; + void (*signal_dispatcher)(E_DBus_Connection *conn, DBusMessage *msg); + + Ecore_Idler *idler; + + int refcount; +}; + +struct E_DBus_Callback +{ + E_DBus_Callback_Func cb_func; + E_DBus_Unmarshal_Func unmarshal_func; + E_DBus_Free_Func free_func; + void *user_data; +}; + +int e_dbus_object_init(void); +void e_dbus_object_shutdown(void); + +extern int e_dbus_idler_active; +void e_dbus_signal_handlers_clean(E_DBus_Connection *conn); +void e_dbus_signal_handlers_free_all(E_DBus_Connection *conn); + + +const char *e_dbus_basic_type_as_string(int type); + + +#endif diff --git a/src/lib/dbus/e_dbus_signal.c b/src/lib/dbus/e_dbus_signal.c new file mode 100644 index 0000000..0688757 --- /dev/null +++ b/src/lib/dbus/e_dbus_signal.c @@ -0,0 +1,271 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "e_dbus_private.h" + +#define SENDER_KEY "sender" +#define PATH_KEY "path" +#define INTERFACE_KEY "interface" +#define MEMBER_KEY "member" +#define NAME_OWNER_MATCH "type='signal',sender='org.freedesktop.DBus',\ + path='/org/freedesktop/DBus',interface='org.freedesktop.DBus',\ + member='NameOwnerChanged',arg0='%s'" +#define MEMBER_NAME_OWNER_CHANGED "NameOwnerChanged" + +struct E_DBus_Signal_Handler +{ + char *sender; + char *path; + char *interface; + char *member; + char *owner; + char *match; + char *match_name_owner_change; + + E_DBus_Signal_Cb cb_signal; + DBusPendingCall *get_name_owner_pending; + void *data; + unsigned char delete_me : 1; +}; + +static void cb_signal_dispatcher(E_DBus_Connection *conn, DBusMessage *msg); + +/* + * Free a signal handler + * @param sh the signal handler to free + */ +static void +e_dbus_signal_handler_free(E_DBus_Signal_Handler *sh) +{ + free(sh->sender); + free(sh->interface); + free(sh->path); + free(sh->member); + free(sh->owner); + free(sh->match); + free(sh->match_name_owner_change); + free(sh); +} + +struct cb_name_owner_data +{ + E_DBus_Connection *conn; + E_DBus_Signal_Handler *sh; +}; + +static void +cb_name_owner(void *data, DBusMessage *msg, DBusError *err) +{ + const char *unique_name = NULL; + struct cb_name_owner_data *d = data; + E_DBus_Signal_Handler *sh; + DBusError new_err; + + sh = d->sh; + sh->get_name_owner_pending = NULL; + free(d); + + if (dbus_error_is_set(err)) return; + + dbus_error_init(&new_err); + dbus_message_get_args(msg, &new_err, DBUS_TYPE_STRING, + &unique_name, DBUS_TYPE_INVALID); + + if (dbus_error_is_set(&new_err)) return; + + if (unique_name != NULL && unique_name[0]) sh->owner = strdup(unique_name); +} + +static void +_match_append(Eina_Strbuf *match, const char *key, const char *value) +{ + if (value == NULL || !value[0]) return; + + if ((eina_strbuf_length_get(match) + 4 + strlen(key) + strlen(value)) + >= DBUS_MAXIMUM_MATCH_RULE_LENGTH) + { + ERR("cannot add match %s='%s' to %s: too long!", key, value, + eina_strbuf_string_get(match)); + return; + } + + eina_strbuf_append_printf(match, ",%s='%s'", key, value); +} + +EAPI E_DBus_Signal_Handler * +e_dbus_signal_handler_add(E_DBus_Connection *conn, const char *sender, const char *path, const char *interface, const char *member, E_DBus_Signal_Cb cb_signal, void *data) +{ + E_DBus_Signal_Handler *sh; + Eina_Strbuf *match; + DBusError err; + + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + sh = calloc(1, sizeof(E_DBus_Signal_Handler)); + if (!sh) + { + ERR("could not allocate signal handler."); + return NULL; + } + + match = eina_strbuf_new(); + eina_strbuf_append(match, "type='signal'"); + + _match_append(match, SENDER_KEY, sender); + _match_append(match, PATH_KEY, path); + _match_append(match, INTERFACE_KEY, interface); + _match_append(match, MEMBER_KEY, member); + + if (sender) sh->sender = strdup(sender); + if (path) sh->path = strdup(path); + if (interface) sh->interface = strdup(interface); + if (member) sh->member = strdup(member); + sh->match = strdup(eina_strbuf_string_get(match)); + + sh->cb_signal = cb_signal; + sh->get_name_owner_pending = NULL; + sh->data = data; + sh->delete_me = 0; + + dbus_error_init(&err); + dbus_bus_add_match(conn->conn, sh->match, &err); + + if (!conn->signal_handlers) conn->signal_dispatcher = cb_signal_dispatcher; + + /* if we have a sender, and it is not a unique name, we need to know the + * unique name to match since signals will have the name owner as their + * sender. + */ + if (sender && sender[0] != ':' && strcmp(sender, E_DBUS_FDO_BUS) != 0) + { + struct cb_name_owner_data *data_cb; + + // listen when the owner of the sender name change + eina_strbuf_reset(match); + eina_strbuf_append_printf(match, NAME_OWNER_MATCH, sh->sender); + sh->match_name_owner_change = strdup(eina_strbuf_string_get(match)); + + dbus_bus_add_match(conn->conn, sh->match_name_owner_change, NULL); + DBG("add name owner match=%s", sh->match_name_owner_change); + + data_cb = malloc(sizeof(*data_cb)); + if (!data_cb) + { + e_dbus_signal_handler_free(sh); + eina_strbuf_free(match); + ERR("could not allocate cb_name_owner_data."); + return NULL; + } + data_cb->conn = conn; + data_cb->sh = sh; + sh->get_name_owner_pending = + e_dbus_get_name_owner(conn, sender, cb_name_owner, data_cb); + } + else if (sender) sh->owner = strdup(sender); + + eina_strbuf_free(match); + conn->signal_handlers = eina_list_append(conn->signal_handlers, sh); + + return sh; +} + +static int e_dbus_handler_deletions = 0; + +EAPI void +e_dbus_signal_handler_del(E_DBus_Connection *conn, E_DBus_Signal_Handler *sh) +{ + if ((!conn) || (!sh)) return; + + if (sh->get_name_owner_pending) + { + dbus_pending_call_cancel(sh->get_name_owner_pending); + sh->get_name_owner_pending = NULL; + } + sh->delete_me = 1; + if (e_dbus_idler_active) + { + e_dbus_handler_deletions = 1; + return; + } + + conn->signal_handlers = eina_list_remove(conn->signal_handlers, sh); + + dbus_bus_remove_match(conn->conn, sh->match, NULL); + if (sh->match_name_owner_change) + dbus_bus_remove_match(conn->conn, sh->match_name_owner_change, NULL); + + e_dbus_signal_handler_free(sh); +} + +static void +cb_signal_dispatcher(E_DBus_Connection *conn, DBusMessage *msg) +{ + E_DBus_Signal_Handler *sh; + Eina_List *l; + + if(dbus_message_has_sender(msg, E_DBUS_FDO_BUS) && + dbus_message_has_path(msg, E_DBUS_FDO_PATH) && + dbus_message_has_interface(msg, E_DBUS_FDO_INTERFACE) && + dbus_message_has_member(msg, MEMBER_NAME_OWNER_CHANGED)) + { + DBusError new_err; + char *bus, *old_owner, *new_owner; + dbus_error_init(&new_err); + dbus_message_get_args(msg, &new_err, DBUS_TYPE_STRING, &bus, + DBUS_TYPE_STRING, &old_owner, + DBUS_TYPE_STRING, &new_owner, DBUS_TYPE_INVALID); + + if (dbus_error_is_set(&new_err)) return; + + EINA_LIST_FOREACH(conn->signal_handlers, l, sh) + { + if (!sh->delete_me && sh->sender && strcmp(sh->sender, bus) == 0) + { + free(sh->owner); + sh->owner = NULL; + if (new_owner != NULL && new_owner[0]) + sh->owner = strdup(new_owner); + } + } + } + + EINA_LIST_FOREACH(conn->signal_handlers, l, sh) + { + if ((!sh->cb_signal) || (sh->delete_me)) continue; + + if (sh->get_name_owner_pending || + (sh->owner && !dbus_message_has_sender(msg, sh->owner))) continue; + if (sh->path && !dbus_message_has_path(msg, sh->path)) continue; + if (sh->interface && !dbus_message_has_interface(msg, sh->interface)) continue; + if (sh->member && !dbus_message_has_member(msg, sh->member)) continue; + + sh->cb_signal(sh->data, msg); + } +} + +void +e_dbus_signal_handlers_clean(E_DBus_Connection *conn) +{ + E_DBus_Signal_Handler *sh; + Eina_List *l, *l_next; + + if (!e_dbus_handler_deletions) return; + e_dbus_handler_deletions = 0; + if (!conn->signal_handlers) return; + EINA_LIST_FOREACH_SAFE(conn->signal_handlers, l, l_next, sh) + { + if (sh->delete_me) + e_dbus_signal_handler_del(conn, sh); + } +} + +void +e_dbus_signal_handlers_free_all(E_DBus_Connection *conn) +{ + E_DBus_Signal_Handler *sh; + EINA_LIST_FREE(conn->signal_handlers, sh) + e_dbus_signal_handler_free(sh); +} diff --git a/src/lib/dbus/e_dbus_util.c b/src/lib/dbus/e_dbus_util.c new file mode 100644 index 0000000..9fda872 --- /dev/null +++ b/src/lib/dbus/e_dbus_util.c @@ -0,0 +1,85 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "e_dbus_private.h" + +EAPI E_DBus_Callback * +e_dbus_callback_new(E_DBus_Callback_Func cb_func, E_DBus_Unmarshal_Func unmarshal_func, E_DBus_Free_Func free_func, void *user_data) +{ + E_DBus_Callback *cb; + + if (!cb_func) return NULL; + + cb = calloc(1, sizeof(E_DBus_Callback)); + if (!cb) return NULL; + cb->cb_func = cb_func; + cb->unmarshal_func = unmarshal_func; + cb->free_func = free_func; + cb->user_data = user_data; + return cb; +} + +EAPI void +e_dbus_callback_free(E_DBus_Callback *callback) +{ + free(callback); +} + +EAPI void +e_dbus_callback_call(E_DBus_Callback *cb, void *data, DBusError *error) +{ + if (cb && cb->cb_func) + cb->cb_func(cb->user_data, data, error); +} + +EAPI void * +e_dbus_callback_unmarshal(E_DBus_Callback *cb, DBusMessage *msg, DBusError *err) +{ + if (cb && cb->unmarshal_func) + return cb->unmarshal_func(msg, err); + return NULL; +} + +EAPI void +e_dbus_callback_return_free(E_DBus_Callback *cb, void *data) +{ + if (cb && cb->free_func) + cb->free_func(data); +} + +const char * +e_dbus_basic_type_as_string(int type) +{ + switch (type) + { + case DBUS_TYPE_BYTE: + return DBUS_TYPE_BYTE_AS_STRING; + case DBUS_TYPE_BOOLEAN: + return DBUS_TYPE_BOOLEAN_AS_STRING; + case DBUS_TYPE_INT16: + return DBUS_TYPE_INT16_AS_STRING; + case DBUS_TYPE_UINT16: + return DBUS_TYPE_UINT16_AS_STRING; + case DBUS_TYPE_INT32: + return DBUS_TYPE_INT32_AS_STRING; + case DBUS_TYPE_UINT32: + return DBUS_TYPE_UINT32_AS_STRING; + case DBUS_TYPE_INT64: + return DBUS_TYPE_INT64_AS_STRING; + case DBUS_TYPE_UINT64: + return DBUS_TYPE_UINT64_AS_STRING; + case DBUS_TYPE_DOUBLE: + return DBUS_TYPE_DOUBLE_AS_STRING; + case DBUS_TYPE_STRING: + return DBUS_TYPE_STRING_AS_STRING; + case DBUS_TYPE_OBJECT_PATH: + return DBUS_TYPE_OBJECT_PATH_AS_STRING; + case DBUS_TYPE_SIGNATURE: + return DBUS_TYPE_SIGNATURE_AS_STRING; + default: + return NULL; + } +} diff --git a/src/lib/hal/E_Hal.h b/src/lib/hal/E_Hal.h new file mode 100644 index 0000000..ab86ebe --- /dev/null +++ b/src/lib/hal/E_Hal.h @@ -0,0 +1,128 @@ +#ifndef E_HAL_H +#define E_HAL_H + +#include +#include + +/** + * @defgroup EHal_Group EHal + * + * @{ + */ + +#define E_HAL_SENDER "org.freedesktop.Hal" +#define E_HAL_MANAGER_PATH "/org/freedesktop/Hal/Manager" +#define E_HAL_MANAGER_INTERFACE "org.freedesktop.Hal.Manager" +#define E_HAL_DEVICE_INTERFACE "org.freedesktop.Hal.Device" +#define E_HAL_DEVICE_VOLUME_INTERFACE "org.freedesktop.Hal.Device.Volume" + +/* message return types */ + +typedef struct E_Hal_Property E_Hal_Property; +typedef struct E_Hal_Properties E_Hal_Properties; + +struct E_Hal_String_List_Return +{ + Eina_List *strings; /* list of const char * */ +}; + +struct E_Hal_Bool_Return +{ + Eina_Bool boolean; +}; + +struct E_Hal_UDI_Return +{ + const char *udi; +}; + +struct E_Hal_Capability +{ + const char *udi; + const char *capability; +}; + +typedef enum +{ + E_HAL_PROPERTY_TYPE_STRING, + E_HAL_PROPERTY_TYPE_INT, + E_HAL_PROPERTY_TYPE_UINT64, + E_HAL_PROPERTY_TYPE_BOOL, + E_HAL_PROPERTY_TYPE_DOUBLE, + E_HAL_PROPERTY_TYPE_STRLIST +} E_Hal_Property_Type; + +struct E_Hal_Property +{ + E_Hal_Property_Type type; + union + { + const char *s; + int i; + dbus_bool_t b; + double d; + dbus_uint64_t u64; + Eina_List *strlist; + } val; +}; + +struct E_Hal_Properties +{ + Eina_Hash *properties; +}; + +typedef struct E_Hal_Properties E_Hal_Device_Get_All_Properties_Return; +typedef struct E_Hal_Property E_Hal_Device_Get_Property_Return; +typedef struct E_Hal_Bool_Return E_Hal_Device_Query_Capability_Return; +typedef struct E_Hal_String_List_Return E_Hal_String_List_Return; +typedef struct E_Hal_String_List_Return E_Hal_Manager_Get_All_Devices_Return; +typedef struct E_Hal_Bool_Return E_Hal_Manager_Device_Exists_Return; +typedef struct E_Hal_String_List_Return E_Hal_Manager_Find_Device_String_Match_Return; +typedef struct E_Hal_String_List_Return E_Hal_Manager_Find_Device_By_Capability_Return; + + +typedef struct E_Hal_UDI_Return E_Hal_Manager_Device_Added; +typedef struct E_Hal_UDI_Return E_Hal_Manager_Device_Removed; +typedef struct E_Hal_Capability E_Hal_Manager_New_Capability; + +#ifdef __cplusplus +extern "C" { +#endif + + EAPI int e_hal_init(void); + EAPI int e_hal_shutdown(void); + +/* org.freedesktop.Hal.Device */ + EAPI DBusPendingCall *e_hal_device_get_property(E_DBus_Connection *conn, const char *udi, const char *property, E_DBus_Callback_Func cb_func, void *data); + EAPI DBusPendingCall *e_hal_device_get_all_properties(E_DBus_Connection *conn, const char *udi, E_DBus_Callback_Func cb_func, void *data); + EAPI DBusPendingCall *e_hal_device_query_capability(E_DBus_Connection *conn, const char *udi, const char *capability, E_DBus_Callback_Func cb_func, void *data); + +/* org.freedesktop.Hal.Manager */ + EAPI DBusPendingCall *e_hal_manager_get_all_devices(E_DBus_Connection *conn, E_DBus_Callback_Func cb_func, void *data); + EAPI DBusPendingCall *e_hal_manager_device_exists(E_DBus_Connection *conn, const char *udi, E_DBus_Callback_Func cb_func, void *data); + EAPI DBusPendingCall *e_hal_manager_find_device_string_match(E_DBus_Connection *conn, const char *key, const char *value, E_DBus_Callback_Func cb_func, void *data); + EAPI DBusPendingCall *e_hal_manager_find_device_by_capability(E_DBus_Connection *conn, const char *capability, E_DBus_Callback_Func cb_func, void *data); + +/* utility functions */ + EAPI void e_hal_property_free(E_Hal_Property *prop); + EAPI const char *e_hal_property_string_get(E_Hal_Properties *properties, const char *key, int *err); + EAPI Eina_Bool e_hal_property_bool_get(E_Hal_Properties *properties, const char *key, int *err); + EAPI int e_hal_property_int_get(E_Hal_Properties *properties, const char *key, int *err); + EAPI uint64_t e_hal_property_uint64_get(E_Hal_Properties *properties, const char *key, int *err); + EAPI double e_hal_property_double_get(E_Hal_Properties *properties, const char *key, int *err); + EAPI const Eina_List *e_hal_property_strlist_get(E_Hal_Properties *properties, const char *key, int *err); + +/* (un)mount */ + EAPI DBusPendingCall *e_hal_device_volume_mount(E_DBus_Connection *conn, const char *udi, const char *mount_point, const char *fstype, Eina_List *options, E_DBus_Callback_Func cb_func, void *data); + EAPI DBusPendingCall *e_hal_device_volume_unmount(E_DBus_Connection *conn, const char *udi, Eina_List *options, E_DBus_Callback_Func cb_func, void *data); + EAPI DBusPendingCall *e_hal_device_volume_eject(E_DBus_Connection *conn, const char *udi, Eina_List *options, E_DBus_Callback_Func cb_func, void *data); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif diff --git a/src/lib/hal/HAL 0.5.10 Specification.html b/src/lib/hal/HAL 0.5.10 Specification.html new file mode 100644 index 0000000..f677bb0 --- /dev/null +++ b/src/lib/hal/HAL 0.5.10 Specification.html @@ -0,0 +1,4050 @@ +HAL 0.5.10 Specification

HAL 0.5.10 Specification

David Zeuthen


+           
+          

Version 0.5.10


Table of Contents

1. Introduction
About
Acknowledgements
Architecture of HAL
Device Objects
Device Capabilities
2. Device Information Files
Matching
Merging
Search Paths
3. Access Control
Device Files
D-Bus Interfaces
4. Locking
Overview
Guidelines
5. Device Properties
General Properties
+ info namespace +
Callouts
Addons
Singleton Addons
Method calls
Subsystem-Specific Properties
+ pci namespace +
+ serial namespace +
+ usb_device namespace +
+ usb namespace +
+ platform namespace +
+ ide_host namespace +
+ ide namespace +
+ scsi_host namespace +
+ scsi namespace +
+ ieee1394_host namespace +
+ ieee1394_node namespace +
+ ieee1394 namespace +
+ mmc_host namespace +
+ mmc namespace +
+ ccw namespace +
+ ccwgroup namespace +
+ iucv namespace +
+ block namespace +
xen namespace
bluetooth_hci namespace
bluetooth_acl namespace
bluetooth_sco namespace
drm namespace
Functional Properties
+ system namespace +
+ volume namespace +
+ volume.disc namespace +
+ storage namespace +
+ storage.cdrom namespace +
+ storage.linux_raid namespace +
+ net namespace +
+ net.80203 namespace +
+ net.80211 namespace +
+ net.bluetooth namespace +
+ net.irda namespace +
+ net.80211control namespace +
+ input namespace +
+ input.keys namespace +
+ input.keypad namespace +
+ input.keyboard namespace +
+ input.mouse namespace +
+ input.switch namespace +
+ input.joystick namespace +
+ input.tablet namespace +
+ input.keymap namespace +
+ input.xkb namespace +
+ pcmcia_socket namespace +
+ printer namespace +
+ portable_audio_player namespace +
+ alsa namespace +
+ oss namespace +
+ camera namespace +
+ scanner namespace +
+ laptop_panel namespace +
+ ac_adapter namespace +
+ battery namespace +
+ button namespace +
+ processor namespace +
+ light_sensor namespace +
+ power_management namespace +
+ tape namespace +
+ killswitch namespace +
Misc. Properties
+ access_control namespace +
Deprecated Properties
6. D-Bus interfaces
org.freedesktop.Hal.Manager interface
org.freedesktop.Hal.Device interface
org.freedesktop.Hal.Device.SystemPowerManagement interface
org.freedesktop.Hal.Device.CPUFreq interface
org.freedesktop.Hal.Device.WakeOnLan interface
org.freedesktop.Hal.Device.LaptopPanel interface
org.freedesktop.Hal.Device.KeyboardBacklight interface
org.freedesktop.Hal.Device.LightSensor interface
org.freedesktop.Hal.Device.Storage interface
org.freedesktop.Hal.Device.Volume interface
org.freedesktop.Hal.Device.Volume.Crypto interface
org.freedesktop.Hal.Device.KillSwitch interface
org.freedesktop.Hal.Device.AccessControl interface
org.freedesktop.Hal.SingletonAddon interface

Chapter 1. Introduction

About

+ This document concerns the specification of HAL which is a + piece of software that provides a view of the various hardware + attached to a system. In addition to this, HAL keeps detailed + metadata for each piece of hardware and provide hooks such + that system- and desktop-level software can react to changes + in the hardware configuration in order to maintain system + policy. +

+ HAL represents a piece of hardware as a device object. + A device object is identified by a unique identifer and carries a set of + key/value paris referred to as device properties. + Some properties are derived from the actual hardware, some are merged + from device information files + and some are related to the + actual device configuration. This document specifies the set + of device properties and gives them well-defined meaning. This + enable system and desktop level components to distinguish + between the different device objects and discover and + configure devices based on these properties. +

+ HAL provides an easy-to-use API through D-Bus which is an IPC + framework that, among other things, provides a system-wide + message-bus that allows applications to talk to one + another. Specifically, D-Bus provides asynchronous + notification such that HAL can notify other peers on the + message-bus when devices are added and removed as well as when + properties on a device are changing. +

+ The most important goal of HAL is to provide plug-and-play + facilities for UNIX-like desktops with focus on providing a + rich and extensible description of device characteristics and + features. HAL has no other major dependencies apart from D-Bus + which, given sufficient infrastructure, allows it to be + implemented on many UNIX-like systems. The major focus, + initially, is systems running the Linux 2.6 series kernels. +

Acknowledgements

+ Havoc Pennington's article + ''Making Hardware Just Work'' + + motivated this work. The specification and software would not exist + without all the useful ideas, suggestions, comments and patches + from the + Free Desktop and + HAL + mailing lists. +

+ All trademarks mentioned belong to their respective owners. +

Architecture of HAL

+ The HAL consists of a number of components as outlined in the + diagram below. Note that this diagram is high-level and doesn't + capture all implementation details. +

+ +

+ Details on each component +

  • + HAL daemon +

    + A system-wide service that maintains a database of device + objects. The daemon is responsible for merging information + from device information files and managing the life cycle + of device objects. The service is implemented as a daemon + and uses helpers to query devices for specific information. +

  • + Applications +

    + These are applications consuming services from HAL; this + includes desktop-wide session daemons for maintaining + policy such as power and disk/volume management. +

  • + Callouts +

    + Callouts are programs that run when device objects are + added and removed in the HAL daemon. This is useful for + 3rd party software to merge additional information onto + the device object before it is announced on + D-Bus. Callouts are specified on a per-device basis with + the info.callouts.add and + info.callouts.remove. See + the section called “ + info namespace + ” for details. +

  • + Methods +

    + It is possible to specify that a given HAL device object + implements a specific D-Bus interface, + e.g. org.freedesktop.Hal.Device.Frob + with a set of + methods Foo, Bar + and Baz and have programs run when + applications call into this interface. This is defined in + the info.interfaces property, consult + the section called “ + info namespace + ” for details. +

  • + Addons +

    + An addon can be characterized as a + daemon whose life cycle is tied to a device object in + HAL. And addon can also claim a + specific interface on the device object to provide + services to applications for configuring / using the + device without having to spawn a new program for every + method call. HAL provides a facility to launch/destroy one + or more addons per device object using + the info.addons property. See + the section called “ + info namespace + ” for details. +

  • + Device Information Files +

    + A set of files that matches properties on device objects + and merges additional information. These files are used, + for among other things, to specify what callouts, methods + and addons to associate with a device object. For example, + for drives using removable media, HAL includes an add-on + daemon which sole purpose is to continously poll the drive + to detect media change. +

+

+ The D-Bus system message bus is used to provide a ''network + API'' to applications. As D-Bus is designed to be language + independent, potentially many languages / runtime systems will + be able to easily access the services offered by HAL. +

Device Objects

+ It is important to precisely define the term HAL device + object. It's actually a bit blurry to define in general, it + includes what most UNIX-like systems consider first class + objects when it comes to hardware. In particular, a device + object should represent the smallest unit of addressable + hardware. This means there can be a one-to-many relationship + between a physical device and the device objects exported by + HAL. Specifically, a multi-function printer, which appear to + users as a single device may show up as several device + objects; e.g. one HAL device object for each of the printing, + scanning, fax and storage interfaces. Conversely, some devices + may be implemented such that the HAL device object represent + several functional interfaces. HAL is not concerned with this + duality of either one-to-many or many-to-one relationships + between device objects and the actual iron constituting what + users normally understand as a single piece of hardware; + a device object represents the smallest addressable unit. +

+ Device objects in HAL are organised on a by-connection basis, + e.g. for a given device object X it is possible to find the + device object Y where X is attached to Y. This gives structure + to the device database of HAL; it is possible to map the devices + out in a tree. Further, software emulation devices exported by + the operating system kernel, such as SCSI emulation for USB + Storage Devices, are also considered device objects in HAL. This + implies that operating system kernel specific bits leak into the + device object database. However applications using HAL will not + notice this, such device objects are not referenced anywhere in + the device objects that users are interested in; they are merely + used as glue to build the device tree. +

+ In addition to provide information about what kind of hardware a + device object represents (such as a PCI or USB device) and how + to address it, HAL merges information about the functional + interfaces the operating system kernel provides in order to use + the device; in most cases this is represented on the device + object as a string property with the name of the special device + file in + /dev. In addition to the special device file, + a number of other useful properties are merged. This means that + both hardware and functional properties are on the same device + object, which may prove to be useful for an application + programmer. For example, an application might query HAL for the + device object that exports the special device file + /dev/input/mouse2 and learn that this is + provide by an USB mouse from a certain manufacturer by + checking the properties that export the USB vendor and product + identifiers. See the section called “Device Capabilities” + and + Chapter 5, Device Properties + for details. +

+ Finally, HAL provides one or more D-Bus + interfaces for applications to configure and/or use + the device. These interfaces are discussed in + Chapter 6, D-Bus interfaces. +

+ Summarizing, a device object is comprised by +

  • + UDI +

    + This is the the Unique Device + Identifer, that is unique for a device object - + that is, no other device object can have the same UDI at the + same time. The UDI is computed using bus-specific + information and is meant to be unique across device + insertions and independent of the physical port or slot the + device may be plugged into. +

  • + Properties +

    + Each device object got a set of properties which are + key/value pairs. The key is an ASCII string while the value + can be one of several types, see below. Properties are + arranged into name spaces using ''.'' as a separator. +

    • + string - UTF8 string +

    • + strlist - ordered list with UTF8 strings +

    • + int - 32-bit signed integer +

    • + uint64 - 64-bit unsigned integer +

    • + bool - truth value +

    • + double - IEEE754 double precision + floating point number +

    +

  • + Interfaces +

    + Applications can configure and/or use a device using D-Bus + interfaces. Typically, there's a one-to-one relationship + between capabilities/namespaces and interfaces. +

+ Properties of a device object carry all the important + information about a device object. For organisational reasons + properties are also namespaced using ''.'' as a separator. +

+ It can be useful to classify properties into four groups +

  • Metadata - Information about how the devices + are connected with respect to each other + (parent/child relationships), what kind of + device it is, what functionality it provides + etc. +

  • Facts - + vendor ID, product ID, disk serial numbers, + number of buttons on a mouse, formats accepted + by a mp3 player and so on. +

  • Usage specific information - + Network link status, special device file name, + filesystem mount location etc. +

  • Policy - + How the device is to be used be users; usually + defined by the system administrator. +

+ The first category is determined by HAL, the second category + includes information merged from either querying the hardware + itself or from device information files. The third category is + intercepted by monitoring the hardware and finally the last is + merged from files under control of the system + administrator. This document is concerned with precisely + defining several properties; see + Chapter 5, Device Properties and onwards for more + information. As a complement to device properties, HAL also + provides conditions on HAL device + objects. Conditions are used to relay events that are happening + on devices which are not easily expressed in properties. This + includes events such as ''processor is overheating'' or ''block + device unmounted''. +

+ There is a special hal device object referred to as the ''root + computer device object''. This device object represent the + entire system as a whole and all other devices are either + directly or indirectly childs of this device object. It has + the + UDI /org/freedesktop/Hal/devices/computer. +

+ The fundamental idea about HAL is that all ''interesting'' + information about hardware that a desktop application needs, + can be obtained by querying HAL. Below is a screenshot of a + simple device manager application shipped with HAL + called hal-device-manager. This + application is communicating with the HAL daemon and displays + the tree of device objects. The shown properties are for a + device object representing a harddisk. +

+ +

+

Device Capabilities

+ Mainstream hardware isn't very good at reporting what it really + is, it only reports, at best, how to interact with it. This is a + problem; many devices, such as MP3 players or digital still + cameras, appear to the operating system as plain USB Mass + Storage devices when the device in fact is a lot more than just + that. The core of the problem is that without external metadata, + the operating system and desktop environment will present it to + the user as just e.g. a mass storage device. +

+ As HAL is concerned with merging of external metadata, through + e.g. device information files, there needs to be some scheme on + how to record what the device actually is. This is achieved by + two textual properties, info.category and + info.capabilities. The former describes + what the device is (as a single + alphanumeric keyword) and the latter describes + what the device does (as a number of + alphanumeric keywords separated by whitespace). The keywords + available for use is defined in this document; we'll refer to + them in following simply as capabilities. +

+ HAL itself, assigns capabilities on device detection time by + inspecting the device class (if available, it depends on the + bus type) and looking at information from the operating system + and the hardware itself. +

+ User mode drivers such as libgphoto2 + and sane provides device information to + merge information about devices they can drive. As such, + device objects represent an USB interface gain additional + properties such as ''scanner'' or ''camera''. +

+ Having a capability also means that part of the property + namespace, prefixed with the capability name, will be populated + with more specific information about the capability. Indeed, + some properties may even be required such that applications and + device libraries have something to expect. For instance, the + capability for being a MP3 player may require properties + defining what audio formats the device support (e.g. Ogg and + MP3), whether it support recording of audio, and how to interact + with the device. For example, the latter may specify ''USB + Storage Device'' or ''proprietary protocol, use libfooplayer''. +

+ Finally, capabilities have an inheritance scheme, e.g. if a device + has a capability foo.bar, it must also have + the capability foo. +

Chapter 2. Device Information Files

+ Device information files (.fdi files is a + shorthand) are used to merge arbitrary properties onto device + objects. The way device information files works is that once all + device properties are merged onto a device object it is tried + against the set of installed device information files. Device + information files are used for both merging facts and policy + settings about devices. +

Matching

+ Each device information file got a number of + <match key="some_property" + [string|int|bool|..]="required_value" > + + directives + that is tested against the properties of the device object. If + all the match directives passes then the device information can + include <[merge|append|prepend|addset] key="some_property" + type="[string|int|bool|..]"> + + directives to + respectively merge new properties or append to existing + properties on the device object. It's important to emphasize + that any previously property stemming from device detection can + be overridden by a device information file. +

+ The <match>, + <merge>, <append>, + <prepend> + and <addset> directives always + requires the key attribute which must be + either a property name on the device object in question or a + path to a property on another device object. The latter case is + expressed either through direct specification of the UDI, such + as + /org/freedesktop/Hal/devices/computer:foo.bar + or indirect references such as + @info.parent:baz where the latter means that + the device object specified by the UDI in the string property + info.parent should be used to query the + property baz. It is also possible to use + multiple indirections, e.g. for a volume on a USB memory stick + the indirection @block.storage_device:@storage.originating_device:usb.vendor_id + will reference the usb.vendor_id property + on the device object representing the USB interface. +

+ When the property to match have been determined a number of + attributes can be used within the <match> + tag: +

  • + string - match a string property; for example + <match key="foo.bar" string="baz"> + will match only if 'foo.bar' is a string property assuming the value 'baz'. +

  • + string_outof - work like string but + the match is done against a list of strings separated by ';'. + For example: + <match key="system.hardware.product" string_outof="Satellite A30;Portable PC"> + In this example the line matches if system.hardware.product + is exactly 'Satellite A30' or 'Portable PC'. +

  • + int - match an integer property +

  • + int_outof - work like int but + the match is done against a list of integer separated by ';'. + For example: + <match key="usb.product_id" int_outof="0x1007;0x1008;0x1009"> + In this example the line matches if usb.product_id is 0x1007, 0x1008 or 0x1009. +

  • + uint64 - match property with the 64-bit unsigned type +

  • + bool - match a boolean property +

  • + double - match a property of type double +

  • + exists - used as + <match key="foo.bar" exists="true">. Can be used with + 'true' and 'false' respectively to match when a property exists and it doesn't. +

  • + empty - can only be used on string or strlist properties + with 'true' and 'false'. + The semantics for 'true' is to match only when the string is non-empty. +

  • + is_ascii - matches only when a string property + contain only ASCII characters. Can be used with 'true' or 'false'. +

  • + is_absolute_path - matches only when a string + property represents an absolute path (the path doesn't have to exist). + Can be used with 'true' or 'false'. +

  • + sibling_contains - can only be used with string and + strlist (string list). + For a string key this matches when a sibling item contains the + (sub-)string in the same property. For a string list, this is if a string + matches an item in the list. +

  • + contains - can only be used with string and + strlist (string list). + For a string key this matches when the property contains the given + (sub-)string. For a string list this match if the given string match + a item of the list. +

  • + contains_ncase - like contains + but the property and the given key are converted to lowercase before check. +

  • + contains_not - can only be used with strlist (string list) + and string properties. + For a string list this match if the given string not match any of the + item of the list (or the property is not set for the device). For a string + this match of the property not contains the (sub-)string. You can use this + attribute to construct if/else blocks together with e.g. contains. +

  • + contains_outof - like contains but can be + used only with strings and the match is done against a list of (sub-)strings + separated by ';'. + For example: + <match key="system.hardware.product" contains_outof="D600;D610;C540"> + In this example the line matches if system.hardware.product + contains D600, D610 or C540. +

  • + prefix - can only be used with string properties. + Matches if property begins with the key. +

  • + prefix_ncase - like prefix but the + property and the given key are converted to lowercase before the check. +

  • + prefix_outof - like prefix but the + match is done against a list of prefixes separated by ';'. + For example: + <match key="system.hardware.product" prefix_outof="1860;2366;2371"> + In this example the line matches if system.hardware.product + starts with 1860, 2366 or 2371. +

  • + suffix - can only be used with string properties. + Matches if property ends with the key. +

  • + suffix_ncase - like suffix but the + property and the given key are converted to lowercase before the check. +

  • + compare_lt - can be used on int, uint64, double + and string properties to compare with a constant. + Matches when the given property is less than the given constant + using the default ordering. +

  • + compare_le - like compare_lt + but matches when less than or equal. +

  • + compare_gt - like compare_lt + but matches when greater than. +

  • + compare_ge - like compare_lt + but matches when greater than or equal. +

  • + compare_ne - like compare_lt + but matches when not equal. +

+

Merging

+ + The <merge>, <append>, + <prepend> + and <addset> directives all require + the type attribute which specifies what to + merge. The following values are supported +

  • + string - The value is copied to the property. For example + <merge key="foo.bar" type="string">baz</merge> + will merge the value 'baz' into the property 'foo.bar'. +

  • + strlist - For <merge> the value is + copied to the property and the current property will be overwritten. For + <append> + and <prepend> the value is + append or prepend to the list as new + item. For <addset> the strlist + is treated as a set and the value is appended if, and only + if, the value doesn't exist already. Usage of + <copy_property> overwrite the complete list with the + value of the given property to copy from. +

  • + bool - Can merge the values 'true' or 'false' +

  • + int - Merges an integer +

  • + uint64 - Merges an unsigned 64-bit integer +

  • + double - Merges a double precision floating point number +

  • + copy_property - Copies the value of a given + property - supports paths with direct and indirect UDI's. For example + <merge key="foo.bar" type="copy_property">@info.parent:baz.bat</merge> + will merge the value of the property baz.bat on the device object with the UDI from + the property info.parent into the property foo.bar on + the device object being processed. +

+ The <remove>, directive require only a key and can be used with all keys. + For strlist there is additionally a special syntax to remove a item from the + string list. For example to remove item 'bla' from property 'foo.bar': + <remove key="foo.bar" type="strlist">bla</remove> +

Search Paths

+ Device Information files are read from two directories + +

  • + /usr/share/hal/fdi - for files provided by packages +

  • + /etc/hal/fdi - for files provided by the system administrator / user +

+ + in exactly that order. This means that the files provided by the + system administrator will be processed last such that they can + overwrite / change properties caused by the device information + files provided by packages. + + The following directory structure is used in /usr/share/hal/fdi + +

  • + information - device information files used to merge device information +

    • + 10freedesktop - included with the hal package +

    • + 20thirdparty - from a 3rd party, not included in hal package +

    +

  • + policy - device information files to merge policy properties such as addons or callouts. +

    • + 10osvendor - included with the hal package +

    • + 20thirdparty - from a 3rd party, not included in hal package +

    +

  • + preprobe - device information files read before probing devices +

    • + 10osvendor - included with the hal package +

    • + 20thirdparty - from a 3rd party, not included in hal package +

    +

+ + As evident, third party packages should drop device information files in + +

  • + /usr/share/hal/fdi/information/20thirdparty +

  • + /usr/share/hal/fdi/policy/20thirdparty +

  • + /usr/share/hal/fdi/preprobe/20thirdparty +

+ +

+ The /etc/hal/fdi tree uses this layout + +

  • + information - device information files used to merge device information +

  • + policy - device information files to merge policy properties such as addons or callouts. +

  • + preprobe - device information files to read before probing devices +

+ + All device information files are matched for every hal device + object in the following order. + +

  1. + When a device is discovered, the preprobe + device information files (e.g. all files + from /usr/share/hal/fdi/preprobe + and /etc/hal/fdi/preprobe) are + processed. +

    + Typically, this class of device information files is used to + tell HAL to leave the device alone by setting the bool + property info.ignore to TRUE. It can also + be used to run programs, preprobe callouts, prior to normal + device investigation. +

  2. + HAL now runs the preprobe callouts. +

  3. + HAL now probes/investigates the device. +

  4. + All the information device information + files (e.g. all files + from /usr/share/hal/fdi/information + and /etc/hal/fdi/information) are + processed. +

    + These device information files are typically used to + associate extra information with a device object. +

  5. + All the policy policy information + files (e.g. all files + from /usr/share/hal/fdi/policy + and /etc/hal/fdi/policy) are + processed. +

    + These device information files are typically used to + associate callouts and addons with a device object. +

  6. + HAL now runs the callouts, starts addons, and then finally + announces the device on the system message bus. +

+ +

Chapter 3. Access Control

+ Access to hardware by unprivileged users is traditionally granted + in two ways either by granting access to the special + device file or allowing access through another process, + using IPC acting on behalf of the user. HAL follows the latter + model and uses the system-wide message bus (D-Bus) as the IPC + mechanism. In addition, HAL has support for modifying the ACL's + (access control lists) on a device file to grant/revoke access to + users based on several criteria. +

Device Files

+ If HAL is built with --enable-acl-management + (requires both --enable-console-kit + and --enable-policy-kit) then ACL's on device + objects with the capability access_control + are automatically managed according to the properties defined in + the section called “ + access_control namespace + ”. In addition, + for this configuration, HAL ships with a device information file + (normally installed in + /usr/share/hal/fdi/policy/10osvendor/20-acl-management.fdi) + that merges this capability on device objects that are normally + accessed by unprivileged users through the device file. This + includes e.g. sound cards, webcams and other devices but + excludes drives and volumes as the latter two are normally + accessed by a user through mounting them into the file system. +

+ HAL uses PolicyKit to decide what users should have access + according to PolicyKit configuration; see the PolicyKit + privilege definition + file /etc/PolicyKit/privileges/hal-device-file.priv + on a system with HAL installed for the default access suggested + by the HAL package and/or OS vendor. +

+ In addition, 3rd party packages can supply device information + files to specify (via + the access_control.grant_user + and access_control.grant_group properties) + that a given user or group should always have access to a device + file. This is useful for system-wide software (such as AV + streaming management) that runs as an unprivileged system + user. This interface is supposed to be stable so 3rd party + packages can depend on it. +

D-Bus Interfaces

+ If HAL is built without ConsoleKit support + (e.g. without --enable-console-kit) access to + the various D-Bus interfaces that provides mechanisms is only + protected by the D-Bus security configuration files + (e.g. using at_console to restrict to console + user on Red Hat systems) and, in certain cases, restricted to + the super user. +

+ If ConsoleKit support is enabled, access to D-Bus interfaces is + currently hardcoded to only allow active users at the system + console. If PolicyKit support is enabled, the PolicyKit library + will be in charge of determining access; see the PolicyKit + privilege definition files + in /etc/PolicyKit/privileges on a system with + HAL installed for the default access suggested by the HAL + package and/or OS vendor. +

Chapter 4. Locking

Table of Contents

Overview
Guidelines

+ As HAL is a mechanism that enables programs in a desktop session + to enforce the policy of the users choice, unexpected things can + happen. For example, if the user is in the middle of partitioning + a disk drive, it is desirable to keep the desktop from mounting + partitions that have not yet been prepared with a suitable file + system. In fact, in such a situation data loss may be the result + if a volume have an old file system signature indicating it's + mountable and, simultenously, another tool is writing to the raw + block device. The mechanism that automounters use, HAL, provides + locking primitives to avoid this. +

+ Further, for multi-user systems, several desktop sessions may run + on a system each on their own display. Suppose that one session + becomes idle and the power management daemon in that session + decides to suspend the system according to user preferences in the + idle session. The result is that users at other seats will see the + system suspend and this is not desirable. The power management + daemons in all sessions need to cooperate to ensure that the + system only suspends when e.g. all sessions are idle or not at + all. The mechanism that each power management daemon uses, HAL, + provides locking primitives that can be used to achieve this. +

Overview

+ HAL provides a mechanism to lock a specific D-Bus interface + either for a specific device or for all the devices the caller + have access to. +

+ The former is achieved by using + the AcquireInterfaceLock() + and ReleaseInterfaceLock() methods on + the org.freedesktop.Hal.Device interface that + every device object implements (see + the section called “org.freedesktop.Hal.Device interface”). By using this API, a caller + can prevent any other caller from invoking methods on the given + interface for the given device object - other callers will + simply see + the org.freedesktop.Hal.Device.InterfaceLocked + exception if they attempt to invoke a method on the given + interface on the given device. The locker can specify whether + the lock is exclusive meaning if multiple + clients clients can hold the lock or if only one client can hold + the lock at one time. If a client don't have access to the + interface of the device, attempts to lock will fail with + a org.freedesktop.Hal.PermissionDenied + exception. If a client loses access to a device (say, if his + session is switched away from using fast user switching) while + holding a lock, he will lose the lock; this can be tracked by + listening to the InterfaceLockReleased + signal. +

+ All local clients, whether they are active or not, can always + lock interfaces on the root computer device object (this doesn't + mean that they are privileged to use the interfaces though) - + the rationale is that this device object represents shared + infrastructure, e.g. power management, and even inactive + sessions needs to participate in managing this. +

+ If another client already holds a lock exclusively, attempts + from other clients to acquire the lock will fail with + the org.freedesktop.Hal.Device.InterfaceAlreadyLocked + exception even if they have access to the device. +

+ In addition, a client may opt to lock all devices that he got + access to by using + the AcquireGlobalInterfaceLock() + and ReleaseGlobalInterfaceLock() methods on + the org.freedesktop.Hal.Manager interface on + the /org/freedesktop/Hal/Manager object (see + the section called “org.freedesktop.Hal.Manager interface”). Global interface locks can + also be obtained exclusively if the caller so desires. Unlike + per-device interface locking, it is not checked at locking time + whether the locker have access to a given device; instead + checking is done when callers attempt to access the + interface. +

+ The algorithm used for determining if a caller is locked out is + shown below. A caller A is locked out of an interface IFACE on a + device object DEVICE if, and only if, +

  1. + Another caller B is holding a lock on the interface IFACE on + DEVICE and A don't have either a global lock on IFACE or a + lock on IFACE on DEVICE; or +

  2. + Another caller B is holding the global lock on the + interface IFACE and B has access to DEVICE and and A don't + have either a global lock on IFACE or a lock on IFACE on + DEVICE. +

+ In other words, a caller A can grab a global lock, but that + doesn't mean A can lock other clients out of devices that A + doesn't have access to. Specifically a caller is never locked + out if he has locked an interface either globally or on the + device in question. However, if two clients have a lock on a + device, then both can access it. To ensure that everyone is + locked out, a caller needs to use an exclusive lock. +

+ Note that certain interfaces will also check whether other locks + are being held on other device objects. This is specified on a + per-interface basis in Chapter 6, D-Bus interfaces. +

+ If a process holding locks disconnects from the system bus, the + locks being held by that process will be released. +

Guidelines

+ Locking is only useful if applications requiring exclusive + access actually use the locking primitives to cooperate with + other applications. Here is a list of guidelines. +

  • + Disk Management / Partitioning +

    + In order to prevent HAL-based automounters from mounting + partitions that are being prepared, applications that access + block devices directly (and pokes the kernel to reload the + partitioning table) should lock out automounters by either + a) obtaining + the org.freedesktop.Hal.Device.Storage + lock on each drive being processed; or b) obtaintaing the + global + org.freedesktop.Hal.Device.Storage + lock. This includes programs like fdisk, gparted, parted and + operating system installers. See also + the section called “org.freedesktop.Hal.Device.Volume interface” and + the hal-lock(1) program and manual page. +

  • + Power Management +

    + Typically, a desktop session includes a session-wide power + management daemon that enforces the policy of the users + choice, e.g. whether the system should suspend to ram on lid + close, whether to hibernate the system after the user being + idle for 30 minutes and so on. In a multi-user setup (both + fast user switching and multi-seat), this can break in + various interesting ways unless the power management daemons + cooperate. Also, there may be software running at the system + level who will want to inhibit a desktop session power + management daemon from suspending / shutting down. +

    • + System-level software that do not wish to be interrupted + by the effect of someone calling into the + org.freedesktop.Hal.Device.SystemPowerManagement + interface MUST hold the + org.freedesktop.Hal.Device.SystemPowerManagement + lock non-exclusively on the root computer device + object. For example, the YUM software updater should + hold the lock when doing an RPM transaction. +

    + In addition, any power management session daemon instance +

    • + ... MUST hold the org.freedesktop.Hal.Device.SystemPowerManagement lock + non-exclusively on the root computer device object + unless it is prepared to call into this interface + itself. This typically means that the PM daemon instance + simply acquires the lock on start up and releases it + just before it calls into + the org.freedesktop.Hal.Device.SystemPowerManagement + interface. In other words, the PM daemon instance needs + to hold the lock exactly when it doesn't want other PM + daemon instances to call into + the org.freedesktop.Hal.Device.SystemPowerManagement interface. + This means that if the user have configured the PM + daemon instance to go to sleep after 30 minutes of + inactivity, the lock should be released then. +

    • + ... MUST not hold the lock when the session is inactive + (fast user switching) UNLESS an application in the + session have explicitly called Inhibit() on + the org.freedesktop.PowerManagement + D-Bus session bus interface of the PM daemon. +

    • + ... MUST check that no other process is holding the lock (using the IsLockedByOthers method on the standard org.freedesktop.Hal.Device interface) + before calling into + the org.freedesktop.Hal.Device.SystemPowerManagement + interface. If another process is holding the lock, it + means that either 1) another session is not prepared to + call into + the org.freedesktop.Hal.Device.SystemPowerManagement + interface; OR 2) some system-level software is holding + the lock. The PM daemon instance MUST respect this by + not calling into + the org.freedesktop.Hal.Device.SystemPowerManagement + interface itself. +

    + However, any Power management daemon instance +

    • + ... MAY prompt the user, if applicable, to ask if she + still wants to perform the requested action (e.g. call + into + the org.freedesktop.Hal.Device.SystemPowerManagement + interface) despite the fact that another process + (possibly from another user) is indicating that it does + not want the system to e.g. suspend. Only if the user + agrees, the power management instance should call into + the org.freedesktop.Hal.Device.SystemPowerManagement + interface. Typically, it's only useful to prompt the + user with such questions if the request to call into + the org.freedesktop.Hal.Device.SystemPowerManagement + interface originates from user input, e.g. either a + hotkey, the user clicking a suspend button in the UI or + an application invoking the Suspend() method on the + org.freedesktop.PowerManagement D-Bus + session interface of the PM daemon. +

    • + ... MAY ignore that other processes are holding the lock + and call into + the org.freedesktop.Hal.Device.SystemPowerManagement + interface anyway, but ONLY if if the request to call + into + the org.freedesktop.Hal.Device.SystemPowerManagement + interface originated from e.g. lid close, critically low + battery or other similar conditions. +

    • + ... MAY still call SetPowerSave() on + the org.freedesktop.Hal.Device.SystemPowerManagement + interface even if other processes are holding the lock. +

Chapter 5. Device Properties

Table of Contents

General Properties
+ info namespace +
Callouts
Addons
Singleton Addons
Method calls
Subsystem-Specific Properties
+ pci namespace +
+ serial namespace +
+ usb_device namespace +
+ usb namespace +
+ platform namespace +
+ ide_host namespace +
+ ide namespace +
+ scsi_host namespace +
+ scsi namespace +
+ ieee1394_host namespace +
+ ieee1394_node namespace +
+ ieee1394 namespace +
+ mmc_host namespace +
+ mmc namespace +
+ ccw namespace +
+ ccwgroup namespace +
+ iucv namespace +
+ block namespace +
xen namespace
bluetooth_hci namespace
bluetooth_acl namespace
bluetooth_sco namespace
drm namespace
Functional Properties
+ system namespace +
+ volume namespace +
+ volume.disc namespace +
+ storage namespace +
+ storage.cdrom namespace +
+ storage.linux_raid namespace +
+ net namespace +
+ net.80203 namespace +
+ net.80211 namespace +
+ net.bluetooth namespace +
+ net.irda namespace +
+ net.80211control namespace +
+ input namespace +
+ input.keys namespace +
+ input.keypad namespace +
+ input.keyboard namespace +
+ input.mouse namespace +
+ input.switch namespace +
+ input.joystick namespace +
+ input.tablet namespace +
+ input.keymap namespace +
+ input.xkb namespace +
+ pcmcia_socket namespace +
+ printer namespace +
+ portable_audio_player namespace +
+ alsa namespace +
+ oss namespace +
+ camera namespace +
+ scanner namespace +
+ laptop_panel namespace +
+ ac_adapter namespace +
+ battery namespace +
+ button namespace +
+ processor namespace +
+ light_sensor namespace +
+ power_management namespace +
+ tape namespace +
+ killswitch namespace +
Misc. Properties
+ access_control namespace +
Deprecated Properties

+ Properties are arranged in a namespaces using ''.'' as a separator + and are key/value pairs. The value may assume different types; currently + int32, double, bool, UTF8 strings and UTF8 string lists are supported. + The key of a property is always an ASCII string without any whitespace. + When a property changes, HAL will emit a D-Bus signal that applications + can catch. +

General Properties

+ The section represents properties that aren't tied to either + physical or functional characteristics of what the device + object represents. +

+ info namespace +

+ The info namespace contain properties that + can be considered metadata about device objects. These + properties are always available. +

Key (type)ValuesMandatoryDescription
+ info.subsystem (string) + pci, usb, ide_host, ide, block, usb, usbif, scsi_host, scsiYesDescribes what subsystem the device is connected to
+ info.udi (string) + example: /org/freedesktop/Hal/devices/pci_10ec_8139YesThe HAL unique device id
+ info.capabilities (strlist) + example: 'block, storage, storage.cdrom'NoA string list of capabilities describing what the devices does
+ info.category (string) + example: storage.cdromNoThe prominent capability describing what the device is
+ info.product (string) + examples: ''SleekKeyboard'', ''MouseMan 2003'', ''Volume'', ''LS-120 SLIM3 00 UHD Floppy''NoThe name of the device; should not be used in any UI; use subsystem / capability specific properties instead.
+ info.vendor (string) + examples: Logitch, MustekNoThe name of the vendor of the device; should not be used in any UI; use subsystem / capability specific properties instead.
+ info.parent (string) + example: /org/freedesktop/Hal/devices/computerYes, for all non-root device objectsThe UDI of the device object that this device object + is connected to. +
+ info.locked (bool) +  No + If this property is available and set + to TRUE it means that a process + is using the device that the hal device object in + question represents and no other process should attempt + to use or configure the device. The lock is only + advisory. +
+ info.locked.reason (string) + + example: ''The optical drive is currently being used to + record a CD-RW disc.'' + + Only available if info.locked is set + to TRUE. + A localized text suitable for UI display
+ info.locked.dbus_service (string) + example: :1.278 + Only available if info.locked is set + to TRUE. + The base D-BUS service of the process holding the lock.
+ info.is_recalled (bool) +  No + This is set if the hardware may be recalled and should + be checked for any potential problem. +
+ info.recall.vendor (string) + Dell, Sony, HP, Panasonic, etc.Yes, if info.is_recalled is TRUE + The vendor responsible for the hardware recall. +
+ info.recall.website_url (string) +  Yes, if info.is_recalled is TRUE + Users should check this website for more details and if their + hardware may affected by any possible fault. +

Callouts

+ Callouts are programs invoked when the device object are added + and removed. As such, callouts can be used to maintain + system-wide policy (that may be specific to the particular OS) + such as changing permissions on device nodes, updating the + systemwide + /etc/fstab file or configuring the networking + subsystem. +

+ There are three different classes of callouts. A callout + involves sequentially invoking all executable programs in the + string list in listed order. +

+ All callouts are searched for and execute in a minimal + environment. In addition, the UDI of the device object is + exported in the environment + variable UDI. All properties of the device + object are exported in the environment prefixed + with HAL_PROP_. If a device is added or + removed is exported in the environment + variable HALD_ACTION + . The search path for the callout includes the + following paths: +

  1. + $libexecdir (typically /usr/libexec (e.g. Red Hat) or /usr/lib/hal (e.g. Debian)) +

  2. + $libdir/hal/scripts (typically /usr/lib/hal/scripts or + /usr/lib64/hal/scripts) +

  3. + $bindir/ (typically /usr/bin) +

+ including $PATH the HAL daemon was started with during system + initialization. Depending on the distribution, this typically + includes /sbin, + /usr/sbin, + /bin, + /usr/sbin. If the program to run is not + found in any of these paths, the it will + not run even if the given path is absolute. To be + portable across operating systems, third party packages + providing callouts must therefore only + use $libdir/hal/scripts. +

+ If ConsoleKit support is enabled, the variables + CK_NUM_SEATS (number of seats), + CK_NUM_SESSIONS (number of sessions), + CK_SEATS (tab sep. list of seat-id's), + CK_SEAT_seat-id (tab sep. list of session-id's for a seat), + CK_SEAT_NUM_SESSIONS_seat-id (number of sessions on a seat), + CK_SESSION_SEAT_session-id (the seat that a session belongs to) and + CK_SESSION_IS_ACTIVE_session-id (whether a given session is active) and + CK_SESSION_UID_session-id (the user of the session) and + CK_SESSION_IS_LOCAL_session-id (whether a session is local), + CK_SESSION_HOSTNAME_session-id (host name of session's display if it's not local), + will be exported as well. Example: +

+CK_NUM_SEATS=1
+CK_NUM_SESSIONS=2
+CK_SEATS=Seat1
+CK_SEAT_Seat1=Session1  Session3
+CK_SEAT_NUM_SESSIONS_Seat1=2
+CK_SESSION_IS_ACTIVE_Session1=true
+CK_SESSION_IS_ACTIVE_Session3=false
+CK_SESSION_IS_LOCAL_Session1=true
+CK_SESSION_IS_LOCAL_Session3=true
+CK_SESSION_SEAT_Session1=Seat1
+CK_SESSION_SEAT_Session3=Seat1
+CK_SESSION_UID_Session1=500
+CK_SESSION_UID_Session3=501
+      

+ Note that all ConsoleKit object paths given are just base + names; the real D-Bus object path can be reconstructed by + appending /org/freedesktop/ConsoleKit/ + prepended to the given identifer. +

+ The HAL daemon is not suspended while callouts are executing. Thus, + callouts can communicate with the HAL daemon using the D-BUS network + API. Hence, one application of callouts is to merge or modify + properties on a device object. +

+ To reduce round trips and increase privacy, callouts can (and + should) communicate with the HAL daemon using a peer to peer + D-Bus connection specified by + the HALD_DIRECT_ADDR environment + variable. There is convience API in libhal to do this. +

Key (type)ValuesMandatoryDescription
+ info.callouts.add (string list) +  No + A string list with the programs which should be + executed (with HALD_ACTION=add) + when the device is added to the GDL (global device + list) but just before it is announced through the + D-BUS network API. +
+ info.callouts.remove (string list) +  No + A string list with the programs that should be + executed (with HALD_ACTION=remove) + when the device is removed from the GDL (global device + list). The device isn't removed before the last + callout has finished. +
+ info.callouts.preprobe (string list) +  No + A string list with the programs that should be + executed + (with HALD_ACTION=preprobe) before + the device is probed (e.g. investigated) and can be + used to avoid causing unnecessary I/O. +
+ info.callouts.session_add (string list) +  No + A string list with all programs that should be + executed + (with HALD_ACTION=session_add) when + a session is added. Can only be set on the root + computer device object. The environment also contains + the variables + HALD_SESSION_ADD_SESSION_ID, + HALD_SESSION_ADD_SESSION_UID and + HALD_SESSION_ADD_SESSION_IS_ACTIVE + to identify the session. This is only used when HAL is + built with ConsoleKit support. +
+ info.callouts.session_remove (string list) +  No + A string list with all programs which should be + executed + (with HALD_ACTION=session_remove) + when a session is removed. Can only be set on the root + computer device object. The environment also contains + the variables + HALD_SESSION_REMOVE_SESSION_ID, + HALD_SESSION_REMOVE_SESSION_UID and + HALD_SESSION_REMOVE_SESSION_IS_ACTIVE + to identify the session. This is only used when HAL is + built with ConsoleKit support. +

Addons

+ Addons are programs that run for the life time of the device + object. They are searched for and execute in the same + environment as callouts + (e.g. with HAL_PROP_* set in the + environment to represent the device properties) and are + launched just before the device is announced on D-Bus (but + just after the last add callouts have finished). When the + device object goes away, HAL will send + a SIGTERM to the process. +

Key (type)ValuesMandatoryDescription
+ info.addons (strlist) +  No + List of programs to run when device is added. Each + program will need to call + the AddonIsReady() method in order + for the device to show up on D-Bus. +

Singleton Addons

+ Singleton Addons are programs that are started by HAL to + handle a set of devices. They are identified by the command line + used to start them. They MUST implement the + + org.freedesktop.Hal.SingletonAddon + + interface. on the path + /org/freedesktop/Hal/Singleton path on + the direct connection to the HAL daemon. +

+ When a device is added with an info.addons.singleton + string list key, the elements of that key are used as the command line + to start the singleton if the singleton is not already running. + Once the singleton has called SingletonAddonIsReady on + + org.freedesktop.Hal.Manager + interface, it will receive + DeviceAdded calls on its + + org.freedesktop.Hal.SingletonAddon + + interface for all devices that have + its commandline in info.addons.singletona. +

+ If a device is added and the singleton specified in + info.addons.singleton is already running, the + singleton will recieve DeviceAdded on its + + org.freedesktop.Hal.SingletonAddon + + interface for that new device.

+ When a device is removed that is being handled by a singleton, the + singleton will recieve DeviceRemoved on + + org.freedesktop.Hal.SingletonAddon + . + When it is no longer handling any more devices it should exit cleanly. +

Key (type)ValuesMandatoryDescription
+ info.addons.singleton (strlist) +  No + A list of commandlines for singleton addons which should + service this device. +

Method calls

+ Method calls on a specific interface on a device object can be + implemented by the HAL daemon running a program. Note that + this is not the only way to implement support for method + calls; if you expect a lot of method calls it is preferable to + implement an addon and use + the ClaimInterface() API since it reduces + the overhead of spawning a process and it can handle both + complex incoming and return types as well. See the section called “org.freedesktop.Hal.Device interface” for + details on claiming interfaces via an addon.. +

+ Note that method calls implemented via running a program are + limited to the return type being an a signed 32-bit integer + (this will change in a future release). The incoming + parameters are limited to only basic types and arrays of + strings. The parameters are passed via stdin using a textual + representation. As such, there is a lot of overhead with + handling method calls by spawning programs and as such it + should only be used for situtations where the nature of the + method call is that it will not be frequently used. +

+ As with addons, method calls are searched for and execute in + the same minimal environment as callouts + (e.g. with HAL_PROP_* set in the + environment to represent the device properties) and in + addition the environment variables + HAL_METHOD_INVOKED_BY_UID (the uid of the caller) + and + HAL_METHOD_INVOKED_BY_SYSTEMBUS_CONNECTION_NAME + (the unique system bus connection name of the caller) are + set. Additionally, if HAL is built with ConsoleKit support, + HAL_METHOD_INVOKED_BY_PID and + HAL_METHOD_INVOKED_BY_SELINUX_CONTEXT (but + only if the running system have SELinux enabled) will be + set. If HAL itself, or a HAL addon, is invoking a method, then + these variables will not be present. Here's an example +

+HAL_METHOD_INVOKED_BY_UID=500
+HAL_METHOD_INVOKED_BY_PID=22553
+HAL_METHOD_INVOKED_BY_SELINUX_CONTEXT=user_u:system_r:unconfined_t
+HAL_METHOD_INVOKED_BY_SYSTEMBUS_CONNECTION_NAME=:1.138
+      

+ In addition, with ConsoleKit + support, HAL_METHOD_INVOKED_BY_SESSION will + be set to (the basename) of the ConsoleKit session object path + but only if the caller is in a session. The method handler can + then use the previously + mentioned CK_SESSION_* to learn everything + about the context of the caller. + +

Key (type)ValuesMandatoryDescription
+ info.interfaces (strlist) +  No + A list of D-Bus interfaces that the device object + supports apart from the + standard org.freedesktop.Hal.Device + interface. +
+ <iface>.method_names (strlist) + example: 'Foo', 'Bar', 'Baz'No + If a D-Bus interface is implemented by executing a + program for every method, this property contains an + ordered list of the method names. +
+ <iface>.method_argnames (strlist) + example: 'foo_arg1 foo_arg2', '', 'baz_arg1'No + This property contains the names of the arguments for + each method. Each entry is a white-space separated + list for that particular method. +
+ <iface>.method_signatures (strlist) + example: 'si', '', 'as'No + This property contains the D-Bus signature for each + method. The signature should only cover incoming + arguments; each method is defined as returning an + integer. +
+ <iface>.method_execpaths (strlist) + example: 'foo-binary', 'bar-binary', 'baz-binary'No + This property contains the name of the program to + execute when this method is called. The return code + of the program will be passed as the integer result + to the D-Bus caller. + + If a program wants to return an error, it just needs + to write two lines to stderr; the first line is the + exception name to throw and the second line is the + exception detail. +

+ Items in the <iface>.* clearly must + correspond with each other. The whole mechanism is best + explained by an example: + +

+info.interfaces = {'org.freedesktop.Hal.Device.Volume'}
+org.freedesktop.Hal.Device.Volume.method_argnames = {'mount_point fstype extra_options', 'extra_options', 'extra_options'}
+org.freedesktop.Hal.Device.Volume.method_execpaths = {'hal-storage-mount', 'hal-storage-unmount', 'hal-storage-eject'}
+org.freedesktop.Hal.Device.Volume.method_names = {'Mount', 'Unmount', 'Eject'}
+org.freedesktop.Hal.Device.Volume.method_signatures = {'ssas', 'as', 'as'}
+        

+ + which, for example, shows that the Mount() + method on the + interface org.freedesktop.Hal.Device.Volume + takes three arguments: mount_point (a + string), fstype (a string) + and extra_options (an array of strings). + +

Subsystem-Specific Properties

+ In this section properties for device objects that represent + addressable hardware is described. Availability of + these depends on the value of the info.subsystem + property. These properties are not of particular interest to + application developers, instead they are useful for libraries + and userspace drivers that needs to interact with the device + given a UDI. Knowledge of various subsystem-specific + technologies is assumed for this section to be useful. +

+ pci namespace +

+ This namespace contains properties for device objects representing + functions on devices on a PCI bus. These properties are available + exactly when info.subsystem equals pci. +

Key (type)ValuesMandatoryDescription
+ pci.device_class (int) + example: 3YesDevice Class
+ pci.device_subclass (int) + example: 0YesPCI Device Sub Class
+ pci.device_protocol (int) + example: 0YesDevice Protocol
+ pci.product_id (int) + example: 0x4c4dYesProduct ID
+ pci.vendor_id (int) + example: 0x1002YesVendor ID
+ pci.subsys_product_id (int) + example: 0x009eYesSubsystem product id
+ pci.subsys_vendor_id (int) + example: 0x1028YesSubsystem vendor id
+ pci.linux.sysfs_path (string) + example: /sys/devices/pci0000:00/0000:00:01/0000:01:00.0Yes (only on Linux) + Equals linux.sysfs_path +
+ pci.product (string) + Rage Mobility P/M AGP 2xNoName of the product per the PCI database
+ pci.vendor (string) + ATI Technologies IncNoName of the vendor per the PCI database
+ pci.subsys_product (string) + Inspiron 7500NoName of the subsystem product per the PCI database
+ pci.subsys_vendor (string) + Dell Computer CorporationNoName of the subsystem vendor per the PCI database

+ (FIXME: Some key PCI information (bus, slot, port, function + etc.) is missing here) +

+ serial namespace +

+ Device objects that represent serial devices (e.g. /dev/ttyS* or + /dev/ttyUSB*). +

Key (type)ValuesMandatoryDescription
+ serial.originating_device (string) + + example: /org/freedesktop/Hal/devices/pnp_PNP0501 + YesUDI of the device the serial device is bound to.
+ serial.device (string) + example: /dev/ttyS0YesThe device node to access the OSS device.
+ serial.port (int) + example: 0Yes + The port number of the device, based on the number in + serial.device +
+ serial.type (string) + example: platform, usb, unknownYesThis property defines the type of the serial device.

+ usb_device namespace +

+ For device objects representing USB devices the property + info.subsystem will be usb_device, + and the following properties will be available. Note that the + corresponding USB interfaces are represented by separate + device objects as children. +

Key (type)ValuesMandatoryDescription
+ usb_device.bus_number (int) + example: 1YesThe USB bus the device is attached to
+ usb_device.configuration_value (int) + example: 1YesThe current configuration the USB device is in; + starting from 1 +
+ usb_device.configuration (int) + example: Bulk transfer configurationNoHuman-readable description of the current configuration the USB device is in +
+ usb_device.num_configurations (int) + example: 1YesNumber of configurations this USB device + can assume +
+ usb_device.device_class (int) + example: 0YesUSB Device Class
+ usb_device.device_subclass (int) + example: 0YesUSB Device Sub Class
+ usb_device.device_protocol (int) + example: 0YesUSB Device Protocol
+ usb_device.is_self_powered (bool) + example: falseYesThe device, in the current configuration, is self + powered +
+ usb_device.can_wake_up (bool) + example: trueYesThe device, in the current configuration, can wake up
+ usb_device.max_power (int) + example: 98YesMax power drain of device, in mA
+ usb_device.num_interfaces (int) + example: 1YesNumber of USB Interfaces in the current configuration
+ usb_device.num_ports (int) + example: 0YesNumber of ports on a hub. Zero for non-hubs
+ usb_device.port_number (int) + example: 1YesThe port number on the parent hub that the device is attached to, starting from 1
+ usb_device.speed (double) + examples: 1.5, 12.0, 480.0YesSpeed of device, in Mbit/s
+ usb_device.version (double) + examples: 1.0, 1.1, 2.0YesUSB version of device
+ usb_device.level_number (int) + example: 2YesDepth in USB tree, where the virtual root hub + is at depth 0 +
+ usb_device.linux.device_number (string) + example: 19Yes (only on Linux)USB Device Number as assigned by the Linux kernel
+ usb_device.linux.parent_number (string) + example: 19Yes (only on Linux)Device number of parent device as assigned by the + Linux kernel +
+ usb_device.linux.sysfs_path (string) + example: /sys/devices/pci0000:00/0000:00:07.2/usb1/1-1/1-1.1Yes (only on Linux) + Equals linux.sysfs_path +
+ usb_device.product_id (int) + example: 0x3005YesUSB Product ID
+ usb_device.vendor_id (int) + example: 0x04b3YesUSB Vendor ID
+ usb_device.device_revision_bcd (int) + example: 0x0100YesDevice Revision Number encoded in BCD with two decimals
+ usb_device.serial (string) +  NoA string uniquely identifying the instance + of the device; ie. it will be different for two devices + of the same type. Note that the serial number is broken + on some USB devices. +
+ usb_device.product (string) + example: IBM USB HUB KEYBOARDNoName of the product per the USB ID Database
+ usb_device.vendor (string) + example: IBM Corp.NoName of the vendor per the USB ID Database

+ usb namespace +

+ Device objects that represent USB interfaces, ie. when + info.subsystem assumes usb, + are represented by the properties below. In addition all + the usb_device.* properties from the parent + USB device is available in this namespace but only with + the usb prefix instead of + usb_device. +

Key (type)ValuesMandatoryDescription
+ usb.interface.class (int) + example: 0x03YesUSB Class for the interface
+ usb.interface.subclass (int) + example: 0x01YesUSB Sub Class for this interface
+ usb.interface.protocol (int) + example: 0x01YesUSB Protocol for the interface
+ usb.interface.description (int) + example: SyncML interfaceNoHuman-readable description for the interface provided by the device
+ usb.interface.number (int) + example: 1YesNumber of this interface, starting from zero
+ usb.linux.sysfs_path (string) + example: /sys/devices/pci0000:00/0000:00:07.2/usb1/1-1/1-1.1/1-1.1:1.0Yes (only on Linux) + Equals linux.sysfs_path +

+ platform namespace +

+ Devices that are built into the platform or present on busses that + cannot be properly enumerated (e.g. ISA) are represented by device + objects where info.subsystem equals + platform. These kind of devices are commonly, + somewhat incorrectly, called legacy devices. +

Key (type)ValuesMandatoryDescription
+ platform.id (string) + example: serialYesDevice identification

+ ide_host namespace +

+ The ide_host namespace is present for + device objects where info.subsystem is set + to ide_host. Such device objects represent + IDE and ATA host adaptors for harddisks and optical drives as + found in the majority of computer systems. +

Key (type)ValuesMandatoryDescription
+ ide_host.number (int) +  YesA unique number identifying the IDE host adaptor
+ ide_host.linux.sysfs_path (string) + example: /sys/devices/pci0000:00/0000:00:07.1/ide0Yes (only on Linux) + Equals linux.sysfs_path +

+ ide namespace +

+ ATA and IDE drives are represented by device objects where + info.subsystem equals ide. The + following properties are available for such device objects. +

Key (type)ValuesMandatoryDescription
+ ide.host (int) +  YesCorresponds + to ide_host.host_number of + the ide_host device that is the + parent of this device object +
+ ide.channel (int) +  YesIdentifies the IDE channel of the host interface

+ scsi_host namespace +

+ The scsi_host namespace is present for + device objects where info.subsystem is set + to scsi_host. Such device objects represent + SCSI host adaptors for SCSI devices as found in some computer + systems. +

Key (type)ValuesMandatoryDescription
+ scsi_host.host (int) +  YesA unique number identifying the SCSI host adaptor

+ scsi namespace +

+ SCSI devices are represented by device objects where + info.subsystem equals scsi. + The following properties are available for such device objects. +

Key (type)ValuesMandatoryDescription
+ scsi.host (int) +  Yes + Corresponds to scsi_host.host + of the scsi_host device that is the + parent of this device object +
+ scsi.bus (int) +  YesSCSI channel number
+ scsi.target (int) +  YesSCSI identifier number
+ scsi.lun (int) +  YesSCSI Logical Unit Number
+ scsi.type (string) + Example: diskYesSCSI device type
 cdrom This is a SCSI cdrom device.
 comm This is a SCSI communication device.
 disk This is a SCSI disk device.
 medium_changer This is a SCSI media changer (e.g. for CD/Tape).
 printer This is a SCSI printer.
 processor This is a SCSI processor device.
 raid This is a SCSI raid device.
 scanner This is a SCSI scanner.
 tape This is a SCSI tape device.
 unknown The type of this SCSI device is unknwon.

+ ieee1394_host namespace +

+ Device objects with info.subsystem set to + ieee1394_host represent IEEE 1394 host + adaptors. The following properties are available for such + device objects. +

Key (type)ValuesMandatoryDescription
+ ieee1394_host.is_busmgr (bool) +  YesTODO
+ ieee1394_host.is_irn (bool) +  YesTODO
+ ieee1394_host.is_root (bool) +  YesTODO
+ ieee1394_host.node_count (int) +  YesTODO
+ ieee1394_host.nodes_active (int) +  YesTODO

+ ieee1394_node namespace +

+ Device objects with info.subsystem set to + ieee1394_node represent IEEE 1394 nodes on + a IEEE 1394 bus. The following properties are available for + such device objects. +

Key (type)ValuesMandatoryDescription
+ ieee1394_node.capabilities (int) +  YesTODO
+ ieee1394_node.guid (int) +  YesTODO
+ ieee1394_node.nodeid (int) +  YesTODO
+ ieee1394_node.vendor (int) +  YesTODO
+ ieee1394_node.vendor_id (int) +  YesTODO

+ ieee1394 namespace +

+ Device objects with info.subsystem set to + ieee1394 represent IEEE 1394 devices. The + following properties are available for such device objects. +

Key (type)ValuesMandatoryDescription
+ ieee1394.specifier_id (int) +  YesTODO

+ mmc_host namespace +

+ Device objects with info.subsystem set to + mmc_host represent MultiMediaCard or + Secure Digital host adaptors. The following properties + are available for such device objects. +

Key (type)ValuesMandatoryDescription
+ mmc_host.host (int) +  YesA unique number identifying the MMC/SD host adaptor

+ mmc namespace +

+ Device objects with info.subsystem set to + mmc represent MultiMediaCard or Secure + Digital cards. The following properties are available for + such device objects. +

Key (type)ValuesMandatoryDescription
+ mmc.cid (string) + example: 0150415330303842413a1a8083003a9dYesCard Identification Data register (unique for every card + in existence) +
+ mmc.csd (string) + example: 005d013213598067b6d9cfff1640002dYesCard Specific Data register
+ mmc.scr (string) + example: 00a5000000410000Only for SD cardsSD Card Register
+ mmc.rca (int) + example: 8083YesCard bus address
+ mmc.oem (string) +  YesCard OEM distributor
+ mmc.date (string) + example: 10/2003YesManufacturing date
+ mmc.serial (int) + example: 0x3a1a8083YesCard serial number
+ mmc.hwrev (int) + example: 4YesHardware revision
+ mmc.fwrev (int) + example: 1YesFirmware revision

+ ccw namespace +

+ Device objects that represent s390 ccw devices (when info.subsystem + + is set to ccw) are represented by the + properties below. +

Key (type)ValuesMandatoryDescription
+ ccw.devtype (string) + example: 1732/01YesDevice type/model or n/a
+ ccw.cutype (string) + example: 1731/01YesControl unit type/model
+ ccw.cmb_enable (int) + example: 1YesIf channel measurements are enabled
+ ccw.availability (string) + example: goodYesCan be one of 'good', 'boxed', 'no path', + or 'no device' +
+ ccw.online (int) + example: 1YesOnline status
+ ccw.bus_id (string) + example: 0.0.f588YesThe device's bus id in sysfs
+ ccw.subchannel.pim (int) + example: 0x80Nopath installed mask
+ ccw.subchannel.pam (int) + example: 0x80Nopath available mask
+ ccw.subchannel.pom (int) + example: 0xffNopath operational mask
+ ccw.subchannel.chpid0..7 (int) + example: 0x40Nochannel path ids

+ The following properties describe ccw devices where + linux.driver is either dasd-eckd + or dasd-fba. +

Key (type)ValuesMandatoryDescription
+ ccw.dasd.use_diag (int) + example: 0YesIf the device driver shall use diagnose calls to access + the device +
+ ccw.dasd.readonly (int) + example: 0YesIf the device can only be accessed readonly
+ ccw.dasd.discipline (string) + example: ECKDNoThe dasd discipline used to access the device

+ The following properties describe ccw devices where + linux.driver is zfcp. They are + only present when ccw.online = 1. +

Key (type)ValuesMandatoryDescription
+ ccw.zfcp.in_recovery (int) + example: 0YesShows whether the adapter is currently in recovery
+ ccw.zfcp.failed (int) + example: 0YesShows whether the adapter is in failed state

+ The following properties describe ccw devices where + linux.driver is of the form tape_3xxx + + . +

Key (type)ValuesMandatoryDescription
+ ccw.tape.state (string) + example: IN_USEYesThe current status of the tape
+ ccw.tape.operation (string) + example: REWYesA three-letter mnemonic of the current tape operation +
+ ccw.tape.medium_state (string) + example: no mediumNo + If ccw.online = 1, shows whether a tape + is loaded +
+ ccw.tape.blocksize (int) + example: 512No + If ccw.online = 1, shows the blocksize + used for reads and writes to the tape +

+ The following properties describe ccw devices where + linux.driver is 3270. +

Key (type)ValuesMandatoryDescription
+ ccw.3270.model (int) + example: 3YesThe model of the device, determining rows and columns +
+ ccw.3270.rows (int) + example: 32YesThe number of rows
+ ccw.3270.columns (int) + example: 80YesThe number of columns

+ ccwgroup namespace +

+ Device objects that represent groups of ccw devices + (when info.subsystem is set to ccwgroup + have the properties specified below. +

Key (type)ValuesMandatoryDescription
+ ccwgroup.online (int) + example: 1YesOnline status
+ ccwgroup.bus_id (string) + example: 0.0.f588YesThe device's bus id in sysfs

+ The following properties describe ccwgroup devices + where linux.driver is qeth. +

Key (type)ValuesMandatoryDescription
+ ccwgroup.qeth.large_send (string) + example: TSONoWhether large send is provided. Can be "no", "EDDP" + (software) or "TSO" (hardware). +
+ ccwgroup.qeth.card_type (string) + example: OSD_1000YesType of the card
+ ccwgroup.qeth.checksumming (string) + example: sw checksummingNoThe method used to checksum incoming packets
+ ccwgroup.qeth.canonical_macaddr (int) + example: 0NoSpecifies the token ring macaddress format. Not valid in + layer2 mode and for ethernet devices. +
+ ccwgroup.qeth.broadcast_mode (string) + example: broadcast_allringsNoThe scope of token ring broadcasts. Not valid in layer2 + mode and for ethernet devices. +
+ ccwgroup.qeth.fake_broadcast (int) + example: 0NoWhether to fake broadcast capability. Not valid in layer2 + mode. +
+ ccwgroup.qeth.fake_ll (int) + example: 0NoWhether to add a faked link level header to packets. + Not valid in layer2 mode. +
+ ccwgroup.qeth.layer2 (int) + example: 0NoWhether the card operates in layer 2 mode
+ ccwgroup.qeth.portname (string) + example: OSAPORTNoThe port name which has been specified for the card
+ ccwgroup.qeth.portno (int) + example: 0NoThe relative port number on the card
+ ccwgroup.qeth.buffer_count (int) + example: 16YesNumber of inbound buffers used
+ ccwgroup.qeth.add_hhlen (int) + example: 0NoHow much additional space is provided in the hardware + header in skbs in front of packets +
+ ccwgroup.qeth.priority_queueing + (string) + example: always queue 2NoWhich priority queueing algorithm is to be used
+ ccwgroup.qeth.route4 (string) + example: noNoWhether the card has a routing functionality for ipv4. + Not valid in layer2 mode. +
+ ccwgroup.qeth.route6 (string) + example: noNoWhether the card has a routing functionality for ipv6. + Not valid in layer2 mode. +
+ ccwgroup.qeth.state (string) + example: UP (LAN ONLINE)YesThe device's current state

+ The following properties describe ccwgroup devices + where linux.driver is ctc. +

Key (type)ValuesMandatoryDescription
+ ccwgroup.ctc.protocol (int) + example: 0YesThe protocol/method used by the connection
+ ccwgroup.ctc.type (string) + example: CTC/AYesThe device/connection type
+ ccwgroup.ctc.buffer (int) + example: 32768NoThe maximum buffer size of the connection

+ The following properties describe ccwgroup devices + where linux.driver is lcs. +

Key (type)ValuesMandatoryDescription
+ ccwgroup.lcs.portnumber (int) + example: 0YesThe port on the card that is used
+ ccwgroup.lcs.type (string) + example: OSA LCS cardYesThe type of the card
+ ccwgroup.lcs.lancmd_timeout (int) + example: 5YesThe timeout value for LAN commands in seconds

+ The following properties describe ccwgroup devices + where linux.driver is claw. +

Key (type)ValuesMandatoryDescription
+ ccwgroup.claw.api_type (string) +  YesDetermines the packing algorithm for outgoing pakets + (matching the remote peer) +
 IP Using the IP protocol
 PACKED Using an enhanced packing algorithm
 TCPIP Using the TCP/IP protocol
+ ccwgroup.claw.adapter_name (string) + example: RS1YesThe host name of the remote communication peer.
+ ccwgroup.claw.host_name (string) + example: LNX1YesThe host name of the local adapter.
+ ccwgroup.claw.read_buffer (int) + example: 4YesThe number of read buffers allocated
+ ccwgroup.claw.write_buffer (int) + example: 5YesThe number of write buffers allocated

+ iucv namespace +

+ Device objects with info.subsystem set to iucv + + are using the "Intra-User Comminication Vehicle" and are + described by the following properties. +

Key (type)ValuesMandatoryDescription
+ iucv.bus_id (string) + example: netiucv0YesThe device's bus id in sysfs

+ The following properties describe iucv devices + where linux.driver is netiucv. +

Key (type)ValuesMandatoryDescription
+ iucv.netiucv.user (string) + example: linux12YesThe guest name of the connection's target
+ iucv.netiucv.buffer (int) + example: 32768YesThe maximum buffer size of the connection

+ block namespace +

+ Device objects representing addressable block devices, such as + drives and partitions, will have info.subsystem + set to block and will export a number of + properties in the block namespace. +

Key (type)ValuesMandatoryDescription
+ block.device (string) + example: /dev/sda YesSpecial device file to interact with the block device
+ block.major (int) + example: 8YesMajor number of special file to interact with the + device +
+ block.minor (int) + example: 1YesMinor number of special file to interact with the + device +
+ block.is_volume (bool) +  YesTrue only when the block device is a volume that can + be mounted into the file system. In this case the + volume capability will be set and + thus, properties, in the volume + namespace are available. +
+ block.no_partitions (bool) +  YesFor toplevel block devices, this is TRUE only + when no known partition tables have been found on the + media (In this case, if the storage device contain a + file system it will be accessible using the same + special device file as the one for this device object + and the device object representing the filesystem will + appear as a separate device object as a child). For + the child, that is + when block.is_volume is true, this + property is TRUE exactly when it was created for a + storage device with + the storage.no_partitions_hint set + to TRUE. +
+ block.have_scanned (bool) +  Yes + An internal property used by HAL to specify whether a top + level block device have already been scanned for filesystems. +

xen namespace

+ Device objects representing virtual devices under the Xen + Virtual Machine Monitor, such as frontend network or block + devices, will have info.subsystem set to + block and will export a number of + properties in then xen namespace. +

Key (type)ValuesMandatoryDescription
xen.bus_id (string)example: vif-0 YesThe XenBus ID of the device
xen.path (string)example: device/vif/0 YesThe XenBus path of the device
xen.type (string)example: vifYesThe type of Xen device

bluetooth_hci namespace

+ Device objects representing a Bluetooth Host Controller Interface. +

Key (type)ValuesMandatoryDescription
bluetooth_hci.address (uint64) YesAddress of the host controller interface.
bluetooth_hci.originating_device (string) YesThe UDI of the physical device (e.g. an USB interface) that provides the HCI hardware.

bluetooth_acl namespace

+ Device objects representing Asynchronous Connection-oriented Links. +

Key (type)ValuesMandatoryDescription
bluetooth_acl.address (uint64) YesAddress of the device at the other end of the connection.
bluetooth_acl.originating_device (string) YesThe UDI of the Bluetooth HCI (of + capability bluetooth_hci) that the + connection is made through. +

bluetooth_sco namespace

+ Device objects representing Synchronous Connection-Oriented links. +

Key (type)ValuesMandatoryDescription
bluetooth_sco.address (uint64) YesAddress of the device at the other end of the connection.
bluetooth_sco.originating_device (string) YesThe UDI of the Bluetooth HCI (of + capability bluetooth_hci) that the + connection is made through. +

drm namespace

+ The drm namespace is present for Direct Rendering Manager device objects. + They represent a Direct Rendering Interface. +

Key (type)ValuesMandatoryDescription
drm.dri_library (string) YesName of the dri (Direct Rendering Interface) library (e.g. i915).
drm.version (string) YesThe drm version (of the kernel module/diver).

Functional Properties

+ The section describe functional properties of device objects, + that is, properties that are merged onto device objects + representing addressable hardware. In most + circumstances such properties stem from a kernel level + driver attached to the device represented by the device object, + however, as HAL can merge properties from anywhere, they + may have been merged from device information files or callouts. +

+ system namespace +

+ This namespace is found on the toplevel "Computer" device, + and represents information about the system and the currently + running kernel. +

Key (type)ValuesMandatoryDescription
+ system.kernel.name (string) + example: LinuxNo + The name of the kernel, usually the equivalent of + uname -s. +
+ system.kernel.version (string) + example: 2.6.5-7.104-defaultNo + The version of the currently running kernel. Usually + the equivalent of uname -r. +
+ system.kernel.machine (string) + example: i686No + The "machine hardware name" of the currently running kernel. + Usually the equivalent of uname -m. +
+ system.formfactor (string) + example: laptop, desktop, server, unknownYes + The formfactor of the system. Usually the equivalent of + system.chassis.type or set from information + about ACPI/APM/PMU properties. +
+ system.hardware.vendor (string) +  No + The name of the manufacturer of the machine. +
+ system.product (string) +  No + The product name of the machine. +
+ system.hardware.version (string) +  No + The version of the machine. +
+ system.hardware.serial (string) +  No + The serial number of the machine. +
+ system.hardware.uuid (string) +  No + The unique ID of the machine. +
+ system.hardware.primary_video.vendor (int) +  No + The PCI vendor ID of the primary graphics card in the system. +
+ system.hardware.primary_video.product (int) +  No + The PCI device ID of the primary graphics card in the system. +
+ system.firmware.vendor (string) +  No + The firmware vendor. +
+ system.firmware.version (string) +  No + The firmware version. +
+ system.firmware.release_date (string) +  No + The release date of the firmware. +
+ system.chassis.manufacturer (string) +  No + The manufacturer of the chassis. +
+ system.chassis.type (string) +  No + The chassis type of the machine. +

+ volume namespace +

+ This namespace is for device objects that represent storage + devices with a filesystem that can be mounted. Such device + objects will have the capability volume and + they will export the properties below. Note that device + objects can only have the volume capability + if they already have the capability block + and the property block.is_volume set to TRUE. +

Key (type)ValuesMandatoryDescription
+ volume.ignore (bool) +  YesThis is a hint to software higher in the stack + that this volume should be ignored. If TRUE, the volume + should be invisible in the UI and mount wrappers should + refuse to mount it on behalf on an unprivileged + user. This is useful for hiding e.g. firmware partitions + (e.g. bootstrap on Mac's) and OS reinstall partitions on + e.g. OEM systems. +
+ volume.is_mounted (bool) +  YesThis property is TRUE if and only if the volume is mounted
+ volume.is_mounted_read_only (bool) +  YesThis property is TRUE if and only if the volume is mounted and + the volume's file-system is read-only. +
+ volume.mount_point (string) + example: /media/compact_flash1 Yes (is blank only when volume.is_mounted is FALSE)A fully qualified path to the mount point of the volume
+ volume.fsusage (string) + example: filesystemYes + This property specifies the expected usage of the volume +
 filesystem The volume is a mountable filesystem
 partitiontable  + The volume contains a partitiontable. +
 raid The volume is a member of a raid set and not mountable
 other The volume is not mountable like a swap partition
 unused The volume is marked a unused or free
+ volume.fstype (string) + examples: ext3, vfatYes (is blank if the type is unknown)The specific type of either the file system or what the volume is used for, cf. volume.fsusage
+ volume.fsversion (string) + example: FAT32 Version number or subtype of the filesystem
+ volume.label (string) + example: 'Fedora Core 1.90' Yes (is blank if no label is found)The label of the volume
+ volume.uuid (string) + example: 4060-6C11Yes (is blank if no UUID is found)The Universal Unique Identifer for the volume
+ volume.is_disc (bool) +  YesIf the volume stems from an optical disc, this + property is true and the device object will also have + the capability volume.disc +
+ volume.block_size (string) +  No + The block size of the volume +
+ volume.num_blocks (string) +  No + Number of blocks on the volume +
+ volume.size (uint64) +  No + Size of the volume in bytes +
+ volume.is_partition (bool) +  Yes + If the volume stems from a partition on e.g. a hard + disk, this property is set to TRUE. +
+ volume.linux.is_device_mapper (bool) +  Yes, but only on Linux + If the volume stems from the Linux Device Mapper this property is set to TRUE. +
+ volume.partition.number (int) +   + If, and only if, volume.is_partition + is set to TRUE. + + The number of the partition. +
+ volume.partition.label (string) +   + If, and only if, volume.is_partition + is set to TRUE. + + Label of partition. Only available for "apm" and "gpt" + partition tables. Note that this is not the same as the + file system label defined in volume.label. +
+ volume.partition.uuid (string) +   + If, and only if, volume.is_partition + is set to TRUE. + + The UUID or GUID of the partition table entry. Only available for + "gpt" partition tables. +
+ volume.partition.scheme (string) +   + If, and only if, volume.is_partition + is set to TRUE. + + The scheme of the partition table this entry is part of. + Note that this is not necessarily the same as + storage.partitioning_scheme as + some partition tables can embed other partition tables. +
 mbr  + Master Boot Record +
 embr  + Extended Master Boot Record +
 gpt  + GUID Partition Table as defined by EFI +
 apm  + Apple Partition Map +
+ volume.partition.type (string) +   + If, and only if, volume.is_partition + is set to TRUE. + + The type of the partition table entry. Depends on + volume.partition.scheme. +
 mbr and embr entries  + The hexadecimal encoding of the 8-bit partition type, see + http://www.win.tue.nl/~aeb/partitions/partition_types-1.html + for a list. For example the Linux partition type is represented + as the string "0x83". +
 gpt entries  + The GUID encoded as a string. See http://en.wikipedia.org/wiki/GUID_Partition_Table + for a list of well-known GUID's. +
 apm entries  + Defined in http://developer.apple.com/documentation/mac/Devices/Devices-126.html. + Also note that for FAT file systems, it appears that "DOS_FAT_32", "DOS_FAT_16" + and "DOS_FAT_12" are also recognized under Mac OS X (I've tested this too) cf. + http://lists.apple.com/archives/Darwin-drivers/2003/May/msg00021.html +
+ volume.partition.flags (strlist) +   + If, and only if, volume.is_partition + is set to TRUE. + + Flags conveying specific information about the partition + entry. Dependent on the partitioning scheme. +
 mbr and embr entries  + Only one flag, "boot", is defined. This is used by some BIOS'es and + boot loaders to populate a boot menu. It means that a partition is + bootable. +
 gpt entries  + Only the flag "required" is recognized. This corresponds to + bit 0 of the attibutes (at offset 48), meaning + "Required for the platform to function. The system cannot + function normally if this partition is removed. This + partition should be considered as part of the hardware of the + system, and if it is removed the system may not boot. It may + contain diagnostics, recovery tools, or other code or data that is + critical to the functioning of a system independent of any OS." +
 apm entries  + The following flags are recognized: + "allocated" if the partition is already allocated; and + "in_use" if the partition is in use; may be cleared after a system reset; and + "boot" if partition contains valid boot information; and + "allow_read" if partition allows reading; and + "allow_write"; if partition allows writing; and + "boot_code_is_pic"; if boot code is position independent +
+ volume.partition.media_size (uint64) + example: 500107862016 + If, and only if, volume.is_partition + is set to TRUE. + + If available, size of the current media or the fixed disk in the storage device. +
+ volume.partition.start (uint64) + example: 32256 + If, and only if, volume.is_partition + is set to TRUE. + + If available, the offset where the partition starts on the media or the fixed disk in the storage device. +

+ Device objects with this capability may emit the following + device conditions +

Condition NameParametersExampleDescription
+ VolumeMount + + block.device (string), + volume.mount_point (string) + + /dev/sda1, + /media/compact_flash + Emitted when a volume is mounted
+ VolumeUnmount + + block.device (string), + volume.mount_point (string) + + /dev/sda1, + /media/compact_flash + Emitted when a volume is unmounted
+ VolumeUnmountForced + + block.device (string), + volume.mount_point (string) + + /dev/sda1, + /media/compact_flash + + Emitted when a volume is forcibly unmounted because + the media backing the volume was removed. +

+ volume.disc namespace +

+ This namespace is for device objects that represent optical + discs, e.g. device objects with the capability + volume.disc. Such device objects will + also have the capability volume. +

Key (type)ValuesMandatoryDescription
+ volume.disc.has_audio (bool) +  YesIs true only if the disc contains audio
+ volume.disc.has_data (bool) +  YesIs true only if the disc contains data
+ volume.disc.is_vcd (bool) +  YesIs true only if the disc is a Video CD
+ volume.disc.is_svcd (bool) +  YesIs true only if the disc is a Super Video CD
+ volume.disc.is_videodvd (bool) +  YesIs true only if the disc is a Video DVD
+ volume.disc.is_appendable (bool) +  YesIs true only if it's possible to write additional data
+ volume.disc.is_blank (bool) +  YesIs true only if the disc is blank
+ volume.disc.is_rewritable (bool) +  YesIs true only if the disc is rewritable
+ volume.disc.capacity (uint64) +  NoCapacity of disc, in bytes
+ volume.disc.type (string) +  YesThis property specifies the physical type of the disc
 cd_rom CD-ROM disc
 cd_r CD-R disc
 cd_rw CD-RW disc
 dvd_rom DVD-ROM disc
 dvd_ram DVD-RAM disc
 dvd_r DVD-R disc
 dvd_rw DVD-RW disc
 dvd_plus_r DVD+R disc
 dvd_plus_rw DVD+RW disc
 bd_rom BD-ROM disc
 bd_r BD-R disc
 bd_re BD-RE disc
 hddvd_rom HD DVD-ROM disc
 hddvd_r HD DVD-R disc
 hddvd_rw HD DVD-Rewritable disc
 unknown Unknown type or lack of support from drive to determine the type

+ storage namespace +

+ This namespace is used to describe storage devices + and their capabilities. Such device objects will have the + capability storage and + they will export the properties below. Note that device + objects can only have the storage capability + if they already got capability block and the + property block.is_volume set to FALSE. + One significant between the storage and + block namespace is that the properties + exported in the storage represents + constant vital product information, whereas the properties + in the block namespace represent + variable system-dependent information. +

Key (type)ValuesMandatoryDescription
+ storage.bus (string) +  YesInterface the storage device is attached to
 ide IDE or ATA interface
 usb USB interface
 ieee1394 IEEE 1394 interface
 scsi SCSI interface
 sata SATA interface
 platform Legacy device that is part of the platform
 linux_raid Linux MD (multi disk) RAID device
    
+ storage.drive_type (string) +  Yes + The type of the drive. Note that it may not be + possible to probe for some of these properties so in + some cases memory card readers may appear as + harddisks. Device information files can be used to + override this value. +
 disk The device is a harddisk
 cdrom  + The device is an optical drive. The device object will also have the capability storage.cdrom in this case. +
 floppy The device is a floppy disk drive
 tape The device is a tape drive
 compact_flash The device is a card reader for Compact Flash memory cards
 memory_stick The device is a card reader for MemoryStick memory cards
 smart_media The device is a card reader for SmartMedia memory cards
 sd_mmc The device is a card reader for SecureDigital/MultiMediaCard memory cards
    
+ storage.removable (bool) +  YesMedia can be removed from the storage device
+ storage.removable.media_available (bool) +  Yestrue, if and only if, media have been detected in storage device
+ storage.removable.media_size (uint64) +  YesSize of media in storage device. Available only if media have been detected in storage device.
+ storage.removable.support_async_notification (bool) +  YesWhether the drive reports asynchronous notification for media change.
+ storage.partitioning_scheme (string) +  Only when media is inserted and is partitionedThe partitioning scheme of the media.
 mbr Master Boot Record partitioning scheme used in most PC's
 gpt GUID Partitioning Table as defined by UEFI
 apm Apple Partition Map, used in non-Intel Apple computers
+ storage.size (uint64) +  Nosize in bytes of the storage device - only meaningful if storage.removable is FALSE
+ storage.requires_eject (bool) +  YesThe eject ioctl is required to properly eject the media
+ storage.hotpluggable (bool) +  YesThe storage device can be removed while the system is running
+ storage.media_check_enabled (bool) +  YesIf this property is set to FALSE then HAL will not continuosly poll for media changes.
+ storage.automount_enabled_hint (bool) +  YesThis property is a hint to desktop file managers that they shouldn't automount volumes of the storage device when they appear.
+ storage.no_partitions_hint (bool) +  Yes + This property is a hint to programs that maintain the + /etc/fstab file to signal, when + TRUE, that the storage drive (such as floppy or + optical drives) is used for media with no partition + table so an entry can be added ahead of media + insertion time. Note that this is only a hint; media + may be inserted that has partition tables that the + kernel may respect. Conversely, when this is FALSE + media without partition tables may be inserted (an + example is a Compact Flash card; this media is normally + formatted with a PC style partition table and a single + FAT partition. However, it may be formatted with just + a single FAT partition and no partition table). +
+ storage.originating_device (string) +  Yes + This contains the UDI of the device object + representing the device or blank if + there is no such device. +
+ storage.model (string) +  YesThe name of the drive
+ storage.vendor (string) +  YesThe vendor of the drive
+ storage.serial (string) +  NoThe serial number of the drive
+ storage.firmware_revision (string) +  NoThe revision of the firmware of the drive
+ storage.icon.drive (string) +  No + Name of icon to use for displaying the drive. The name + must comply with freedesktop.org icon-theme specification + and must not be an absolute path. + This property exists such that e.g. OEM's can install + icons in /usr/share/icons/hicolor + a device information file matching their device. +
+ storage.icon.volume (string) +  No + Name of icon to use for displaying volumes from the drive. + The name must comply with freedesktop.org icon-theme + specification and must not be an absolute path. + This property exists such that e.g. OEM's can install + icons in /usr/share/icons/hicolor + a device information file matching their device. +

+ storage.cdrom namespace +

+ This namespace is used to describe optical storage drives + and their capabilities.Such device objects will have the + capability storage.cdrom and + they will export the properties below. Note that device + objects can only have the storage.cdrom capability + if they already got the capability storage. +

Key (type)ValuesMandatoryDescription
+ storage.cdrom.cdr (bool) +  YesTRUE when the optical drive can write CD-R discs
+ storage.cdrom.cdrw (bool) +  YesTRUE when the optical drive can blank and write to CD-RW discs
+ storage.cdrom.dvd (bool) +  YesTRUE when the optical drive can read DVD-ROM discs
+ storage.cdrom.dvdr (bool) +  YesTRUE when the optical drive can write to DVD-R discs
+ storage.cdrom.dvdrw (bool) +  YesTRUE when the optical drive can blank and write to DVD-RW discs
+ storage.cdrom.dvdram (bool) +  YesTRUE when the optical drive can write to DVD-RAM discs
+ storage.cdrom.dvdplusr (bool) +  YesTRUE when the optical drive can write to DVD+R discs
+ storage.cdrom.dvdplusrw (bool) +  YesTRUE when the optical drive can blank and write to DVD+RW discs
+ storage.cdrom.dvdplusrwdl (bool) +  YesTRUE when the optical drive can blank and write to DVD+RW Dual-Layer discs
+ storage.cdrom.dvdplusrdl (bool) +  YesTRUE when the optical drive can write to DVD+R Dual-Layer discs
+ storage.cdrom.bd (bool) +  YesTRUE when the optical drive can read Blu-ray ROM discs
+ storage.cdrom.bdr (bool) +  YesTRUE when the optical drive can write to Blu-ray Recordable discs
+ storage.cdrom.bdre (bool) +  YesTRUE when the optical drive can write to Blu-ray Rewritable discs
+ storage.cdrom.hddvd (bool) +  YesTRUE when the optical drive can read Read-only HD DVD discs
+ storage.cdrom.hddvdr (bool) +  YesTRUE when the optical drive can write to Write-once HD DVD discs
+ storage.cdrom.hddvdrw (bool) +  YesTRUE when the optical drive can write to Rewritable HD DVD discs
+ storage.cdrom.mrw (bool) +  YesTRUE when the optical drive can read MRW (Mount Rainier Rewrite) discs
+ storage.cdrom.mrw_w (bool) +  YesTRUE when the optical drive can write MRW (Mount Rainier Rewrite) discs
+ storage.cdrom.mo (bool) +  NoTRUE when the optical drive is a MO (Magneto Optical) device.
+ storage.cdrom.support_multisession (bool) +  YesTRUE if the drive can read multisession discs
+ storage.cdrom.support_media_changed (bool) +  YesTRUE if the drive can generate media changed events
+ storage.cdrom.read_speed (int) +  YesThe maximum reading speed, in kb/s
+ storage.cdrom.write_speed (int) +  YesThe maximum writing speed, in kb/s
+ storage.cdrom.write_speeds (strlist) +  NoBy the device supported write speeds in kb/s

+ storage.linux_raid namespace +

+ This namespace is used to describe logical Software RAID + devices under Linux using the md driver. By + and large, all the same properties under + the storage name space applies except + that storage.serial is set to the UUID of + the RAID set, storage.firmware_version is + set to the version of the md driver and the + value of storage.hotpluggable is taken from + the enclosing drive of the first RAID component + encountered. In addition, the following properties are + available. +

Key (type)ValuesMandatoryDescription
+ storage.linux_raid.level (string) +  Yesthe RAID level of the device as reported by the kernel (linear, raid0, raid1, raid4, raid5, raid6, raid10)
+ storage.linux_raid.sysfs_path (string) +  Yessysfs path of device, e.g. /sys/block/md0
+ storage.linux_raid.num_components (int) +  YesNumber of components in the RAID array
+ storage.linux_raid.num_components_active (int) +  Yes + Number of active components in the RAID array. If less + than storage.linux_raid.num_components + it means that the RAID array is running in degraded + mode. +
+ storage.linux_raid.components (strlist) +  YesUDI's of the volumes constituting the array.
+ storage.linux_raid.is_syncing (bool) +  YesTRUE if, and only if, the array is currently syncing
+ storage.linux_raid.sync.action (string) +  only if .is_syncing is TRUEThe syncing mechanism as reported by the kernel (idle, resync, check, repair, recover)
+ storage.linux_raid.sync.progress (double) +  only if .is_syncing is TRUENumber between 0 and 1 representing progress of the sync operation. This is updated regulary when syncing is happening.
+ storage.linux_raid.sync.speed (uint64) +  only if .is_syncing is TRUESpeed of the sync operation, in kB/s. This is updated regulary when syncing is happening.

+ net namespace +

+ This namespace is used to describe networking devices and + their capabilities.Such device objects will have the + capability net and they will export the + properties below. This namespace only describe the generic + aspect of networking devices; specific networking technologies + such as IEEE 802.3, IEEE 802.11 and Bluetooth have separate + namespaces. +

Key (type)ValuesMandatoryDescription
+ net.address (string) +  YesHardware address as a string. Is hardware dependant
+ net.arp_proto_hw_id (string) +  YesARP protocol hardware identifier
+ net.interface (string) +  YesName of the interface; may change if an interface is + renamed +
+ net.interface_up (bool) +  NoWhether the interface is up
+ net.linux.ifindex (string) +  Yes (only on Linux)Index of the interface
+ net.originating_device (string) +  YesUDI of the device the network device is bound to.
+ net.media (string) + example: EthernetYesTextual description of the networking media

+ net.80203 namespace +

+ Ethernet networking devices is described in this namespace + for device objects with the capability + net.80203. + Note that device + objects can only have the net.80203 capability + if they already have the capability net. +

Key (type)ValuesMandatoryDescription
+ net.80203.link (bool) +   + Only if the net.80203 capability is set + and net.interface_up is + TRUE. + True if the ethernet adaptor is connected to a + another transceiver. NOTE: property not implemented yet. +
+ net.80203.rate (uint64) + example: 100000000 + Only if the net.80203 capability is set + and net.80203.link is + TRUE. + Bandwidth of connection, in bits/s. NOTE: property not + implemented yet. +
+ net.80203.mac_address (uint64) + example: 0x0010605d8ef4 + Only if the net.80203 is set + 48-bit address

+

+ net.80211 namespace +

+ Wireless ethernet networking devices is described in this namespace + for device objects with the capability + net.80211. + Note that device + objects can only have the net.80211 capability + if they already have the capability net. +

Key (type)ValuesMandatoryDescription
+ net.80211.mac_address (uint64) + example: 0x0010605d8ef4 + Only if the net.80211 capability is set + 48-bit address

+

+ net.bluetooth namespace +

+ Bluetooth ethernet networking devices is described in this namespace + for device objects with the capability + net.bluetooth. + Note that device + objects can only have the net.bluetooth capability + if they already have the capability net. +

Key (type)ValuesMandatoryDescription
+ net.bluetooth.mac_address (uint64) + example: 0x0010605d8ef4 + Only if the net.bluetooth capability is set + 48-bit address
+ net.bluetooth.name (string) + example: Network Access Point Service + Only if the net.bluetooth capability is set and Bluez is being used. + Displayable Name for network connection
+ net.bluetooth.uuid (string) + example: 00001116-0000-1000-8000-00805f9b34fb + Only if the net.bluetooth capability is set and Bluez is being used. + Universal Unique Identifier for network connection

+

+ net.irda namespace +

+ IrDA (Infrared Data Association) Networking devices are described in + this namespace for device objects with the capability + net.irda. + Note that device objects can only have the net.irda + capability if they already have the capability net. +

+

+ net.80211control namespace +

+ Control devices for Wireless ethernet networking devices are described in + this namespace for device objects with the capability + net.80211control. + Note that device objects can only have the net.80211control + capability if they already have the capability net. + Warning: You should know what you do if you touch this devices. They are + not always stable and can cause (kernel) crashes (on linux). +

+

+ input namespace +

+ This namespace is concerned with human input devices such as + keyboards, mice, pointing devices and game controllers. If a + device object has the capability input then + the following properties are available +

Key (type)ValuesMandatoryDescription
+ input.device (string) +  YesSpecial device file for recieving input events
+ input.x11_driver (string) + e.g. "evdev"NoX11 input driver to use

+ input.keys namespace +

+ The input device have keys that can be pressed. No namespace + specific properties. +

+ input.keypad namespace +

+ The input device have keypad keys. No namespace + specific properties. +

+ input.keyboard namespace +

+ The input device is a normal keyboard. No namespace specific + properties. +

+ input.mouse namespace +

+ The input device is a mouse. No namespace specific + properties. +

+ input.switch namespace +

+ The input device is a switch, e.g. it has buttons with + state. No namespace specific properties. +

+ input.joystick namespace +

+ The input device is a joystick. No namespace specific + properties. +

+ input.tablet namespace +

+ The input device is a tablet. No namespace specific + properties. +

+ input.keymap namespace +

+ Device objects with the capability input.keymap + provide facilities to remap keyboard buttons. +

Key (type)ValuesMandatoryDescription
+ input.keymap.data (strlist) + e.g. "e017:brightnessup"No + The scancode is represented in hex and the keycode name as + as string. The keycode name is not case sensitive. + On Linux, the keycode name should be the same constant as + present in /usr/include/linux/input.h with the 'KEY_' + prefix removed, e.g. 'KEY_SLEEP' -> 'sleep'. + You can append as many input.keymap.data + values as there are keys to remap. +

+ input.xkb namespace +

+ Device objects with the capability input.keys + can provide information about their physical layout. +

Key (type)ValuesMandatoryDescription
+ input.xkb.rules (string) + e.g. "base"Yes + XKB rules file to use; 'base' is standard, but 'xorg' + or 'xfree86' may be needed for backwards compatibility + with very old versions of XKB data. +
+ input.xkb.model (string) + e.g. "logicdp"Yes + Physical keyboard model (e.g. Logitech Cordless Freedom + Pro), as given to XKB. +
+ input.xkb.layout (string) + e.g. "us"Yes + Keyboard layout (as engraved on the keys). +
+ input.xkb.variant (string) + e.g. "nodeadkeys"No + Variant of the XKB layout (if any) to use. +
+ input.xkb.options (strlist) + e.g. "ctrl:nocaps"No + Options to be provided to XKB. +

+ pcmcia_socket namespace +

+ Device objects with the capability pcmcia_socket + represent bridge devices (the actual bus of the device may differ) + that PCMCIA cards can be attached to. The following properties are + available. +

Key (type)ValuesMandatoryDescription
+ pcmcia_socket.number (int) +  YesPCMCIA socket number, starting from zero

+ printer namespace +

+ Device objects with the capability printer + represent printers. The following properties are available. +

Key (type)ValuesMandatoryDescription
+ printer.device (string) +  YesTODO
+ printer.vendor (string) +  YesTODO
+ printer.product (string) +  YesTODO
+ printer.serial (string) +  YesTODO
+ printer.description (string) +  YesTODO
+ printer.commandset (strlist) +  NoList of supported commands / printer languages.

+ portable_audio_player namespace +

+ Device objects with the capability + portable_audio_player represent portable + audio players that can be attached to a computer to exchange + files. They can also playback audio. Sometimes they can also + record audio. This capability can't, in general, be reliably + probed from the hardware so the information needs to be merged + from either device information files or callouts. Therefore + this capability should be merged on the appropriate device + object that represents the addressable piece of hardware that + is the portable music player; for USB devices this would be + the device object representing the appropriate USB + interface. The following properties are available: +

Key (type)ValuesMandatoryDescription
+ portable_audio_player.access_method.protocols (strlist) + example: storage ipod mtp pde iriver karmaNo + Indicates transfer protocols that this device can speak. + storage indicates USB Mass Storage (UMS) is an access + protocol. ipod indicates UMS plus an iTunes-style database. + mtp indicates a device using Microsoft's Media Transfer Protocol. + Arbitrary values for newer or obscure protocols are allowed but + entities providing this information should try to ensure that + they are not duplicating protocols under a different name. +
+ portable_audio_player.access_method.drivers (strlist) + example: libgpod, libmtp, libnjb, libifp, libkarmaNo + Indicates installed device driver libraries that can + handle this device. These drivers can export information + in portable_audio_player.[drivername] sub-namespaces. + Can also be used by libraries or programs providing extra device information + to indicate the presence of this information in the appropriate sub-namespace. +
+ portable_audio_player.[drivername].protocol (strlist) + example: mtpYes + This entry is required for drivers listed in + portable_audio_player.access_method.drivers. Indicates which + protocol in portable_audio_player.access_method.protocols + a particular driver will use. If the driver is providing information only, this + should be set to information. +
+ portable_audio_player.output_formats (strlist) + example: audio/mpeg audio/x-ms-wmaYes + A string list of MIME-types representing the kind of audio + formats that the device can play back. +
+ portable_audio_player.input_formats (strlist) + example: audio/x-wavYes + A string list of MIME-types representing the kind of audio + formats that the device can record. If empty, it means that + the device is not capable of recording. +
+ portable_audio_player.folder_depth (int) + example: 1 (If the device only supports one sub-folder)No + If portable_audio_player.access_method.protocols contains "storage", + this tells applications exactly how deep of directory hierarchies + files should be placed in. If all files are put in a + sub-folder (with the audio_folders property), only the depth within + that sub-folder should be entered here. If the device does not have + a limit, do not set this property. +
+ portable_audio_player.audio_folders (strlist) + example: music/ voice/ linein/No + If portable_audio_player.access_method.protocols contains "storage", + this may contain a string list of folders in which music + can be found. Paths are relative to the mount point of the + device. If there is one or more entry in this property, the + first one is where files will be written to by applications. + Do not enter a folder and a parent of that folder. + If the device places files in its root directory, then do not + set this property. +
+ portable_audio_player.playlist_format (strlist) + example: audio/x-mpegurl audio/x-somethingelseNo + A string list of the MIME-type of the playlist formats accepted by + this device. Leave blank if none. +
+ portable_audio_player.playlist_path (string) + examples: playlists/%File or Playlist.m3uNo + Set to the path to which playlists should be written. Leave + blank if playlist files are not supported. If the device supports a single playlist with a specific name/path, + set this to the path relative to the mount point that it should be saved to. If it supports multiple + playlists, use the %File variable as needed. Applications are responsible for substituting %File with the + desired playlist file name, noting that it's use in this string is optional. +

+ alsa namespace +

+ Device objects with the capability alsa + represent all the streams available through ALSA on a soundcard. +

Key (type)ValuesMandatoryDescription
+ alsa.card (int) +  Yes + Card number in system as registered by ALSA. +
+ alsa.card_id (string) + + Examples: I82801DBICH4, MP3 + No + Textual description of the card. +
+ alsa.device (int) +  Yes + Device number assigned by ALSA for a current card. +
+ alsa.device_file (string) +  Yes + The device node to access the ALSA device. +
+ alsa.device_id (string) + + Examples: Intel 82801DB-ICH4 MIC2 ADC + No + Textual description of the specific device for a card +
+ alsa.device_pcm_class (string) +  No + The PCM class of the device. +
 generic  + A standard PCM sound device (SND_PCM_CLASS_GENERIC). +
 multi  + A multichannel device PCM sound device (SND_PCM_CLASS_MULTI) which + e.g. contains a generic and a modem device. +
 digitizer  + A PCM digitizer device (SND_PCM_CLASS_DIGITIZER). +
 modem  + A PCM modem device (SND_PCM_CLASS_MODEM). +
 unknown  + The value is 'unknown' if the kernel provide no information about the + PCM device class of the device (e.g. the file pcm_class is missing). +
 none  + The value is 'none' if this there is no PCM class for this device. +
+ alsa.originating_device (string) +  Yes + UDI of the device the ALSA device is bound to. +
+ alsa.type (string) +  Yes + The type of the stream. +
  + control +   + Stream is control device. +
  + capture +   + Stream is capture device. +
  + midi +   + Stream is MIDI device. +
  + playback +   + Stream is playback device. +
  + unknown +   + The type of the device is unknown. +
  + hw_specific +   + This is a hardware specific device (as e.g. from snd_fm801 for Fortemedia FM801 + PCI Audio). The driver can use it freely for purposes that are not covered by + standard ALSA API. +
  + timer +   + Stream is the global ALSA timer device. + This means, the device is for all ALSA devices/cards. +
  + sequencer +   + Stream is the global ALSA sequencer device. + This means, the device is for all ALSA devices/cards. +
  + unknown +   + Stream is unknown device. +

+ oss namespace +

+ Device objects with the capability oss + represent all the streams available through OSS on a soundcard. + OSS devices could be emulated by ALSA. +

Key (type)ValuesMandatoryDescription
+ oss.card (int) +  Yes + Card number in system as registered by OSS (and/or ALSA). +
+ oss.card_id (string) + + Examples: I82801DBICH4, MP3 + No + Textual description of the card. +
+ oss.device (int) +  Yes + Device number assigned by OSS/ALSA for a current card. +
+ oss.device_file (string) +  Yes + The device node to access the OSS device. +
+ oss.device_id (string) + + Examples: Intel 82801DB-ICH4 MIC2 ADC + No + Textual description of the specific device for a card +
+ oss.originating_device (string) +  Yes + UDI of the device the OSS device is bound to. +
+ oss.type (string) +  Yes + The type of the stream. +
  + mixer +   + Stream is control/mixer device. +
  + pcm +   + Stream is PCM device. +
  + midi +   + Stream is MIDI device. +
  + sequencer +   + Stream is a global OSS sequencer device. + This means, the device is for all OSS devices/cards. +
  + unknown +   + Stream is unknown device. +

+ camera namespace +

+ Device objects with the capability camera + represent digital still cameras that can be attached to a + computer to exchange files. This does not include card readers + for memory cards used for cameras. This capability can't, in + general, be reliably probed from the hardware so the + information needs to be merged from either device information + files or callouts. Therefore this capability should be merged + on the appropriate device object that represents the + addressable piece of hardware that is the digital still + camera; for USB devices this would be the device object + representing the appropriate USB interface. The following + properties are available: +

Key (type)ValuesMandatoryDescription
+ camera.access_method (string) +  YesThis property defines how the device is accessed
 storage  + The device is accessed as a Mass Storage device + through a kernel driver. Application Developers + should descent down the device object tree to find the + device object of capability + storage in order to access the + device. +
 user  + The device is accessed from userspace through + a userspace driver. +
    
+ camera.libgphoto2.support (bool) +  No + If true, the device is supported by a userspace driver + from the libgphoto2 project. +

+ scanner namespace +

+ Device objects with the capability scanner + represent image scanners. This capability should be merged + on the appropriate device object that represents the + addressable piece of hardware that is the digital still + camera; for USB devices this would be the device object + representing the appropriate USB interface. The following + properties are available: +

Key (type)ValuesMandatoryDescription
+ scanner.access_method (string) +  YesThis property defines how the device is accessed
 proprietary  + The device is accessed from userspace through + a userspace driver such as SANE. +
    

+ laptop_panel namespace +

+ Device objects with the capability laptop_panel + represent devices capable of changing the brightness of the display. +

Key (type)ValuesMandatoryDescription
+ laptop_panel.num_levels (int) +  Yes + The brightness levels supported by the adaptor. +
+ laptop_panel.access_method (string) +  Yes + The access method to use in scripts, e.g. pmu, toshiba, ibm, sony. +
+ laptop_panel.brightness_in_hardware (bool) +  No + On some laptops, the brightness control is all done in hardware + but the hardware also synthesizes keypresses when the + brightness is changed. + If this key is set true, then any power manager software should + not attempt to set any new values on brightness keypress, as it + may cause the panel to flash uncontrollably. +

+ The following methods exist on the interface + org.freedesktop.Hal.Device.LaptopPanel. +

Method (parameter types)ParametersMandatoryDescription
+ SetBrightness (integer) + + The hardware brightness state, which should be between 0 and + laptop_panel.num_levels - 1. + No + This method adjusts the brightness on an laptop screen. + The values are returned as hardware values rather than + percentages as we cannot easily to floating point rounding in + shell code and therefore use the raw values to prevent integer + rounding errors. +
+ integer GetBrightness (void) + + Returns the hardware brightness state, which should be + between 0 and laptop_panel.num_levels - 1. + No + This method gets the hardware brightness of the laptop screen, + which we may need to do fairly regually on hardware that + changes the values in hardware without a software event. +

+ ac_adapter namespace +

+ Device objects with the capability ac_adapter + represent all the devices capable of powering the system from AC power +

Key (type)ValuesMandatoryDescription
+ ac_adapter.present (bool) +  Yes + The state of the adapter, i.e. whether it is providing power to + the unit from mains power. +

+ battery namespace +

+ Device objects with the capability battery + represent all the devices having some battery (in many cases - + rechargeable) inside. +

Key (type)ValuesMandatoryDescription
+ battery.present (bool) +  Yes + This is present as some smart batteries can have acpi/pmu + entries, and be physically missing. +
+ battery.type (string) +  Yes + This property defines the type of the device holding the + battery. This property is defined for the development + simplicity - battery indicators can use it to find the + proper iconic representation. +
  + pda +   + The device containing the battery is a personal digital + assistant, e.g. a device that looks like a handheld computer + to do specific tasks such as keeping notes or containing + a personal database +
  + ups +   + A battery powered power supply that is + guaranteed to provide power to a computer in the event of + interruptions in the incoming electrical power. Most of the + time this is an external device. +
  + primary +   + The battery is a primary power source for the system - an + example are laptop batteries. +
  + mouse +   + The device containing the battery is a mouse. +
  + keyboard +   + The device containing the battery is a keyboard. +
  + keyboard_mouse +   + The device containing the battery is a combined mouse and keyboard. +
  + camera +   + The device containing the battery is a camera. +
  + usb +   + The device containing the battery is a generic usb device. +
  + unknown +   + The device containing the battery is not covered by other types. +
+ battery.charge_level.unit (string) + Examples: + mWh, + percent + No + The physical unit used by the charge level properties + (maximum and current). In many cases, this property is + omitted - which indicates that the charge properties + are measured in some unknown units. + The units should never be mAh as this is not a measurement + of charge. +
+ battery.charge_level.design (int) +  Yes + The maximum level of charge the device was designed for. + Measured in "battery.charge_level.unit" + units. +
+ battery.charge_level.last_full (int) +  Yes + The maximum level of charge the device could hold the last + time it was full. + Measured in "battery.charge_level.unit" + units. +
+ battery.charge_level.current (int) +  Yes + The current level of charge which the device can is holding. + Measured in "battery.charge_level.unit" + units. +
+ battery.charge_level.rate (int) +  No + The discharge/charge rate measured + in "battery.charge_level.unit" + units per second. +
+ battery.charge_level.warning (int) +  No + Once the charge level of the battery drops below this value its + state changes to 'warning'. + Measured in "battery.charge_level.unit" + units. +
+ battery.charge_level.low (int) +  No + Once the charge level of the battery drops below this value its + state changes to 'low'. + Measured in "battery.charge_level.unit" + units. +
+ battery.charge_level.granularity_1 (int) +  No + Granularity value one of the battery measured + in "battery.charge_level.unit" + units . +
+ battery.charge_level.granularity_2 (int) +  No + Granularity value two of the battery measured + in "battery.charge_level.unit" + units. +
+ battery.reporting.unit (string) + Examples: + mWh, + mAh, + percent + No + The physical unit used by the charge level properties + (maximum and current) as reported by the hardware. + In many cases, this property is omitted - which indicates + that the charge properties are measured in some unknown units. +
+ battery.reporting.design (int) +  Yes + The maximum level of charge the device was designed for, + as reported by the hardware. + Measured in "battery.reporting.unit" + units. +
+ battery.reporting.last_full (int) +  No + The maximum level of charge the device could hold the last + time it was full, as reported by the hardware. + Measured in "battery.reporting.unit" + units. +
+ battery.reporting.current (int) +  No + The current level of charge which the device is holding, + as reported by the hardware. + Measured in "battery.reporting.unit" + units. +
+ battery.reporting.rate (int) +  No + The discharge/charge rate as reported by the hardware measured + in "battery.reporting.unit" + units per second. +
+ battery.reporting.warning (int) +  No + Once the hardware charge level of the battery drops below + this value its state changes to 'warning'. + Measured in "battery.reporting.unit" + units. +
+ battery.reporting.low (int) +  No + Once the hardware charge level of the battery drops below + this value its state changes to 'low'. + Measured in "battery.reporting.unit" + units. +
+ battery.reporting.granularity_1 (int) +  No + Hardware granularity value one of the battery measured + in "battery.reporting.unit" + units . +
+ battery.reporting.granularity_2 (int) +  No + Hardware granularity value two of the battery measured + in "battery.reporting.unit" + units. +
+ battery.charge_level.capacity_state (string) + + Examples: ok, critical + No + The capacity state of the battery. +
+ battery.voltage.unit (string) + + Examples: mV + No + The physical measurement unit used by the voltage properties + (design and current). +
+ battery.voltage.design (int) +  Yes + The voltage level for which the battery is designed for. + Measured in "battery.voltage.unit" + units. +
+ battery.voltage.current (int) +  Yes + The voltage level currently emitted by the battery. + Measured in "battery.voltage.unit" + units. +
+ battery.alarm.unit (string) + + Examples: mWh, mAh + No + The physical measurement unit used by the alarm property. +
+ battery.alarm.design (int) +  No + Once the charge level of the battery drops below this value + its state changes to 'alarm'. + Measured in "battery.alarm.unit" + units. +
+ battery.remaining_time (int) +  No + Remaining time, in seconds, that the battery can provide + power (if discharging) or the time until charged (if charging). + This is an estimate and may be imprecise. + This key is not present for invalid data. +
+ battery.remaining_time.calculate_per_time (bool) +  No + If this property is true the + battery.remaining_time becomes guessed from + battery.charge_level.current and time. +
+ battery.charge_level.percentage (int) +  No + Charge, normalised to percent. This is useful if an application + does not want to process the raw values and do all the extra + checks on the result. This key is not present for invalid data. +
+ battery.is_rechargeable (bool) +  No + True if the battery unit is rechargeable, false if its is + one-time (disposable after one usage). +
+ battery.rechargeable.is_charging (bool) +   + Only if battery.is_rechargeable is TRUE + + TRUE if, and only if, the battery is charging. +
+ battery.rechargeable.is_discharging (bool) +   + Only if battery.is_rechargeable is TRUE + + TRUE if, and only if, the battery is discharging. +
+ battery.command_interface (string) +  No + The abstract name allowing daemons and/or user-level apps + to distinguish some groups of devices having similar + programming interface. Introduced mostly for the daemons' + coding simplicity. +
+ battery.vendor (string) +  No + Vendor of the battery. +
+ battery.model (string) +  No + Make of the battery. +
+ battery.reporting.technology (string) + example: LIONNo + The technology of the battery as reported by the hardware. +
+ battery.technology (string) + + lead-acid, lithium-ion, lithium-polymer, + nickel-metal-hydride, unknown + No + The technology of the battery processed to a few standard types. + This key is needed as the hardware often does not specify the + description text for a battery, and so we have to calculate it + from the output of battery.reporting.technology. +
+ battery.serial (string) +  No + A string uniquely identifying the instance of the battery; + it will be different for two (otherwise) identical batteries. +

+ button namespace +

+ Device objects with the capability button + represent the devices capable of providing a state to the system. +

Key (type)ValuesMandatoryDescription
+ button.type (string) +  NoThe type of button
 lid  + The switch on a laptop that senses whether the lid is + open or closed +
 power The main power button on the computer.
 sleep  + The sleep button on a computer capable of putting the computer + into a suspend state +
+ button.has_state (bool) +  noTrue if the button maintains state, e.g. can toggled on/off
+ button.state.value (bool) +   + Only when button.has_state is + TRUE + State of the button, TRUE if it is enabled

+ Device objects with this capability may emit the following events. +

Condition NameParametersExampleDescription
+ ButtonPressed + + button.type (string) + sleepEmitted when a button is pressed

+ processor namespace +

+ Device objects with the capability processor + represent CPU's in the system. +

Key (type)ValuesMandatoryDescription
+ processor.number (int) +  Yes + The internal processor number in the system, starting from zero +
+ processor.can_throttle (bool) +  No + Whether the processor supports throttling to decrease it's + own clock speed +
+ processor.maximum_speed (long) + example: 2200NoThe maximum speed of the processor in units of MHz

+ light_sensor namespace +

+ Device objects with the capability sensor + represent light sensors in the system. +

Key (type)ValuesMandatoryDescription
+ light_sensor.sensor_locations (strlist) +  YesThe locations of the sensors
+ light_sensor.num_sensors (int) +  YesNumber of physical sensors
+ light_sensor.num_levels (int) +  YesThe number of levels of the sensors

+ power_management namespace +

+ Keys with the prefix power_management + provide information about power management supported by + the system. +

Key (type)ValuesMandatoryDescription
+ power_management.type (string) + Examples: + apm, + acpi, + pmu + Yes + The power management subsystem used on the computer. +
+ power_management.can_suspend (bool) +  Yes + If suspend support is compiled into the kernel. + NB. This may not mean the machine is able to suspend + successfully. +
+ power_management.can_suspend_hybrid (bool) +  Yes + If the system is capable of hybrid suspend. +
+ power_management.can_hibernate (bool) +  Yes + If hibernation support is compiled into the kernel. + NB. This may not mean the machine is able to hibernate + successfully. +
+ power_management.is_powersave_set (bool) +  Yes + Is the last value passed to the SetPowerSave method. +
+ power_management.quirk.s3_bios (bool) +  NoUse the S3_BIOS kernel command for suspend.
+ power_management.quirk.s3_mode (bool) +  NoUse the S3_MODE kernel command for suspend.
+ power_management.quirk.dpms_suspend (bool) +  NoSuspend the video card via DPMS on suspend.
+ power_management.quirk.vga_mode_3 (bool) +  NoReset the VGA text mode to mode 3 on resume.
+ power_management.quirk.dpms_on (bool) +  NoReactivate the screen with DPMS on resume.
+ power_management.quirk.vbe_post (bool) +  NoRun the VGA BIOS Power On Self Test (POST) on resume.
+ power_management.quirk.vbestate_restore (bool) +  NoSave the VGA BIOS state before suspend, and restore it on resume.
+ power_management.quirk.vbemode_restore (bool) +  NoSave the VGA BIOS mode before suspend, and restore it on resume.
+ power_management.quirk.radeon_off (bool) +  NoTurn off the Radeon DAC off before suspend.
+ power_management.quirk.reset_brightness (bool) +  NoReset the brightness state after resume.
+ power_management.quirk.no_fb (bool) +  NoTrue if the machine can only suspend when not using framebuffer.
+ power_management.quirk.none (bool) +  NoNo quirks are necessary for suspend or resume.

+ tape namespace +

+ Device objects with the capability tape + represent tape devices. +

Key (type)ValuesMandatoryDescription
+ tape.major (int) + example: 254YesThe device's major number
+ tape.minor (int) + example: 0YesThe device's minor number

+ killswitch namespace +

+ Device objects with the capability killswitch + represent switches to turn a radio on and off. See also the section called “org.freedesktop.Hal.Device.KillSwitch interface”. +

Key (type)ValuesMandatoryDescription
+ killswitch.type (string) +  YesType of the kill switch
 wlan Kill switch is for turning Wireless networking on/off
 bluetooth Kill switch is for turning Bluetooth on/off
+ killswitch.access_method (string) +  YesHow HAL should program the switch

Misc. Properties

+ access_control namespace +

+ Device objects with the + capability access_control represent devices + where access to a special device file can be granted/revoked + to unprivileged users. +

Key (type)ValuesMandatoryDescription
+ access_control.file (string) + Example: /dev/snd/pcmC0D1pYes + Name of the special device file that access can be granted to. +
+ access_control.type (string) + Example: cdromYes + Type of access - only makes sense when PolicyKit + support is enabled; it's used by PolicyKit to compute + what privilege to check for by + prepending hal-device-file- to the + value. +
+ access_control.grant_user (strlist) + Example: "gdm, flumotion"No + List of UNIX user names to always grant access to the + device. This is useful for 3rd party system-wide + packages that need access to a device to function + properly. +
+ access_control.grant_group (strlist) + Example: "pvr_software, staff"No + List of UNIX group names to always grant access to the + device. This is useful for 3rd party system-wide + packages that need access to a device to function + properly. +

+ See also the section called “org.freedesktop.Hal.Device.AccessControl interface”. +

Deprecated Properties

+ The section represents properties that are deprecated and should be no longer used. + The properties/keys will be removed, but not before the date in the following table: +

Key (type)ReplacementRemove (date)Notes
info.bus (string)info.subsystem2008-03-01Renamed to something more abstract, available until removed.
*.physical_device (string)*.originating_device2008-03-01Renamed to something more abstract, available until removed.
portable_audio_player.access_method (string)portable_audio_player.access_method.[drivers, protocols] (strlist)2008-05-03Original entry can only indicate UMS or userspace driver devices, while some devices can be both. New structure doesn't have this limitation.
portable_audio_player.type (string)portable_audio_player.access_method.[drivers, protocols] (strlist)2008-05-03New structure allows for better definition of access protocols and handlers.
power_management.can_suspend_to_ram (bool)power_management.can_suspend2007-05-01 
power_management.can_suspend_to_disk (bool)power_management.can_hibernate2007-05-01 
smbios.system.manufacturer (string)system.hardware.vendor2008-02-28Renamed to something more abstract.
smbios.system.product (string)system.hardware.product2008-02-28Renamed to something more abstract.
smbios.system.version (string)system.hardware.version2008-02-28Renamed to something more abstract.
smbios.system.serial (string)system.hardware.serial2008-02-28Renamed to something more abstract.
smbios.system.uuid (string)system.hardware.uuid2008-02-28Renamed to something more abstract.
smbios.bios.vendor (string)system.firmware.vendor2008-02-28Renamed to something more abstract.
smbios.bios.version (string)system.firmware.version2008-02-28Renamed to something more abstract.
smbios.bios.release_date (string)system.firmware.release_date2008-02-28Renamed to something more abstract.
smbios.chassis.manufacturer (string)system.chassis.manufacturer2008-02-28Renamed to something more abstract.
smbios.chassis.type (string)system.chassis.type2008-02-28Renamed to something more abstract.
system.vendor (string)system.hardware.vendor2008-02-28Duplicate of system.hardware.vendor.
usb_device.speed_bcd (int)usb_device.speed (double)2008-03-21changed from 'BCD with two decimals' to double
usb_device.version_bcd (int)usb_device.version (double)2008-03-21changed from 'BCD with two decimals' to double

Chapter 6. D-Bus interfaces

+ All of the HAL D-Bus interfaces are introspectable using the + standard D-Bus introspection methods (e.g. they all implement + the org.freedesktop.DBus.Introspectable + interface). For example, a command like +

+$ dbus-send --system --print-reply --dest=org.freedesktop.Hal \
+            /org/freedesktop/Hal/devices/computer             \
+            org.freedesktop.DBus.Introspectable.Introspect
+    

+ will print out the introspection XML for what interfaces + (ie. methods and signals) the given hal device object + supports. For brevity, the org.freedesktop.Hal + prefix have been stripped from the exceptions listed in the + following sections. +

+ Also note that other exceptions than the ones listed may be + thrown; for example + the org.freedesktop.Hal.Device.InterfaceLocked + exception may be thrown regardless of how the interface is + implemented (depending on if some other process is holding a lock + on the device cf. Chapter 4, Locking); if PolicyKit support + is enabled, + the org.freedesktop.Hal.Device.PermissionDeniedByPolicy + exception may be thrown (the two first words in the exception + detail is resp. a) the privilege the caller didn't have; b) the + textual result code from PolicyKit specifying if the caller can + obtain the privilege) if the caller is not privileged and so on. +

org.freedesktop.Hal.Manager interface

+ Only the /org/freedesktop/Hal/Manager object + implements this interface. It's primarily used to discover + devices. The following methods are available: +

MethodReturnsParametersThrowsDescription
GetAllDevicesObjref[]   + Get all UDI's in the database. +
DeviceExistsBool   + Determines if a given object exists. +
FindDeviceStringMatchObjref[]String key, String value  + Find devices for which the given string property assumes the given value. +
FindDeviceByCapabilityObjref[]String capability  + Finds devices of the given capability. +
NewDeviceObjref PermissionDenied + Creates a new device object in the temporary device list + (TDL) and return the UDI. Caller must be uid 0. +
Remove Objref tmp_udiNoSuchDevice, PermissionDenied + Removes a device object that was created in the + TDL. Caller must be uid 0. +
CommitToGdl Objref tmp_udi, Objref udiNoSuchDevice, PermissionDenied + Moves a device from the temporary device list (TDL) to + the global device list (GDL). Caller must be uid 0. +
AcquireGlobalInterfaceLock String interface_name, Bool exclusiveDevice.InterfaceAlreadyLocked + Acquires a global lock on an interface. See + Chapter 4, Locking for details. +
ReleaseGlobalInterfaceLock String interface_nameDevice.InterfaceNotLocked + Releases a global lock on an interface. See + Chapter 4, Locking for details. +
SingletonAddonIsReady String command_linePermissionDenied, SyntaxError + Called by singleton addons to signal that they are + ready to handle devices. A singleton addon should + implement the + org.freedesktop.Hal.Singleton interface. +

+ The following signals are emitted: +

SignalParametersDescription
DeviceAddedObjref obj + A device was added to the global device list (GDL). +
DeviceRemovedObjref obj + A device was removed from the global device list (GDL). +
NewCapabilityObjref obj, String cap + A device gained a new capability. +
GlobalInterfaceLockAcquiredString lock_name, String lock_owner, Int num_holders + Sent when a process acquires a global interface lock. +
GlobalInterfaceLockReleasedString lock_name, String lock_owner, Int num_holders + Sent when a process releases a global interface lock. +

org.freedesktop.Hal.Device interface

+ Every hal device object (e.g. objects where the object path is + prefixed with /org/freedesktop/Hal/devices/) + implements this interface. It provides generic + functionality. The following methods are available: +

MethodReturnsParametersThrowsDescription
GetPropertyVariantString keyNoSuchProperty + Get property. +
GetPropertyStringStringString keyNoSuchProperty, TypeMismatch + Get property. +
GetPropertyStringListString[]String keyNoSuchProperty, TypeMismatch + Get property. +
GetPropertyIntegerIntString keyNoSuchProperty, TypeMismatch + Get property. +
GetPropertyUInt64UInt64String keyNoSuchProperty, TypeMismatch + Get property. +
GetPropertyBooleanBoolString keyNoSuchProperty, TypeMismatch + Get property. +
GetPropertyDoubleDoubleString keyNoSuchProperty, TypeMismatch + Get property. +
SetPropertyVariantString keyPermissionDenied + Set property. +
SetPropertyStringStringString keyPermissionDenied, TypeMismatch + Set property. +
SetPropertyStringListString[]String keyPermissionDenied, TypeMismatch + Set property. +
SetPropertyIntegerIntString keyPermissionDenied, TypeMismatch + Set property. +
SetPropertyUInt64UInt64String keyPermissionDenied, TypeMismatch + Set property. +
SetPropertyBooleanBoolString keyPermissionDenied, TypeMismatch + Set property. +
SetPropertyDoubleDoubleString keyPermissionDenied, TypeMismatch + Set property. +
RemoveProperty String keyNoSuchProperty, PermissionDenied + Remove a property. +
GetPropertyTypeIntString keyNoSuchProperty + Get the type of a property. Returns the D-Bus type as an integer. +
PropertyExistsBoolString key  + Determine if a property exists. +
AddCapability String capabilityPermissionDenied + Adds a capability to a device. +
QueryCapabilityBoolString capability  + Determine if a device have a capability. +
LockBoolString reasonDeviceAlreadyLocked + Acquires an advisory lock on the device. Returns TRUE if the lock was acquired. +
UnlockBool DeviceNotLocked + Releases an advisory lock on the device. Returns TRUE if the lock was released. +
AcquireInterfaceLock String interface_name, Bool exclusivePermissionDenied, Device.InterfaceAlreadyLocked + Acquires a lock on an interface for a specific + device. See Chapter 4, Locking for details. +
ReleaseInterfaceLock String interface_namePermissionDenied, Device.InterfaceNotLocked + Releases a lock on an interface for a specific device. See + Chapter 4, Locking for details. +
IsCallerLockedOutBoolString interface_name, String caller_unique_namePermissionDenied + Determines whether a given process on the system message + bus is locked out from an interface on a specific + device. Only HAL helpers are privileged to use this + method. See Chapter 4, Locking for details. +
IsCallerPrivilegedStringString privilege, String caller_unique_namePermissionDenied, Error +

+ Determines whether a given process on the system + message bus is authorized according to PolicyKit on a + specific device for a specific PolicyKit + privilege. Unprivileged callers (e.g. with a non-zero + uid) can only ask + about caller_unique_name that + matches their own uid; if this is violated + PermissionDenied will be + thrown. This can be used ahead of time to see if a + given call will succeed or if it requires privilege + elevation (TODO: clarify this once PolicyKit can auth + over D-Bus). +

+

+ Returns the textual representation of a PolKitResult + value on success. +

+

+ If HAL is not built with PolicyKit support, this + method always throws + the org.freedesktop.Hal.Device.Error + exception. +

+
IsLockedByOthersBoolString interface_name  + Determines whether a determines other processes than the + caller holds a lock on the given device. See + Chapter 4, Locking for details. +
StringListAppend String key, String valuePermissionDenied, TypeMismatch + Appends an item to a string list. +
StringListPrepend String key, String valuePermissionDenied, TypeMismatch + Prepends an item to a string list. +
StringListRemove String key, String valuePermissionDenied, TypeMismatch + Removes an item from a string list. +
EmitConditionBoolString name, String detailsPermissionDenied + Emit a condition on a device object. +
RescanBool PermissionDenied + Force an updates of the properties of a device object by + rereading data that is not monitored for changes. +
ReprobeBool PermissionDenied + Cause a synthetic remove and subsequent add of the given + device object including all children beneath it. Will + generate at least one pair of DeviceRemoved() and + DeviceAdded() signals on the Manager interface. +
ClaimInterfaceBoolString name, String introspection_xmlPermissionDenied + An addon can use this method for making the HAL daemon + route all D-Bus calls on the given interface to the + addon via the peer to peer D-Bus connection between the + addon and the HAL daemon. +
AddonIsReadyBool PermissionDenied + An addon needs to call this method when it's ready for + the device to be announced on D-Bus. The rationale for + this method is to allow an addon to modify the device + object and claim interfaces before the device is + announced on D-Bus. +

+ The following signals are emitted: +

SignalParametersDescription
PropertyModifiedInt num_changes, Array of struct {String property_name, Bool added, Bool removed} + One or more properties on the device object have changed. +
ConditionString name, String details + A generic mechanism used to specify a device condition + that cannot be expressed in device properties. (NOTE: + newly written code should use dedicated signals on a + dedicated interface.). +
InterfaceLockAcquiredString lock_name, String lock_owner, Int num_holders + Sent when a process acquires an interface lock on the device. +
InterfaceLockReleasedString lock_name, String lock_owner, Int num_holders + Sent when a process releases an interface lock on the device. +

org.freedesktop.Hal.Device.SystemPowerManagement interface

+ This interface provides a mechanism to affect system-wide power + management. Normally only the root computer device object + (/org/freedesktop/Hal/devices/computer) + implements this interface. The following methods are available: +

MethodReturnsParametersThrowsDescription
SuspendIntInt num_secs_to_wakeupSystemPowerManagement.NotSupported, SystemPowerManagement.AlarmNotSupported + Puts the system in a suspended state (typically ACPI S3) + for num_secs_to_wakeup seconds. If + the given time is zero, the system is put in the + suspended state indefinitely. If wake-up isn't supported + the the AlarmNotSupported exception is thrown. Latency + for the system to return to an operational state is in + the order of magnitude of 5 seconds. +
HibernateInt SystemPowerManagement.NotSupported + Save system state to persistent storage and power off + the system (typically ACPI S4). Latency for the system + to return to an operational state is in the order of + magnitude of one minute. +
SuspendHybridIntInt num_secs_to_wakeupSystemPowerManagement.NotSupported, SystemPowerManagement.AlarmNotSupported + Puts the system in a suspended state (typically ACPI S3) + for num_secs_to_wakeup seconds but + also write the system state to persistent storage so the + system can resume even if power is removed. Like with + Suspend(), if the given time is zero, the system is put + in the suspended state indefinitely. If wake-up isn't + supported the the AlarmNotSupported exception is thrown. +
ShutdownInt SystemPowerManagement.NotSupported + Shut down the system. +
RebootInt SystemPowerManagement.NotSupported + Reboot the system. +
SetPowerSaveIntBool should_save_powerSystemPowerManagement.NotSupported + If the boolean passed is TRUE, the system will be + configured to save as much power as possible by + e.g. enabling laptop mode to avoid + spinning up disks. Typically, power management daemons + will call this method when it determines that the system + is running on battery power. +

+ This interface does not emit any signals. +

+ Implementors of power management daemons should make sure that + their software respects the locking guidelines described in + Chapter 6, D-Bus interfaces. +

org.freedesktop.Hal.Device.CPUFreq interface

+ This interface provides a mechanism to configure CPU frequency + scaling. The following methods are available: +

MethodReturnsParametersThrowsDescription
GetCPUFreqAvailableGovernorsString[]   + Retrieves a list of available CPU scaling governors. +
GetCPUFreqGovernorString   + Get currently selected CPU Frequency governor. +
SetCPUFreqGovernorVoidString governorCPUFreq.UnknownGovernor + Selects a CPU frequency scaling governor for all CPUFreq + interfaces the kernel provides. If the userspace governor is + set, this interface also contains a proper scaling + mechanism. +
SetCPUFreqPerformanceVoidInt (1 to 100)CPUFreq.NoSuitableGovernor + Sets the performance of the dynamic scaling + mechanism. This method summarizes and abstracts all the + different settings which can be taken for dynamic + frequency adjustments, like at which load to switch up + frequency or how many steps the mechanism should + traverse until reaching the maximum frequency. The + higher the value, the more performance you + get. Respectively, the higher the value, the sooner and + the more often the frequency is switched up. +
GetCPUFreqPerformanceInt CPUFreq.NoSuitableGovernor + Get the tuning value for the governor. +
SetCPUFreqConsiderNiceVoidBool consider_niced_processesCPUFreq.NoSuitableGovernor + Whether or not niced processes should be considered on + CPU load calculation. If niced processes are considered, + they can cause a frequency increment although their + absolute load percentage wouldn't trigger the scaling + mechanism to switch up the frequency. The default + setting is 'false'. +
GetCPUFreqConsiderNiceBool CPUFreq.NoSuitableGovernor + Whether nice'ed processes are considered by the governor. +

+ This interface does not emit any signals. +

org.freedesktop.Hal.Device.WakeOnLan interface

+ This interface provides a mechanism to configure Wake On LAN + capabilities. The following methods are available: +

MethodReturnsParametersThrowsDescription
GetSupportedBool WakeOnLan.NoEthtool + Get if device supports Wake On LAN +
GetEnabledBool WakeOnLan.NoEthtool + Get if Wake On LAN is enabled +
SetEnabledVoidBoolWakeOnLan.NoEthtool + Enable or disable Wake On LAN +

+ This interface does not emit any signals. +

org.freedesktop.Hal.Device.LaptopPanel interface

+ This interface provides a mechanism to get/set the brightness of + a laptop panel. The following methods are available: +

MethodReturnsParametersThrowsDescription
GetBrightnessInt   + Get the current brightness. +
SetBrightness Int brightness  + Set the current brightness. +

+ This interface does not emit any signals. +

org.freedesktop.Hal.Device.KeyboardBacklight interface

+ This interface provides a mechanism to get/set the brightness of + the keyboard backlight. The following methods are available: +

MethodReturnsParametersThrowsDescription
GetBrightnessInt   + Get the current brightness. +
SetBrightness Int brightness  + Set the current brightness. +

+ This interface does not emit any signals. +

org.freedesktop.Hal.Device.LightSensor interface

+ This interface provides a mechanism to get information from a + light sensor. The following methods are available: +

MethodReturnsParametersThrowsDescription
GetBrightnessInt[]   + The current brightness as measured by the light sensor. +

+ This interface does not emit any signals. +

org.freedesktop.Hal.Device.Storage interface

+ This interface provides a mechanism to interact with a storage + device. The following methods are available: +

MethodReturnsParametersThrowsDescription
EjectIntString[] optionsVolume.PermissionDenied, Volume.NotMounted, Volume.NotMountedByHal, Volume.Busy, Volume.InvalidEjectOption + Unmounts all volumes and possibly ejects the media. Note + that this method is not only restricted to optical + drives. +
CloseTrayIntString[] optionsStorage.InvalidCloseTrayOption + Attempts to close the tray. Only really makes sense for + (optical) drives that uses a tray loading mechanism. +

+ This interface does not emit any signals. +

org.freedesktop.Hal.Device.Volume interface

+ This interface provides a mechanism to interact with a volume + that has a mountable file system. The following methods are + available: +

MethodReturnsParametersThrowsDescription
MountIntString mount_point, String fstype, String[] optionsVolume.PermissionDenied, Volume.AlreadyMounted, Volume.InvalidMountOption, Volume.UnknownFilesystemType, Volume.InvalidMountPoint, Volume.MountPointNotAvailable, Volume.CannotRemount + Mounts a volume. +
UnmountIntString[] optionsVolume.PermissionDenied, Volume.NotMounted, Volume.NotMountedByHal, Volume.Busy, Volume.InvalidUnmountOption + Unmount a volume. +
EjectIntString[] optionsVolume.PermissionDenied, Volume.NotMounted, Volume.NotMountedByHal, Volume.Busy, Volume.InvalidEjectOption + Unmounts all volumes from the originating drive and + possibly ejects the media. Note that this method is not + only restricted to optical drives. +

+ This interface does not emit any signals. +

+ If a volume originates from a storage device (and all volumes + do), it also is checked whether the caller is locked out of the + org.freedesktop.Hal.Device.Storage interface + of the originating storage device. As a corollary, it is + sufficient to just either a) lock the storage device; or b) + globally lock + the org.freedesktop.Hal.Device.Storage + interface if one wants to lock out callers from mounting + volumes from either a specific drive or all drives. + +

org.freedesktop.Hal.Device.Volume.Crypto interface

+ This interface provides a mechanism to interact with a volume that + is encrypted at the block layer. The following methods are + available: +

MethodReturnsParametersThrowsDescription
SetupIntString passphraseVolume.Crypto.CryptSetupMissing, Volume.Crypto.SetupError, Volume.Crypto.SetupPasswordError + Unlocks an encrypted file system. If successful, a cleartext volume will appear. +
TeardownInt Volume.Crypto.TeardownError + Teardown the cleartext volume. +

+ This interface does not emit any signals. +

+ For objects implementing this interface, it will also be checked + if the caller is locked out of the Volume interface on the + device (and per the section called “org.freedesktop.Hal.Device.Volume interface” this + includes checking whether the caller is locked out of + the org.freedesktop.Hal.Device.Storage + interface for the storage device that the volume originates + from). +

org.freedesktop.Hal.Device.KillSwitch interface

+ This interface provides a mechanism for both querying whether a + radio is on as well as turning it on and off. The following + methods are available: +

MethodReturnsParametersThrowsDescription
GetPowerInt   + Returns 1 if, and only if, the power is on. +
SetPower Bool  + Set the power of the radio. +

+ This interface does not emit any signals. +

org.freedesktop.Hal.Device.AccessControl interface

+ This interface provides a mechanism for discovering when an ACL + is added or removed for a device file. The following signals are + available: +

MethodParametersDescription
ACLAddedUInt unix_user_id + Emitted when an ACL have been added for a UNIX user on a + device object with the access_control + capability. +
ACLRemovedUInt unix_user_id + Emitted when an ACL have been removed for a UNIX user on + a device object with + the access_control capability. +

+ This interface does not export any methods. +

org.freedesktop.Hal.SingletonAddon interface

+ This interface is provided by singleton addons to allow the Manager to + request handling of new devices and removal of old ones. + + This differs from other HAL interface definitions in that it + is provided by addon processes, rather than the HAL daemon itself. + + It should be exported on the path + /org/freedesktop/Hal/Manager. +

MethodReturnsParametersThrowsDescription
+ DeviceAdded + +

String udi

+

Dict(String,Variant) property_set

+
   +

+ Called by the HAL Manager when a device is added that has + this singleton listed in + + info.addons.singleton + +

+

+ An addon implementing this function should start handling the + device before returning, and keep track that is is handling this + udi. +

+
+ DeviceRemoved + +

String udi

+

Dict(String,Variant) property_set

+
   +

+ Called by the HAL Manager when a device is added that has + this singleton listed in + + info.addons.singleton + +

+

+ The implementer of this function should keep track of + which devices it is still handling and exit when no longer + handling any devices. +

+

+ This interface does not export any methods. +

diff --git a/src/lib/hal/Makefile.am b/src/lib/hal/Makefile.am new file mode 100644 index 0000000..405ac3e --- /dev/null +++ b/src/lib/hal/Makefile.am @@ -0,0 +1,22 @@ +MAINTAINERCLEANFILES = Makefile.in + +AM_CPPFLAGS = \ +-I $(top_srcdir)/src/lib/dbus \ +@EDBUS_CFLAGS@ + +lib_LTLIBRARIES = libehal.la +includes_HEADERS = E_Hal.h +includesdir = $(includedir)/e_dbus-@VMAJ@ + +libehal_la_SOURCES = \ +e_hal_device.c \ +e_hal_manager.c \ +e_hal_util.c \ +e_hal_main.c + +libehal_la_LIBADD = \ +@EDBUS_LIBS@ $(top_builddir)/src/lib/dbus/libedbus.la + +libehal_la_LDFLAGS = -version-info @version_info@ @release_info@ + +EXTRA_DIST = e_hal_private.h diff --git a/src/lib/hal/e_hal_device.c b/src/lib/hal/e_hal_device.c new file mode 100644 index 0000000..79604e6 --- /dev/null +++ b/src/lib/hal/e_hal_device.c @@ -0,0 +1,383 @@ +#include + +#include +#include "e_hal_private.h" + +#define e_hal_device_call_new(udi, member) dbus_message_new_method_call(E_HAL_SENDER, udi, E_HAL_DEVICE_INTERFACE, member) +#define e_hal_device_volume_call_new(udi, member) dbus_message_new_method_call(E_HAL_SENDER, udi, E_HAL_DEVICE_VOLUME_INTERFACE, member) + +#if 0 +static void cb_device_get_property(void *data, DBusMessage *msg, DBusError *err); +static void cb_device_get_all_properties(void *data, DBusMessage *msg, DBusError *err); +static void cb_device_query_capability(void *data, DBusMessage *msg, DBusError *err); +#endif + +/* Device.GetProperty */ +static void * +unmarshal_device_get_property(DBusMessage *msg, DBusError *err) +{ + E_Hal_Device_Get_Property_Return *ret = NULL; + DBusMessageIter iter; + int type; + const char *str; + + ret = calloc(1, sizeof(E_Hal_Device_Get_Property_Return)); + if (!ret) + { + dbus_set_error(err, DBUS_ERROR_NO_MEMORY, ""); + return NULL; + } + + dbus_message_iter_init(msg, &iter); + type = dbus_message_iter_get_arg_type(&iter); + switch(type) + { + case DBUS_TYPE_STRING: + ret->type = E_HAL_PROPERTY_TYPE_STRING; + dbus_message_iter_get_basic(&iter, &str); + ret->val.s = eina_stringshare_add(str); + break; + case DBUS_TYPE_INT32: + ret->type = E_HAL_PROPERTY_TYPE_INT; + dbus_message_iter_get_basic(&iter, &(ret->val.i)); + break; + case DBUS_TYPE_BOOLEAN: + ret->type = E_HAL_PROPERTY_TYPE_BOOL; + dbus_message_iter_get_basic(&iter, &(ret->val.b)); + break; + case DBUS_TYPE_DOUBLE: + ret->type = E_HAL_PROPERTY_TYPE_DOUBLE; + dbus_message_iter_get_basic(&iter, &(ret->val.d)); + break; + } + + return ret; +} + +static void +free_device_get_property(void *data) +{ + E_Hal_Device_Get_Property_Return *ret = data; + + if (!ret) return; + if (ret->type == E_HAL_PROPERTY_TYPE_STRLIST) + { + const char *str; + EINA_LIST_FREE(ret->val.strlist, str) + eina_stringshare_del(str); + } + else if (ret->type == E_HAL_PROPERTY_TYPE_STRING) + eina_stringshare_del(ret->val.s); + free(ret); +} + +EAPI DBusPendingCall * +e_hal_device_get_property(E_DBus_Connection *conn, const char *udi, const char *property, E_DBus_Callback_Func cb_func, void *data) +{ + DBusMessage *msg; + DBusPendingCall *ret; + + msg = e_hal_device_call_new(udi, "GetProperty"); + dbus_message_append_args(msg, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID); + ret = e_dbus_method_call_send(conn, msg, unmarshal_device_get_property, cb_func, free_device_get_property, -1, data); + dbus_message_unref(msg); + return ret; +} + +/* Device.GetAllProperties */ + +static void * +unmarshal_device_get_all_properties(DBusMessage *msg, DBusError *err) +{ + E_Hal_Device_Get_All_Properties_Return *ret = NULL; + DBusMessageIter iter, a_iter, s_iter, v_iter; + + /* a{sv} = array of string+variant */ + if (!dbus_message_has_signature(msg, "a{sv}")) + { + dbus_set_error(err, DBUS_ERROR_INVALID_SIGNATURE, ""); + return NULL; + } + + ret = calloc(1, sizeof(E_Hal_Device_Get_All_Properties_Return)); + if (!ret) + { + dbus_set_error(err, DBUS_ERROR_NO_MEMORY, ""); + return NULL; + } + + ret->properties = eina_hash_string_small_new(EINA_FREE_CB(e_hal_property_free)); + + dbus_message_iter_init(msg, &iter); + dbus_message_iter_recurse(&iter, &a_iter); + while (dbus_message_iter_get_arg_type(&a_iter) != DBUS_TYPE_INVALID) + { + const char *name; + const char *str; + E_Hal_Property *prop = calloc(1, sizeof(E_Hal_Property)); + dbus_message_iter_recurse(&a_iter, &s_iter); + dbus_message_iter_get_basic(&s_iter, &name); + dbus_message_iter_next(&s_iter); + dbus_message_iter_recurse(&s_iter, &v_iter); + + switch(dbus_message_iter_get_arg_type(&v_iter)) + { + case DBUS_TYPE_STRING: + prop->type = E_HAL_PROPERTY_TYPE_STRING; + dbus_message_iter_get_basic(&v_iter, &str); + prop->val.s = eina_stringshare_add(str); + break; + case DBUS_TYPE_INT32: + prop->type = E_HAL_PROPERTY_TYPE_INT; + dbus_message_iter_get_basic(&v_iter, &(prop->val.i)); + break; + case DBUS_TYPE_UINT64: + prop->type = E_HAL_PROPERTY_TYPE_UINT64; + dbus_message_iter_get_basic(&v_iter, &(prop->val.u64)); + break; + case DBUS_TYPE_BOOLEAN: + prop->type = E_HAL_PROPERTY_TYPE_BOOL; + dbus_message_iter_get_basic(&v_iter, &(prop->val.b)); + break; + case DBUS_TYPE_DOUBLE: + prop->type = E_HAL_PROPERTY_TYPE_DOUBLE; + dbus_message_iter_get_basic(&v_iter, &(prop->val.d)); + break; + case DBUS_TYPE_ARRAY: + prop->type = E_HAL_PROPERTY_TYPE_STRLIST; + { + DBusMessageIter list_iter; + prop->val.strlist = NULL; + dbus_message_iter_recurse(&v_iter, &list_iter); + while (dbus_message_iter_get_arg_type(&list_iter) != DBUS_TYPE_INVALID) + { + dbus_message_iter_get_basic(&list_iter, &str); + prop->val.strlist = eina_list_append(prop->val.strlist, eina_stringshare_add(str)); + dbus_message_iter_next(&list_iter); + } + } + break; + default: + WARN("Ehal Error: unexpected property type (%s): %c", name, dbus_message_iter_get_arg_type(&v_iter)); + break; + } + eina_hash_add(ret->properties, name, prop); + + dbus_message_iter_next(&a_iter); + } + + return ret; +} + +static void +free_device_get_all_properties(void *data) +{ + E_Hal_Device_Get_All_Properties_Return *ret = data; + + if (!ret) return; + eina_hash_free(ret->properties); + free(ret); +} + +EAPI DBusPendingCall * +e_hal_device_get_all_properties(E_DBus_Connection *conn, const char *udi, E_DBus_Callback_Func cb_func, void *data) +{ + DBusMessage *msg; + DBusPendingCall *ret; + + msg = e_hal_device_call_new(udi, "GetAllProperties"); + ret = e_dbus_method_call_send(conn, msg, unmarshal_device_get_all_properties, cb_func, free_device_get_all_properties, -1, data); + dbus_message_unref(msg); + return ret; +} + + + +/* bool Device.QueryCapability(string udi) */ + +// XXX this is same as Device_Exists in manager. +static void * +unmarshal_device_query_capability(DBusMessage *msg, DBusError *err) +{ + E_Hal_Device_Query_Capability_Return *ret = NULL; + dbus_bool_t val; + + ret = calloc(1, sizeof(E_Hal_Manager_Device_Exists_Return)); + if (!ret) + { + dbus_set_error(err, DBUS_ERROR_NO_MEMORY, ""); + return NULL; + } + + dbus_message_get_args(msg, err, DBUS_TYPE_BOOLEAN, &val, DBUS_TYPE_INVALID); + + if (dbus_error_is_set(err)) + { + free(ret); + return NULL; + } + + ret->boolean = val; + return ret; +} + +static void +free_device_query_capability(void *data) +{ + E_Hal_Device_Query_Capability_Return *ret = data; + + if (!ret) return; + free(ret); +} + +EAPI DBusPendingCall * +e_hal_device_query_capability(E_DBus_Connection *conn, const char *udi, const char *capability, E_DBus_Callback_Func cb_func, void *data) +{ + DBusMessage *msg; + DBusPendingCall *ret; + + msg = e_hal_device_call_new(udi, "QueryCapability"); + dbus_message_append_args(msg, DBUS_TYPE_STRING, &capability, DBUS_TYPE_INVALID); + ret = e_dbus_method_call_send(conn, msg, unmarshal_device_query_capability, cb_func, free_device_query_capability, -1, data); + dbus_message_unref(msg); + return ret; +} + + + +/* void Device.Mount(string mount_point, string fstype, array{string}options) */ + +/** + * @brief Mount a Volume + * + * @param conn the E_DBus_Connection + * @param udi the udi of the device object + * @param mount_point the path to mount to, or null for default + * @param fstype the fstype of the device (e.g. volume.fstype property) + * @param options a list of additional options (not sure... fstype dependent?) + * @param cb_func an optional callback to call when the mount is done + * @param data custom data pointer for the callback function + */ +EAPI DBusPendingCall * +e_hal_device_volume_mount(E_DBus_Connection *conn, const char *udi, const char *mount_point, const char *fstype, Eina_List *options, E_DBus_Callback_Func cb_func, void *data) +{ + DBusMessage *msg; + DBusMessageIter iter, subiter; + Eina_List *l; + DBusPendingCall *ret; + + msg = e_hal_device_volume_call_new(udi, "Mount"); + + dbus_message_iter_init_append(msg, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &mount_point); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fstype); + if (dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &subiter)) + { + if (options) + { + const char *opt; + + EINA_LIST_FOREACH(options, l, opt) + { + dbus_message_iter_append_basic(&subiter, DBUS_TYPE_STRING, &opt); + } + } + dbus_message_iter_close_container(&iter, &subiter); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + + ret = e_dbus_method_call_send(conn, msg, NULL, cb_func, NULL, -1, data); + dbus_message_unref(msg); + return ret; +} + +/* void Unmount(array{string} options) */ + +/** + * @brief Unmount a Volume + * + * @param conn the E_DBus_Connection + * @param udi the udi of the device object + * @param options a list of additional options (not sure... fstype dependent?) + * @param cb_func an optional callback to call when the unmount is done + * @param data cuatom data pointer for the callback function + */ +EAPI DBusPendingCall * +e_hal_device_volume_unmount(E_DBus_Connection *conn, const char *udi, Eina_List *options, E_DBus_Callback_Func cb_func, void *data) +{ + DBusMessage *msg; + DBusMessageIter iter, subiter; + Eina_List *l; + DBusPendingCall *ret; + + msg = e_hal_device_volume_call_new(udi, "Unmount"); + + dbus_message_iter_init_append(msg, &iter); + if (dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &subiter)) + { + if (options) + { + const char *opt; + + EINA_LIST_FOREACH(options, l, opt) + { + dbus_message_iter_append_basic(&subiter, DBUS_TYPE_STRING, &opt); + } + } + dbus_message_iter_close_container(&iter, &subiter); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + + ret = e_dbus_method_call_send(conn, msg, NULL, cb_func, NULL, -1, data); + dbus_message_unref(msg); + return ret; +} + +/** + * @brief Eject a Volume + * + * @param conn the E_DBus_Connection + * @param udi the udi of the device object + * @param options a list of additional options (not sure... fstype dependent?) + * @param cb_func an optional callback to call when the eject is done + * @param data cuatom data pointer for the callback function + */ +EAPI DBusPendingCall * +e_hal_device_volume_eject(E_DBus_Connection *conn, const char *udi, Eina_List *options, E_DBus_Callback_Func cb_func, void *data) +{ + DBusMessage *msg; + DBusMessageIter iter, subiter; + Eina_List *l; + DBusPendingCall *ret; + + msg = e_hal_device_volume_call_new(udi, "Eject"); + + dbus_message_iter_init_append(msg, &iter); + if (dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &subiter)) + { + if (options) + { + const char *opt; + + EINA_LIST_FOREACH(options, l, opt) + { + dbus_message_iter_append_basic(&subiter, DBUS_TYPE_STRING, &opt); + } + } + dbus_message_iter_close_container(&iter, &subiter); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + + ret = e_dbus_method_call_send(conn, msg, NULL, cb_func, NULL, -1, data); + dbus_message_unref(msg); + return ret; +} diff --git a/src/lib/hal/e_hal_device.h b/src/lib/hal/e_hal_device.h new file mode 100644 index 0000000..3fc478a --- /dev/null +++ b/src/lib/hal/e_hal_device.h @@ -0,0 +1,14 @@ +#ifndef E_HAL_DEVICE_H +#define E_HAL_DEVICE_H + +#include "E_Hal.h" + +typedef struct E_Hal_Properties E_Hal_Device_Get_All_Properties_Return; +typedef struct E_Hal_Property E_Hal_Device_Get_Property_Return; +typedef struct E_Hal_Bool_Return E_Hal_Device_Query_Capability_Return; + +int e_hal_device_get_property(E_DBus_Connection *conn, const char *udi, const char *property, E_Hal_Callback_Func cb_func, void *data); +int e_hal_device_get_all_properties(E_DBus_Connection *conn, const char *udi, E_Hal_Callback_Func cb_func, void *data); +int e_hal_device_query_capability(E_DBus_Connection *conn, const char *udi, const char *capability, E_Hal_Callback_Func cb_func, void *data); + +#endif diff --git a/src/lib/hal/e_hal_main.c b/src/lib/hal/e_hal_main.c new file mode 100644 index 0000000..c9d217b --- /dev/null +++ b/src/lib/hal/e_hal_main.c @@ -0,0 +1,53 @@ +#include +#include "e_hal_private.h" + +int _e_dbus_hal_log_dom = -1; +int _e_dbus_hal_init_count = 0; + +EAPI int +e_hal_init(void) +{ + if (++_e_dbus_hal_init_count != 1) + return _e_dbus_hal_init_count; + + if (!eina_init()) + return --_e_dbus_hal_init_count; + + _e_dbus_hal_log_dom = eina_log_domain_register + ("e_hal", E_DBUS_COLOR_DEFAULT); + if (_e_dbus_hal_log_dom < 0) + { + EINA_LOG_ERR("Could not register 'e_hal' log domain."); + goto shutdown_eina; + } + + if (!e_dbus_init()) { + ERR("Could not initialize E_DBus."); + goto unregister_log_domain; + } + + return _e_dbus_hal_init_count; + + unregister_log_domain: + eina_log_domain_unregister(_e_dbus_hal_log_dom); + _e_dbus_hal_log_dom = -1; + shutdown_eina: + eina_shutdown(); + + return _e_dbus_hal_init_count; +} + +EAPI int +e_hal_shutdown(void) +{ + if (--_e_dbus_hal_init_count != 0) + return _e_dbus_hal_init_count; + + e_dbus_shutdown(); + + eina_log_domain_unregister(_e_dbus_hal_log_dom); + _e_dbus_hal_log_dom = -1; + eina_shutdown(); + + return _e_dbus_hal_init_count; +} diff --git a/src/lib/hal/e_hal_manager.c b/src/lib/hal/e_hal_manager.c new file mode 100644 index 0000000..c22fb8b --- /dev/null +++ b/src/lib/hal/e_hal_manager.c @@ -0,0 +1,146 @@ +#include + +#include "E_Hal.h" +#include "e_hal_private.h" + +#define e_hal_manager_call_new(member) dbus_message_new_method_call(E_HAL_SENDER, E_HAL_MANAGER_PATH, E_HAL_MANAGER_INTERFACE, member) + +/* GetAllDevices */ + +static void * +unmarshal_string_list(DBusMessage *msg, DBusError *err) +{ + E_Hal_String_List_Return *ret = NULL; + DBusMessageIter iter, sub; + + if (!dbus_message_has_signature(msg, "as")) + { + dbus_set_error(err, DBUS_ERROR_INVALID_SIGNATURE, ""); + return NULL; + } + + ret = calloc(1, sizeof(E_Hal_String_List_Return)); + if (!ret) + { + dbus_set_error(err, DBUS_ERROR_NO_MEMORY, ""); + return NULL; + } + + ret->strings = NULL; + + dbus_message_iter_init(msg, &iter); + dbus_message_iter_recurse(&iter, &sub); + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) + { + char *dev = NULL; + + dbus_message_iter_get_basic(&sub, &dev); + if (dev) + ret->strings = eina_list_append(ret->strings, eina_stringshare_add(dev)); + dbus_message_iter_next(&sub); + } + + return ret; +} + +static void +free_string_list(void *data) +{ + E_Hal_String_List_Return *ret = data; + const char *str; + + if (!ret) return; + EINA_LIST_FREE(ret->strings, str) + eina_stringshare_del(str); + free(ret); +} + +EAPI DBusPendingCall * +e_hal_manager_get_all_devices(E_DBus_Connection *conn, E_DBus_Callback_Func cb_func, void *data) +{ + DBusMessage *msg; + DBusPendingCall *ret; + + msg = e_hal_manager_call_new("GetAllDevices"); + ret = e_dbus_method_call_send(conn, msg, unmarshal_string_list, cb_func, free_string_list, -1, data); + dbus_message_unref(msg); + return ret; +} + +/* Manager.DeviceExists(string udi) */ + +static void * +unmarshal_manager_device_exists(DBusMessage *msg, DBusError *err) +{ + E_Hal_Manager_Device_Exists_Return *ret = NULL; + dbus_bool_t val; + + ret = calloc(1, sizeof(E_Hal_Manager_Device_Exists_Return)); + if (!ret) + { + dbus_set_error(err, DBUS_ERROR_NO_MEMORY, ""); + return NULL; + } + + dbus_message_get_args(msg, err, DBUS_TYPE_BOOLEAN, &val, DBUS_TYPE_INVALID); + + if (dbus_error_is_set(err)) + { + free(ret); + return NULL; + } + + ret->boolean = val; + return ret; +} + +static void +free_manager_device_exists(void *data) +{ + E_Hal_Manager_Device_Exists_Return *ret = data; + + if (!ret) return; + free(ret); +} + +EAPI DBusPendingCall * +e_hal_manager_device_exists(E_DBus_Connection *conn, const char *udi, E_DBus_Callback_Func cb_func, void *data) +{ + DBusPendingCall *ret; + DBusMessage *msg; + + msg = e_hal_manager_call_new("DeviceExists"); + dbus_message_append_args(msg, DBUS_TYPE_STRING, &udi, DBUS_TYPE_INVALID); + ret = e_dbus_method_call_send(conn, msg, unmarshal_manager_device_exists, cb_func, free_manager_device_exists, -1, data); + dbus_message_unref(msg); + return ret; +} + +/* Manager.FindDeviceStringMatch */ +EAPI DBusPendingCall * +e_hal_manager_find_device_string_match(E_DBus_Connection *conn, const char *key, const char *value, E_DBus_Callback_Func cb_func, void *data) +{ + DBusMessage *msg; + DBusPendingCall *ret; + + msg = e_hal_manager_call_new("FindDeviceStringMatch"); + dbus_message_append_args(msg, DBUS_TYPE_STRING, &key, DBUS_TYPE_STRING, &value, DBUS_TYPE_INVALID); + ret = e_dbus_method_call_send(conn, msg, unmarshal_string_list, cb_func, free_string_list, -1, data); + dbus_message_unref(msg); + return ret; +} + +/* Manager.FindDeviceByCapability */ + +EAPI DBusPendingCall * +e_hal_manager_find_device_by_capability(E_DBus_Connection *conn, const char *capability, E_DBus_Callback_Func cb_func, void *data) +{ + DBusMessage *msg; + DBusPendingCall *ret; + + msg = e_hal_manager_call_new("FindDeviceByCapability"); + dbus_message_append_args(msg, DBUS_TYPE_STRING, &capability, DBUS_TYPE_INVALID); + ret = e_dbus_method_call_send(conn, msg, unmarshal_string_list, cb_func, free_string_list, -1, data); + dbus_message_unref(msg); + return ret; +} diff --git a/src/lib/hal/e_hal_manager.h b/src/lib/hal/e_hal_manager.h new file mode 100644 index 0000000..3714dde --- /dev/null +++ b/src/lib/hal/e_hal_manager.h @@ -0,0 +1,21 @@ +#ifndef E_HAL_MANAGER_H +#define E_HAL_MANAGER_H + +#include "E_Hal.h" + +typedef struct E_Hal_String_List_Return E_Hal_Manager_Get_All_Devices_Return; +typedef struct E_Hal_Bool_Return E_Hal_Manager_Device_Exists_Return; +typedef struct E_Hal_String_List_Return E_Hal_Manager_Find_Device_String_Match_Return; +typedef struct E_Hal_String_List_Return E_Hal_Manager_Find_Device_By_Capability_Return; + +typedef struct E_Hal_UDI_Return E_Hal_Manager_Device_Added; +typedef struct E_Hal_UDI_Return E_Hal_Manager_Device_Removed; +typedef struct E_Hal_Capability E_Hal_Manager_New_Capability; + + +int e_hal_manager_get_all_devices(E_DBus_Connection *conn, E_Hal_Callback_Func cb_func, void *data); +int e_hal_manager_device_exists(E_DBus_Connection *conn, const char *udi, E_Hal_Callback_Func cb_func, void *data); +int e_hal_manager_find_device_string_match(E_DBus_Connection *conn, const char *key, const char *value, E_Hal_Callback_Func cb_func, void *data); +int e_hal_manager_find_device_by_capability(E_DBus_Connection *conn, const char *capability, E_Hal_Callback_Func cb_func, void *data); + +#endif diff --git a/src/lib/hal/e_hal_private.h b/src/lib/hal/e_hal_private.h new file mode 100644 index 0000000..1190714 --- /dev/null +++ b/src/lib/hal/e_hal_private.h @@ -0,0 +1,26 @@ +#ifndef E_HAL_PRIVATE_H +#define E_HAL_PRIVATE_H + +#ifndef E_DBUS_COLOR_DEFAULT +#define E_DBUS_COLOR_DEFAULT EINA_COLOR_CYAN +#endif +extern int _e_dbus_hal_log_dom; +#ifdef ERR +#undef ERR +#endif +#ifdef INF +#undef INF +#endif +#ifdef WARN +#undef WARN +#endif +#ifdef DBG +#undef DBG +#endif + +#define DBG(...) EINA_LOG_DOM_DBG(_e_dbus_hal_log_dom, __VA_ARGS__) +#define INFO(...) EINA_LOG_DOM_INFO(_e_dbus_hal_log_dom, __VA_ARGS__) +#define WARN(...) EINA_LOG_DOM_WARN(_e_dbus_hal_log_dom, __VA_ARGS__) +#define ERR(...) EINA_LOG_DOM_ERR(_e_dbus_hal_log_dom, __VA_ARGS__) + +#endif diff --git a/src/lib/hal/e_hal_util.c b/src/lib/hal/e_hal_util.c new file mode 100644 index 0000000..b66f35b --- /dev/null +++ b/src/lib/hal/e_hal_util.c @@ -0,0 +1,120 @@ +#include + +#include +#include +#include "e_hal_private.h" + +/** + * @internal + * @brief free a property structure + * @param prop the property to free + */ +EAPI void +e_hal_property_free(E_Hal_Property *prop) +{ + if (prop->type == E_HAL_PROPERTY_TYPE_STRLIST) + { + const char *str; + EINA_LIST_FREE(prop->val.strlist, str) + eina_stringshare_del(str); + } + else if (prop->type == E_HAL_PROPERTY_TYPE_STRING) + eina_stringshare_del(prop->val.s); + free(prop); +} + +/** + * @brief Retrive a string from an element of a property hash + * @param properties the E_Hal_Properties structure + * @param key the key of the property to retrieve + * @param err a pointer to an int, which if supplied, will be set to 0 on success and 1 on an error + */ +EAPI const char * +e_hal_property_string_get(E_Hal_Properties *properties, const char *key, int *err) +{ + E_Hal_Property *prop; + if (err) *err = 0; + if (!properties) return NULL; + if (!properties->properties) return NULL; + if (!key) return NULL; + prop = eina_hash_find(properties->properties, key); + if (prop) return prop->val.s; + + if (err) *err = 1; + return NULL; +} + +EAPI Eina_Bool +e_hal_property_bool_get(E_Hal_Properties *properties, const char *key, int *err) +{ + E_Hal_Property *prop; + if (err) *err = 0; + if (!properties) return EINA_FALSE; + if (!properties->properties) return EINA_FALSE; + if (!key) return EINA_FALSE; + prop = eina_hash_find(properties->properties, key); + if (prop) return prop->val.b; + + if (err) *err = 1; + return EINA_FALSE; +} + +EAPI int +e_hal_property_int_get(E_Hal_Properties *properties, const char *key, int *err) +{ + E_Hal_Property *prop; + if (err) *err = 0; + if (!properties) return 0; + if (!properties->properties) return 0; + if (!key) return 0; + prop = eina_hash_find(properties->properties, key); + if (prop) return prop->val.i; + + if (err) *err = 1; + return 0; +} + +EAPI uint64_t +e_hal_property_uint64_get(E_Hal_Properties *properties, const char *key, int *err) +{ + E_Hal_Property *prop; + if (err) *err = 0; + if (!properties) return 0; + if (!properties->properties) return 0; + if (!key) return 0; + prop = eina_hash_find(properties->properties, key); + if (prop) return prop->val.u64; + + if (err) *err = 1; + return 0; +} + +EAPI double +e_hal_property_double_get(E_Hal_Properties *properties, const char *key, int *err) +{ + E_Hal_Property *prop; + if (err) *err = 0; + if (!properties) return 0; + if (!properties->properties) return 0; + if (!key) return 0; + prop = eina_hash_find(properties->properties, key); + if (prop) return prop->val.d; + + if (err) *err = 1; + return 0; +} + +EAPI const Eina_List * +e_hal_property_strlist_get(E_Hal_Properties *properties, const char *key, int *err) +{ + E_Hal_Property *prop; + if (err) *err = 0; + if (!properties) return NULL; + if (!properties->properties) return NULL; + if (!key) return NULL; + prop = eina_hash_find(properties->properties, key); + if (prop) return prop->val.strlist; + + if (err) *err = 1; + return NULL; +} diff --git a/src/lib/hal/e_hal_util.h b/src/lib/hal/e_hal_util.h new file mode 100644 index 0000000..0413510 --- /dev/null +++ b/src/lib/hal/e_hal_util.h @@ -0,0 +1,14 @@ +#ifndef E_HAL_UTIL_H +#define E_HAL_UTIL_H + +#include "E_Hal.h" + +void e_hal_property_free(E_Hal_Property *prop); +const char *e_hal_property_string_get(E_Hal_Properties *properties, const char *key, int *err); +char e_hal_property_bool_get(E_Hal_Properties *properties, const char *key, int *err); +int e_hal_property_int_get(E_Hal_Properties *properties, const char *key, int *err); +uint64_t e_hal_property_uint64_get(E_Hal_Properties *properties, const char *key, int *err); +double e_hal_property_double_get(E_Hal_Properties *properties, const char *key, int *err); +const Eina_List *e_hal_property_strlist_get(E_Hal_Properties *properties, const char *key, int *err); + +#endif diff --git a/src/lib/notification/E_Notification_Daemon.h b/src/lib/notification/E_Notification_Daemon.h new file mode 100644 index 0000000..fc530ba --- /dev/null +++ b/src/lib/notification/E_Notification_Daemon.h @@ -0,0 +1,95 @@ +#ifndef E_NOTIFICATION_DAEMON_H +#define E_NOTIFICATION_DAEMON_H + +#define E_NOTIFICATION_DAEMON_VERSION "0.9" +#define E_NOTIFICATION_DAEMON_SUPPORTS_SPEC_VERSION "1.2" +#include + +#ifdef EAPI +#undef EAPI +#endif +#ifdef _MSC_VER +# ifdef BUILDING_DLL +# define EAPI __declspec(dllexport) +# else +# define EAPI __declspec(dllimport) +# endif +#else +# ifdef __GNUC__ +# if __GNUC__ >= 4 +# define EAPI __attribute__ ((visibility("default"))) +# else +# define EAPI +# endif +# else +# define EAPI +# endif +#endif + + +typedef struct E_Notification_Daemon E_Notification_Daemon; + +/* daemon callbacks */ +typedef int (*E_Notification_Daemon_Callback_Notify) (E_Notification_Daemon *daemon, E_Notification *notification); +typedef void (*E_Notification_Daemon_Callback_Close_Notification) (E_Notification_Daemon *daemon, unsigned int notification_id); + +/* gui */ +typedef struct E_Notification_View E_Notification_View; + +struct E_Notification_Daemon +{ + E_DBus_Connection *conn; + E_DBus_Interface *iface; + E_DBus_Object *obj; + + char *name; + char *vendor; + + struct + { + E_Notification_Daemon_Callback_Notify notify; + E_Notification_Daemon_Callback_Close_Notification close_notification; + } func; + void *data; + + int state; +}; + +#ifdef __cplusplus +extern "C" { +#endif + + EAPI int e_notification_daemon_init(void); + EAPI int e_notification_daemon_shutdown(void); + +/* daemon */ + EAPI E_Notification_Daemon *e_notification_daemon_add(const char *name, const char *vendor); + EAPI void e_notification_daemon_free(E_Notification_Daemon *d); +/* TODO + void e_notification_daemon_close(E_Notification_Daemon *d, + E_Notification *n, unsigned int reason); + void e_notification_daemon_action_invoke(E_Notification_Daemon *d, + E_Notification *n, const char *action_id); +*/ + + EAPI void e_notification_daemon_data_set(E_Notification_Daemon *daemon, void *data); + EAPI void *e_notification_daemon_data_get(E_Notification_Daemon *daemon); + + EAPI void e_notification_daemon_callback_notify_set(E_Notification_Daemon *daemon, E_Notification_Daemon_Callback_Notify func); + EAPI void e_notification_daemon_callback_close_notification_set(E_Notification_Daemon *daemon, E_Notification_Daemon_Callback_Close_Notification func); + + EAPI void e_notification_daemon_signal_notification_closed(E_Notification_Daemon *daemon, unsigned int id, E_Notification_Closed_Reason reason); + EAPI void e_notification_daemon_signal_action_invoked(E_Notification_Daemon *daemon, unsigned int notification_id, const char *action_id); + +/***** gui *****/ +/* TODO + E_Notification_View *e_notification_view_add(E_Notification_Daemon *d, E_Notification *n); + void e_notification_view_close(E_Notification_View *nv); + Evas_Object * e_notification_view_icon_get(Evas *evas, E_Notification *n); +*/ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/lib/notification/E_Notify.h b/src/lib/notification/E_Notify.h new file mode 100644 index 0000000..7fdf5c6 --- /dev/null +++ b/src/lib/notification/E_Notify.h @@ -0,0 +1,190 @@ +#ifndef E_NOTIFY_H +#define E_NOTIFY_H + +#include +#include +#include + +/** + * @defgroup ENotify_Group ENotify + * + * @{ + */ + + +/* notifications */ +typedef struct E_Notification_Image E_Notification_Image; +typedef struct E_Notification E_Notification; +typedef struct E_Notification_Action E_Notification_Action; + +typedef enum E_Notification_Urgency E_Notification_Urgency; +typedef enum E_Notification_Hint_Type E_Notification_Hint_Type; +typedef enum E_Notification_Closed_Reason E_Notification_Closed_Reason; + +/* method returns */ +typedef struct E_Notification_Return_Notify E_Notification_Return_Notify; +typedef struct E_Notification_Return_Get_Capabilities E_Notification_Return_Get_Capabilities; +typedef struct E_Notification_Return_Get_Server_Information E_Notification_Return_Get_Server_Information; + + +/* signal events */ +typedef struct E_Notification_Event_Action_Invoked E_Notification_Event_Action_Invoked; +typedef struct E_Notification_Event_Notification_Closed E_Notification_Event_Notification_Closed; + +/* enums */ + +enum E_Notification_Urgency +{ + E_NOTIFICATION_URGENCY_LOW, + E_NOTIFICATION_URGENCY_NORMAL, + E_NOTIFICATION_URGENCY_CRITICAL +}; + +enum E_Notification_Closed_Reason +{ + E_NOTIFICATION_CLOSED_EXPIRED, + E_NOTIFICATION_CLOSED_DISMISSED, + E_NOTIFICATION_CLOSED_REQUESTED, + E_NOTIFICATION_CLOSED_UNDEFINED +}; + +enum E_Notification_Hint_Type +{ + E_NOTIFICATION_HINT_URGENCY = (1 << 0), + E_NOTIFICATION_HINT_CATEGORY = (1 << 1), + E_NOTIFICATION_HINT_DESKTOP = (1 << 2), + E_NOTIFICATION_HINT_SOUND_FILE = (1 << 3), + E_NOTIFICATION_HINT_TRANSIENT = (1 << 4), + E_NOTIFICATION_HINT_RESIDENT = (1 << 5), + E_NOTIFICATION_HINT_ACTION_ICONS = (1 << 6), + E_NOTIFICATION_HINT_SUPPRESS_SOUND = 0x10, + E_NOTIFICATION_HINT_XY = 0x20, + E_NOTIFICATION_HINT_IMAGE_DATA = 0x40 +}; + +/* client method returns */ +struct E_Notification_Return_Notify +{ + unsigned int notification_id; + E_Notification *notification; +}; + +struct E_Notification_Return_Get_Capabilities +{ + Eina_List *capabilities; +}; + +struct E_Notification_Return_Get_Server_Information +{ + const char *name; + const char *vendor; + const char *version; + const char *spec_version; +}; + +/* signals */ +struct E_Notification_Event_Notification_Closed +{ + unsigned int notification_id; + E_Notification_Closed_Reason reason; +}; + +struct E_Notification_Event_Action_Invoked +{ + unsigned int notification_id; + char *action_id; +}; + +#ifdef __cplusplus +extern "C" { +#endif + + EAPI int e_notification_init(void); + EAPI int e_notification_shutdown(void); + +/* client */ + EAPI void e_notification_send(E_Notification *n, E_DBus_Callback_Func func, void *data); + EAPI void e_notification_get_capabilities(E_DBus_Callback_Func func, void *data); + EAPI void e_notification_get_server_information(E_DBus_Callback_Func func, void *data); + + +/* Notifications */ + + EAPI E_Notification *e_notification_new(void); + EAPI void e_notification_ref(E_Notification *n); + EAPI void e_notification_unref(E_Notification *n); + EAPI void e_notification_free(E_Notification *n); + + EAPI E_Notification *e_notification_full_new(const char *app_name, + unsigned int replaces_id, + const char *app_icon, + const char *summary, + const char *body, + int expire_timeout); + +/* notification mutators */ + EAPI void e_notification_id_set(E_Notification *note, unsigned int id); + EAPI void e_notification_app_name_set(E_Notification *n, const char *app_name); + EAPI void e_notification_app_icon_set(E_Notification *n, const char *app_icon); + EAPI void e_notification_summary_set(E_Notification *n, const char *summary); + EAPI void e_notification_body_set(E_Notification *n, const char *body); + EAPI void e_notification_replaces_id_set(E_Notification *n, int replaces_id); + EAPI void e_notification_timeout_set(E_Notification *n, int timeout); + EAPI void e_notification_closed_set(E_Notification *note, unsigned char closed); + +/* notification accessors */ + EAPI unsigned int e_notification_id_get(E_Notification *note); + EAPI const char *e_notification_app_name_get(E_Notification *n); + EAPI const char *e_notification_app_icon_get(E_Notification *n); + EAPI const char *e_notification_summary_get(E_Notification *n); + EAPI const char *e_notification_body_get(E_Notification *n); + EAPI int e_notification_replaces_id_get(E_Notification *note); + EAPI int e_notification_timeout_get(E_Notification *note); + EAPI unsigned char e_notification_closed_get(E_Notification *note); + +/* actions */ + EAPI void e_notification_action_add(E_Notification *n, const char *action_id, const char *action_name); + EAPI Eina_List *e_notification_actions_get(E_Notification *n); + EAPI const char *e_notification_action_id_get(E_Notification_Action *a); + EAPI const char *e_notification_action_name_get(E_Notification_Action *a); + +/* hint mutators */ + EAPI void e_notification_hint_transient_set(E_Notification *n, Eina_Bool transient); + EAPI void e_notification_hint_resident_set(E_Notification *n, Eina_Bool resident); + EAPI void e_notification_hint_action_icons_set(E_Notification *n, Eina_Bool action_icons); + EAPI void e_notification_hint_image_path_set(E_Notification *n, const char *path); + EAPI void e_notification_hint_urgency_set(E_Notification *n, char urgency); + EAPI void e_notification_hint_category_set(E_Notification *n, const char *category); + EAPI void e_notification_hint_desktop_set(E_Notification *n, const char *desktop); + EAPI void e_notification_hint_sound_file_set(E_Notification *n, const char *sound_file); + EAPI void e_notification_hint_suppress_sound_set(E_Notification *n, char suppress_sound); + EAPI void e_notification_hint_xy_set(E_Notification *n, int x, int y); + EAPI void e_notification_hint_image_data_set(E_Notification *n, E_Notification_Image *image); + +/* hint accessors */ + EAPI char e_notification_hint_urgency_get(E_Notification *n); + EAPI const char *e_notification_hint_category_get(E_Notification *n); + EAPI const char *e_notification_hint_desktop_get(E_Notification *n); + EAPI const char *e_notification_hint_sound_file_get(E_Notification *n); + EAPI const char *e_notification_hint_image_path_get(E_Notification *n); + EAPI char e_notification_hint_suppress_sound_get(E_Notification *n); + EAPI int e_notification_hint_xy_get(E_Notification *n, int *x, int *y); + EAPI E_Notification_Image *e_notification_hint_image_data_get(E_Notification *n); + /* icon_data is deprecated, we do not support setting it */ + EAPI E_Notification_Image *e_notification_hint_icon_data_get(E_Notification *n); + +/* image hint */ + EAPI E_Notification_Image *e_notification_image_new(void); + EAPI void e_notification_image_free(E_Notification_Image *img); + EAPI Eina_Bool e_notification_image_init(E_Notification_Image *img, Evas_Object *obj) EINA_WARN_UNUSED_RESULT; + EAPI Evas_Object *e_notification_image_evas_object_add(Evas *evas, E_Notification_Image *img); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif diff --git a/src/lib/notification/Makefile.am b/src/lib/notification/Makefile.am new file mode 100644 index 0000000..d2cc308 --- /dev/null +++ b/src/lib/notification/Makefile.am @@ -0,0 +1,24 @@ +MAINTAINERCLEANFILES = Makefile.in + +AM_CPPFLAGS = \ +-I $(top_srcdir)/src/lib/dbus \ +@EDBUS_CFLAGS@ \ +@EVAS_CFLAGS@ + +lib_LTLIBRARIES = libenotify.la +includes_HEADERS = E_Notify.h E_Notification_Daemon.h +includesdir = $(includedir)/e_dbus-@VMAJ@ + +libenotify_la_SOURCES = \ +notification.c \ +marshal.c \ +client.c \ +daemon.c + +libenotify_la_LIBADD = \ +@EDBUS_LIBS@ $(top_builddir)/src/lib/dbus/libedbus.la \ +@EVAS_LIBS@ + +libenotify_la_LDFLAGS = -version-info @version_info@ @release_info@ + +EXTRA_DIST = e_notify_private.h diff --git a/src/lib/notification/client.c b/src/lib/notification/client.c new file mode 100644 index 0000000..30ca889 --- /dev/null +++ b/src/lib/notification/client.c @@ -0,0 +1,63 @@ +#include "E_Notify.h" +#include "e_notify_private.h" + +static E_DBus_Connection *client_conn; +static int init_count = 0; + +EAPI int +e_notification_init(void) +{ + if (init_count) return ++init_count; + + if (!e_dbus_init()) return 0; + client_conn = e_dbus_bus_get(DBUS_BUS_SESSION); + if (!client_conn) + { + e_dbus_shutdown(); + return 0; + } + + return ++init_count; +} + +EAPI int +e_notification_shutdown(void) +{ + if (--init_count) return init_count; + e_dbus_connection_close(client_conn); + client_conn = NULL; + e_dbus_shutdown(); + return 0; +} + +/**** client api ****/ +EAPI void +e_notification_send(E_Notification *n, E_DBus_Callback_Func func, void *data) +{ + DBusMessage *msg; + + msg = e_notify_marshal_notify(n); + e_dbus_method_call_send(client_conn, msg, e_notify_unmarshal_notify_return, func, e_notify_free_notify_return, -1, data); + dbus_message_unref(msg); +} + +EAPI void +e_notification_get_capabilities(E_DBus_Callback_Func func, void *data) +{ + DBusMessage *msg; + + msg = e_notify_marshal_get_capabilities(); + e_dbus_method_call_send(client_conn, msg, e_notify_unmarshal_get_capabilities_return, func, e_notify_free_get_capabilities_return, -1, data); + dbus_message_unref(msg); +} + +EAPI void +e_notification_get_server_information(E_DBus_Callback_Func func, void *data) +{ + DBusMessage *msg; + + msg = e_notify_marshal_get_server_information(); + e_dbus_method_call_send(client_conn, msg, e_notify_unmarshal_get_server_information_return, func, e_notify_free_get_server_information_return, -1, data); + dbus_message_unref(msg); +} + diff --git a/src/lib/notification/daemon.c b/src/lib/notification/daemon.c new file mode 100644 index 0000000..be01e5e --- /dev/null +++ b/src/lib/notification/daemon.c @@ -0,0 +1,246 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "E_Notify.h" +#include "E_Notification_Daemon.h" +#include "e_notify_private.h" + +#include +#include + +static int init_count = 0; +static E_DBus_Interface *daemon_iface = NULL; + +static int e_notification_daemon_bus_init(E_Notification_Daemon *ndaemon); +static int e_notification_daemon_object_init(E_Notification_Daemon *ndaemon); + +DBusMessage * +method_get_capabilities(E_DBus_Object *obj __UNUSED__, DBusMessage *message) +{ + const char *capabilities[] = { + "body", + "actions", + NULL + }; + return e_notify_marshal_get_capabilities_return(message, capabilities); +} + +DBusMessage * +method_notify(E_DBus_Object *obj, DBusMessage *message) +{ + E_Notification *n; + E_Notification_Daemon *ndaemon; + int id = -1; + + ndaemon = e_dbus_object_data_get(obj); + n = e_notify_unmarshal_notify(message, NULL); + if (ndaemon->func.notify) + id = ndaemon->func.notify(ndaemon, n); + else + return dbus_message_new_error(message, E_NOTIFICATION_INTERFACE ".Unimplemented", "This functionality has not yet been implemented"); + + e_notification_unref(n); + return e_notify_marshal_notify_return(message, id); +} + +DBusMessage * +method_close_notification(E_DBus_Object *obj, DBusMessage *message) +{ + E_Notification_Daemon *ndaemon; + dbus_uint32_t id; + + ndaemon = e_dbus_object_data_get(obj); + id = e_notify_unmarshal_close_notification(message, NULL); + if (ndaemon->func.close_notification) + ndaemon->func.close_notification(ndaemon, id); + return dbus_message_new_method_return(message); +} + +DBusMessage * +method_get_server_information(E_DBus_Object *obj, DBusMessage *message) +{ + E_Notification_Daemon *ndaemon; + + ndaemon = e_dbus_object_data_get(obj); + + return e_notify_marshal_get_server_information_return(message, ndaemon->name, ndaemon->vendor, E_NOTIFICATION_DAEMON_VERSION, E_NOTIFICATION_DAEMON_SUPPORTS_SPEC_VERSION); +} + +/**** daemon api ****/ + +EAPI int +e_notification_daemon_init(void) +{ + if (init_count) return ++init_count; + if (!e_dbus_init()) return 0; + + daemon_iface = e_dbus_interface_new(E_NOTIFICATION_INTERFACE); + + return ++init_count; +} + +EAPI int +e_notification_daemon_shutdown(void) +{ + if (--init_count) return init_count; + e_dbus_shutdown(); + return 0; +} + +EAPI E_Notification_Daemon * +e_notification_daemon_add(const char *name, const char *vendor) +{ + E_Notification_Daemon *ndaemon; + + loginit(); + ndaemon = calloc(1, sizeof(E_Notification_Daemon)); + if (ndaemon) + e_notification_daemon_bus_init(ndaemon); + + if (!ndaemon || !ndaemon->conn) + { + if (ndaemon) free(ndaemon); + e_dbus_shutdown(); + return NULL; + } + + ndaemon->name = strdup(name); + ndaemon->vendor = strdup(vendor); + + e_dbus_interface_ref(daemon_iface); + ndaemon->iface = daemon_iface; + e_dbus_interface_method_add(ndaemon->iface, "GetCapabilities", "", "as", method_get_capabilities); + e_dbus_interface_method_add(ndaemon->iface, "Notify", "susssasa{sv}i", "u", method_notify); + e_dbus_interface_method_add(ndaemon->iface, "CloseNotification", "u", "u", method_close_notification); + e_dbus_interface_method_add(ndaemon->iface, "GetServerInformation", "", "ssss", method_get_server_information); + + return ndaemon; +} + +EAPI void +e_notification_daemon_free(E_Notification_Daemon *ndaemon) +{ + e_dbus_release_name(ndaemon->conn, E_NOTIFICATION_BUS_NAME, NULL, NULL); + if (ndaemon->obj) + { + e_dbus_object_interface_detach(ndaemon->obj, ndaemon->iface); + e_dbus_object_free(ndaemon->obj); + } + if (ndaemon->conn) e_dbus_connection_close(ndaemon->conn); + if (ndaemon->name) free(ndaemon->name); + if (ndaemon->vendor) free(ndaemon->vendor); + if (ndaemon->iface) e_dbus_interface_unref(ndaemon->iface); + free(ndaemon); +} + +EAPI void +e_notification_daemon_data_set(E_Notification_Daemon *ndaemon, void *data) +{ + ndaemon->data = data; +} + +EAPI void * +e_notification_daemon_data_get(E_Notification_Daemon *ndaemon) +{ + return ndaemon->data; +} + +EAPI void +e_notification_daemon_callback_notify_set(E_Notification_Daemon *ndaemon, E_Notification_Daemon_Callback_Notify func) +{ + ndaemon->func.notify = func; +} + +EAPI void +e_notification_daemon_callback_close_notification_set(E_Notification_Daemon *ndaemon, E_Notification_Daemon_Callback_Close_Notification func) +{ + ndaemon->func.close_notification = func; +} + +static void +cb_request_name(void *data, DBusMessage *msg, DBusError *err) +{ + E_Notification_Daemon *ndaemon = data; + dbus_uint32_t ret; + DBusError new_err; + + if (dbus_error_is_set(err)) + { + ERR("request_name: %s", err->message); + dbus_error_free(err); + return; + } + + INF("received response with signature: '%s'", + dbus_message_get_signature(msg)); + dbus_error_init(&new_err); + dbus_message_get_args + (msg, &new_err, DBUS_TYPE_UINT32, &ret, DBUS_TYPE_INVALID); + if (dbus_error_is_set(&new_err)) + { + ERR("req name unmarshal: %s", new_err.message); + dbus_error_free(&new_err); + return; + } + + switch (ret) + { + case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: + case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER: + e_notification_daemon_object_init(ndaemon); + break; + + case DBUS_REQUEST_NAME_REPLY_IN_QUEUE: + //XXX mark ndaemon as queued? + break; + + case DBUS_REQUEST_NAME_REPLY_EXISTS: + //XXX exit? + break; + } +} + +static int +e_notification_daemon_bus_init(E_Notification_Daemon *ndaemon) +{ + ndaemon->conn = e_dbus_bus_get(DBUS_BUS_SESSION); + if (!ndaemon->conn) return 0; + + // this blocks... make it async, and handle failure, etc + e_dbus_request_name(ndaemon->conn, E_NOTIFICATION_BUS_NAME, DBUS_NAME_FLAG_REPLACE_EXISTING, cb_request_name, ndaemon); + + return 1; +} + +static int +e_notification_daemon_object_init(E_Notification_Daemon *ndaemon) +{ + if (!ndaemon || !ndaemon->conn) return 0; + ndaemon->obj = e_dbus_object_add(ndaemon->conn, E_NOTIFICATION_PATH, ndaemon); + if (!ndaemon->obj) return 0; + + e_dbus_object_interface_attach(ndaemon->obj, ndaemon->iface); + + return 1; +} + +EAPI void +e_notification_daemon_signal_notification_closed(E_Notification_Daemon *ndaemon, unsigned int id, E_Notification_Closed_Reason reason) +{ + DBusMessage *msg = e_notify_marshal_notification_closed_signal(id, reason); + e_dbus_message_send(ndaemon->conn, + msg, + NULL, -1, NULL); + dbus_message_unref(msg); +} + +EAPI void +e_notification_daemon_signal_action_invoked(E_Notification_Daemon *ndaemon, unsigned int notification_id, const char *action_id) +{ + DBusMessage *msg = e_notify_marshal_action_invoked_signal(notification_id, action_id); + e_dbus_message_send(ndaemon->conn, + msg, + NULL, -1, NULL); + dbus_message_unref(msg); +} diff --git a/src/lib/notification/e_notify_private.h b/src/lib/notification/e_notify_private.h new file mode 100644 index 0000000..8a29e7c --- /dev/null +++ b/src/lib/notification/e_notify_private.h @@ -0,0 +1,109 @@ +#ifndef E_NOTIFY_PRIVATE_H +#define E_NOTIFY_PRIVATE_H + +#define E_NOTIFICATION_BUS_NAME "org.freedesktop.Notifications" +#define E_NOTIFICATION_INTERFACE "org.freedesktop.Notifications" +#define E_NOTIFICATION_PATH "/org/freedesktop/Notifications" + +#define e_notification_call_new(member) dbus_message_new_method_call(E_NOTIFICATION_BUS_NAME, E_NOTIFICATION_PATH, E_NOTIFICATION_INTERFACE, member) + +typedef void (*E_DBus_Variant_Marshaller) (DBusMessageIter *iter, void *data); +#define E_DBUS_VARIANT_MARSHALLER(x) ((E_DBus_Variant_Marshaller)(x)) + +void e_notify_marshal_dict_variant(DBusMessageIter *iter, const char *key, char *type_str, E_DBus_Variant_Marshaller func, void *data); +void e_notify_marshal_dict_string(DBusMessageIter *iter, const char *key, const char *value); +void e_notify_marshal_dict_byte(DBusMessageIter *iter, const char *key, char value); +void e_notify_marshal_dict_int(DBusMessageIter *iter, const char *key, int value); + +void e_notify_marshal_string_array(DBusMessageIter *iter, const char **strings); +void e_notify_marshal_string_list_as_array(DBusMessageIter *iter, Eina_List *strings); +Eina_List * e_notify_unmarshal_string_array_as_list(DBusMessageIter *iter, DBusError *err); +DBusMessage * e_notify_marshal_get_capabilities(); +DBusMessage * e_notify_marshal_get_capabilities_return(DBusMessage *method_call, const char **capabilities); +void * e_notify_unmarshal_get_capabilities_return(DBusMessage *msg, DBusError *err); +void e_notify_free_get_capabilities_return(void *data); +DBusMessage * e_notify_marshal_get_server_information(); +DBusMessage * e_notify_marshal_get_server_information_return(DBusMessage *method_call, const char *name, const char *vendor, const char *version, const char *spec_version); +void * e_notify_unmarshal_get_server_information_return(DBusMessage *msg, DBusError *err); +void e_notify_free_get_server_information_return(void *data); +DBusMessage * e_notify_marshal_close_notification(dbus_uint32_t id); +dbus_uint32_t e_notify_unmarshal_close_notification(DBusMessage *msg, DBusError *err); +DBusMessage * e_notify_marshal_notification_closed_signal(dbus_uint32_t id, dbus_uint32_t reason); +E_Notification_Event_Notification_Closed * e_notify_unmarshal_notification_closed_signal(DBusMessage *msg, DBusError *err); +DBusMessage * e_notify_marshal_action_invoked_signal(dbus_uint32_t id, const char *action_id); +E_Notification_Event_Action_Invoked * e_notify_unmarshal_action_invoked_signal(DBusMessage *msg, DBusError *err); +DBusMessage * e_notify_marshal_notify(E_Notification *n); +E_Notification * e_notify_unmarshal_notify(DBusMessage *msg, DBusError *err); +DBusMessage * e_notify_marshal_notify_return(DBusMessage *method_call, dbus_uint32_t notification_id); +void * e_notify_unmarshal_notify_return(DBusMessage *msg, DBusError *err); +void e_notify_free_notify_return(void *data); +void e_notify_unmarshal_notify_actions(E_Notification *n, DBusMessageIter *iter); +void e_notify_unmarshal_notify_hints(E_Notification *n, DBusMessageIter *iter); +void e_notify_marshal_hint_image(DBusMessageIter *iter, E_Notification_Image *img); +E_Notification_Image * e_notify_unmarshal_hint_image(DBusMessageIter *iter); +void loginit(void); +extern int _e_dbus_notify_log_dom; + +#ifndef E_DBUS_COLOR_DEFAULT +#define E_DBUS_COLOR_DEFAULT EINA_COLOR_CYAN +#endif + +#undef DBG +#undef INF +#undef WRN +#undef ERR + +#define DBG(...) EINA_LOG_DOM_DBG(_e_dbus_notify_log_dom, __VA_ARGS__) +#define INF(...) EINA_LOG_DOM_INFO(_e_dbus_notify_log_dom, __VA_ARGS__) +#define WRN(...) EINA_LOG_DOM_WARN(_e_dbus_notify_log_dom, __VA_ARGS__) +#define ERR(...) EINA_LOG_DOM_ERR(_e_dbus_notify_log_dom, __VA_ARGS__) + +struct E_Notification_Image +{ + int width; + int height; + int rowstride; + char has_alpha; + int bits_per_sample; + int channels; + unsigned char *data; +}; + +struct E_Notification +{ + int id; + const char *app_name; + unsigned int replaces_id; + const char *app_icon; + const char *summary; + const char *body; + int expire_timeout; + + Eina_List *actions; + + struct + { + char urgency; + const char *category; + const char *desktop; + const char *sound_file; + const char *image_path; + char suppress_sound; + int x, y; + E_Notification_Image *image_data; + E_Notification_Image *icon_data; + } hints; + + int hint_flags; + unsigned char closed; + + int refcount; +}; + +struct E_Notification_Action +{ + const char *id; + const char *name; +}; + +#endif diff --git a/src/lib/notification/marshal.c b/src/lib/notification/marshal.c new file mode 100644 index 0000000..ec4fbbd --- /dev/null +++ b/src/lib/notification/marshal.c @@ -0,0 +1,706 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "E_Notify.h" +#include "e_notify_private.h" + +void +e_notify_marshal_dict_variant(DBusMessageIter *iter, const char *key, char *type_str, E_DBus_Variant_Marshaller func, void *data) +{ + DBusMessageIter entry, variant; + + if (dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry)) + { + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + if (dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, type_str, &variant)) + { + func(&variant, data); + dbus_message_iter_close_container(&entry, &variant); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + dbus_message_iter_close_container(iter, &entry); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } +} + +void +e_notify_marshal_dict_string(DBusMessageIter *iter, const char *key, const char *value) +{ + DBusMessageIter entry, variant; + + if (dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry)) + { + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + if (dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "s", &variant)) + { + dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING, &value); + dbus_message_iter_close_container(&entry, &variant); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + dbus_message_iter_close_container(iter, &entry); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } +} + +void +e_notify_marshal_dict_byte(DBusMessageIter *iter, const char *key, char value) +{ + DBusMessageIter entry, variant; + + if (!key || !value) return; + + if (dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry)) + { + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + if (dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "y", &variant)) + { + dbus_message_iter_append_basic(&variant, DBUS_TYPE_BYTE, &value); + dbus_message_iter_close_container(&entry, &variant); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + dbus_message_iter_close_container(iter, &entry); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } +} + +void +e_notify_marshal_dict_int(DBusMessageIter *iter, const char *key, int value) +{ + DBusMessageIter entry, variant; + + if (!key || !value) return; + + if (dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry)) + { + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + if (dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "i", &variant)) + { + dbus_message_iter_append_basic(&variant, DBUS_TYPE_INT32, &value); + dbus_message_iter_close_container(&entry, &variant); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + dbus_message_iter_close_container(iter, &entry); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } +} + +void +e_notify_marshal_string_array(DBusMessageIter *iter, const char **strings) +{ + const char **str; + DBusMessageIter arr; + + if (dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &arr)) + { + for (str = strings; *str; str++) + dbus_message_iter_append_basic(&arr, DBUS_TYPE_STRING, str); + dbus_message_iter_close_container(iter, &arr); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } +} + +void +e_notify_marshal_string_list_as_array(DBusMessageIter *iter, Eina_List *strings) +{ + const char *str; + DBusMessageIter arr; + Eina_List *l; + + if (dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &arr)) + { + EINA_LIST_FOREACH (strings, l, str) + dbus_message_iter_append_basic(&arr, DBUS_TYPE_STRING, &str); + dbus_message_iter_close_container(iter, &arr); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } +} + +Eina_List * +e_notify_unmarshal_string_array_as_list(DBusMessageIter *iter, DBusError *err __UNUSED__) +{ + Eina_List *strings; + char *sig; + int ret; + DBusMessageIter arr; + + sig = dbus_message_iter_get_signature(iter); + ret = !strcmp(sig, "as"); + dbus_free(sig); + if (!ret) return NULL; + + strings = NULL; + + dbus_message_iter_recurse(iter, &arr); + while (dbus_message_iter_has_next(&arr)) + { + const char *str; + dbus_message_iter_get_basic(&arr, &str); + strings = eina_list_append(strings, eina_stringshare_add(str)); //XXX use eina_stringshare_instance? + dbus_message_iter_next(&arr); + } + return strings; +} + +DBusMessage * +e_notify_marshal_get_capabilities() +{ + DBusMessage *msg; + + msg = e_notification_call_new("GetCapabilities"); + return msg; +} + +DBusMessage * +e_notify_marshal_get_capabilities_return(DBusMessage *method_call, const char **capabilities) +{ + DBusMessage *msg; + DBusMessageIter iter; + + msg = dbus_message_new_method_return(method_call); + dbus_message_iter_init_append(msg, &iter); + e_notify_marshal_string_array(&iter, capabilities); + + return msg; +} + +void * +e_notify_unmarshal_get_capabilities_return(DBusMessage *msg, DBusError *err) +{ + DBusMessageIter iter; + E_Notification_Return_Get_Capabilities *ret; + + if (!dbus_message_has_signature(msg, "as")) return NULL; + + ret = calloc(1, sizeof(E_Notification_Return_Get_Capabilities)); + dbus_message_iter_init(msg, &iter); + ret->capabilities = e_notify_unmarshal_string_array_as_list(&iter, err); + + return ret; +} + +void +e_notify_free_get_capabilities_return(void *data) +{ + E_Notification_Return_Get_Capabilities *ret = data; + + if (!ret) return; + eina_list_free(ret->capabilities); + free(ret); +} + +DBusMessage * +e_notify_marshal_get_server_information() +{ + DBusMessage *msg; + + msg = e_notification_call_new("GetServerInformation"); + return msg; +} + +DBusMessage * +e_notify_marshal_get_server_information_return(DBusMessage *method_call, const char *name, const char *vendor, const char *version, const char *spec_version) +{ + DBusMessage *msg; + msg = dbus_message_new_method_return(method_call); + dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &vendor, DBUS_TYPE_STRING, &version, DBUS_TYPE_STRING, &spec_version, DBUS_TYPE_INVALID); + return msg; +} + +void * +e_notify_unmarshal_get_server_information_return(DBusMessage *msg, DBusError *err) +{ + E_Notification_Return_Get_Server_Information *info; + if (dbus_message_has_signature(msg, "ssss")) + { + info = calloc(1, sizeof(E_Notification_Return_Get_Server_Information)); + dbus_message_get_args(msg, err, + DBUS_TYPE_STRING, &(info->name), + DBUS_TYPE_STRING, &(info->vendor), + DBUS_TYPE_STRING, &(info->version), + DBUS_TYPE_STRING, &(info->spec_version), + DBUS_TYPE_INVALID + ); + } + else if (dbus_message_has_signature(msg, "sss")) + { + // stay combatible with servers <= 0.9 spec + info = calloc(1, sizeof(E_Notification_Return_Get_Server_Information)); + dbus_message_get_args(msg, err, + DBUS_TYPE_STRING, &(info->name), + DBUS_TYPE_STRING, &(info->vendor), + DBUS_TYPE_STRING, &(info->version), + DBUS_TYPE_INVALID + ); + } + else + { + return NULL; + } + if (dbus_error_is_set(err)) + { + free(info); + return NULL; + } + return info; +} + +void +e_notify_free_get_server_information_return(void *data) +{ + E_Notification_Return_Get_Server_Information *info = data; + + if (!info) return; + free(info); +} + +DBusMessage * +e_notify_marshal_close_notification(dbus_uint32_t id) +{ + DBusMessage *msg; + + msg = e_notification_call_new("CloseNotification"); + dbus_message_append_args(msg, DBUS_TYPE_UINT32, &id, DBUS_TYPE_INVALID); + return msg; +} + +dbus_uint32_t +e_notify_unmarshal_close_notification(DBusMessage *msg, DBusError *err) +{ + dbus_uint32_t id; + if (!dbus_message_has_signature(msg, "u")) return 0; + dbus_message_get_args(msg, err, DBUS_TYPE_UINT32, &id, DBUS_TYPE_INVALID); + if (err && dbus_error_is_set(err)) + return 0; + + return id; +} + +DBusMessage * +e_notify_marshal_notification_closed_signal(dbus_uint32_t id, dbus_uint32_t reason) +{ + DBusMessage *msg; + msg = dbus_message_new_signal(E_NOTIFICATION_PATH, E_NOTIFICATION_INTERFACE, "NotificationClosed"); + dbus_message_append_args(msg, DBUS_TYPE_UINT32, &id, DBUS_TYPE_UINT32, &reason, DBUS_TYPE_INVALID); + return msg; +} + +E_Notification_Event_Notification_Closed * +e_notify_unmarshal_notification_closed_signal(DBusMessage *msg, DBusError *err) +{ + E_Notification_Event_Notification_Closed *ev; + + if (!dbus_message_has_signature(msg, "uu")) + { + dbus_set_error(err, DBUS_ERROR_INVALID_SIGNATURE, ""); + return NULL; + } + ev = calloc(1, sizeof(E_Notification_Event_Notification_Closed)); + dbus_message_get_args(msg, err, DBUS_TYPE_UINT32, &(ev->notification_id), DBUS_TYPE_UINT32, &(ev->reason), DBUS_TYPE_INVALID); + if (dbus_error_is_set(err)) + { + free(ev); + return NULL; + } + return ev; +} + +DBusMessage * +e_notify_marshal_action_invoked_signal(dbus_uint32_t id, const char *action_id) +{ + DBusMessage *msg; + msg = dbus_message_new_signal(E_NOTIFICATION_PATH, E_NOTIFICATION_INTERFACE, "ActionInvoked"); + dbus_message_append_args(msg, DBUS_TYPE_UINT32, &id, DBUS_TYPE_STRING, &action_id, DBUS_TYPE_INVALID); + return msg; +} + +E_Notification_Event_Action_Invoked * +e_notify_unmarshal_action_invoked_signal(DBusMessage *msg, DBusError *err) +{ + E_Notification_Event_Action_Invoked *ev; + + if (!dbus_message_has_signature(msg, "us")) + { + dbus_set_error(err, DBUS_ERROR_INVALID_SIGNATURE, ""); + return NULL; + } + ev = calloc(1, sizeof(E_Notification_Event_Action_Invoked)); + dbus_message_get_args(msg, err, DBUS_TYPE_UINT32, &(ev->notification_id), DBUS_TYPE_STRING, &(ev->action_id), DBUS_TYPE_INVALID); + if (dbus_error_is_set(err)) + { + free(ev); + return NULL; + } + return ev; +} + +DBusMessage * +e_notify_marshal_notify(E_Notification *n) +{ + DBusMessage *msg; + DBusMessageIter iter, sub; + Eina_List *l; + + if (!n->app_name) n->app_name = eina_stringshare_add(""); + if (!n->app_icon) n->app_icon = eina_stringshare_add(""); + if (!n->summary) n->summary = eina_stringshare_add(""); + if (!n->body) n->body = eina_stringshare_add(""); + + msg = e_notification_call_new("Notify"); + dbus_message_append_args(msg, + DBUS_TYPE_STRING, &(n->app_name), + DBUS_TYPE_UINT32, &(n->replaces_id), + DBUS_TYPE_STRING, &(n->app_icon), + DBUS_TYPE_STRING, &(n->summary), + DBUS_TYPE_STRING, &(n->body), + DBUS_TYPE_INVALID + ); + + dbus_message_iter_init_append(msg, &iter); + if (dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) + { + if (n->actions) + { + E_Notification_Action *action; + EINA_LIST_FOREACH (n->actions, l, action) + { + dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &(action->id)); + dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &(action->name)); + } + } + dbus_message_iter_close_container(&iter, &sub); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + + /* hints */ + if (dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub)) + { + if (n->hints.urgency) /* we only need to send this if its non-zero*/ + e_notify_marshal_dict_byte(&sub, "urgency", n->hints.urgency); + if (n->hints.category) + e_notify_marshal_dict_string(&sub, "category", n->hints.category); + if (n->hints.desktop) + e_notify_marshal_dict_string(&sub, "desktop_entry", n->hints.desktop); + if (n->hints.image_data) + e_notify_marshal_dict_variant(&sub, "image-data", "(iiibiiay)", E_DBUS_VARIANT_MARSHALLER(e_notify_marshal_hint_image), n->hints.image_data); + if (n->hints.sound_file) + e_notify_marshal_dict_string(&sub, "sound-file", n->hints.sound_file); + if (n->hints.suppress_sound) /* we only need to send this if its true */ + e_notify_marshal_dict_byte(&sub, "suppress-sound", n->hints.suppress_sound); + if (n->hints.x > -1 && n->hints.y > -1) + { + e_notify_marshal_dict_int(&sub, "x", n->hints.x); + e_notify_marshal_dict_int(&sub, "y", n->hints.y); + } + dbus_message_iter_close_container(&iter, &sub); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &(n->expire_timeout)); + return msg; +} + +E_Notification * +e_notify_unmarshal_notify(DBusMessage *msg, DBusError *err __UNUSED__) +{ + E_Notification *n; + const char *s_val; + dbus_uint32_t u_val; + dbus_int32_t i_val; + DBusMessageIter iter; + if (!dbus_message_has_signature(msg, "susssasa{sv}i")) return NULL; + + n = e_notification_new(); + if (!n) return NULL; + dbus_message_iter_init(msg, &iter); + + dbus_message_iter_get_basic(&iter, &s_val); + e_notification_app_name_set(n, s_val); + dbus_message_iter_next(&iter); + + dbus_message_iter_get_basic(&iter, &u_val); + e_notification_replaces_id_set(n, u_val); + dbus_message_iter_next(&iter); + + dbus_message_iter_get_basic(&iter, &s_val); + e_notification_app_icon_set(n, s_val); + dbus_message_iter_next(&iter); + + dbus_message_iter_get_basic(&iter, &s_val); + e_notification_summary_set(n, s_val); + dbus_message_iter_next(&iter); + + dbus_message_iter_get_basic(&iter, &s_val); + e_notification_body_set(n, s_val); + dbus_message_iter_next(&iter); + + e_notify_unmarshal_notify_actions(n, &iter); + dbus_message_iter_next(&iter); + + e_notify_unmarshal_notify_hints(n, &iter); + dbus_message_iter_next(&iter); + + dbus_message_iter_get_basic(&iter, &i_val); + e_notification_timeout_set(n, i_val); + + return n; +} + +DBusMessage * +e_notify_marshal_notify_return(DBusMessage *method_call, dbus_uint32_t notification_id) +{ + DBusMessage *msg; + msg = dbus_message_new_method_return(method_call); + dbus_message_append_args(msg, DBUS_TYPE_UINT32, ¬ification_id, DBUS_TYPE_INVALID); + return msg; +} + +void * +e_notify_unmarshal_notify_return(DBusMessage *msg, DBusError *err) +{ + E_Notification_Return_Notify *ret; + ret = calloc(1, sizeof(E_Notification_Return_Notify)); + dbus_message_get_args(msg, err, DBUS_TYPE_UINT32, &(ret->notification_id), DBUS_TYPE_INVALID); + if (dbus_error_is_set(err)) + { + free(ret); + return NULL; + } + return ret; +} + +void +e_notify_free_notify_return(void *data) +{ + E_Notification_Return_Notify *ret = data; + + if (!ret) return; + free(ret); +} + +void +e_notify_unmarshal_notify_actions(E_Notification *n, DBusMessageIter *iter) +{ + DBusMessageIter arr; + const char *id, *name; + dbus_message_iter_recurse(iter, &arr); + while (dbus_message_iter_has_next(&arr)) + { + dbus_message_iter_get_basic(&arr, &id); + dbus_message_iter_next(&arr); + dbus_message_iter_get_basic(&arr, &name); + dbus_message_iter_next(&arr); + e_notification_action_add(n, id, name); + } +} + +void +e_notify_unmarshal_notify_hints(E_Notification *n, DBusMessageIter *iter) +{ + DBusMessageIter arr; + const char *key; + int x_set = 0, y_set = 0; + int x, y; + dbus_message_iter_recurse(iter, &arr); + + if (dbus_message_iter_get_arg_type(&arr) == DBUS_TYPE_INVALID) + return; + + do + { + DBusMessageIter dict; + dbus_message_iter_recurse(&arr, &dict); + do + { + DBusMessageIter variant; + const char *s_val; + char y_val; + dbus_bool_t b_val; + + dbus_message_iter_get_basic(&dict, &key); + dbus_message_iter_next(&dict); + dbus_message_iter_recurse(&dict, &variant); + + if (!strcmp(key, "urgency")) + { + dbus_message_iter_get_basic(&variant, &y_val); + e_notification_hint_urgency_set(n, y_val); + } + else if (!strcmp(key, "category")) + { + dbus_message_iter_get_basic(&variant, &s_val); + e_notification_hint_category_set(n, s_val); + } + else if (!strcmp(key, "desktop-entry")) + { + dbus_message_iter_get_basic(&variant, &s_val); + e_notification_hint_desktop_set(n, s_val); + } + else if ((!strncmp(key, "image", 5))) + { + if ((key[5] != '-') && (key[5] != '_')) continue; + if (!strcmp(key + 6, "path")) + { + dbus_message_iter_get_basic(&variant, &s_val); + e_notification_hint_image_path_set(n, s_val); + } + else if (!strcmp(key + 6, "data")) + { + dbus_message_iter_recurse(&dict, &variant); + n->hints.image_data = e_notify_unmarshal_hint_image(&variant); + } + } + else if (!strcmp(key, "sound-file")) + { + dbus_message_iter_get_basic(&variant, &s_val); + e_notification_hint_sound_file_set(n, s_val); + } + else if (!strcmp(key, "suppress-sound")) + { + dbus_message_iter_get_basic(&variant, &b_val); + e_notification_hint_suppress_sound_set(n, b_val); + } + else if (!strcmp(key, "transient")) + { + dbus_message_iter_get_basic(&variant, &b_val); + e_notification_hint_transient_set(n, b_val); + } + else if (!strcmp(key, "resident")) + { + dbus_message_iter_get_basic(&variant, &b_val); + e_notification_hint_resident_set(n, b_val); + } + else if (!strcmp(key, "x")) + { + dbus_message_iter_get_basic(&variant, &x); + x_set = 1; + } + else if (!strcmp(key, "y")) + { + dbus_message_iter_get_basic(&variant, &y); + y_set = 1; + } + else if (!strcmp(key, "icon_data")) + { + dbus_message_iter_recurse(&dict, &variant); + n->hints.icon_data = e_notify_unmarshal_hint_image(&variant); + } + } + while (dbus_message_iter_next(&dict)); + } + while (dbus_message_iter_next(&arr)); + + if (x_set && y_set) + e_notification_hint_xy_set(n, x, y); +} + +void +e_notify_marshal_hint_image(DBusMessageIter *iter, E_Notification_Image *img) +{ + DBusMessageIter sub, arr; + int data_len = 0; + + data_len = ((img->height - 1) * img->rowstride) + (img->width * (((img->channels * img->bits_per_sample) + 7) / 8)); + if (dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &sub)) + { + dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &(img->width)); + dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &(img->height)); + dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &(img->rowstride)); + dbus_message_iter_append_basic(&sub, DBUS_TYPE_BOOLEAN, &(img->has_alpha)); + dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &(img->bits_per_sample)); + dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &(img->channels)); + if (dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY, "y", &arr)) + { + dbus_message_iter_append_fixed_array(&arr, DBUS_TYPE_BYTE, &(img->data), data_len); + dbus_message_iter_close_container(&sub, &arr); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + dbus_message_iter_close_container(iter, &sub); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } +} + +E_Notification_Image * +e_notify_unmarshal_hint_image(DBusMessageIter *iter) +{ + DBusMessageIter sub, arr; + char *byte_array; + int array_len; + E_Notification_Image *img; + char *sig; + int sig_matches; + + sig = dbus_message_iter_get_signature(iter); + sig_matches = strcmp(sig, "(iiibiiay)"); + dbus_free(sig); + if (sig_matches) return NULL; + + img = e_notification_image_new(); + if (!img) return NULL; + + dbus_message_iter_recurse(iter, &sub); + dbus_message_iter_get_basic(&sub, &(img->width)); + dbus_message_iter_next(&sub); + dbus_message_iter_get_basic(&sub, &(img->height)); + dbus_message_iter_next(&sub); + dbus_message_iter_get_basic(&sub, &(img->rowstride)); + dbus_message_iter_next(&sub); + dbus_message_iter_get_basic(&sub, &(img->has_alpha)); + dbus_message_iter_next(&sub); + dbus_message_iter_get_basic(&sub, &(img->bits_per_sample)); + dbus_message_iter_next(&sub); + dbus_message_iter_get_basic(&sub, &(img->channels)); + dbus_message_iter_next(&sub); + + dbus_message_iter_recurse(&sub, &arr); + dbus_message_iter_get_fixed_array(&arr, &(byte_array), &array_len); + img->data = malloc(array_len); + memcpy(img->data, byte_array, array_len); + + return img; +} + diff --git a/src/lib/notification/notification-spec.xml b/src/lib/notification/notification-spec.xml new file mode 100644 index 0000000..2923fa1 --- /dev/null +++ b/src/lib/notification/notification-spec.xml @@ -0,0 +1,1277 @@ + + +
+ + Desktop Notifications Specification + Version 1.2 + 28 October 2010 + + + Mike + Hearn + +
+ mike@navi.cx +
+
+
+ + Christian + Hammond + +
+ chipx86@chipx86.com +
+
+
+ + William Jon + McCann + +
+ jmccann@redhat.com +
+
+
+
+
+ + + Introduction + + This is a draft standard for a desktop notifications service, + through which applications can generate passive popups to notify + the user in an asynchronous manner of events. + + + This specification explicitly does not include other types of + notification presentation such as modal message boxes, window manager + decorations or window list annotations. + + + Example use cases include: + + + Messages from chat programs + Scheduled alarm + Completed file transfer + New mail notification + Low disk space/battery warnings + + + + + Basic Design + + In order to ensure that multiple notifications can easily be + displayed at once, and to provide a convenient implementation, all + notifications are controlled by a single session-scoped service which + exposes a D-BUS interface. + + + On startup, a conforming implementation should take the + org.freedesktop.Notifications service on + the session bus. This service will be referred to as the "notification + server" or just "the server" in this document. It can optionally be + activated automatically by the bus process, however this is not required + and notification server clients must not assume that it is available. + + + The server should implement the + org.freedesktop.Notifications interface on + an object with the path "/org/freedesktop/Notifications". + This is the only interface required by this version of the specification. + + + A notification has the following components: + + + Notification Components + + + + Component + Description + + + + + Application Name + + This is the optional name of the application sending the notification. + This should be the application's formal name, rather than some sort + of ID. An example would be "FredApp E-Mail Client," rather than + "fredapp-email-client." + + + + Replaces ID + + An optional ID of an existing notification that this + notification is intended to replace. + + + + Notification Icon + + The notification icon. See . + + + + Summary + + This is a single line overview of the notification. For instance, + "You have mail" or "A friend has come online". It should generally + not be longer than 40 characters, though this is not a requirement, + and server implementations should word wrap if necessary. The summary + must be encoded using UTF-8. + + + + Body + + + This is a multi-line body of text. Each line is a paragraph, server + implementations are free to word wrap them as they see fit. + + + The body may contain simple markup as specified in + . It must be encoded using UTF-8. + + + If the body is omitted, just the summary is displayed. + + + + + Actions + + + The actions send a request message back to the notification client + when invoked. This functionality may not be implemented by the + notification server, conforming clients should check if it is available + before using it (see the GetCapabilities message in + ). An implementation is free to ignore any + requested by the client. As an example one possible rendering of + actions would be as buttons in the notification popup. + + + Actions are sent over as a list of pairs. Each even element in the + list (starting at index 0) represents the identifier for the action. + Each odd element in the list is the localized string that will be + displayed to the user. + + + The default action (usually invoked my clicking the notification) + should have a key named "default". The name can + be anything, though implementations are free not to display it. + + + + + Hints + + + Hints are a way to provide extra data to a notification server that + the server may be able to make use of. + + See for a list of available hints. + + + + Expiration Timeout + + + The timeout time in milliseconds since the display of the notification + at which the notification should automatically close. + + + If -1, the notification's expiration time is dependent on the + notification server's settings, and may vary for the type of + notification. + + + If 0, the notification never expires. + + + + + +
+ + Each notification displayed is allocated a unique ID by the server. + This is unique within the session. While the notification server is + running, the ID will not be recycled unless the capacity of a uint32 is + exceeded. + + + This can be used to hide the notification before the expiration timeout + is reached. It can also be used to atomically replace the notification + with another. This allows you to (for instance) modify the contents of + a notification while it's on-screen. + +
+ + + Backwards Compatibility + + Clients should try and avoid making assumptions about the presentation and + abilities of the notification server. The message content is the most + important thing. + + + Clients can check with the server what capabilities are supported + using the GetCapabilities message. See + . + + + If a client requires a response from a passive popup, it should be + coded such that a non-focus-stealing message box can be used in the + case that the notification server does not support this feature. + + + + + Markup + + Body text may contain markup. The markup is XML-based, and consists + of a small subset of HTML along with a few additional tags. + + + The following tags should be supported by the notification server. + Though it is optional, it is recommended. Notification servers that do + not support these tags should filter them out. + + + + + + + b ... + b + + Bold + + + + i ... + i + + Italic + + + + u ... + u + + Underline + + + + a href="..." ... + a + + Hyperlink + + + + img src="..." alt="..." + + Image + + + + + + A full-blown HTML implementation is not required of this spec, and + notifications should never take advantage of tags that are not listed + above. As notifications are not a substitute for web browsers or complex + dialogs, advanced layout is not necessary, and may in fact limit the + number of systems that notification services can run on, due to memory + usage and screen space. Such examples are PDAs, certain cell phones, and + slow PCs or laptops with little memory. + + + For the same reason, a full XML or XHTML implementation using XSLT or + CSS stylesheets is not part of this specification. Information that + must be presented in a more complex form should use an application-specific + dialog, a web browser, or some other display mechanism. + + + The tags specified above mark up the content in a way that allows them + to be stripped out on some implementations without impacting the actual + content. + + + + Hyperlinks + + Hyperlinks allow for linking one or more words to a URI. There is no + requirement to allow for images to be linked, and it is highly suggested + that implementations do not allow this, as there is no clean-looking, + standard visual indicator for a hyperlinked image. + + + Hyperlinked text should appear in the standard blue underline format. + + + Hyperlinks cannot function as a replacement for actions. They are + used to link to local directories or remote sites using standard URI + schemes. + + + Implementations are not required to support hyperlinks. + + + + + Images + + Images may be placed in the notification, but this should be done with + caution. The image should never exceed 200x100, but this should be thought + of as a maximum size. Images should always have alternative text + provided through the alt="..." attribute. + + + Image data cannot be embedded in the message itself. Images referenced + must always be local files. + + + Implementations are not required to support images. + + + + + + Icons and Images + + A notification can optionally have an associated icon and/or image. + + + The icon is defined by the "app_icon" parameter. + The image can be defined by the "image-path", the "image-data" hint or the + deprecated "icon_data" hint. + + + Priorities + + An implementation which only displays one image or icon must choose which one + to display using the following order: + + "image-data" + "image-path" + app_icon parameter + for compatibility reason, "icon_data" + + + + + An implementation which can display both the image and icon must show the + icon from the "app_icon" parameter and choose which image to display using + the following order: + + "image-data" + "image-path" + for compatibility reason, "icon_data" + + + + + + Formats + + The "image-data" and "icon_data" hints should be a DBus structure + of signature (iiibiiay). The components of this structure are as follows: + + width (i): Width of image in pixels + height (i): Height of image in pixels + rowstride (i): Distance in bytes between row starts + has_alpha (b): Whether the image has an alpha channel + bits_per_sample (i): Must always be 8 + channels (i): If has_alpha is TRUE, must be 4, otherwise 3 + data (ay): The image data, in RGB byte order + + This image format is derived from gdk-pixbuf. + + + The "app_icon" parameter and "image-path" hint should be either an URI + (file:// is the only URI schema supported right now) or a name in a + freedesktop.org-compliant icon theme (not a GTK+ stock ID). + + + + + + Categories + + Notifications can optionally have a type indicator. Although neither + client or nor server must support this, some may choose to. Those servers + implementing categories may use them to intelligently display + the notification in a certain way, or group notifications of similar + types. + + + Categories are in + class.specific form. + class specifies the generic type of notification, and + specific specifies the more specific type of + notification. + + + If a specific type of notification does not exist for your notification, + but the generic kind does, a notification of type + class is acceptable. + + + Third parties, when defining their own categories, should discuss + the possibility of standardizing on the hint with other parties, preferably + in a place such as the + xdg + mailing list at + freedesktop.org. If it + warrants a standard, it will be added to the table above. If no + consensus is reached, the category should be in the form of + "x-vendor.class.name." + + + The following table lists standard notifications as defined by this spec. + More will be added in time. + + + Categories + + + + Type + Description + + + + + "device" + + A generic device-related notification that doesn't fit into + any other category. + + + + "device.added" + A device, such as a USB device, was added to the system. + + + "device.error" + A device had some kind of error. + + + "device.removed" + + A device, such as a USB device, was removed from the system. + + + + "email" + + A generic e-mail-related notification that doesn't fit into any + other category. + + + + "email.arrived" + A new e-mail notification. + + + "email.bounced" + A notification stating that an e-mail has bounced. + + + "im" + + A generic instant message-related notification that doesn't fit + into any other category. + + + + "im.error" + An instant message error notification. + + + "im.received" + A received instant message notification. + + + "network" + + A generic network notification that doesn't fit into any other + category. + + + + "network.connected" + + A network connection notification, such as successful sign-on to a + network service. This should not be confused with + device.added for new network devices. + + + + "network.disconnected" + + A network disconnected notification. This should not be confused with + device.removed for disconnected network devices. + + + + "network.error" + + A network-related or connection-related error. + + + + "presence" + + A generic presence change notification that doesn't fit into + any other category, such as going away or idle. + + + + "presence.offline" + An offline presence change notification. + + + "presence.online" + An online presence change notification. + + + "transfer" + + A generic file transfer or download notification that doesn't fit + into any other category. + + + + "transfer.complete" + A file transfer or download complete notification. + + + "transfer.error" + A file transfer or download error. + + + +
+
+ + + Urgency Levels + + Notifications have an urgency level associated with them. This defines + the importance of the notification. For example, "Joe Bob signed on" + would be a low urgency. "You have new mail" or "A USB device was unplugged" + would be a normal urgency. "Your computer is on fire" would be a critical + urgency. + + Urgency levels are defined as follows: + + Urgency Levels + + + + Type + Description + + + + + 0 + Low + + + 1 + Normal + + + 2 + Critical + + + +
+ + Developers must use their own judgement when deciding the urgency of a + notification. Typically, if the majority of programs are using the same + level for a specific type of urgency, other applications should follow + them. + + + For low and normal urgencies, server implementations may display the + notifications how they choose. They should, however, have a sane + expiration timeout dependent on the urgency level. + + + Critical notifications should not automatically expire, as they are + things that the user will most likely want to know about. They should + only be closed when the user dismisses them, for example, by clicking on + the notification. + +
+ + + Hints + + Hints are a way to provide extra data to a notification server that + the server may be able to make use of. + + + Neither clients nor notification servers are required to support any + hints. Both sides should assume that hints are not passed, and should + ignore any hints they do not understand. + + + Third parties, when defining their own hints, should discuss the + possibility of standardizing on the hint with other parties, preferably + in a place such as the + xdg + mailing list at + freedesktop.org. If it + warrants a standard, it will be added to the table above. If no + consensus is reached, the hint name should be in the form of + "x-vendor-name." + + + The value type for the hint dictionary in D-BUS is of the + DBUS_TYPE_VARIANT container type. This allows different + data types (string, integer, boolean, etc.) to be used for hints. When + adding a dictionary of hints, this type must be used, rather than putting + the actual hint value in as the dictionary value. + + + The following table lists the standard hints as defined by this + specification. Future hints may be proposed and added to this list + over time. Once again, implementations are not required to support these. + + + Standard Hints + + + + Name + Value Type + Description + Spec Version + + + + + "action-icons" + boolean + + When set, a server that has the "action-icons" capability will + attempt to interpret any action identifier as a named icon. + The localized display name will be used to annotate the icon + for accessibility purposes. The icon name should be compliant + with the Freedesktop.org Icon Naming Specification. + + >= 1.2 + + + "category" + string + + The type of notification this is. + + + + + "desktop-entry" + string + + This specifies the name of the desktop filename representing the + calling program. This should be the same as the prefix used for the + application's .desktop file. An example would be "rhythmbox" from + "rhythmbox.desktop". This can be used by the daemon to retrieve the + correct icon for the application, for logging purposes, etc. + + + + + "image-data" + (iiibiiay) + + This is a raw data image format which describes the width, height, + rowstride, has alpha, bits per sample, channels and image data + respectively. + + >= 1.2 + + + "image_data" + (iiibiiay) + + Deprecated. Use image-data instead. + + = 1.1 + + + "image-path" + string + + Alternative way to define the notification image. See . + + >= 1.2 + + + "image_path" + string + + Deprecated. Use image-path instead. + + = 1.1 + + + "icon_data" + (iiibiiay) + + Deprecated. Use image-data instead. + + < 1.1 + + + "resident" + boolean + + When set the server will not automatically remove the + notification when an action has been invoked. The notification + will remain resident in the server until it is explicitly + removed by the user or by the sender. This hint is likely only + useful when the server has the "persistence" capability. + + >= 1.2 + + + "sound-file" + string + + The path to a sound file to play when the notification pops up. + + + + + "sound-name" + string + + A themeable named sound from the freedesktop.org + sound naming specification + to play when the notification pops up. Similar to icon-name, only for + sounds. An example would be "message-new-instant". + + + + + "suppress-sound" + boolean + + Causes the server to suppress playing any sounds, if it has that + ability. This is usually set when the client itself is going to + play its own sound. + + + + + "transient" + boolean + + When set the server will treat the notification as transient + and by-pass the server's persistence capability, if it should + exist. + + >= 1.2 + + + "x" + int + + Specifies the X location on the screen that the notification should + point to. The "y" hint must also be specified. + + + + + "y" + int + + Specifies the Y location on the screen that the notification should + point to. The "x" hint must also be specified. + + + + + "urgency" + byte + + The urgency level. + + + + + +
+
+ + + D-BUS Protocol + + The following messages must be supported by all + implementations. + + + + Message commands + + + <literal>org.freedesktop.Notifications.GetCapabilities</literal> + + + STRING_ARRAY + org.freedesktop.Notifications.GetCapabilities + + + + + + This message takes no parameters. + + + It returns an array of strings. Each string describes an optional + capability implemented by the server. The following values are + defined by this spec: + + + Server Capabilities + + + + "action-icons" + + Supports using icons instead of text for displaying actions. + Using icons for actions must be enabled on a per-notification + basis using the "action-icons" hint. + + + + "actions" + + The server will provide the specified actions to the user. Even if + this cap is missing, actions may still be specified by the client, + however the server is free to ignore them. + + + + "body" + + Supports body text. Some implementations may only show the + summary (for instance, onscreen displays, marquee/scrollers) + + + + "body-hyperlinks" + + The server supports hyperlinks in the notifications. + + + + "body-images" + + The server supports images in the notifications. + + + + "body-markup" + + Supports markup in the body text. If marked up text is sent + to a server that does not give this cap, the markup will show + through as regular text so must be stripped clientside. + + + + "icon-multi" + + The server will render an animation of all the frames in a given + image array. The client may still specify multiple frames even if + this cap and/or "icon-static" is missing, however + the server is free to ignore them and use only the primary frame. + + + + "icon-static" + + Supports display of exactly 1 frame of any given image array. + This value is mutually exclusive with + "icon-multi", it is a protocol error for the + server to specify both. + + + + "persistence" + + The server supports persistence of notifications. + Notifications will be retained until they are acknowledged or + removed by the user or recalled by the sender. The presence + of this capability allows clients to depend on the server to + ensure a notification is seen and eliminate the need for + the client to display a reminding function (such as a status + icon) of its own. + + + + "sound" + + The server supports sounds on notifications. If returned, the + server must support the "sound-file" and + "suppress-sound" hints. + + + + +
+ + New vendor-specific caps may be specified as long as they start with + "x-vendor". For instance, + "x-gnome-foo-cap". Capability names must not + contain spaces. They are limited to alpha-numeric characters and dashes + ("-"). + +
+ + + <literal>org.freedesktop.Notifications.Notify</literal> + + + UINT32 + org.freedesktop.Notifications.Notify + + STRING app_name + UINT32 replaces_id + STRING app_icon + STRING summary + STRING body + ARRAY actions + DICT hints + INT32 expire_timeout + + + + Sends a notification to the notification server. + + + Notify Parameters + + + + Name + Type + Description + + + + + app_name + STRING + + The optional name of the application sending the notification. + Can be blank. + + + + replaces_id + UINT32 + + The optional notification ID that this notification replaces. The + server must atomically (ie with no flicker or other visual cues) + replace the given notification with this one. This allows clients to + effectively modify the notification while it's active. A value of + value of 0 means that this notification won't replace any + existing notifications. + + + + app_icon + STRING + + The optional program icon of the calling application. See . + Can be an empty string, indicating no icon. + + + + summary + STRING + The summary text briefly describing the notification. + + + body + STRING + The optional detailed body text. Can be empty. + + + actions + ARRAY + + Actions are sent over as a list of pairs. Each even element in + the list (starting at index 0) represents the identifier for the + action. Each odd element in the list is the localized string + that will be displayed to the user. + + + + hints + DICT + + Optional hints that can be passed to the server from the client + program. Although clients and servers should never assume each other + supports any specific hints, they can be used to pass along + information, such as the process PID or window ID, that the server + may be able to make use of. See . Can be + empty. + + + + expire_timeout + INT32 + + + The timeout time in milliseconds since the display of the notification at + which the notification should automatically close. + + + If -1, the notification's expiration time is dependent on the + notification server's settings, and may vary for the type of + notification. + + If 0, never expire. + + + + + +
+ + If replaces_id is 0, the return value is a + UINT32 that represent the notification. It is unique, and will not be + reused unless a MAXINT number of notifications + have been generated. An acceptable implementation may just use an + incrementing counter for the ID. The returned ID is always greater than + zero. Servers must make sure not to return zero as an ID. + + + If replaces_id is not 0, the returned value + is the same value as replaces_id. + +
+ + + <literal>org.freedesktop.Notifications.CloseNotification</literal> + + + void + org.freedesktop.Notifications.CloseNotification + + UINT32 id + + + + Causes a notification to be forcefully closed and removed from the user's + view. It can be used, for example, in the event that what the + notification pertains to is no longer relevant, or to cancel a + notification with no expiration time. + + + The NotificationClosed signal is emitted by this + method. + + + If the notification no longer exists, an empty D-BUS Error message is + sent back. + + + + + <literal>org.freedesktop.Notifications.GetServerInformation</literal> + + + + void + org.freedesktop.Notifications.GetServerInformation + + out STRING name + out STRING vendor + out STRING version + out STRING spec_version + + + + This message returns the information on the server. Specifically, + the server name, vendor, and version number. + + + GetServerInformation Return Values + + + + Name + Type + Description + + + + + name + STRING + The product name of the server. + + + vendor + STRING + + The vendor name. For example, "KDE," "GNOME," + "freedesktop.org," or "Microsoft." + + + + version + STRING + The server's version number. + + + spec_version + STRING + The specification version the server is compliant with. + + + +
+
+
+ + + Signals + + + <literal>org.freedesktop.Notifications.NotificationClosed</literal> + + + + org.freedesktop.Notifications.NotificationClosed + + UINT32 id + UINT32 reason + + + + A completed notification is one that has timed out, or has been + dismissed by the user. + + + NotificationClosed Parameters + + + + Name + Type + Description + + + + + id + UINT32 + The ID of the notification that was closed. + + + reason + UINT32 + + The reason the notification was closed. + 1 - The notification expired. + 2 - The notification was dismissed by the user. + 3 - The notification was closed by a call to + CloseNotification. + 4 - Undefined/reserved reasons. + + + + +
+ + The ID specified in the signal is invalidated + before the signal is sent and may not be used + in any further communications with the server. + +
+ + + <literal>org.freedesktop.Notifications.ActionInvoked</literal> + + + + org.freedesktop.Notifications.ActionInvoked + + UINT32 id + STRING action_key + + + + This signal is emitted when one of the following occurs: + + + + + The user performs some global "invoking" action upon a notification. + For instance, clicking somewhere on the notification itself. + + + + + The user invokes a specific action as specified in the original + Notify request. For example, clicking on an action button. + + + + + ActionInvoked Parameters + + + + Name + Type + Description + + + + + id + UINT32 + + The ID of the notification emitting the ActionInvoked signal. + + + + action_key + STRING + + The key of the action invoked. These match the keys sent over + in the list of actions. + + + + +
+ + + Clients should not assume the server will generate this signal. Some + servers may not support user interaction at all, or may not support + the concept of being able to "invoke" a notification. + + +
+
+
+
diff --git a/src/lib/notification/notification.c b/src/lib/notification/notification.c new file mode 100644 index 0000000..a56d2c5 --- /dev/null +++ b/src/lib/notification/notification.c @@ -0,0 +1,557 @@ +#include "E_Notify.h" +#include +#include +#include +#include +#include "e_notify_private.h" + +/* private functions */ +static Eina_List *e_notification_action_list_new(void); +static E_Notification_Action *e_notification_action_new(const char *id, + const char *name); + +int _e_dbus_notify_log_dom = -1; + +void +loginit(void) +{ + if (_e_dbus_notify_log_dom == -1) + _e_dbus_notify_log_dom = eina_log_domain_register("e_dbus_notify", E_DBUS_COLOR_DEFAULT); +} + +/* (con|de)structor */ + +EAPI E_Notification * +e_notification_full_new(const char *app_name, unsigned int replaces_id, const char *app_icon, const char *summary, const char *body, int expire_timeout) +{ + E_Notification *n; + + loginit(); + n = e_notification_new(); + if (!n) return NULL; + + n->app_name = eina_stringshare_add(app_name); + n->replaces_id = replaces_id; + n->app_icon = eina_stringshare_add(app_icon); + n->summary = eina_stringshare_add(summary); + n->body = eina_stringshare_add(body); + n->expire_timeout = expire_timeout; + n->hints.x = n->hints.y = -1; + + return n; +} + +EAPI E_Notification * +e_notification_new(void) +{ + E_Notification *n; + + loginit(); + n = calloc(1, sizeof(E_Notification)); + if (!n) return NULL; + n->refcount = 1; + + return n; +} + +EAPI void +e_notification_ref(E_Notification *n) +{ + loginit(); + n->refcount++; +} + +EAPI void +e_notification_unref(E_Notification *n) +{ + loginit(); + if (--n->refcount == 0) e_notification_free(n); +} + +EAPI void +e_notification_free(E_Notification *n) +{ + loginit(); + if (!n) return; + + eina_stringshare_del(n->app_name); + eina_stringshare_del(n->app_icon); + eina_stringshare_del(n->summary); + eina_stringshare_del(n->body); + + eina_list_free(n->actions); + + eina_stringshare_del(n->hints.category); + eina_stringshare_del(n->hints.desktop); + eina_stringshare_del(n->hints.sound_file); + if (n->hints.image_data) e_notification_image_free(n->hints.image_data); + if (n->hints.icon_data) e_notification_image_free(n->hints.icon_data); + free(n); +} + +/* mutators */ +EAPI void +e_notification_id_set(E_Notification *note, unsigned int id) +{ + loginit(); + note->id = id; +} + +EAPI void +e_notification_app_name_set(E_Notification *note, const char *app_name) +{ + loginit(); + eina_stringshare_replace(¬e->app_name, app_name); +} + +EAPI void +e_notification_app_icon_set(E_Notification *note, const char *app_icon) +{ + loginit(); + eina_stringshare_replace(¬e->app_icon, app_icon); +} + +EAPI void +e_notification_summary_set(E_Notification *note, const char *summary) +{ + loginit(); + eina_stringshare_replace(¬e->summary, summary); +} + +EAPI void +e_notification_body_set(E_Notification *note, const char *body) +{ + loginit(); + eina_stringshare_replace(¬e->body, body); +} + +EAPI void +e_notification_action_add(E_Notification *n, const char *action_id, const char *action_name) +{ + E_Notification_Action *a; + + loginit(); + if (!n->actions) + n->actions = e_notification_action_list_new(); + + a = e_notification_action_new(action_id, action_name); + n->actions = eina_list_append(n->actions, a); +} + +EAPI void +e_notification_replaces_id_set(E_Notification *note, int replaces_id) +{ + loginit(); + note->replaces_id = replaces_id; +} + +EAPI void +e_notification_timeout_set(E_Notification *note, int timeout) +{ + loginit(); + note->expire_timeout = timeout; +} + +EAPI void +e_notification_closed_set(E_Notification *note, unsigned char closed) +{ + loginit(); + note->closed = closed; +} + +/* accessors */ +EAPI unsigned int +e_notification_id_get(E_Notification *note) +{ + loginit(); + return note->id; +} + +EAPI const char * +e_notification_app_name_get(E_Notification *note) +{ + loginit(); + return note->app_name; +} + +EAPI const char * +e_notification_app_icon_get(E_Notification *note) +{ + loginit(); + return note->app_icon; +} + +EAPI const char * +e_notification_summary_get(E_Notification *note) +{ + loginit(); + return note->summary; +} + +EAPI const char * +e_notification_body_get(E_Notification *note) +{ + loginit(); + return note->body; +} + +EAPI Eina_List * +e_notification_actions_get(E_Notification *note) +{ + loginit(); + return note->actions; +} + +EAPI int +e_notification_replaces_id_get(E_Notification *note) +{ + loginit(); + return note->replaces_id; +} + +EAPI int +e_notification_timeout_get(E_Notification *note) +{ + loginit(); + return note->expire_timeout; +} + +EAPI unsigned char +e_notification_closed_get(E_Notification *note) +{ + loginit(); + return note->closed; +} + +/***** actions *****/ + +static Eina_List * +e_notification_action_list_new(void) +{ + Eina_List *alist; + + loginit(); + alist = NULL; + return alist; +} + +static E_Notification_Action * +e_notification_action_new(const char *id, const char *name) +{ + E_Notification_Action *act; + + loginit(); + act = malloc(sizeof(E_Notification_Action)); + act->id = eina_stringshare_add(id); + act->name = eina_stringshare_add(name); + return act; +} + +EAPI const char * +e_notification_action_id_get(E_Notification_Action *a) +{ + loginit(); + EINA_SAFETY_ON_NULL_RETURN_VAL(a, NULL); + return a->id; +} + +EAPI const char * +e_notification_action_name_get(E_Notification_Action *a) +{ + loginit(); + EINA_SAFETY_ON_NULL_RETURN_VAL(a, NULL); + return a->name; +} + +/********* hints *******/ +EAPI void +e_notification_hint_transient_set(E_Notification *n, Eina_Bool transient) +{ + loginit(); + if (transient) + n->hint_flags |= E_NOTIFICATION_HINT_TRANSIENT; + else + n->hint_flags ^= E_NOTIFICATION_HINT_TRANSIENT; +} + +EAPI void +e_notification_hint_resident_set(E_Notification *n, Eina_Bool resident) +{ + loginit(); + if (resident) + n->hint_flags |= E_NOTIFICATION_HINT_RESIDENT; + else + n->hint_flags ^= E_NOTIFICATION_HINT_RESIDENT; +} + +EAPI void +e_notification_hint_action_icons_set(E_Notification *n, Eina_Bool action_icons) +{ + loginit(); + if (action_icons) + n->hint_flags |= E_NOTIFICATION_HINT_ACTION_ICONS; + else + n->hint_flags ^= E_NOTIFICATION_HINT_ACTION_ICONS; +} + +EAPI void +e_notification_hint_urgency_set(E_Notification *n, char urgency) +{ + loginit(); + n->hints.urgency = urgency; + n->hint_flags |= E_NOTIFICATION_HINT_URGENCY; +} + +EAPI void +e_notification_hint_image_path_set(E_Notification *n, const char *path) +{ + loginit(); + eina_stringshare_replace(&n->hints.image_path, path); +} + +EAPI void +e_notification_hint_category_set(E_Notification *n, const char *category) +{ + loginit(); + eina_stringshare_replace(&n->hints.category, category); + n->hint_flags |= E_NOTIFICATION_HINT_CATEGORY; +} + +EAPI void +e_notification_hint_desktop_set(E_Notification *n, const char *desktop) +{ + loginit(); + eina_stringshare_replace(&n->hints.desktop, desktop); + n->hint_flags |= E_NOTIFICATION_HINT_DESKTOP; +} + +EAPI void +e_notification_hint_sound_file_set(E_Notification *n, const char *sound_file) +{ + loginit(); + eina_stringshare_replace(&n->hints.sound_file, sound_file); + n->hint_flags |= E_NOTIFICATION_HINT_SOUND_FILE; +} + +EAPI void +e_notification_hint_suppress_sound_set(E_Notification *n, char suppress_sound) +{ + loginit(); + n->hints.suppress_sound = suppress_sound; + n->hint_flags |= E_NOTIFICATION_HINT_SUPPRESS_SOUND; +} + +EAPI void +e_notification_hint_xy_set(E_Notification *n, int x, int y) +{ + loginit(); + n->hints.x = x; + n->hints.y = y; + n->hint_flags |= E_NOTIFICATION_HINT_XY; +} + +EAPI void +e_notification_hint_image_data_set(E_Notification *n, E_Notification_Image *image) +{ + loginit(); + n->hints.image_data = image; +} + +EAPI char +e_notification_hint_urgency_get(E_Notification *n) +{ + loginit(); + return n->hint_flags & E_NOTIFICATION_HINT_URGENCY ? n->hints.urgency : 1; +} + +EAPI const char * +e_notification_hint_category_get(E_Notification *n) +{ + loginit(); + return n->hints.category; +} + +EAPI const char * +e_notification_hint_desktop_get(E_Notification *n) +{ + loginit(); + return n->hints.desktop; +} + +EAPI const char * +e_notification_hint_image_path_get(E_Notification *n) +{ + loginit(); + return n->hints.image_path; +} + +EAPI const char * +e_notification_hint_sound_file_get(E_Notification *n) +{ + loginit(); + return n->hints.sound_file; +} + +EAPI char +e_notification_hint_suppress_sound_get(E_Notification *n) +{ + loginit(); + return n->hints.suppress_sound; +} + +EAPI int +e_notification_hint_xy_get(E_Notification *n, int *x, int *y) +{ + loginit(); + if (x) *x = n->hints.x; + if (y) *y = n->hints.y; + + return n->hint_flags & E_NOTIFICATION_HINT_XY ? 1 : 0; +} + +EAPI E_Notification_Image * +e_notification_hint_image_data_get(E_Notification *n) +{ + loginit(); + return n->hints.image_data; +} + +EAPI E_Notification_Image * +e_notification_hint_icon_data_get(E_Notification *n) +{ + loginit(); + return n->hints.icon_data; +} + +EAPI E_Notification_Image * +e_notification_image_new(void) +{ + E_Notification_Image *img; + + loginit(); + img = calloc(1, sizeof(E_Notification_Image)); + img->bits_per_sample = 8; + return img; +} + +EAPI Eina_Bool +e_notification_image_init(E_Notification_Image *img, Evas_Object *obj) +{ + int x, y, w = 0, h = 0; + unsigned char *d, *imgdata; + int rowstride; + int *s; + + loginit(); + EINA_SAFETY_ON_NULL_RETURN_VAL(img, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EINA_FALSE); + + evas_object_image_size_get(obj, &w, &h); + if ((w <= 0) || (h <= 0)) return EINA_FALSE; + imgdata = evas_object_image_data_get(obj, EINA_FALSE); + if (!imgdata) return EINA_FALSE; + + img->data = malloc(4 * w * h); + if (!img->data) + { + evas_object_image_data_set(obj, imgdata); + return EINA_FALSE; + } + img->channels = 4; + img->rowstride = 4 * w; + img->width = w; + img->height = h; + img->bits_per_sample = 8; + img->has_alpha = EINA_TRUE; + + rowstride = evas_object_image_stride_get(obj); + for (y = 0; y < img->height; y++) + { + s = (int *)(imgdata + (y * rowstride)); + d = img->data + (y * img->rowstride); + + for (x = 0; x < img->width; x++, s++) + { + *d++ = (*s >> 16) & 0xff; + *d++ = (*s >> 8) & 0xff; + *d++ = (*s) & 0xff; + *d++ = (*s >> 24) & 0xff; + } + } + + evas_object_image_data_set(obj, imgdata); + return EINA_TRUE; +} + +EAPI void +e_notification_image_free(E_Notification_Image *img) +{ + loginit(); + if (!img) return; + free(img->data); + free(img); +} + +static Eina_Bool +_e_notification_image_evas_object_fill(E_Notification_Image *img, Evas_Object *obj) +{ + unsigned char *imgdata; + + EINA_SAFETY_ON_NULL_RETURN_VAL(img, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EINA_FALSE); + + evas_object_image_colorspace_set(obj, EVAS_COLORSPACE_ARGB8888); + evas_object_image_alpha_set(obj, img->has_alpha); + evas_object_image_size_set(obj, img->width, img->height); + + imgdata = evas_object_image_data_get(obj, EINA_TRUE); + if (!imgdata) return EINA_FALSE; + + if (img->bits_per_sample == 8) + { + /* Although not specified. + * The data are very likely to come from a GdkPixbuf + * which align each row on a 4-bytes boundary when using RGB. + * And is RGBA otherwise. */ + int x, y, *d; + unsigned char *s; + int rowstride; + + rowstride = evas_object_image_stride_get(obj); + for (y = 0; y < img->height; y++) + { + s = img->data + (y * img->rowstride); + d = (int *)(imgdata + (y * rowstride)); + + for (x = 0; x < img->width; x++, s += img->channels, d++) + { + if (img->has_alpha) + *d = (s[3] << 24) | (s[0] << 16) | (s[1] << 8) | (s[2]); + else + *d = (0xff << 24) | (s[0] << 16) | (s[1] << 8) | (s[2]); + } + } + } + evas_object_image_data_update_add(obj, 0, 0, img->width, img->height); + evas_object_image_data_set(obj, imgdata); + + return EINA_TRUE; +} + +EAPI Evas_Object * +e_notification_image_evas_object_add(Evas *evas, E_Notification_Image *img) +{ + Evas_Object *o = NULL; + + loginit(); + if ((!evas) || (!img)) return NULL; + o = evas_object_image_filled_add(evas); + evas_object_resize(o, img->width, img->height); + if (!_e_notification_image_evas_object_fill(img, o)) + { + evas_object_del(o); + return NULL; + } + return o; +} + diff --git a/src/lib/ofono/E_Ofono.h b/src/lib/ofono/E_Ofono.h new file mode 100644 index 0000000..d96f74b --- /dev/null +++ b/src/lib/ofono/E_Ofono.h @@ -0,0 +1,122 @@ +#ifndef E_OFONO_H +#define E_OFONO_H + +#include +#include + +#include +#include +#include + +/** + * @defgroup EOfono_Group EOfono + * + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Ecore Events */ +extern int E_OFONO_EVENT_MANAGER_IN; +extern int E_OFONO_EVENT_MANAGER_OUT; +extern int E_OFONO_EVENT_ELEMENT_ADD; +extern int E_OFONO_EVENT_ELEMENT_DEL; +extern int E_OFONO_EVENT_ELEMENT_UPDATED; + +typedef struct _E_Ofono_Element E_Ofono_Element; + +struct _E_Ofono_Element +{ + const char *path; + const char *interface; + E_DBus_Signal_Handler *signal_handler; + Eina_Inlist *props; + + /* private */ + struct + { + Eina_Inlist *properties_get; + Eina_Inlist *property_set; + Eina_Inlist *send_sms; + } _pending; + struct + { + Ecore_Idler *changed; + } _idler; + Eina_Inlist *_listeners; + int _references; +}; + +/* General Public API */ +EAPI unsigned int e_ofono_system_init(E_DBus_Connection *edbus_conn) EINA_ARG_NONNULL(1); +EAPI unsigned int e_ofono_system_shutdown(void); + +/* Manager Methods */ +EAPI E_Ofono_Element * e_ofono_manager_get(void) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_ofono_manager_modems_get(Eina_Array **array); + +/* Modem Methods */ +EAPI Eina_Bool e_ofono_modem_powered_get(const E_Ofono_Element *element, Eina_Bool *powered) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_ofono_modem_powered_set(E_Ofono_Element *element, Eina_Bool powered, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_ofono_modem_name_get(const E_Ofono_Element *element, const char **name) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +/* NetworkRegistration Methods */ +EAPI Eina_Bool e_ofono_netreg_mode_get(const E_Ofono_Element *element, const char **mode) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_ofono_netreg_status_get(const E_Ofono_Element *element, const char **status) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_ofono_netreg_operator_get(const E_Ofono_Element *element, const char **op) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_ofono_netreg_strength_get(const E_Ofono_Element *element, uint8_t *strength) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +/* SMS Methods */ +EAPI Eina_Bool e_ofono_sms_sca_get(const E_Ofono_Element *element, const char **sca) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_ofono_sms_sca_set(E_Ofono_Element *element, const char *sca, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_ofono_sms_send_message(E_Ofono_Element *element, const char *number, const char *message, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1, 2, 3) EINA_WARN_UNUSED_RESULT; + +/* Low-Level API: + * + * Should just be used to work around problems until proper solution + * is made into e_ofono. + */ +EAPI Eina_Bool e_ofono_manager_sync_elements(void); + +EAPI Eina_Bool e_ofono_elements_get_all(unsigned int *count, E_Ofono_Element ***p_elements) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_ofono_elements_get_all_type(const char *type, unsigned int *count, E_Ofono_Element ***p_elements) EINA_ARG_NONNULL(1, 2, 3) EINA_WARN_UNUSED_RESULT; +EAPI E_Ofono_Element * e_ofono_element_get(const char *path, const char *interface) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +EAPI void e_ofono_element_listener_add(E_Ofono_Element *element, void (*cb)(void *data, const E_Ofono_Element *element), const void *data, void (*free_data)(void *data)) EINA_ARG_NONNULL(1, 2); +EAPI void e_ofono_element_listener_del(E_Ofono_Element *element, void (*cb)(void *data, const E_Ofono_Element *element), const void *data) EINA_ARG_NONNULL(1, 2); + +EAPI int e_ofono_element_ref(E_Ofono_Element *element) EINA_ARG_NONNULL(1); +EAPI int e_ofono_element_unref(E_Ofono_Element *element) EINA_ARG_NONNULL(1); + +EAPI void e_ofono_element_print(FILE *fp, const E_Ofono_Element *element) EINA_ARG_NONNULL(1, 2); + +EAPI Eina_Bool e_ofono_element_properties_sync(E_Ofono_Element *element) EINA_ARG_NONNULL(1); +EAPI Eina_Bool e_ofono_element_properties_sync_full(E_Ofono_Element *element, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1); + +EAPI Eina_Bool e_ofono_element_property_set(E_Ofono_Element *element, const char *prop, int type, const void *value) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_ofono_element_property_set_full(E_Ofono_Element *element, const char *prop, int type, const void *value, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_ofono_element_property_dict_set_full(E_Ofono_Element *element, const char *prop, const char *key, int type, const void *value, E_DBus_Method_Return_Cb cb, const void *data) EINA_ARG_NONNULL(1, 2, 3) EINA_WARN_UNUSED_RESULT; + +EAPI void e_ofono_element_properties_list(const E_Ofono_Element *element, Eina_Bool (*cb)(void *data, const E_Ofono_Element *element, const char *name, int type, const void *value), const void *data) EINA_ARG_NONNULL(1, 2); + +EAPI Eina_Bool e_ofono_element_property_type_get_stringshared(const E_Ofono_Element *element, const char *name, int *type) EINA_ARG_NONNULL(1, 2, 3) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_ofono_element_property_type_get(const E_Ofono_Element *element, const char *name, int *type) EINA_ARG_NONNULL(1, 2, 3) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_ofono_element_property_dict_get_stringshared(const E_Ofono_Element *element, const char *dict_name, const char *key_name, int *type, void *value) EINA_ARG_NONNULL(1, 2, 4) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_ofono_element_property_get_stringshared(const E_Ofono_Element *element, const char *name, int *type, void *value) EINA_ARG_NONNULL(1, 2, 4) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_ofono_element_property_get(const E_Ofono_Element *element, const char *name, int *type, void *value) EINA_ARG_NONNULL(1, 2, 4) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool e_ofono_element_is_manager(const E_Ofono_Element *element) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_ofono_element_is_modem(const E_Ofono_Element *element) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool e_ofono_element_is_netreg(const E_Ofono_Element *element) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* E_OFONO_H */ diff --git a/src/lib/ofono/Makefile.am b/src/lib/ofono/Makefile.am new file mode 100644 index 0000000..f90cb8d --- /dev/null +++ b/src/lib/ofono/Makefile.am @@ -0,0 +1,24 @@ +MAINTAINERCLEANFILES = Makefile.in + +AM_CPPFLAGS = \ +-I $(top_srcdir)/src/lib/dbus \ +@EDBUS_CFLAGS@ + +lib_LTLIBRARIES = libeofono.la +includes_HEADERS = E_Ofono.h +includesdir = $(includedir)/e_dbus-@VMAJ@ + +libeofono_la_SOURCES = \ +e_ofono.c \ +e_ofono_element.c \ +e_ofono_manager.c \ +e_ofono_modem.c \ +e_ofono_network_reg.c \ +e_ofono_sms.c + +libeofono_la_LIBADD = \ +@EDBUS_LIBS@ $(top_builddir)/src/lib/dbus/libedbus.la + +libeofono_la_LDFLAGS = -version-info @version_info@ @release_info@ + +EXTRA_DIST = e_ofono_private.h diff --git a/src/lib/ofono/e_ofono.c b/src/lib/ofono/e_ofono.c new file mode 100644 index 0000000..db0566f --- /dev/null +++ b/src/lib/ofono/e_ofono.c @@ -0,0 +1,339 @@ +#include "e_ofono_private.h" +#include +#include + +static E_DBus_Signal_Handler *cb_name_owner_changed = NULL; +static DBusPendingCall *pending_get_name_owner = NULL; +static unsigned int init_count = 0; +static char *unique_name = NULL; + +static const char bus_name[] = "org.ofono"; + +E_DBus_Connection *e_ofono_conn = NULL; + +EAPI int E_OFONO_EVENT_MANAGER_IN = 0; +EAPI int E_OFONO_EVENT_MANAGER_OUT = 0; +EAPI int E_OFONO_EVENT_ELEMENT_ADD = 0; +EAPI int E_OFONO_EVENT_ELEMENT_DEL = 0; +EAPI int E_OFONO_EVENT_ELEMENT_UPDATED = 0; + +const char *e_ofono_iface_manager = NULL; +const char *e_ofono_prop_modems = NULL; +const char *e_ofono_iface_modem = NULL; +const char *e_ofono_prop_name = NULL; +const char *e_ofono_prop_powered = NULL; +const char *e_ofono_prop_interfaces = NULL; +const char *e_ofono_iface_netreg = NULL; +const char *e_ofono_prop_mode = NULL; +const char *e_ofono_prop_status = NULL; +const char *e_ofono_prop_operator = NULL; +const char *e_ofono_prop_strength = NULL; +const char *e_ofono_iface_sms = NULL; +const char *e_ofono_prop_sca = NULL; +const char *e_ofono_method_send_sms = NULL; + +int _e_dbus_ofono_log_dom = -1; + +const char * +e_ofono_system_bus_name_get(void) +{ + return unique_name ? unique_name : bus_name; +} + +/*********************************************************************** +* Manager +***********************************************************************/ + +/* + * FIXME: Do we really need to call Manager.GetProperties()? + */ + +/** + * Synchronize elements with server. + * + * This will call Manager.GetProperties() on server, retrieve properties + * and some element paths and then request their properties. + * + * This call will add events E_OFONO_EVENT_ELEMENT_ADD and + * E_OFONO_EVENT_ELEMENT_UPDATED to the main loop. + * + * This will not remove stale elements. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_manager_sync_elements(void) +{ + E_Ofono_Element *manager; + + if (!unique_name) + return FALSE; + + manager = e_ofono_element_register(manager_path, e_ofono_iface_manager); + if (manager) + e_ofono_element_properties_sync(manager); + else + return FALSE; + + DBG("sync_manager: %s (%s)", unique_name, bus_name); + + return TRUE; +} + +static void +_e_ofono_system_name_owner_exit(void) +{ + e_ofono_manager_clear_elements(); + ecore_event_add(E_OFONO_EVENT_MANAGER_OUT, NULL, NULL, NULL); + + free(unique_name); + unique_name = NULL; +} + +static void +_e_ofono_system_name_owner_enter(const char *uid) +{ + DBG("enter ofono at %s (old was %s)", uid, unique_name); + if (unique_name && strcmp(unique_name, uid) == 0) + { + DBG("same unique_name for ofono, ignore."); + return; + } + + if (unique_name) + _e_ofono_system_name_owner_exit(); + + unique_name = strdup(uid); + + ecore_event_add(E_OFONO_EVENT_MANAGER_IN, NULL, NULL, NULL); + e_ofono_manager_sync_elements(); +} + +static void +_e_ofono_system_name_owner_changed(void *data __UNUSED__, DBusMessage *msg) +{ + DBusError err; + const char *name, *from, *to; + + dbus_error_init(&err); + if (!dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &from, + DBUS_TYPE_STRING, &to, + DBUS_TYPE_INVALID)) + { + ERR("could not get NameOwnerChanged arguments: %s: %s", + err.name, err.message); + dbus_error_free(&err); + return; + } + + if (strcmp(name, bus_name) != 0) + return; + + DBG("NameOwnerChanged from=[%s] to=[%s]", from, to); + + if (from[0] == '\0' && to[0] != '\0') + { + _e_ofono_system_name_owner_enter(to); + } + else if (from[0] != '\0' && to[0] == '\0') + { + DBG("exit ofono at %s", from); + if (strcmp(unique_name, from) != 0) + DBG("%s was not the known name %s, ignored.", from, unique_name); + else + _e_ofono_system_name_owner_exit(); + } + else + { + DBG("unknow change from %s to %s", from, to); + } +} + +static void +_e_ofono_get_name_owner(void *data __UNUSED__, DBusMessage *msg, DBusError *err) +{ + DBusMessageIter itr; + int t; + const char *uid; + + pending_get_name_owner = NULL; + + if (!_dbus_callback_check_and_init(msg, &itr, err)) + return; + + t = dbus_message_iter_get_arg_type(&itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_STRING)) + return; + + dbus_message_iter_get_basic(&itr, &uid); + if (!uid) + { + ERR("no name owner!"); + return; + } + + _e_ofono_system_name_owner_enter(uid); + return; +} + +/** + * Initialize E oFono (E_Ofono) system. + * + * This will connect and watch org.ofono.Manager and Element + * events and translate to Ecore main loop events, also provide a + * proxy for method invocation on server. + * + * Interesting events are: + * - E_OFONO_EVENT_MANAGER_IN: issued when ofono is avaiable. + * - E_OFONO_EVENT_MANAGER_OUT: issued when ofono connection is lost. + * - E_OFONO_EVENT_ELEMENT_ADD: element was added. + * - E_OFONO_EVENT_ELEMENT_DEL: element was deleted. + * - E_OFONO_EVENT_ELEMENT_UPDATED: element was updated (properties + * or state changed). + * + * Manager IN/OUT events do not provide any event information, just + * tells you that system is usable or not. After manager is out, all + * elements will be removed, so after this event do not use the system anymore. + * + * Element events will give you an element object. After DEL event callback + * returns, that element will not be valid anymore. + */ +unsigned int +e_ofono_system_init(E_DBus_Connection *edbus_conn) +{ + init_count++; + + if (init_count > 1) + return init_count; + + _e_dbus_ofono_log_dom = eina_log_domain_register + ("e_dbus_ofono", EINA_LOG_DEFAULT_COLOR); + + if(_e_dbus_ofono_log_dom < 0) + { + EINA_LOG_ERR + ("impossible to create a log domain for edbus_ofono module"); + return -1; + } + + if (E_OFONO_EVENT_MANAGER_IN == 0) + E_OFONO_EVENT_MANAGER_IN = ecore_event_type_new(); + + if (E_OFONO_EVENT_MANAGER_OUT == 0) + E_OFONO_EVENT_MANAGER_OUT = ecore_event_type_new(); + + if (E_OFONO_EVENT_ELEMENT_ADD == 0) + E_OFONO_EVENT_ELEMENT_ADD = ecore_event_type_new(); + + if (E_OFONO_EVENT_ELEMENT_DEL == 0) + E_OFONO_EVENT_ELEMENT_DEL = ecore_event_type_new(); + + if (E_OFONO_EVENT_ELEMENT_UPDATED == 0) + E_OFONO_EVENT_ELEMENT_UPDATED = ecore_event_type_new(); + + if (!e_ofono_iface_manager) + e_ofono_iface_manager = eina_stringshare_add("org.ofono.Manager"); + + if (!e_ofono_prop_modems) + e_ofono_prop_modems = eina_stringshare_add("Modems"); + + if (!e_ofono_iface_modem) + e_ofono_iface_modem = eina_stringshare_add("org.ofono.Modem"); + + if (!e_ofono_prop_name) + e_ofono_prop_name = eina_stringshare_add("Name"); + + if (!e_ofono_prop_powered) + e_ofono_prop_powered = eina_stringshare_add("Powered"); + + if (!e_ofono_prop_interfaces) + e_ofono_prop_interfaces = eina_stringshare_add("Interfaces"); + + if (!e_ofono_iface_netreg) + e_ofono_iface_netreg = eina_stringshare_add("org.ofono.NetworkRegistration"); + + if (!e_ofono_prop_mode) + e_ofono_prop_mode = eina_stringshare_add("Mode"); + + if (!e_ofono_prop_status) + e_ofono_prop_status = eina_stringshare_add("Status"); + + if (!e_ofono_prop_operator) + e_ofono_prop_operator = eina_stringshare_add("Operator"); + + if (!e_ofono_prop_strength) + e_ofono_prop_strength = eina_stringshare_add("Strength"); + + if (!e_ofono_iface_sms) + e_ofono_iface_sms = eina_stringshare_add("org.ofono.SmsManager"); + + if (!e_ofono_prop_sca) + e_ofono_prop_sca = eina_stringshare_add("ServiceCenterAddress"); + + if (!e_ofono_method_send_sms) + e_ofono_method_send_sms = eina_stringshare_add("SendMessage"); + + e_ofono_conn = edbus_conn; + cb_name_owner_changed = e_dbus_signal_handler_add + (e_ofono_conn, E_DBUS_FDO_BUS, E_DBUS_FDO_PATH, E_DBUS_FDO_INTERFACE, "NameOwnerChanged", + _e_ofono_system_name_owner_changed, NULL); + + if (pending_get_name_owner) + dbus_pending_call_cancel(pending_get_name_owner); + + pending_get_name_owner = e_dbus_get_name_owner + (e_ofono_conn, bus_name, _e_ofono_get_name_owner, NULL); + + e_ofono_elements_init(); + + return init_count; +} + +static inline void +_stringshare_del(const char **str) +{ + if (!*str) + return; + + eina_stringshare_del(*str); + *str = NULL; +} + +/** + * Shutdown ofono system. + * + * When count drops to 0 resources will be released and no calls should be + * made anymore. + */ +unsigned int +e_ofono_system_shutdown(void) +{ + if (init_count == 0) + { + ERR("ofono system already shut down."); + return 0; + } + + init_count--; + if (init_count > 0) + return init_count; + + _stringshare_del(&e_ofono_iface_manager); + _stringshare_del(&e_ofono_prop_modems); + _stringshare_del(&e_ofono_iface_modem); + _stringshare_del(&e_ofono_prop_name); + _stringshare_del(&e_ofono_prop_powered); + _stringshare_del(&e_ofono_prop_interfaces); + _stringshare_del(&e_ofono_iface_netreg); + _stringshare_del(&e_ofono_prop_mode); + _stringshare_del(&e_ofono_prop_status); + _stringshare_del(&e_ofono_prop_operator); + _stringshare_del(&e_ofono_prop_strength); + _stringshare_del(&e_ofono_iface_sms); + _stringshare_del(&e_ofono_prop_sca); + _stringshare_del(&e_ofono_method_send_sms); + return 0; +} + diff --git a/src/lib/ofono/e_ofono_element.c b/src/lib/ofono/e_ofono_element.c new file mode 100644 index 0000000..716c071 --- /dev/null +++ b/src/lib/ofono/e_ofono_element.c @@ -0,0 +1,2482 @@ +#include "e_ofono_private.h" +#include +#include +#include + +/* + * Maximum size for elements hash key. + * + * The elements hash key is contructed by concatenating the object path and the + * interface for the element (with a colon separating the two strings). D-Bus + * interfaces have a maximum size of 255 but object paths have unlimited size. + * We're assuming a maximum key size of 4k here, but this might need to be + * increased if oFono object paths grows bigger than that. + */ +#define MAX_KEY_SIZE 4096 +static Eina_Hash *elements = NULL; + +typedef struct _E_Ofono_Element_Pending E_Ofono_Element_Pending; +typedef struct _E_Ofono_Element_Call_Data E_Ofono_Element_Call_Data; +typedef struct _E_Ofono_Element_Property E_Ofono_Element_Property; +typedef struct _E_Ofono_Element_Listener E_Ofono_Element_Listener; +typedef struct _E_Ofono_Element_Dict_Entry E_Ofono_Element_Dict_Entry; + +struct _E_Ofono_Element_Pending +{ + EINA_INLIST; + DBusPendingCall *pending; + void *data; + E_DBus_Method_Return_Cb user_cb; + void *user_data; +}; + +struct _E_Ofono_Element_Call_Data +{ + E_Ofono_Element *element; + E_DBus_Method_Return_Cb cb; + E_Ofono_Element_Pending *pending; + Eina_Inlist **p_list; +}; + +struct _E_Ofono_Element_Property +{ + EINA_INLIST; + const char *name; + int type; + union { + Eina_Bool boolean; + const char *str; + unsigned short u16; + unsigned int u32; + unsigned char byte; + const char *path; + void *variant; + E_Ofono_Array *array; + } value; +}; + +struct _E_Ofono_Element_Dict_Entry +{ + const char *name; + int type; + union { + Eina_Bool boolean; + const char *str; + unsigned short u16; + unsigned int u32; + unsigned char byte; + const char *path; + } value; +}; + +struct _E_Ofono_Element_Listener +{ + EINA_INLIST; + void (*cb)(void *data, const E_Ofono_Element *element); + void *data; + void (*free_data)(void *data); +}; + +static void +_e_ofono_element_event_no_free(void *data __UNUSED__, void *ev) +{ + E_Ofono_Element *element = ev; + e_ofono_element_unref(element); +} + +static void +e_ofono_element_event_add(int event_type, E_Ofono_Element *element) +{ + e_ofono_element_ref(element); + ecore_event_add + (event_type, element, _e_ofono_element_event_no_free, element); +} + +static void +e_ofono_element_call_dispatch_and_free(void *d, DBusMessage *msg, DBusError *err) +{ + E_Ofono_Element_Call_Data *data = d; + E_Ofono_Element_Pending *pending; + + pending = data->pending; + pending->pending = NULL; + + if (data->cb) + data->cb(data->element, msg, err); + + if (pending->user_cb) + pending->user_cb(pending->user_data, msg, err); + + pending->data = NULL; + *data->p_list = eina_inlist_remove(*data->p_list, EINA_INLIST_GET(pending)); + free(pending); + free(data); +} + +static void +e_ofono_element_pending_cancel_and_free(Eina_Inlist **pending) +{ + while (*pending) + { + E_Ofono_Element_Pending *p = (E_Ofono_Element_Pending *)*pending; + DBusError err; + + dbus_pending_call_cancel(p->pending); + + dbus_error_init(&err); + dbus_set_error(&err, "Canceled", "Pending method call was canceled."); + e_ofono_element_call_dispatch_and_free(p->data, NULL, &err); + dbus_error_free(&err); + } +} + +void +e_ofono_element_listener_add(E_Ofono_Element *element, void (*cb)(void *data, const E_Ofono_Element *element), const void *data, void (*free_data)(void *data)) +{ + E_Ofono_Element_Listener *l; + + if (!element) + { + ERR("safety check failed: element == NULL"); + goto error; + } + + if (!cb) + { + ERR("safety check failed: cb == NULL"); + goto error; + } + + l = malloc(sizeof(*l)); + if (!l) + { + ERR("could not allocate E_Ofono_Element_Listener"); + goto error; + } + + l->cb = cb; + l->data = (void *)data; + l->free_data = free_data; + + element->_listeners = eina_inlist_append + (element->_listeners, EINA_INLIST_GET(l)); + + return; + +error: + if (free_data) + free_data((void *)data); +} + +void +e_ofono_element_listener_del(E_Ofono_Element *element, void (*cb)(void *data, const E_Ofono_Element *element), const void *data) +{ + E_Ofono_Element_Listener *l; + + EINA_SAFETY_ON_NULL_RETURN(element); + EINA_SAFETY_ON_NULL_RETURN(cb); + + EINA_INLIST_FOREACH(element->_listeners, l) + if ((l->cb == cb) && (l->data == data)) + { + element->_listeners = eina_inlist_remove + (element->_listeners, EINA_INLIST_GET(l)); + if (l->free_data) + l->free_data(l->data); + + free(l); + return; + } +} + +static void +_e_ofono_element_listeners_call_do(E_Ofono_Element *element) +{ + E_Ofono_Element_Listener *l, **shadow; + unsigned int i, count; + + /* NB: iterate on a copy in order to allow listeners to be deleted + * from callbacks. number of listeners should be small, so the + * following should do fine. + */ + count = eina_inlist_count(element->_listeners); + if (count < 1) + goto end; + + shadow = alloca(sizeof(*shadow) * count); + if (!shadow) + goto end; + + i = 0; + EINA_INLIST_FOREACH(element->_listeners, l) + shadow[i++] = l; + + for (i = 0; i < count; i++) + shadow[i]->cb(shadow[i]->data, element); + +end: + e_ofono_element_event_add(E_OFONO_EVENT_ELEMENT_UPDATED, element); +} + +static Eina_Bool +_e_ofono_element_listeners_call_idler(void *data) +{ + E_Ofono_Element *element = data; + _e_ofono_element_listeners_call_do(element); + element->_idler.changed = NULL; + return ECORE_CALLBACK_CANCEL; +} + +static void +_e_ofono_element_listeners_call(E_Ofono_Element *element) +{ + if (element->_idler.changed) + return; + + element->_idler.changed = ecore_idler_add + (_e_ofono_element_listeners_call_idler, element); +} + +/*********************************************************************** +* Property +***********************************************************************/ + +static void +_e_ofono_element_dict_entry_free(E_Ofono_Element_Dict_Entry *entry) +{ + switch (entry->type) + { + case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_BYTE: + case DBUS_TYPE_UINT16: + case DBUS_TYPE_UINT32: + break; + + case DBUS_TYPE_OBJECT_PATH: + eina_stringshare_del(entry->value.path); + break; + + case DBUS_TYPE_STRING: + eina_stringshare_del(entry->value.str); + break; + + default: + ERR("don't know how to free dict entry '%s' of type %c (%d)", + entry->name, entry->type, entry->type); + } + + eina_stringshare_del(entry->name); + free(entry); +} + +static E_Ofono_Element_Dict_Entry * +_e_ofono_element_dict_entry_new(DBusMessageIter *itr) +{ + E_Ofono_Element_Dict_Entry *entry; + DBusMessageIter e_itr, v_itr; + int t; + const char *key = NULL; + void *value = NULL; + + dbus_message_iter_recurse(itr, &e_itr); + + t = dbus_message_iter_get_arg_type(&e_itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_STRING)) + { + ERR("invalid format for dict entry. first type not a string: %c (%d)", + t, t); + return NULL; + } + + dbus_message_iter_get_basic(&e_itr, &key); + if (!key || !key[0]) + { + ERR("invalid format for dict entry. no key."); + return NULL; + } + + dbus_message_iter_next(&e_itr); + t = dbus_message_iter_get_arg_type(&e_itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_VARIANT)) + { + ERR("invalid format for dict entry '%s'. " + "second type not a variant: %c (%d)", + key, t, t); + return NULL; + } + + dbus_message_iter_recurse(&e_itr, &v_itr); + + t = dbus_message_iter_get_arg_type(&v_itr); + if ((t == DBUS_TYPE_INVALID) || (t == DBUS_TYPE_ARRAY)) + { + ERR("invalid type for dict value for entry '%s': %c (%d)", + key, t, t); + return NULL; + } + + entry = calloc(1, sizeof(*entry)); + if (!entry) + { + ERR("could not allocate memory for dict entry."); + return NULL; + } + + dbus_message_iter_get_basic(&v_itr, &value); + switch (t) + { + case DBUS_TYPE_BOOLEAN: + entry->value.boolean = (Eina_Bool)(long)value; + break; + + case DBUS_TYPE_BYTE: + entry->value.byte = (unsigned char)(long)value; + break; + + case DBUS_TYPE_UINT16: + entry->value.u16 = (unsigned short)(long)value; + break; + + case DBUS_TYPE_UINT32: + entry->value.u32 = (unsigned int)(long)value; + break; + + case DBUS_TYPE_STRING: + entry->value.str = eina_stringshare_add(value); + break; + + case DBUS_TYPE_OBJECT_PATH: + entry->value.path = eina_stringshare_add(value); + break; + + default: + ERR("don't know how to create dict entry '%s' for of type %c (%d)", + key, t, t); + free(entry); + return NULL; + } + + entry->name = eina_stringshare_add(key); + entry->type = t; + return entry; +} + +static E_Ofono_Element_Dict_Entry * +_e_ofono_element_array_dict_find_stringshared(const E_Ofono_Array *array, const char *key) +{ + E_Ofono_Element_Dict_Entry *entry; + Eina_Array_Iterator iterator; + unsigned int i; + + EINA_ARRAY_ITER_NEXT(array->array, i, entry, iterator) + if (entry->name == key) + return entry; + + return NULL; +} + +static void +_e_ofono_element_array_free(E_Ofono_Array *array, E_Ofono_Array *new __UNUSED__) +{ + Eina_Array_Iterator iterator; + unsigned int i; + void *item; + + if (!array) + return; + + switch (array->type) + { + case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_BYTE: + case DBUS_TYPE_UINT16: + case DBUS_TYPE_UINT32: + break; + + case DBUS_TYPE_OBJECT_PATH: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + eina_stringshare_del(item); + break; + + case DBUS_TYPE_STRING: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + eina_stringshare_del(item); + break; + + case DBUS_TYPE_DICT_ENTRY: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + _e_ofono_element_dict_entry_free(item); + break; + + default: + ERR("don't know how to free array of values of type %c (%d)", + array->type, array->type); + break; + } + eina_array_free(array->array); + free(array); +} + +static void +_e_ofono_element_property_value_free(E_Ofono_Element_Property *property) +{ + switch (property->type) + { + case 0: + return; + + case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_BYTE: + case DBUS_TYPE_UINT16: + case DBUS_TYPE_UINT32: + break; + + case DBUS_TYPE_STRING: + eina_stringshare_del(property->value.str); + break; + + case DBUS_TYPE_OBJECT_PATH: + eina_stringshare_del(property->value.path); + break; + + case DBUS_TYPE_ARRAY: + _e_ofono_element_array_free(property->value.array, NULL); + break; + + default: + ERR("don't know how to free value of property type %c (%d)", + property->type, property->type); + } +} + +static const char * +_e_ofono_element_get_interface(const char *key) +{ + const char *interface = NULL, *tail; + char head; + + head = key[0]; + tail = key + 1; + + switch (head) + { + case 'M': + if (strcmp(tail, "odems") == 0) + interface = e_ofono_iface_modem; + + break; + + default: + break; + } + + if (!interface) + ERR("failed to find interface for property \"%s\"", key); + + return interface; +} + +static E_Ofono_Element * +_e_ofono_element_item_register(const char *key, const char *item) +{ + E_Ofono_Element *element; + const char *interface; + + interface = _e_ofono_element_get_interface(key); + if (!interface) + return NULL; + + element = e_ofono_element_register(item, interface); + if ((element) && (!e_ofono_element_properties_sync(element))) + WRN("could not get properties of %s", element->path); + + return element; +} + +/* Match 2 arrays to find which are new and which are old elements + * For new elements, register them under prop_name property + * For old elements, unregister them, sending proper DEL event + */ +static void +_e_ofono_element_array_match(E_Ofono_Array *old, E_Ofono_Array *new, const char *prop_name, E_Ofono_Element *element) +{ + Eina_List *deleted = NULL; + Eina_Array_Iterator iter_old, iter_new; + unsigned int i_old = 0, i_new = 0; + void *item_old = NULL, *item_new; + Eina_List *l; + void *data; + Eina_Bool interfaces = EINA_FALSE; + + if (!old) + return; + + if ((old->type != DBUS_TYPE_OBJECT_PATH) && + (old->type != DBUS_TYPE_STRING)) + return; + + /* is this a list of interfaces? */ + interfaces = !strcmp(prop_name, "Interfaces"); + + if ((!new) || (!new->array) || eina_array_count(new->array) == 0) + { + if ((!old) || (!old->array) || eina_array_count(old->array) == 0) + { + return; + } + else + { + iter_old = old->array->data; + goto out_remove_remaining; + } + } + + iter_new = new->array->data; + item_new = *iter_new; + EINA_ARRAY_ITER_NEXT(old->array, i_old, item_old, iter_old) + { + if (item_old == item_new) + { + i_new++; + if (i_new >= eina_array_count(new->array)) + { + i_old++; + break; + } + + iter_new++; + item_new = *iter_new; + } + else + { + deleted = eina_list_append(deleted, item_old); + } + } + + for(; i_new < eina_array_count(new->array); iter_new++, i_new++) + { + Eina_Bool found = EINA_FALSE; + item_new = *iter_new; + if (!item_new) + break; + + EINA_LIST_FOREACH(deleted, l, data) + { + if (data == item_new) + { + deleted = eina_list_remove_list(deleted, l); + found = EINA_TRUE; + break; + } + } + if (!found) + { + E_Ofono_Element *e = NULL; + + if (interfaces) + e = e_ofono_element_register(item_new, element->path); + else + e = _e_ofono_element_item_register(prop_name, item_new); + + if (e) + DBG("Add element %s (%s)\n", e->path, e->interface); + } + } + + /* everybody after i_old on old->array + everybody from deleted list + will be removed + */ + EINA_LIST_FREE(deleted, data) + { + E_Ofono_Element *e; + if (interfaces) + e = e_ofono_element_get(element->path, item_old); + else + e = e_ofono_element_get(data, + _e_ofono_element_get_interface(prop_name)); + + if (e) + { + e_ofono_element_unregister(e); + DBG("Deleted element %s %s\n", e->path, e->interface); + } + } + +out_remove_remaining: + for(; i_old < eina_array_count(old->array); iter_old++, i_old++) + { + E_Ofono_Element *e; + item_old = *iter_old; + if (!item_old) + break; + + if (interfaces) + e = e_ofono_element_get(element->path, item_old); + else + e = e_ofono_element_get(item_old, + _e_ofono_element_get_interface(prop_name)); + + if (e) + { + e_ofono_element_unregister(e); + DBG("Deleted element %s %s\n", e->path, e->interface); + } + } +} + +static Eina_Bool +_e_ofono_element_property_update(E_Ofono_Element_Property *property, int type, void *data, E_Ofono_Element *element) +{ + Eina_Bool changed = EINA_FALSE; + + if ((type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) && data) + data = (char *)eina_stringshare_add(data); + + if (property->type != type) + { + if (property->type) + DBG("property type changed from '%c' to '%c'", + property->type, type); + + _e_ofono_element_property_value_free(property); + memset(&property->value, 0, sizeof(property->value)); + property->type = type; + changed = EINA_TRUE; + } + + switch (type) + { + case DBUS_TYPE_BOOLEAN: + if (changed || property->value.boolean != (Eina_Bool)(long)data) + { + property->value.boolean = (Eina_Bool)(long)data; + changed = EINA_TRUE; + } + + break; + + case DBUS_TYPE_BYTE: + if (changed || property->value.byte != (unsigned char)(long)data) + { + property->value.byte = (unsigned char)(long)data; + changed = EINA_TRUE; + } + + break; + + case DBUS_TYPE_UINT16: + if (changed || property->value.u16 != (unsigned short)(long)data) + { + property->value.u16 = (unsigned short)(long)data; + changed = EINA_TRUE; + } + + break; + + case DBUS_TYPE_UINT32: + if (changed || property->value.u32 != (unsigned int)(long)data) + { + property->value.u32 = (unsigned int)(long)data; + changed = EINA_TRUE; + } + + break; + + case DBUS_TYPE_STRING: + if (changed) + { + property->value.str = data; + } + else + { + if (property->value.str) + eina_stringshare_del(property->value.str); + + if (property->value.str != data) + { + property->value.str = data; + changed = EINA_TRUE; + } + } + + break; + + case DBUS_TYPE_OBJECT_PATH: + if (changed) + { + property->value.path = data; + } + else + { + if (property->value.path) + eina_stringshare_del(property->value.path); + + if (property->value.path != data) + { + property->value.path = data; + changed = EINA_TRUE; + } + } + + break; + + case DBUS_TYPE_ARRAY: + if (!changed) + if (property->value.array) + { + _e_ofono_element_array_match(property->value.array, data, + property->name, element); + _e_ofono_element_array_free(property->value.array, data); + } + + property->value.array = data; + changed = EINA_TRUE; + break; + + default: + ERR("don't know how to update property type %c (%d)", type, type); + } + + return changed; +} + +static E_Ofono_Element_Property * +_e_ofono_element_property_new(const char *name, int type, void *data, E_Ofono_Element *element) +{ + E_Ofono_Element_Property *property; + + property = calloc(1, sizeof(*property)); + if (!property) + { + eina_stringshare_del(name); + ERR("could not allocate property: %s", strerror(errno)); + return NULL; + } + + property->name = name; + _e_ofono_element_property_update(property, type, data, element); + return property; +} + +static void +_e_ofono_element_property_free(E_Ofono_Element_Property *property) +{ + _e_ofono_element_property_value_free(property); + eina_stringshare_del(property->name); + free(property); +} + +/*********************************************************************** +* Element +***********************************************************************/ +unsigned char * +e_ofono_element_bytes_array_get_stringshared(const E_Ofono_Element *element, const char *property, unsigned int *count) +{ + Eina_Array_Iterator iterator; + E_Ofono_Array *array; + unsigned char *ret, *p; + unsigned int i; + void *item; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(property, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(count, NULL); + + *count = 0; + + if (!e_ofono_element_property_get_stringshared + (element, property, NULL, &array)) + return NULL; + + if ((!array) || (!(array->array))) + return NULL; + + *count = eina_array_count(array->array); + ret = malloc(*count * sizeof(unsigned char)); + if (!ret) + { + ERR("could not allocate return array of %d bytes: %s", + *count, strerror(errno)); + return NULL; + } + + p = ret; + + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + { + *p = (unsigned char)(long)item; + p++; + } + return ret; +} + +Eina_Bool +e_ofono_element_objects_array_get_stringshared(const E_Ofono_Element *element, const char *property, unsigned int *count, E_Ofono_Element ***p_elements) +{ + E_Ofono_Element **ret, **p; + Eina_Array_Iterator iterator; + E_Ofono_Array *array; + unsigned int i; + int type; + void *item; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(property, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(p_elements, EINA_FALSE); + + *count = 0; + *p_elements = NULL; + + if (!e_ofono_element_property_get_stringshared + (element, property, &type, &array)) + return EINA_FALSE; + + if (type != DBUS_TYPE_ARRAY) + { + ERR("property %s is not an array!", property); + return EINA_FALSE; + } + + if ((!array) || (!array->array) || (array->type == DBUS_TYPE_INVALID)) + return EINA_FALSE; + + if (array->type != DBUS_TYPE_OBJECT_PATH) + { + ERR("property %s is not an array of object paths!", property); + return EINA_FALSE; + } + + *count = eina_array_count(array->array); + ret = malloc(*count * sizeof(E_Ofono_Element *)); + if (!ret) + { + ERR("could not allocate return array of %d elements: %s", + *count, strerror(errno)); + *count = 0; + return EINA_FALSE; + } + + p = ret; + + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + { + E_Ofono_Element *e = e_ofono_element_get(item, property); + if (!e) + continue; + + *p = e; + p++; + } + *count = p - ret; + *p_elements = ret; + return EINA_TRUE; +} + +/* strings are just pointers (references), no strdup or stringshare_add/ref */ +Eina_Bool +e_ofono_element_strings_array_get_stringshared(const E_Ofono_Element *element, const char *property, unsigned int *count, const char ***strings) +{ + const char **ret, **p; + Eina_Array_Iterator iterator; + E_Ofono_Array *array; + unsigned int i; + int type; + void *item; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(property, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(strings, EINA_FALSE); + + *count = 0; + *strings = NULL; + + if (!e_ofono_element_property_get_stringshared + (element, property, &type, &array)) + return EINA_FALSE; + + if (type != DBUS_TYPE_ARRAY) + { + ERR("property %s is not an array!", property); + return EINA_FALSE; + } + + if ((!array) || (!array->array) || (array->type == DBUS_TYPE_INVALID)) + return EINA_FALSE; + + if (array->type != DBUS_TYPE_STRING) + { + ERR("property %s is not an array of strings!", property); + return EINA_FALSE; + } + + *count = eina_array_count(array->array); + ret = malloc(*count * sizeof(char *)); + if (!ret) + { + ERR("could not allocate return array of %d strings: %s", + *count, strerror(errno)); + *count = 0; + return EINA_FALSE; + } + + p = ret; + + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + { + if (!item) + continue; + + *p = item; + p++; + } + *count = p - ret; + *strings = ret; + return EINA_TRUE; +} + +static void +_e_ofono_element_array_print(FILE *fp, E_Ofono_Array *array) +{ + Eina_Array_Iterator iterator; + unsigned int i; + void *item; + + if (!array) + return; + + switch (array->type) + { + case DBUS_TYPE_OBJECT_PATH: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + fprintf(fp, "\"%s\", ", (const char *)item); + break; + + case DBUS_TYPE_STRING: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + fprintf(fp, "\"%s\", ", (const char *)item); + break; + + case DBUS_TYPE_BYTE: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + fprintf(fp, "%#02hhx (\"%c\"), ", (unsigned char)(long)item, + (unsigned char)(long)item); + break; + + case DBUS_TYPE_UINT16: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + fprintf(fp, "%#04hx (%hu), ", (unsigned short)(long)item, + (unsigned short)(long)item); + break; + + case DBUS_TYPE_UINT32: + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + fprintf(fp, "%#08x (%u), ", (unsigned int)(long)item, + (unsigned int)(long)item); + break; + + case DBUS_TYPE_DICT_ENTRY: + fputs("{ ", fp); + EINA_ARRAY_ITER_NEXT(array->array, i, item, iterator) + { + E_Ofono_Element_Dict_Entry *entry = item; + fprintf(fp, "%s: ", entry->name); + switch (entry->type) + { + case DBUS_TYPE_OBJECT_PATH: + fprintf(fp, "\"%s\", ", entry->value.path); + break; + + case DBUS_TYPE_STRING: + fprintf(fp, "\"%s\", ", entry->value.str); + break; + + case DBUS_TYPE_BYTE: + fprintf(fp, "%#02hhx (\"%c\"), ", + entry->value.byte, entry->value.byte); + break; + + case DBUS_TYPE_UINT16: + fprintf(fp, "%#04hx (%hu), ", + entry->value.u16, entry->value.u16); + break; + + case DBUS_TYPE_UINT32: + fprintf(fp, "%#08x (%u), ", + entry->value.u32, entry->value.u32); + break; + + default: + fprintf(fp, "", entry->type); + } + } + fputs("}", fp); + break; + + default: + fprintf(fp, "", array->type); + } +} + +/** + * Print element to file descriptor. + */ +void +e_ofono_element_print(FILE *fp, const E_Ofono_Element *element) +{ + const E_Ofono_Element_Property *p; + + EINA_SAFETY_ON_NULL_RETURN(fp); + if (!element) + { + fputs("Error: no element to print\n", fp); + return; + } + + fprintf(fp, + "Element %p: %s [%s]\n" + "\tProperties:\n", + element, element->path, element->interface); + + EINA_INLIST_FOREACH(element->props, p) + { + fprintf(fp, "\t\t%s (%c) = ", p->name, p->type); + + switch (p->type) + { + case DBUS_TYPE_STRING: + fprintf(fp, "\"%s\"", p->value.str); + break; + + case DBUS_TYPE_OBJECT_PATH: + fprintf(fp, "\"%s\"", p->value.path); + break; + + case DBUS_TYPE_BOOLEAN: + fprintf(fp, "%hhu", p->value.boolean); + break; + + case DBUS_TYPE_BYTE: + fprintf(fp, "%#02hhx (%d), ", p->value.byte, p->value.byte); + break; + + case DBUS_TYPE_UINT16: + fprintf(fp, "%hu", p->value.u16); + break; + + case DBUS_TYPE_UINT32: + fprintf(fp, "%u", p->value.u32); + break; + + case DBUS_TYPE_ARRAY: + _e_ofono_element_array_print(fp, p->value.array); + break; + + default: + fputs("don't know how to print type", fp); + } + + fputc('\n', fp); + } +} + +static E_Ofono_Element * +e_ofono_element_new(const char *path, const char *interface) +{ + E_Ofono_Element *element; + + element = calloc(1, sizeof(*element)); + if (!element) + { + ERR("could not allocate element: %s", strerror(errno)); + return NULL; + } + + element->path = eina_stringshare_add(path); + element->interface = eina_stringshare_ref(interface); + element->_references = 1; + + return element; +} + +static void +e_ofono_element_extra_properties_free(E_Ofono_Element *element) +{ + while (element->props) + { + E_Ofono_Element_Property *prop; + prop = (E_Ofono_Element_Property *)element->props; + element->props = element->props->next; + _e_ofono_element_property_free(prop); + } +} + +static void +e_ofono_element_free(E_Ofono_Element *element) +{ + if (element->_idler.changed) + ecore_idler_del(element->_idler.changed); + + while (element->_listeners) + { + E_Ofono_Element_Listener *l = (void *)element->_listeners; + element->_listeners = eina_inlist_remove + (element->_listeners, element->_listeners); + + if (l->free_data) + l->free_data(l->data); + + free(l); + } + + e_ofono_element_pending_cancel_and_free(&element->_pending.properties_get); + e_ofono_element_pending_cancel_and_free(&element->_pending.property_set); + e_ofono_element_pending_cancel_and_free(&element->_pending.send_sms); + + e_ofono_element_extra_properties_free(element); + eina_stringshare_del(element->interface); + eina_stringshare_del(element->path); + free(element); +} + +/** + * Add reference to element. + */ +int +e_ofono_element_ref(E_Ofono_Element *element) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, 0); + return ++element->_references; +} + +/** + * Remove reference from element. + * + * If reference count drops to 0 element will be freed. + */ +int +e_ofono_element_unref(E_Ofono_Element *element) +{ + int i; + EINA_SAFETY_ON_NULL_RETURN_VAL(element, 0); + + i = --element->_references; + if (i == 0) + e_ofono_element_free(element); + else if (i < 0) + ERR("element %p references %d < 0", element, i); + + return i; +} + +/** + * Send message with callbacks set to work with ofono elements. + * + * If this call fails (returns 0), pending callbacks will not be called, + * not even with error messages. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_element_message_send(E_Ofono_Element *element, const char *method_name, const char *interface, E_DBus_Method_Return_Cb cb, DBusMessage *msg, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data) +{ + E_Ofono_Element_Call_Data *data; + E_Ofono_Element_Pending *p; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(method_name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(pending, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(msg, EINA_FALSE); + + interface = interface ? : element->interface; + + data = malloc(sizeof(*data)); + if (!data) + { + ERR("could not alloc e_ofono_element_call_data: %s", + strerror(errno)); + dbus_message_unref(msg); + return EINA_FALSE; + } + + p = malloc(sizeof(*p)); + if (!p) + { + ERR("could not alloc E_Ofono_Element_Pending: %s", + strerror(errno)); + free(data); + dbus_message_unref(msg); + return EINA_FALSE; + } + + data->element = element; + data->cb = cb; + data->pending = p; + data->p_list = pending; + p->user_cb = user_cb; + p->user_data = (void *)user_data; + p->data = data; + p->pending = e_dbus_message_send + (e_ofono_conn, msg, e_ofono_element_call_dispatch_and_free, -1, data); + dbus_message_unref(msg); + + if (p->pending) + { + *pending = eina_inlist_append(*pending, EINA_INLIST_GET(p)); + return EINA_TRUE; + } + + ERR("failed to call %s (obj=%s, path=%s, iface=%s)", + method_name, e_ofono_system_bus_name_get(), + element->path, interface); + free(data); + free(p); + return EINA_FALSE; +} + +Eina_Bool +e_ofono_element_call_full(E_Ofono_Element *element, const char *method_name, const char *interface, E_DBus_Method_Return_Cb cb, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data) +{ + DBusMessage *msg; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(method_name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(pending, EINA_FALSE); + + interface = interface ? : element->interface; + + msg = dbus_message_new_method_call + (e_ofono_system_bus_name_get(), element->path, interface, + method_name); + + return e_ofono_element_message_send + (element, method_name, interface, cb, msg, pending, user_cb, user_data); +} + +static Eina_Bool +_e_ofono_element_property_value_add(E_Ofono_Element *element, const char *name, int type, void *value) +{ + E_Ofono_Element_Property *p; + + name = eina_stringshare_add(name); + EINA_INLIST_FOREACH(element->props, p) + { + if (p->name == name) + { + eina_stringshare_del(name); + return _e_ofono_element_property_update(p, type, value, element); + } + } + + p = _e_ofono_element_property_new(name, type, value, element); + if (!p) + { + ERR("could not create property %s (%c)", name, type); + return EINA_FALSE; + } + + element->props = eina_inlist_append(element->props, EINA_INLIST_GET(p)); + return EINA_TRUE; +} + +static E_Ofono_Array * +_e_ofono_element_iter_get_array(DBusMessageIter *itr, const char *key) +{ + E_Ofono_Array *array; + DBusMessageIter e_itr; + + array = malloc(sizeof(E_Ofono_Array)); + if (!array) + { + ERR("could not create new e_ofono array."); + return NULL; + } + + array->array = eina_array_new(16); + if (!(array->array)) + { + ERR("could not create new eina array."); + free(array); + return NULL; + } + + dbus_message_iter_recurse(itr, &e_itr); + array->type = dbus_message_iter_get_arg_type(&e_itr); + if (array->type == DBUS_TYPE_INVALID) + { + DBG("array %s is of type 'invalid' (empty?)", key); + eina_array_free(array->array); + free(array); + return NULL; + } + + do + { + switch (array->type) + { + case DBUS_TYPE_OBJECT_PATH: + { + const char *path; + + dbus_message_iter_get_basic(&e_itr, &path); + path = eina_stringshare_add(path); + eina_array_push(array->array, path); + _e_ofono_element_item_register(key, path); + } + break; + + case DBUS_TYPE_STRING: + { + const char *str; + + dbus_message_iter_get_basic(&e_itr, &str); + str = eina_stringshare_add(str); + eina_array_push(array->array, str); + } + break; + + case DBUS_TYPE_BYTE: + { + unsigned char byte; + dbus_message_iter_get_basic(&e_itr, &byte); + eina_array_push(array->array, (void *)(long)byte); + } + break; + + case DBUS_TYPE_DICT_ENTRY: + { + E_Ofono_Element_Dict_Entry *entry; + entry = _e_ofono_element_dict_entry_new(&e_itr); + if (entry) + eina_array_push(array->array, entry); + } + break; + + default: + ERR("don't know how to build array '%s' of type %c (%d)", + key, array->type, array->type); + eina_array_free(array->array); + free(array); + return NULL; + } + } + while (dbus_message_iter_next(&e_itr)); + return array; +} + +static void +_e_ofono_element_get_properties_callback(void *user_data, DBusMessage *msg, DBusError *err) +{ + E_Ofono_Element *element = user_data; + DBusMessageIter itr, s_itr; + int t, changed; + + DBG("get_properties msg=%p", msg); + + if (!_dbus_callback_check_and_init(msg, &itr, err)) + return; + + t = dbus_message_iter_get_arg_type(&itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_ARRAY)) + return; + + changed = 0; + dbus_message_iter_recurse(&itr, &s_itr); + do + { + DBusMessageIter e_itr, v_itr; + const char *key; + void *value = NULL; + int r; + + t = dbus_message_iter_get_arg_type(&s_itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_DICT_ENTRY)) + continue; + + dbus_message_iter_recurse(&s_itr, &e_itr); + + t = dbus_message_iter_get_arg_type(&e_itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_STRING)) + continue; + + dbus_message_iter_get_basic(&e_itr, &key); + dbus_message_iter_next(&e_itr); + t = dbus_message_iter_get_arg_type(&e_itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_VARIANT)) + continue; + + dbus_message_iter_recurse(&e_itr, &v_itr); + t = dbus_message_iter_get_arg_type(&v_itr); + if (t == DBUS_TYPE_ARRAY) + { + value = _e_ofono_element_iter_get_array(&v_itr, key); + } + else if (t != DBUS_TYPE_INVALID) + { + dbus_message_iter_get_basic(&v_itr, &value); + } + else + { + ERR("property has invalid type %s", key); + continue; + } + + r = _e_ofono_element_property_value_add(element, key, t, value); + if (r < 0) + { + ERR("failed to add property value %s (%c)", key, t); + } + else if (r == 1) + { + INF("property value changed %s (%c)", key, t); + changed = 1; + if ((strcmp(key, "Interfaces") == 0) && value) + { + char *interface; + Eina_Array_Iterator iterator; + unsigned int i; + E_Ofono_Element *e; + + EINA_ARRAY_ITER_NEXT(((E_Ofono_Array *)value)->array, i, + interface, iterator) + { + DBG("Found interface %s on %s", interface, element->path); + e = e_ofono_element_register(element->path, interface); + if ((e) && (!e_ofono_element_properties_sync(e))) + WRN("could not get properties of %s", e->path); + } + } + } + } + while (dbus_message_iter_next(&s_itr)); + + if (changed) + _e_ofono_element_listeners_call(element); +} + +/** + * Sync element properties with server. + * + * Call method GetProperties() at the given element on server in order to sync + * them. + * + * @param element to call method on server. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_element_sync_properties_full(E_Ofono_Element *element, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "GetProperties"; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + return e_ofono_element_call_full + (element, name, element->interface, + _e_ofono_element_get_properties_callback, + &element->_pending.properties_get, cb, data); +} + +/** + * Sync element properties with server, simple version. + * + * Call method GetProperties() at the given element on server in order to sync + * them. This is the simple version and there is no check of server reply + * for errors. + * + * @param element to call method on server. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_element_properties_sync(E_Ofono_Element *element) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + return e_ofono_element_sync_properties_full(element, NULL, NULL); +} + +/** + * Call method SetProperty(prop, {key: value}) at the given element on server. + * + * This is a server call, not local, so it may fail and in that case + * no property is updated locally. If the value was set the event + * E_OFONO_EVENT_ELEMENT_UPDATED will be added to main loop. + * + * @param element to call method on server. + * @param prop property name. + * @param key dict key name. + * @param type DBus type to use for value. + * @param value pointer to value, just like regular DBus, see + * dbus_message_iter_append_basic(). + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_element_property_dict_set_full(E_Ofono_Element *element, const char *prop, const char *key, int type, const void *value, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "SetProperty"; + DBusMessage *msg; + DBusMessageIter itr, variant, dict, entry; + char typestr[32]; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(prop, EINA_FALSE); + + msg = dbus_message_new_method_call + (e_ofono_system_bus_name_get(), element->path, element->interface, name); + + if (!msg) + return EINA_FALSE; + + dbus_message_iter_init_append(msg, &itr); + dbus_message_iter_append_basic(&itr, DBUS_TYPE_STRING, &prop); + + if ((size_t)snprintf(typestr, sizeof(typestr), + (DBUS_TYPE_ARRAY_AS_STRING + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + "%c" + DBUS_DICT_ENTRY_END_CHAR_AS_STRING), + type) >= sizeof(typestr)) + { + ERR("sizeof(typestr) is too small!"); + return EINA_FALSE; + } + + if (dbus_message_iter_open_container(&itr, DBUS_TYPE_VARIANT, typestr, &variant)) + { + snprintf(typestr, sizeof(typestr), + (DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + "%c" + DBUS_DICT_ENTRY_END_CHAR_AS_STRING), + type); + + if (dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, typestr, &dict)) + { + if (dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry)) + { + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + + if ((type == DBUS_TYPE_STRING) || (type == DBUS_TYPE_OBJECT_PATH)) + dbus_message_iter_append_basic(&entry, type, &value); + else + dbus_message_iter_append_basic(&entry, type, value); + + dbus_message_iter_close_container(&dict, &entry); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + dbus_message_iter_close_container(&variant, &dict); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + dbus_message_iter_close_container(&itr, &variant); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + + return e_ofono_element_message_send + (element, name, NULL, NULL, msg, + &element->_pending.property_set, cb, data); +} + +/** + * Call method SetProperty(prop, value) at the given element on server. + * + * This is a server call, not local, so it may fail and in that case + * no property is updated locally. If the value was set the event + * E_OFONO_EVENT_ELEMENT_UPDATED will be added to main loop. + * + * @param element to call method on server. + * @param prop property name. + * @param type DBus type to use for value. + * @param value pointer to value, just like regular DBus, see + * dbus_message_iter_append_basic(). + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_element_property_set_full(E_Ofono_Element *element, const char *prop, int type, const void *value, E_DBus_Method_Return_Cb cb, const void *data) +{ + const char name[] = "SetProperty"; + char typestr[2]; + DBusMessage *msg; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(prop, EINA_FALSE); + + msg = dbus_message_new_method_call + (e_ofono_system_bus_name_get(), element->path, element->interface, name); + + if (!msg) + return EINA_FALSE; + + DBusMessageIter itr, v; + dbus_message_iter_init_append(msg, &itr); + dbus_message_iter_append_basic(&itr, DBUS_TYPE_STRING, &prop); + + typestr[0] = type; + typestr[1] = '\0'; + dbus_message_iter_open_container(&itr, DBUS_TYPE_VARIANT, typestr, &v); + if ((type == DBUS_TYPE_STRING) || (type == DBUS_TYPE_OBJECT_PATH)) + { + dbus_message_iter_append_basic(&v, type, &value); + } + else if (type == DBUS_TYPE_BOOLEAN) + { + unsigned int b = *(Eina_Bool *)value; + dbus_message_iter_append_basic(&v, type, &b); + } + else + { + dbus_message_iter_append_basic(&v, type, value); + } + + dbus_message_iter_close_container(&itr, &v); + + return e_ofono_element_message_send + (element, name, NULL, NULL, msg, + &element->_pending.property_set, cb, data); +} + +/** + * Call method SetProperty(prop, value) at the given element on server. + * + * This is the simple version and there is no check of server reply + * for errors. + * + * @param element to call method on server. + * @param prop property name. + * @param type DBus type to use for value. + * @param value pointer to value, just like regular DBus, see + * dbus_message_iter_append_basic(). + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_element_property_set(E_Ofono_Element *element, const char *prop, int type, const void *value) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(prop, EINA_FALSE); + return e_ofono_element_property_set_full + (element, prop, type, value, NULL, NULL); +} + +Eina_Bool +e_ofono_element_call_with_path(E_Ofono_Element *element, const char *method_name, const char *string, E_DBus_Method_Return_Cb cb, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data) +{ + DBusMessageIter itr; + DBusMessage *msg; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(method_name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(string, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(pending, EINA_FALSE); + + msg = dbus_message_new_method_call + (e_ofono_system_bus_name_get(), element->path, element->interface, + method_name); + + if (!msg) + return EINA_FALSE; + + dbus_message_iter_init_append(msg, &itr); + dbus_message_iter_append_basic(&itr, DBUS_TYPE_OBJECT_PATH, &string); + + return e_ofono_element_message_send + (element, method_name, NULL, cb, msg, pending, user_cb, user_data); +} + +Eina_Bool +e_ofono_element_call_with_string(E_Ofono_Element *element, const char *method_name, const char *string, E_DBus_Method_Return_Cb cb, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data) +{ + DBusMessageIter itr; + DBusMessage *msg; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(method_name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(string, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(pending, EINA_FALSE); + + msg = dbus_message_new_method_call + (e_ofono_system_bus_name_get(), element->path, element->interface, + method_name); + + if (!msg) + return EINA_FALSE; + + dbus_message_iter_init_append(msg, &itr); + dbus_message_iter_append_basic(&itr, DBUS_TYPE_STRING, &string); + + return e_ofono_element_message_send + (element, method_name, NULL, cb, msg, pending, user_cb, user_data); +} + +Eina_Bool +e_ofono_element_call_with_path_and_string(E_Ofono_Element *element, const char *method_name, const char *path, const char *string, E_DBus_Method_Return_Cb cb, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data) +{ + DBusMessageIter itr; + DBusMessage *msg; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(method_name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(path, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(string, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(pending, EINA_FALSE); + + msg = dbus_message_new_method_call + (e_ofono_system_bus_name_get(), element->path, element->interface, + method_name); + + if (!msg) + return EINA_FALSE; + + dbus_message_iter_init_append(msg, &itr); + dbus_message_iter_append_basic(&itr, DBUS_TYPE_OBJECT_PATH, &path); + dbus_message_iter_append_basic(&itr, DBUS_TYPE_STRING, &string); + + return e_ofono_element_message_send + (element, method_name, NULL, cb, msg, pending, user_cb, user_data); +} + +/** + * Get property type. + * + * If zero is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param element which element to get the property + * @param name property name, must be previously stringshared + * @param type will contain the value type. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_element_property_type_get_stringshared(const E_Ofono_Element *element, const char *name, int *type) +{ + const E_Ofono_Element_Property *p; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(type, EINA_FALSE); + + EINA_INLIST_FOREACH(element->props, p) + { + if (p->name == name) + { + *type = p->type; + return EINA_TRUE; + } + } + + WRN("element %s (%p) has no property with name \"%s\".", + element->path, element, name); + return EINA_FALSE; +} + +/** + * Get property type. + * + * If zero is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param element which element to get the property + * @param name property name + * @param type will contain the value type. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_element_property_type_get(const E_Ofono_Element *element, const char *name, int *type) +{ + Eina_Bool ret; + name = eina_stringshare_add(name); + ret = e_ofono_element_property_type_get_stringshared(element, name, type); + eina_stringshare_del(name); + return ret; +} + +void +e_ofono_element_list_properties(const E_Ofono_Element *element, Eina_Bool (*cb)(void *data, const E_Ofono_Element *element, const char *name, int type, const void *value), const void *data) +{ + const E_Ofono_Element_Property *p; + + EINA_SAFETY_ON_NULL_RETURN(element); + EINA_SAFETY_ON_NULL_RETURN(cb); + + EINA_INLIST_FOREACH(element->props, p) + { + const void *value = NULL; + + switch (p->type) + { + case DBUS_TYPE_STRING: + value = &p->value.str; + break; + + case DBUS_TYPE_OBJECT_PATH: + value = &p->value.path; + break; + + case DBUS_TYPE_BOOLEAN: + value = (void *)(unsigned long)p->value.boolean; + break; + + case DBUS_TYPE_UINT16: + value = &p->value.u16; + break; + + case DBUS_TYPE_UINT32: + value = &p->value.u32; + break; + + default: + ERR("unsupported type %c", p->type); + } + + if (!cb((void *)data, element, p->name, p->type, value)) + return; + } +} + +/** + * Get dict value given its key inside a dict property. + * + * This will look into properties for one of type dict that contains + * the given key, to find the property. If no property is found then + * 0 is returned. + * + * If zero is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param element which element to get the property + * @param dict_name property name, must be previously stringshared + * @param key key inside dict, must be previously stringshared + * @param type if provided it will contain the value type. + * @param value where to store the property value, must be a pointer to the + * exact type, (Eina_Bool *) for booleans, (char **) for strings, and so on. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_element_property_dict_get_stringshared(const E_Ofono_Element *element, const char *dict_name, const char *key, int *type, void *value) +{ + const E_Ofono_Element_Property *p; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(dict_name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(value, EINA_FALSE); + + EINA_INLIST_FOREACH(element->props, p) + { + E_Ofono_Element_Dict_Entry *entry; + E_Ofono_Array *array; + + if (p->name != dict_name) + continue; + + if (p->type != DBUS_TYPE_ARRAY) + { + WRN("element %s (%p) has property \"%s\" is not an array: %c (%d)", + element->path, element, dict_name, p->type, p->type); + return EINA_FALSE; + } + + array = p->value.array; + if ((!array) || (array->type != DBUS_TYPE_DICT_ENTRY)) + { + int t = array ? array->type : DBUS_TYPE_INVALID; + WRN("element %s (%p) has property \"%s\" is not a dict: %c (%d)", + element->path, element, dict_name, t, t); + return EINA_FALSE; + } + + entry = _e_ofono_element_array_dict_find_stringshared(array, key); + if (!entry) + { + WRN("element %s (%p) has no dict property with name \"%s\" with " + "key \"%s\".", + element->path, element, dict_name, key); + return EINA_FALSE; + } + + if (type) + *type = entry->type; + + switch (entry->type) + { + case DBUS_TYPE_BOOLEAN: + *(Eina_Bool *)value = entry->value.boolean; + return EINA_TRUE; + + case DBUS_TYPE_BYTE: + *(unsigned char *)value = entry->value.byte; + return EINA_TRUE; + + case DBUS_TYPE_UINT16: + *(unsigned short *)value = entry->value.u16; + return EINA_TRUE; + + case DBUS_TYPE_UINT32: + *(unsigned int *)value = entry->value.u32; + return EINA_TRUE; + + case DBUS_TYPE_STRING: + *(const char **)value = entry->value.str; + return EINA_TRUE; + + case DBUS_TYPE_OBJECT_PATH: + *(const char **)value = entry->value.path; + return EINA_TRUE; + + default: + ERR("don't know how to get property %s, key %s type %c (%d)", + dict_name, key, entry->type, entry->type); + return EINA_FALSE; + } + } + + WRN("element %s (%p) has no property with name \"%s\".", + element->path, element, dict_name); + return EINA_FALSE; +} + +/** + * Get property value given its name. + * + * This will look into properties, to find the property. + * If no property is found then 0 is returned. + * + * If zero is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param element which element to get the property + * @param name property name, must be previously stringshared + * @param type if provided it will contain the value type. + * @param value where to store the property value, must be a pointer to the + * exact type, (Eina_Bool *) for booleans, (char **) for strings, and so on. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_element_property_get_stringshared(const E_Ofono_Element *element, const char *name, int *type, void *value) +{ + const E_Ofono_Element_Property *p; + + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(value, EINA_FALSE); + + EINA_INLIST_FOREACH(element->props, p) + { + if (p->name != name) + continue; + + if (type) + *type = p->type; + + switch (p->type) + { + case DBUS_TYPE_BOOLEAN: + *(Eina_Bool *)value = p->value.boolean; + return EINA_TRUE; + + case DBUS_TYPE_BYTE: + *(unsigned char *)value = p->value.byte; + return EINA_TRUE; + + case DBUS_TYPE_UINT16: + *(unsigned short *)value = p->value.u16; + return EINA_TRUE; + + case DBUS_TYPE_UINT32: + *(unsigned int *)value = p->value.u32; + return EINA_TRUE; + + case DBUS_TYPE_STRING: + *(const char **)value = p->value.str; + return EINA_TRUE; + + case DBUS_TYPE_OBJECT_PATH: + *(const char **)value = p->value.path; + return EINA_TRUE; + + case DBUS_TYPE_ARRAY: + *(E_Ofono_Array **)value = p->value.array; + return EINA_TRUE; + + default: + ERR("don't know how to get property type %c (%d)", + p->type, p->type); + return EINA_FALSE; + } + } + + WRN("element %s (%p) has no property with name \"%s\".", + element->path, element, name); + return EINA_FALSE; +} + +/** + * Get property value given its name. + * + * This will look into properties, to find the property. + * If no property is found then 0 is returned. + * + * If zero is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param element which element to get the property + * @param name property name + * @param type if provided it will contain the value type. + * @param value where to store the property value, must be a pointer to the + * exact type, (Eina_Bool *) for booleans, (char **) for strings, and so on. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_element_property_get(const E_Ofono_Element *element, const char *name, int *type, void *value) +{ + Eina_Bool ret; + name = eina_stringshare_add(name); + ret = e_ofono_element_property_get_stringshared + (element, name, type, value); + eina_stringshare_del(name); + return ret; +} + +struct e_ofono_elements_for_each_data +{ + Eina_Hash_Foreach cb; + void *data; +}; + +static Eina_Bool +_e_ofono_elements_for_each(Eina_Hash *hash __UNUSED__, const char *key, void *data, void *fdata) +{ + struct e_ofono_elements_for_each_data *each_data = fdata; + + each_data->cb(elements, key, data, each_data->data); + return EINA_TRUE; +} + +/** + * Call the given function for each existing element. + * + * @param cb function to call for each element. It will get as parameters, + * in order: the element pointer and the given @a user_data. + * @param user_data data to give to @a cb for each element. + */ +void +e_ofono_elements_for_each(Eina_Hash_Foreach cb, const void *user_data) +{ + struct e_ofono_elements_for_each_data data = {cb, (void *)user_data}; + + EINA_SAFETY_ON_NULL_RETURN(cb); + + eina_hash_foreach + (elements, (Eina_Hash_Foreach)_e_ofono_elements_for_each, &data); +} + +static Eina_Bool +_e_ofono_elements_get_allocate(unsigned int *count, E_Ofono_Element ***p_elements) +{ + *count = eina_hash_population(elements); + if (*count == 0) + { + *p_elements = NULL; + return EINA_TRUE; + } + + *p_elements = malloc(*count * sizeof(E_Ofono_Element *)); + if (!*p_elements) + { + ERR("could not allocate return array of %d elements: %s", + *count, strerror(errno)); + *count = 0; + return EINA_FALSE; + } + + return EINA_TRUE; +} + +static Eina_Bool +_e_ofono_elements_get_all(Eina_Hash *hash __UNUSED__, const char *key __UNUSED__, void *data, void *fdata) +{ + E_Ofono_Element *element = data; + E_Ofono_Element ***p_ret = fdata; + + **p_ret = element; + (*p_ret)++; + return EINA_TRUE; +} + +/** + * Get all known elements. + * + * No reference is added to these elements, since there are no threads + * in the system, you are free to add references yourself right after + * the return of this call without race condition, elements by the + * system (ie: elementRemoved signal)could only be touched on the next + * main loop iteration. + * + * @param count return the number of elements in array. + * @param p_elements array with all elements, these are not referenced + * and in no particular order, just set if return is 1. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_elements_get_all(unsigned int *count, E_Ofono_Element ***p_elements) +{ + E_Ofono_Element **p; + + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(p_elements, EINA_FALSE); + + if (!_e_ofono_elements_get_allocate(count, p_elements)) + return EINA_FALSE; + + p = *p_elements; + eina_hash_foreach + (elements, (Eina_Hash_Foreach)_e_ofono_elements_get_all, &p); + return EINA_TRUE; +} + +struct e_ofono_elements_get_all_str_data +{ + E_Ofono_Element **elements; + int count; + const char *str; +}; + +static Eina_Bool +_e_ofono_elements_get_all_type(Eina_Hash *hash __UNUSED__, const char *key __UNUSED__, void *e, void *user_data) +{ + struct e_ofono_elements_get_all_str_data *data = user_data; + E_Ofono_Element *element = e; + + if ((data->str) && (element->interface != data->str)) + return EINA_TRUE; + + data->elements[data->count] = element; + data->count++; + return EINA_TRUE; +} + +/** + * Get all known elements of type. + * + * No reference is added to these elements, since there are no threads + * in the system, you are free to add references yourself right after + * the return of this call without race condition, elements by the + * system (ie: ElementRemoved signal) could only be touched on the next + * main loop iteration. + * + * @param type type to filter, or NULL to get all. + * @param count return the number of elements in array. + * @param p_elements array with all elements, these are not referenced + * and in no particular order, just set if return is 1. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * + * @see e_ofono_elements_get_all() + */ +Eina_Bool +e_ofono_elements_get_all_type(const char *type, unsigned int *count, E_Ofono_Element ***p_elements) +{ + struct e_ofono_elements_get_all_str_data data; + + EINA_SAFETY_ON_NULL_RETURN_VAL(count, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(p_elements, EINA_FALSE); + + if (!_e_ofono_elements_get_allocate(count, p_elements)) + return EINA_FALSE; + + data.elements = *p_elements; + data.count = 0; + data.str = eina_stringshare_add(type); + eina_hash_foreach(elements, + (Eina_Hash_Foreach)_e_ofono_elements_get_all_type, + &data); + + eina_stringshare_del(data.str); + *count = data.count; + return EINA_TRUE; +} + +/** + * Get the element registered at given path. + * + * @param path the path to query for registered object. + * + * @return element pointer if found, NULL otherwise. No references are added. + */ +E_Ofono_Element * +e_ofono_element_get(const char *path, const char *interface) +{ + E_Ofono_Element *element; + char key[MAX_KEY_SIZE]; + + EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(interface, NULL); + + snprintf(key, MAX_KEY_SIZE, "%s:%s", path, interface); + element = eina_hash_find(elements, key); + + return element; +} + +static void +_e_ofono_element_property_changed_callback(void *data, DBusMessage *msg) +{ + E_Ofono_Element *element = (E_Ofono_Element *)data; + DBusMessageIter itr, v_itr; + int t, r, changed = 0; + const char *name = NULL; + void *value = NULL; + + DBG("Property changed in element %s %s", element->path, element->interface); + + if (!_dbus_callback_check_and_init(msg, &itr, NULL)) + return; + + t = dbus_message_iter_get_arg_type(&itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_STRING)) + { + ERR("missing name in property changed signal"); + return; + } + + dbus_message_iter_get_basic(&itr, &name); + + dbus_message_iter_next(&itr); + t = dbus_message_iter_get_arg_type(&itr); + if (!_dbus_iter_type_check(t, DBUS_TYPE_VARIANT)) + { + ERR("missing value in property changed signal"); + return; + } + + dbus_message_iter_recurse(&itr, &v_itr); + t = dbus_message_iter_get_arg_type(&v_itr); + + if (t == DBUS_TYPE_ARRAY) + { + value = _e_ofono_element_iter_get_array(&v_itr, name); + } + else if (t != DBUS_TYPE_INVALID) + { + dbus_message_iter_get_basic(&v_itr, &value); + } + else + { + ERR("property has invalid type %s", name); + return; + } + + r = _e_ofono_element_property_value_add(element, name, t, value); + if (r < 0) + { + ERR("failed to add property value %s (%c)", name, t); + } + else if (r == 1) + { + INF("property value changed %s (%c)", name, t); + changed = 1; + if ((strcmp(name, "Interfaces") == 0) && value) + { + char *interface; + Eina_Array_Iterator iterator; + unsigned int i; + E_Ofono_Element *e; + + EINA_ARRAY_ITER_NEXT(((E_Ofono_Array *)value)->array, i, + interface, iterator) + { + DBG("Found interface %s on %s", interface, element->path); + e_ofono_element_register(element->path, interface); + e = e_ofono_element_register(element->path, interface); + if ((e) && (!e_ofono_element_properties_sync(e))) + WRN("could not get properties of %s", e->path); + } + } + } + + if (changed) + _e_ofono_element_listeners_call(element); +} + +/** + * Register the given pair (path, interface), possibly creating an + * element and return it. + * + * This will check if (path, interface) is already registered, in + * that case the exiting element is returned. If it was not registered + * yet, a new element is created, registered and returned. + * + * This call will not add extra references to the object. + * + * @param path the path to register the element + * + * @return the registered object, no references are added. + */ +E_Ofono_Element * +e_ofono_element_register(const char *path, const char *interface) +{ + E_Ofono_Element *element; + char key[MAX_KEY_SIZE]; + + EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(interface, NULL); + + snprintf(key, MAX_KEY_SIZE, "%s:%s", path, interface); + element = eina_hash_find(elements, key); + if (element) + return element; + + element = e_ofono_element_new(path, interface); + if (!element) + return NULL; + + if (!eina_hash_add(elements, key, element)) + { + ERR("could not add element %s to hash, delete it.", path); + e_ofono_element_free(element); + return NULL; + } + + element->signal_handler = + e_dbus_signal_handler_add + (e_ofono_conn, e_ofono_system_bus_name_get(), + element->path, element->interface, "PropertyChanged", + _e_ofono_element_property_changed_callback, element); + + e_ofono_element_event_add(E_OFONO_EVENT_ELEMENT_ADD, element); + + return element; +} + +static void +_e_ofono_element_event_unregister_and_free(void *data __UNUSED__, void *ev) +{ + E_Ofono_Element *element = ev; + e_ofono_element_unref(element); +} + +static void +_e_ofono_element_unregister_internal(E_Ofono_Element *element) +{ + if (element->signal_handler) + { + e_dbus_signal_handler_del(e_ofono_conn, element->signal_handler); + element->signal_handler = NULL; + } + + ecore_event_add(E_OFONO_EVENT_ELEMENT_DEL, element, + _e_ofono_element_event_unregister_and_free, NULL); +} + +/** + * Forget about the given element. + * + * This will remove the element from the pool of known objects, then + * add an E_OFONO_EVENT_ELEMENT_DEL and after that will unreference it, + * possible freeing it. + * + * @param element element to forget about. Its reference will be removed. + */ +void +e_ofono_element_unregister(E_Ofono_Element *element) +{ + char key[MAX_KEY_SIZE]; + + if (!element) + return; + + snprintf(key, MAX_KEY_SIZE, "%s:%s", element->path, element->interface); + if (elements) + eina_hash_del_by_key(elements, key); +} + +/** + * Remove all known elements. + * + * This will remove all known elements but will NOT add any + * E_OFONO_EVENT_ELEMENT_DEL to main loop. + * + * This is just useful to make sure next e_ofono_manager_sync_elements() + * will not leave any stale elements. This is unlikely to happen, as + * E_Ofono is supposed to catch all required events to avoid stale elements. + */ +void +e_ofono_manager_clear_elements(void) +{ + e_ofono_elements_shutdown(); + e_ofono_elements_init(); +} + +/** + * Creates elements hash. + * + * This has no init counter since its already guarded by other code. + * @internal + */ +void +e_ofono_elements_init(void) +{ + EINA_SAFETY_ON_FALSE_RETURN(!elements); + elements = + eina_hash_string_superfast_new + (EINA_FREE_CB(_e_ofono_element_unregister_internal)); +} + +void +e_ofono_elements_shutdown(void) +{ + EINA_SAFETY_ON_FALSE_RETURN(!!elements); + eina_hash_free(elements); + elements = NULL; +} + +static inline Eina_Bool +_e_ofono_element_is(const E_Ofono_Element *element, const char *interface) +{ + return element->interface == interface; +} + +Eina_Bool +e_ofono_element_is_manager(const E_Ofono_Element *element) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + return _e_ofono_element_is(element, e_ofono_iface_manager); +} + +Eina_Bool +e_ofono_element_is_modem(const E_Ofono_Element *element) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + return _e_ofono_element_is(element, e_ofono_iface_modem); +} + +Eina_Bool +e_ofono_element_is_netreg(const E_Ofono_Element *element) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + return _e_ofono_element_is(element, e_ofono_iface_netreg); +} + diff --git a/src/lib/ofono/e_ofono_manager.c b/src/lib/ofono/e_ofono_manager.c new file mode 100644 index 0000000..a35aec1 --- /dev/null +++ b/src/lib/ofono/e_ofono_manager.c @@ -0,0 +1,45 @@ +#include "e_ofono_private.h" + +/** + * Get the element manager. + * + * @return element pointer if found, NULL otherwise. + */ +E_Ofono_Element * +e_ofono_manager_get(void) +{ + return e_ofono_element_get(manager_path, e_ofono_iface_manager); +} + +/** + * Get array of modem object paths. + * + * If this property isn't found then 0 is returned. + * If zero is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param array where to store the property value, must be a pointer + * to Eina_Array (Eina_Array **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_manager_modems_get(Eina_Array **array) +{ + E_Ofono_Element *element; + E_Ofono_Array *a = NULL; + Eina_Bool r; + + EINA_SAFETY_ON_NULL_RETURN_VAL(array, EINA_FALSE); + + element = e_ofono_element_get(manager_path, e_ofono_iface_manager); + r = e_ofono_element_property_get_stringshared + (element, e_ofono_prop_modems, NULL, &a); + if (a) + *array = a->array; + + return r; +} + diff --git a/src/lib/ofono/e_ofono_modem.c b/src/lib/ofono/e_ofono_modem.c new file mode 100644 index 0000000..2bb7041 --- /dev/null +++ b/src/lib/ofono/e_ofono_modem.c @@ -0,0 +1,68 @@ +#include "e_ofono_private.h" + +/* TODO: add a getter for Interfaces property */ + +/** + * Get property "Powered" value. + * + * @param powered where to store the property value, must be a pointer + * to Eina_Bool (Eina_Bool *), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_modem_powered_get(const E_Ofono_Element *element, Eina_Bool *powered) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(powered, EINA_FALSE); + + return e_ofono_element_property_get_stringshared + (element, e_ofono_prop_powered, NULL, powered); +} + +/** + * Call method SetProperty("Powered", powered) at the given element on server. + * + * + * @param powered value to set. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_modem_powered_set(E_Ofono_Element *element, Eina_Bool powered, E_DBus_Method_Return_Cb cb, const void *data) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + + return e_ofono_element_property_set_full + (element, e_ofono_prop_powered, DBUS_TYPE_BOOLEAN, + &powered, cb, data); +} + +/** + * Get property "Name" value. + * + * If this property isn't found then 0 is returned. + * If zero is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param address where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_modem_name_get(const E_Ofono_Element *element, const char **name) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); + + return e_ofono_element_property_get_stringshared + (element, e_ofono_prop_name, NULL, name); +} + diff --git a/src/lib/ofono/e_ofono_network_reg.c b/src/lib/ofono/e_ofono_network_reg.c new file mode 100644 index 0000000..34446ee --- /dev/null +++ b/src/lib/ofono/e_ofono_network_reg.c @@ -0,0 +1,94 @@ +#include "e_ofono_private.h" + +/** + * Get property "Mode" value. + * + * If this property isn't found then 0 is returned. + * If zero is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param mode where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_netreg_mode_get(const E_Ofono_Element *element, const char **mode) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(mode, EINA_FALSE); + + return e_ofono_element_property_get_stringshared + (element, e_ofono_prop_mode, NULL, mode); +} + +/** + * Get property "Status" value. + * + * If this property isn't found then 0 is returned. + * If zero is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param status where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_netreg_status_get(const E_Ofono_Element *element, const char **status) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(status, EINA_FALSE); + + return e_ofono_element_property_get_stringshared + (element, e_ofono_prop_status, NULL, status); +} + +/** + * Get property "Operator" value. + * + * If this property isn't found then 0 is returned. + * If zero is returned, then this call failed and parameter-returned + * values shall be considered invalid. + * + * @param operator where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_netreg_operator_get(const E_Ofono_Element *element, const char **op) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(op, EINA_FALSE); + + return e_ofono_element_property_get_stringshared + (element, e_ofono_prop_operator, NULL, op); +} + +/** + * Get property "Strength" value. + * + * @param strength where to store the property value, must be a pointer + * to char (char *), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_netreg_strength_get(const E_Ofono_Element *element, uint8_t *strength) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(strength, EINA_FALSE); + + return e_ofono_element_property_get_stringshared + (element, e_ofono_prop_strength, NULL, strength); +} + diff --git a/src/lib/ofono/e_ofono_private.h b/src/lib/ofono/e_ofono_private.h new file mode 100644 index 0000000..c8b75dc --- /dev/null +++ b/src/lib/ofono/e_ofono_private.h @@ -0,0 +1,125 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_ALLOCA_H +# include +#elif defined __GNUC__ +# define alloca __builtin_alloca +#elif defined _AIX +# define alloca __alloca +#elif defined _MSC_VER +# include +# define alloca _alloca +#else +# include +# ifdef __cplusplus +extern "C" +# endif +void * alloca (size_t); +#endif + +#include + +#include +#include + +#include "E_Ofono.h" + +static const char manager_path[] = "/"; + +extern const char *e_ofono_iface_manager; +extern const char *e_ofono_prop_modems; +extern const char *e_ofono_iface_modem; +extern const char *e_ofono_prop_name; +extern const char *e_ofono_prop_powered; +extern const char *e_ofono_prop_interfaces; +extern const char *e_ofono_iface_netreg; +extern const char *e_ofono_prop_mode; +extern const char *e_ofono_prop_status; +extern const char *e_ofono_prop_operator; +extern const char *e_ofono_prop_strength; +extern const char *e_ofono_iface_sms; +extern const char *e_ofono_prop_sca; +extern const char *e_ofono_method_send_sms; + +extern int _e_dbus_ofono_log_dom; + +#ifndef EINA_LOG_DEFAULT_COLOR +#define EINA_LOG_DEFAULT_COLOR EINA_COLOR_CYAN +#endif + +#undef DBG +#undef INF +#undef WRN +#undef ERR + +#define DBG(...) EINA_LOG_DOM_DBG(_e_dbus_ofono_log_dom, __VA_ARGS__) +#define INF(...) EINA_LOG_DOM_INFO(_e_dbus_ofono_log_dom, __VA_ARGS__) +#define WRN(...) EINA_LOG_DOM_WARN(_e_dbus_ofono_log_dom, __VA_ARGS__) +#define ERR(...) EINA_LOG_DOM_ERR(_e_dbus_ofono_log_dom, __VA_ARGS__) + +typedef struct _E_Ofono_Array E_Ofono_Array; + +struct _E_Ofono_Array +{ + int type; + Eina_Array *array; +}; + +static inline Eina_Bool +_dbus_callback_check_and_init(DBusMessage *msg, DBusMessageIter *itr, DBusError *err) +{ + if (!msg) + { + if (err) + ERR("an error was reported by server: " + "name=\"%s\", message=\"%s\"", + err->name, err->message); + else + ERR("callback without message arguments!"); + + return EINA_FALSE; + } + + if (!dbus_message_iter_init(msg, itr)) + { + ERR("could not init iterator."); + return EINA_FALSE; + } + + return EINA_TRUE; +} + +static inline Eina_Bool +__dbus_iter_type_check(int type, int expected, const char *expected_name) +{ + if (type == expected) + return EINA_TRUE; + + ERR("expected type %s (%c) but got %c instead!", + expected_name, expected, type); + + return EINA_FALSE; +} + +#define _dbus_iter_type_check(t, e) __dbus_iter_type_check(t, e, # e) + +extern E_DBus_Connection *e_ofono_conn; + +const char * e_ofono_system_bus_name_get(void); + +void e_ofono_manager_clear_elements(void); + +void e_ofono_elements_init(void); +void e_ofono_elements_shutdown(void); + +E_Ofono_Element * e_ofono_element_register(const char *path, const char *interface); +void e_ofono_element_unregister(E_Ofono_Element *element); + +Eina_Bool e_ofono_element_message_send(E_Ofono_Element *element, const char *method_name, const char *interface, E_DBus_Method_Return_Cb cb, DBusMessage *msg, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data); + +Eina_Bool e_ofono_element_call_full(E_Ofono_Element *element, const char *method_name, const char *interface, E_DBus_Method_Return_Cb cb, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data); +Eina_Bool e_ofono_element_call_with_path(E_Ofono_Element *element, const char *method_name, const char *string, E_DBus_Method_Return_Cb cb, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data); +Eina_Bool e_ofono_element_call_with_string(E_Ofono_Element *element, const char *method_name, const char *string, E_DBus_Method_Return_Cb cb, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data); +Eina_Bool e_ofono_element_call_with_path_and_string(E_Ofono_Element *element, const char *method_name, const char *path, const char *string, E_DBus_Method_Return_Cb cb, Eina_Inlist **pending, E_DBus_Method_Return_Cb user_cb, const void *user_data); diff --git a/src/lib/ofono/e_ofono_sms.c b/src/lib/ofono/e_ofono_sms.c new file mode 100644 index 0000000..af14281 --- /dev/null +++ b/src/lib/ofono/e_ofono_sms.c @@ -0,0 +1,78 @@ +#include "e_ofono_private.h" + +/** + * Get property "ServiceCenterAddress" value. + * + * @param sca where to store the property value, must be a pointer + * to string (const char **), it will not be allocated or + * copied and references will be valid until element changes, + * so copy it if you want to use it later. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_sms_sca_get(const E_Ofono_Element *element, const char **sca) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(sca, EINA_FALSE); + + return e_ofono_element_property_get_stringshared + (element, e_ofono_prop_sca, NULL, sca); +} + +/** + * Call method SetProperty("ServiceCenterAddress", powered) at the given + * element on server. + * + * + * @param sca value to set. + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_sms_sca_set(E_Ofono_Element *element, const char *sca, E_DBus_Method_Return_Cb cb, const void *data) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + + return e_ofono_element_property_set_full + (element, e_ofono_prop_sca, DBUS_TYPE_STRING, sca, cb, data); +} + +/** + * Send SMS message. + * + * Call method SendMessage(number, text) to send a new SMS message. + * + * @param number the destination of the message + * @param message text of message body + * @param cb function to call when server replies or some error happens. + * @param data data to give to cb when it is called. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool +e_ofono_sms_send_message(E_Ofono_Element *element, const char *number, const char *message, E_DBus_Method_Return_Cb cb, const void *data) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(element, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(number, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(message, EINA_FALSE); + + DBusMessage *m; + DBusMessageIter i; + + if (!(m = dbus_message_new_method_call(e_ofono_system_bus_name_get(), + element->path, element->interface, + e_ofono_method_send_sms))) + return EINA_FALSE; + + dbus_message_iter_init_append(m, &i); + dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &number); + dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &message); + + return e_ofono_element_message_send(element, e_ofono_method_send_sms, + e_ofono_iface_sms, NULL, m, + &element->_pending.send_sms, cb, data); +} + diff --git a/src/lib/ukit/E_Ukit.h b/src/lib/ukit/E_Ukit.h new file mode 100644 index 0000000..de86b1d --- /dev/null +++ b/src/lib/ukit/E_Ukit.h @@ -0,0 +1,169 @@ +#ifndef E_UKIT_H +#define E_UKIT_H + +#include +#include + +/** + * @defgroup EUkit_Group EUkit + * + * @{ + */ + +#define E_UDISKS_BUS "org.freedesktop.UDisks" +#define E_UDISKS_PATH "/org/freedesktop/UDisks" +#define E_UDISKS_INTERFACE "org.freedesktop.UDisks.Device" + +#define E_UPOWER_BUS "org.freedesktop.UPower" +#define E_UPOWER_PATH "/org/freedesktop/UPower" +#define E_UPOWER_INTERFACE "org.freedesktop.UPower.Device" + +/* message return types */ + +typedef struct E_Ukit_Property E_Ukit_Property; +typedef struct E_Ukit_Properties E_Ukit_Properties; + +struct E_Ukit_String_List_Return +{ + Eina_List *strings; /* list of const char * */ +}; + +struct E_Ukit_Bool_Return +{ + Eina_Bool boolean; +}; + +struct E_Ukit_UDI_Return +{ + const char *udi; +}; + +struct E_Ukit_Capability +{ + const char *udi; + const char *capability; +}; + +typedef enum +{ + E_UKIT_PROPERTY_TYPE_STRING, + E_UKIT_PROPERTY_TYPE_INT, + E_UKIT_PROPERTY_TYPE_UINT32, + E_UKIT_PROPERTY_TYPE_UINT64, + E_UKIT_PROPERTY_TYPE_INT64, + E_UKIT_PROPERTY_TYPE_BOOL, + E_UKIT_PROPERTY_TYPE_DOUBLE, + E_UKIT_PROPERTY_TYPE_STRLIST +} E_Ukit_Property_Type; + +struct E_Ukit_Property +{ + E_Ukit_Property_Type type; + union + { + const char *s; + int i; + Eina_Bool b; + double d; + uint32_t u; + uint64_t t; + int64_t x; + Eina_List *strlist; + } val; +}; + +struct E_Ukit_Properties +{ + Eina_Hash *properties; +}; + +typedef enum +{ + E_UPOWER_BATTERY_UNKNOWN, + E_UPOWER_BATTERY_LION, + E_UPOWER_BATTERY_LPOLYMER, + E_UPOWER_BATTERY_LIRONPHOS, + E_UPOWER_BATTERY_LEAD, + E_UPOWER_BATTERY_NICAD, + E_UPOWER_BATTERY_METALHYDRYDE +} E_UPower_Battery_Type; + +typedef enum +{ + E_UPOWER_STATE_UNKNOWN, + E_UPOWER_STATE_CHARGING, + E_UPOWER_STATE_DISCHARGING, + E_UPOWER_STATE_EMPTY, + E_UPOWER_STATE_FULL, + E_UPOWER_STATE_PENDINGCHARGE, + E_UPOWER_STATE_PENDINGDISCHARGE +} E_Upower_State; + +typedef enum +{ + E_UPOWER_SOURCE_UNKNOWN, + E_UPOWER_SOURCE_AC, + E_UPOWER_SOURCE_BATTERY, + E_UPOWER_SOURCE_UPS, + E_UPOWER_SOURCE_MONITOR, + E_UPOWER_SOURCE_MOUSE, + E_UPOWER_SOURCE_KEYBOARD, + E_UPOWER_SOURCE_PDA, + E_UPOWER_SOURCE_PHONE +} E_Upower_Source; + + +typedef struct E_Ukit_Properties E_Ukit_Get_All_Properties_Return; +typedef struct E_Ukit_Property E_Ukit_Get_Property_Return; +typedef struct E_Ukit_String_List_Return E_Ukit_String_List_Return; +typedef struct E_Ukit_String_List_Return E_Ukit_Get_All_Devices_Return; + +typedef struct E_Ukit_UDI_Return E_Ukit_Device_Added; +typedef struct E_Ukit_UDI_Return E_Ukit_Device_Removed; +typedef struct E_Ukit_Capability E_Ukit_New_Capability; + +#ifdef __cplusplus +extern "C" { +#endif + + EAPI int e_ukit_init(void); + EAPI int e_ukit_shutdown(void); + + EAPI DBusPendingCall *e_udisks_get_property(E_DBus_Connection *conn, const char *udi, const char *property, E_DBus_Callback_Func cb_func, void *data); + EAPI DBusPendingCall *e_udisks_get_all_properties(E_DBus_Connection *conn, const char *udi, E_DBus_Callback_Func cb_func, void *data); + EAPI DBusPendingCall *e_udisks_get_all_devices(E_DBus_Connection *conn, E_DBus_Callback_Func cb_func, void *data); + + EAPI DBusPendingCall *e_upower_get_property(E_DBus_Connection *conn, const char *udi, const char *property, E_DBus_Callback_Func cb_func, void *data); + EAPI DBusPendingCall *e_upower_get_all_properties(E_DBus_Connection *conn, const char *udi, E_DBus_Callback_Func cb_func, void *data); + EAPI DBusPendingCall *e_upower_get_all_devices(E_DBus_Connection *conn, E_DBus_Callback_Func cb_func, void *data); + + EAPI DBusPendingCall * e_upower_suspend_allowed(E_DBus_Connection *conn, E_DBus_Callback_Func cb_func, void *data); + EAPI DBusPendingCall * e_upower_suspend(E_DBus_Connection *conn, E_DBus_Callback_Func cb_func, void *data); + EAPI DBusPendingCall * e_upower_hibernate(E_DBus_Connection *conn, E_DBus_Callback_Func cb_func, void *data); + EAPI DBusPendingCall * e_upower_hibernate_allowed(E_DBus_Connection *conn, E_DBus_Callback_Func cb_func, void *data); + +/* utility functions */ + EAPI void e_ukit_property_free(E_Ukit_Property *prop); + EAPI const char *e_ukit_property_string_get(E_Ukit_Properties *properties, const char *key, int *err); + EAPI Eina_Bool e_ukit_property_bool_get(E_Ukit_Properties *properties, const char *key, int *err); + EAPI int e_ukit_property_int_get(E_Ukit_Properties *properties, const char *key, int *err); + EAPI uint32_t e_ukit_property_uint32_get(E_Ukit_Properties *properties, const char *key, int *err); + EAPI uint64_t e_ukit_property_uint64_get(E_Ukit_Properties *properties, const char *key, int *err); + EAPI int64_t e_ukit_property_int64_get(E_Ukit_Properties *properties, const char *key, int *err); + EAPI double e_ukit_property_double_get(E_Ukit_Properties *properties, const char *key, int *err); + EAPI const Eina_List *e_ukit_property_strlist_get(E_Ukit_Properties *properties, const char *key, int *err); + +/* (un)mount */ + EAPI DBusPendingCall *e_udisks_volume_mount(E_DBus_Connection *conn, const char *udi, const char *fstype, Eina_List *options); + EAPI DBusPendingCall *e_udisks_volume_unmount(E_DBus_Connection *conn, const char *udi, Eina_List *options); + EAPI DBusPendingCall *e_udisks_volume_eject(E_DBus_Connection *conn, const char *udi, Eina_List *options); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif diff --git a/src/lib/ukit/Makefile.am b/src/lib/ukit/Makefile.am new file mode 100644 index 0000000..dc32484 --- /dev/null +++ b/src/lib/ukit/Makefile.am @@ -0,0 +1,27 @@ +MAINTAINERCLEANFILES = Makefile.in + +AM_CPPFLAGS = \ +-I $(top_srcdir)/src/lib/dbus \ +@EDBUS_CFLAGS@ + +if BUILD_EUKIT + +lib_LTLIBRARIES = libeukit.la +includes_HEADERS = E_Ukit.h +includesdir = $(includedir)/e_dbus-@VMAJ@ + +libeukit_la_SOURCES = \ +e_udisks.c \ +e_upower.c \ +e_ukit_util.c \ +e_ukit_main.c \ +e_ukit_private_util.c + +libeukit_la_LIBADD = \ +@EDBUS_LIBS@ $(top_builddir)/src/lib/dbus/libedbus.la + +libeukit_la_LDFLAGS = -version-info @version_info@ @release_info@ + +endif + +EXTRA_DIST = e_ukit_private.h diff --git a/src/lib/ukit/e_udisks.c b/src/lib/ukit/e_udisks.c new file mode 100644 index 0000000..7a0dd24 --- /dev/null +++ b/src/lib/ukit/e_udisks.c @@ -0,0 +1,209 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "e_ukit_private.h" + +#define E_UKIT_BUS E_UDISKS_BUS +#define E_UKIT_PATH E_UDISKS_PATH +#define E_UKIT_INTERFACE E_UDISKS_INTERFACE + +const char *e_udisks_iface="org.freedesktop.UDisks.Device"; + +#if 0 +static void cb_device_get_property(void *data, DBusMessage *msg, DBusError *err); +static void cb_device_get_all_properties(void *data, DBusMessage *msg, DBusError *err); +static void cb_device_query_capability(void *data, DBusMessage *msg, DBusError *err); +#endif + +/* Properties.Get */ +EAPI DBusPendingCall * +e_udisks_get_property(E_DBus_Connection *conn, const char *udi, const char *property, E_DBus_Callback_Func cb_func, void *data) +{ + DBusMessage *msg; + DBusPendingCall *ret; + + EINA_SAFETY_ON_NULL_RETURN_VAL(udi, NULL); + EINA_SAFETY_ON_TRUE_RETURN_VAL(!udi[0], NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(property, NULL); + + msg = e_ukit_property_call_new(udi, "Get"); + dbus_message_append_args(msg, DBUS_TYPE_STRING, &e_udisks_iface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID); + ret = e_dbus_method_call_send(conn, msg, unmarshal_property, cb_func, free_property, -1, data); + dbus_message_unref(msg); + return ret; +} + +/* Properties.GetAll */ +EAPI DBusPendingCall * +e_udisks_get_all_properties(E_DBus_Connection *conn, const char *udi, E_DBus_Callback_Func cb_func, void *data) +{ + DBusMessage *msg; + DBusPendingCall *ret; + + EINA_SAFETY_ON_NULL_RETURN_VAL(udi, NULL); + EINA_SAFETY_ON_TRUE_RETURN_VAL(!udi[0], NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + + msg = e_ukit_property_call_new(udi, "GetAll"); + dbus_message_append_args(msg, DBUS_TYPE_STRING, &e_udisks_iface, DBUS_TYPE_INVALID); + ret = e_dbus_method_call_send(conn, msg, unmarshal_device_get_all_properties, cb_func, free_device_get_all_properties, -1, data); + dbus_message_unref(msg); + return ret; +} + + + +/* void FilesystemMount(string fstype, array{string}options) */ + +/** + * @brief Mount a Filesystem + * + * @param conn the E_DBus_Connection + * @param udi the udi of the device object + * @param fstype the fstype of the device (e.g. volume.fstype property) + * @param options a list of additional options to pass to mount + * + * @return mount point of fs or error + */ +EAPI DBusPendingCall * +e_udisks_volume_mount(E_DBus_Connection *conn, const char *udi, const char *fstype, Eina_List *options) +{ + DBusMessage *msg; + DBusMessageIter iter, subiter; + Eina_List *l; + DBusPendingCall *ret; + + EINA_SAFETY_ON_NULL_RETURN_VAL(udi, NULL); + EINA_SAFETY_ON_TRUE_RETURN_VAL(!udi[0], NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + + msg = e_ukit_device_call_new(udi, "FilesystemMount"); + + dbus_message_iter_init_append(msg, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fstype); + if (dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &subiter)) + { + if (options) + { + const char *opt; + + EINA_LIST_FOREACH(options, l, opt) + dbus_message_iter_append_basic(&subiter, DBUS_TYPE_STRING, &opt); + } + dbus_message_iter_close_container(&iter, &subiter); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + + ret = e_dbus_method_call_send(conn, msg, NULL, NULL, NULL, -1, NULL); + dbus_message_unref(msg); + return ret; +} + +/* void Unmount(array{string} options) */ + +/** + * @brief Unmount a Volume + * + * @param conn the E_DBus_Connection + * @param udi the udi of the device object + * @param options a list of additional options (currently only 'force' is supported) + */ +EAPI DBusPendingCall * +e_udisks_volume_unmount(E_DBus_Connection *conn, const char *udi, Eina_List *options) +{ + DBusMessage *msg; + DBusMessageIter iter, subiter; + Eina_List *l; + DBusPendingCall *ret; + + EINA_SAFETY_ON_NULL_RETURN_VAL(udi, NULL); + EINA_SAFETY_ON_TRUE_RETURN_VAL(!udi[0], NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + + msg = e_ukit_device_call_new(udi, "FilesystemUnmount"); + + dbus_message_iter_init_append(msg, &iter); + if (dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &subiter)) + { + if (options) + { + const char *opt; + + EINA_LIST_FOREACH(options, l, opt) + dbus_message_iter_append_basic(&subiter, DBUS_TYPE_STRING, &opt); + } + dbus_message_iter_close_container(&iter, &subiter); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + + ret = e_dbus_method_call_send(conn, msg, NULL, NULL, NULL, -1, NULL); + dbus_message_unref(msg); + return ret; +} + +/** + * @brief Eject a Volume + * + * @param conn the E_DBus_Connection + * @param udi the udi of the device object + * @param options a list of additional options (none currently supported) + */ +EAPI DBusPendingCall * +e_udisks_volume_eject(E_DBus_Connection *conn, const char *udi, Eina_List *options) +{ + DBusMessage *msg; + DBusMessageIter iter, subiter; + Eina_List *l; + DBusPendingCall *ret; + + EINA_SAFETY_ON_NULL_RETURN_VAL(udi, NULL); + EINA_SAFETY_ON_TRUE_RETURN_VAL(!udi[0], NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + + msg = e_ukit_device_call_new(udi, "DriveEject"); + + dbus_message_iter_init_append(msg, &iter); + if (dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &subiter)) + { + if (options) + { + const char *opt; + + EINA_LIST_FOREACH(options, l, opt) + dbus_message_iter_append_basic(&subiter, DBUS_TYPE_STRING, &opt); + } + dbus_message_iter_close_container(&iter, &subiter); + } + else + { + ERR("dbus_message_iter_open_container() failed"); + } + + ret = e_dbus_method_call_send(conn, msg, NULL, NULL, NULL, -1, NULL); + dbus_message_unref(msg); + return ret; +} + +/* EnumerateDevices */ +EAPI DBusPendingCall * +e_udisks_get_all_devices(E_DBus_Connection *conn, E_DBus_Callback_Func cb_func, void *data) +{ + DBusMessage *msg; + DBusPendingCall *ret; + + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + + msg = e_ukit_call_new(E_UKIT_PATH, "EnumerateDevices"); + ret = e_dbus_method_call_send(conn, msg, unmarshal_string_list, cb_func, free_string_list, -1, data); + dbus_message_unref(msg); + return ret; +} diff --git a/src/lib/ukit/e_ukit_main.c b/src/lib/ukit/e_ukit_main.c new file mode 100644 index 0000000..c541e3d --- /dev/null +++ b/src/lib/ukit/e_ukit_main.c @@ -0,0 +1,58 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "e_ukit_private.h" + +int _e_dbus_ukit_log_dom = -1; +int _e_dbus_ukit_init_count = 0; + +EAPI int +e_ukit_init(void) +{ + if (++_e_dbus_ukit_init_count != 1) + return _e_dbus_ukit_init_count; + + if (!eina_init()) + return --_e_dbus_ukit_init_count; + + _e_dbus_ukit_log_dom = eina_log_domain_register + ("e_ukit", E_DBUS_COLOR_DEFAULT); + if (_e_dbus_ukit_log_dom < 0) + { + EINA_LOG_ERR("Could not register 'e_ukit' log domain."); + goto shutdown_eina; + } + + if (!e_dbus_init()) + { + ERR("Could not initialize E_DBus."); + goto unregister_log_domain; + } + + return _e_dbus_ukit_init_count; + + unregister_log_domain: + eina_log_domain_unregister(_e_dbus_ukit_log_dom); + _e_dbus_ukit_log_dom = -1; + shutdown_eina: + eina_shutdown(); + + return _e_dbus_ukit_init_count; +} + +EAPI int +e_ukit_shutdown(void) +{ + if (--_e_dbus_ukit_init_count != 0) + return _e_dbus_ukit_init_count; + + e_dbus_shutdown(); + + eina_log_domain_unregister(_e_dbus_ukit_log_dom); + _e_dbus_ukit_log_dom = -1; + eina_shutdown(); + + return _e_dbus_ukit_init_count; +} diff --git a/src/lib/ukit/e_ukit_private.h b/src/lib/ukit/e_ukit_private.h new file mode 100644 index 0000000..c25ee67 --- /dev/null +++ b/src/lib/ukit/e_ukit_private.h @@ -0,0 +1,43 @@ +#ifndef E_UKIT_PRIVATE_H +#define E_UKIT_PRIVATE_H + +#ifndef E_DBUS_COLOR_DEFAULT +# define E_DBUS_COLOR_DEFAULT EINA_COLOR_CYAN +#endif + +extern int _e_dbus_ukit_log_dom; + +#ifdef ERR +# undef ERR +#endif + +#ifdef INF +# undef INF +#endif + +#ifdef WARN +# undef WARN +#endif + +#ifdef DBG +# undef DBG +#endif + +#define e_ukit_call_new(udi, member) dbus_message_new_method_call(E_UKIT_BUS, udi, E_UKIT_BUS, member) +#define e_ukit_device_call_new(udi, member) dbus_message_new_method_call(E_UKIT_BUS, udi, E_UKIT_INTERFACE, member) +#define e_ukit_property_call_new(udi, member) dbus_message_new_method_call(E_UKIT_BUS, udi, E_DBUS_FDO_INTERFACE_PROPERTIES, member) + +#define DBG(...) EINA_LOG_DOM_DBG(_e_dbus_ukit_log_dom, __VA_ARGS__) +#define INFO(...) EINA_LOG_DOM_INFO(_e_dbus_ukit_log_dom, __VA_ARGS__) +#define WARN(...) EINA_LOG_DOM_WARN(_e_dbus_ukit_log_dom, __VA_ARGS__) +#define ERR(...) EINA_LOG_DOM_ERR(_e_dbus_ukit_log_dom, __VA_ARGS__) + +void *unmarshal_property(DBusMessage *msg, DBusError *err); +void free_property(void *data); +void *unmarshal_device_get_all_properties(DBusMessage *msg, DBusError *err); +void free_device_get_all_properties(void *data); +void *unmarshal_string_list(DBusMessage *msg, DBusError *err); +void free_string_list(void *data); + + +#endif diff --git a/src/lib/ukit/e_ukit_private_util.c b/src/lib/ukit/e_ukit_private_util.c new file mode 100644 index 0000000..f024079 --- /dev/null +++ b/src/lib/ukit/e_ukit_private_util.c @@ -0,0 +1,259 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include "e_ukit_private.h" + +void * +unmarshal_property(DBusMessage *msg, DBusError *err) +{ + E_Ukit_Get_Property_Return *ret = NULL; + DBusMessageIter iter, a_iter; + int type; + char *tmp; + + if (!dbus_message_iter_init(msg, &iter)) return NULL; /* no params in message */ + + ret = calloc(1, sizeof(E_Ukit_Get_Property_Return)); + if (!ret) + { + dbus_set_error(err, DBUS_ERROR_NO_MEMORY, ""); + return NULL; + } + + dbus_message_iter_recurse(&iter, &a_iter); + if (dbus_message_iter_get_arg_type(&a_iter) != DBUS_TYPE_INVALID) + { + type = dbus_message_iter_get_arg_type(&a_iter); + + switch(type) + { + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + ret->type = E_UKIT_PROPERTY_TYPE_STRING; + dbus_message_iter_get_basic(&a_iter, &tmp); + ret->val.s = eina_stringshare_add(tmp); + break; + case DBUS_TYPE_INT32: + ret->type = E_UKIT_PROPERTY_TYPE_INT; + dbus_message_iter_get_basic(&a_iter, &(ret->val.i)); + break; + case DBUS_TYPE_UINT32: + ret->type = E_UKIT_PROPERTY_TYPE_UINT32; + dbus_message_iter_get_basic(&a_iter, &(ret->val.u)); + break; + case DBUS_TYPE_UINT64: + ret->type = E_UKIT_PROPERTY_TYPE_UINT64; + dbus_message_iter_get_basic(&a_iter, &(ret->val.t)); + break; + case DBUS_TYPE_INT64: + ret->type = E_UKIT_PROPERTY_TYPE_INT64; + dbus_message_iter_get_basic(&a_iter, &(ret->val.x)); + break; + case DBUS_TYPE_BOOLEAN: + ret->type = E_UKIT_PROPERTY_TYPE_BOOL; + dbus_message_iter_get_basic(&a_iter, &(ret->val.b)); + break; + case DBUS_TYPE_DOUBLE: + ret->type = E_UKIT_PROPERTY_TYPE_DOUBLE; + dbus_message_iter_get_basic(&a_iter, &(ret->val.d)); + break; + case DBUS_TYPE_ARRAY: + ret->type = E_UKIT_PROPERTY_TYPE_STRLIST; + { + DBusMessageIter list_iter; + ret->val.strlist = NULL; + dbus_message_iter_recurse(&a_iter, &list_iter); + while (dbus_message_iter_get_arg_type(&list_iter) != DBUS_TYPE_INVALID) + { + char *str; + dbus_message_iter_get_basic(&list_iter, &str); + tmp = (char*)eina_stringshare_add(str); + ret->val.strlist = eina_list_append(ret->val.strlist, tmp); + dbus_message_iter_next(&list_iter); + } + } + break; + default: + break; + } + } + + return ret; +} + +void +free_property(void *data) +{ + E_Ukit_Get_Property_Return *ret = data; + + if (!ret) return; + if (ret->type == E_UKIT_PROPERTY_TYPE_STRLIST) + { + const char *s; + EINA_LIST_FREE(ret->val.strlist, s) + eina_stringshare_del(s); + } + else if (ret->type == E_UKIT_PROPERTY_TYPE_STRING) + eina_stringshare_del(ret->val.s); + free(ret); +} + +void * +unmarshal_device_get_all_properties(DBusMessage *msg, DBusError *err) +{ + E_Ukit_Get_All_Properties_Return *ret = NULL; + DBusMessageIter iter, a_iter, s_iter, v_iter; + int type; + char *tmp; + + /* a{sv} = array of string+variants */ + if (!dbus_message_has_signature(msg, "a{sv}")) + { + dbus_set_error(err, DBUS_ERROR_INVALID_SIGNATURE, ""); + return NULL; + } + + ret = calloc(1, sizeof(E_Ukit_Get_All_Properties_Return)); + if (!ret) + { + dbus_set_error(err, DBUS_ERROR_NO_MEMORY, ""); + return NULL; + } + + ret->properties = eina_hash_string_small_new(EINA_FREE_CB(e_ukit_property_free)); + + dbus_message_iter_init(msg, &iter); + dbus_message_iter_recurse(&iter, &a_iter); + while (dbus_message_iter_get_arg_type(&a_iter) != DBUS_TYPE_INVALID) + { + const char *name; + E_Ukit_Property *prop = calloc(1, sizeof(E_Ukit_Property)); + dbus_message_iter_recurse(&a_iter, &s_iter); + dbus_message_iter_get_basic(&s_iter, &name); + dbus_message_iter_next(&s_iter); + dbus_message_iter_recurse(&s_iter, &v_iter); + + type = dbus_message_iter_get_arg_type(&v_iter); + switch(type) + { + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + prop->type = E_UKIT_PROPERTY_TYPE_STRING; + dbus_message_iter_get_basic(&v_iter, &tmp); + prop->val.s = eina_stringshare_add(tmp); + break; + case DBUS_TYPE_INT32: + prop->type = E_UKIT_PROPERTY_TYPE_INT; + dbus_message_iter_get_basic(&v_iter, &(prop->val.i)); + break; + case DBUS_TYPE_UINT32: + prop->type = E_UKIT_PROPERTY_TYPE_UINT32; + dbus_message_iter_get_basic(&v_iter, &(prop->val.u)); + break; + case DBUS_TYPE_UINT64: + prop->type = E_UKIT_PROPERTY_TYPE_UINT64; + dbus_message_iter_get_basic(&v_iter, &(prop->val.t)); + break; + case DBUS_TYPE_INT64: + prop->type = E_UKIT_PROPERTY_TYPE_INT64; + dbus_message_iter_get_basic(&v_iter, &(prop->val.x)); + break; + case DBUS_TYPE_BOOLEAN: + prop->type = E_UKIT_PROPERTY_TYPE_BOOL; + dbus_message_iter_get_basic(&v_iter, &(prop->val.b)); + break; + case DBUS_TYPE_DOUBLE: + prop->type = E_UKIT_PROPERTY_TYPE_DOUBLE; + dbus_message_iter_get_basic(&v_iter, &(prop->val.d)); + break; + case DBUS_TYPE_ARRAY: + prop->type = E_UKIT_PROPERTY_TYPE_STRLIST; + { + DBusMessageIter list_iter; + prop->val.strlist = NULL; + dbus_message_iter_recurse(&v_iter, &list_iter); + while (dbus_message_iter_get_arg_type(&list_iter) != DBUS_TYPE_INVALID) + { + char *str; + dbus_message_iter_get_basic(&list_iter, &str); + tmp = (char*)eina_stringshare_add(str); + prop->val.strlist = eina_list_append(prop->val.strlist, tmp); + dbus_message_iter_next(&list_iter); + } + } + break; + default: + WARN("EUkit Error: unexpected property type (%s): %c", name, dbus_message_iter_get_arg_type(&v_iter)); + break; + } + eina_hash_add(ret->properties, name, prop); + + dbus_message_iter_next(&a_iter); + } + + return ret; +} + +void +free_device_get_all_properties(void *data) +{ + E_Ukit_Get_All_Properties_Return *ret = data; + + if (!ret) return; + eina_hash_free(ret->properties); + free(ret); +} + +void * +unmarshal_string_list(DBusMessage *msg, DBusError *err) +{ + E_Ukit_String_List_Return *ret = NULL; + DBusMessageIter iter, sub; + char *tmp; + + /* ao = array of object strings */ + if (!dbus_message_has_signature(msg, "ao")) + { + dbus_set_error(err, DBUS_ERROR_INVALID_SIGNATURE, ""); + return NULL; + } + + ret = calloc(1, sizeof(E_Ukit_String_List_Return)); + if (!ret) + { + dbus_set_error(err, DBUS_ERROR_NO_MEMORY, ""); + return NULL; + } + + ret->strings = NULL; + + dbus_message_iter_init(msg, &iter); + dbus_message_iter_recurse(&iter, &sub); + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) + { + char *dev = NULL; + + dbus_message_iter_get_basic(&sub, &dev); + tmp = (char*)eina_stringshare_add(dev); + if (dev) ret->strings = eina_list_append(ret->strings, tmp); + dbus_message_iter_next(&sub); + } + + return ret; +} + +void +free_string_list(void *data) +{ + E_Ukit_String_List_Return *ret = data; + const char *x; + + if (!ret) return; + EINA_LIST_FREE(ret->strings, x) + eina_stringshare_del(x); + free(ret); +} diff --git a/src/lib/ukit/e_ukit_util.c b/src/lib/ukit/e_ukit_util.c new file mode 100644 index 0000000..cb97154 --- /dev/null +++ b/src/lib/ukit/e_ukit_util.c @@ -0,0 +1,129 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "e_ukit_private.h" + +/** + * @internal + * @brief free a property structure + * @param prop the property to free + */ +EAPI void +e_ukit_property_free(E_Ukit_Property *prop) +{ + if (prop->type == E_UKIT_PROPERTY_TYPE_STRLIST) + eina_list_free(prop->val.strlist); + free(prop); +} + +/** + * @brief Retrive a string from an element of a property hash + * @param properties the E_Ukit_Properties structure + * @param key the key of the property to retrieve + * @param err a pointer to an int, which if supplied, will be set to 0 on success and 1 on an error + */ +EAPI const char * +e_ukit_property_string_get(E_Ukit_Properties *properties, const char *key, int *err) +{ + E_Ukit_Property *prop; + if (err) *err = 0; + if (!properties->properties) return NULL; + prop = eina_hash_find(properties->properties, key); + if (prop) return prop->val.s; + + if (err) *err = 1; + return NULL; +} + +EAPI Eina_Bool +e_ukit_property_bool_get(E_Ukit_Properties *properties, const char *key, int *err) +{ + E_Ukit_Property *prop; + if (err) *err = 0; + if (!properties->properties) return EINA_FALSE; + prop = eina_hash_find(properties->properties, key); + if (prop) return prop->val.b; + + if (err) *err = 1; + return EINA_FALSE; +} + +EAPI int +e_ukit_property_int_get(E_Ukit_Properties *properties, const char *key, int *err) +{ + E_Ukit_Property *prop; + if (err) *err = 0; + if (!properties->properties) return 0; + prop = eina_hash_find(properties->properties, key); + if (prop) return prop->val.i; + + if (err) *err = 1; + return 0; +} + +EAPI uint32_t +e_ukit_property_uint32_get(E_Ukit_Properties *properties, const char *key, int *err) +{ + E_Ukit_Property *prop; + if (err) *err = 0; + if (!properties->properties) return 0; + prop = eina_hash_find(properties->properties, key); + if (prop) return prop->val.u; + + if (err) *err = 1; + return 0; +} + +EAPI uint64_t +e_ukit_property_uint64_get(E_Ukit_Properties *properties, const char *key, int *err) +{ + E_Ukit_Property *prop; + if (err) *err = 0; + if (!properties->properties) return 0; + prop = eina_hash_find(properties->properties, key); + if (prop) return prop->val.t; + + if (err) *err = 1; + return 0; +} + +EAPI int64_t +e_ukit_property_int64_get(E_Ukit_Properties *properties, const char *key, int *err) +{ + E_Ukit_Property *prop; + if (err) *err = 0; + if (!properties->properties) return 0; + prop = eina_hash_find(properties->properties, key); + if (prop) return prop->val.x; + + if (err) *err = 1; + return 0; +} + +EAPI double +e_ukit_property_double_get(E_Ukit_Properties *properties, const char *key, int *err) +{ + E_Ukit_Property *prop; + if (err) *err = 0; + if (!properties->properties) return 0; + prop = eina_hash_find(properties->properties, key); + if (prop) return prop->val.d; + + if (err) *err = 1; + return 0; +} + +EAPI const Eina_List * +e_ukit_property_strlist_get(E_Ukit_Properties *properties, const char *key, int *err) +{ + E_Ukit_Property *prop; + if (err) *err = 0; + if (!properties->properties) return NULL; + prop = eina_hash_find(properties->properties, key); + if (prop) return prop->val.strlist; + + if (err) *err = 1; + return NULL; +} diff --git a/src/lib/ukit/e_upower.c b/src/lib/ukit/e_upower.c new file mode 100644 index 0000000..f3bc6e5 --- /dev/null +++ b/src/lib/ukit/e_upower.c @@ -0,0 +1,131 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "e_ukit_private.h" + +#define E_UKIT_BUS E_UPOWER_BUS +#define E_UKIT_PATH E_UPOWER_PATH +#define E_UKIT_INTERFACE E_UPOWER_INTERFACE + +const char *e_upower_iface="org.freedesktop.UPower.Device"; + +#if 0 +static void cb_device_get_property(void *data, DBusMessage *msg, DBusError *err); +static void cb_device_get_all_properties(void *data, DBusMessage *msg, DBusError *err); +static void cb_device_query_capability(void *data, DBusMessage *msg, DBusError *err); +#endif + +/* Properties.Get */ +EAPI DBusPendingCall * +e_upower_get_property(E_DBus_Connection *conn, const char *udi, const char *property, E_DBus_Callback_Func cb_func, void *data) +{ + DBusMessage *msg; + DBusPendingCall *ret; + + EINA_SAFETY_ON_NULL_RETURN_VAL(udi, NULL); + EINA_SAFETY_ON_TRUE_RETURN_VAL(!udi[0], NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(property, NULL); + + msg = e_ukit_property_call_new(udi, "Get"); + dbus_message_append_args(msg, DBUS_TYPE_STRING, &e_upower_iface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID); + ret = e_dbus_method_call_send(conn, msg, unmarshal_property, cb_func, free_property, -1, data); + dbus_message_unref(msg); + return ret; +} + +/* Properties.GetAll */ +EAPI DBusPendingCall * +e_upower_get_all_properties(E_DBus_Connection *conn, const char *udi, E_DBus_Callback_Func cb_func, void *data) +{ + DBusMessage *msg; + DBusPendingCall *ret; + + EINA_SAFETY_ON_NULL_RETURN_VAL(udi, NULL); + EINA_SAFETY_ON_TRUE_RETURN_VAL(!udi[0], NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + + msg = e_ukit_property_call_new(udi, "GetAll"); + dbus_message_append_args(msg, DBUS_TYPE_STRING, &e_upower_iface, DBUS_TYPE_INVALID); + ret = e_dbus_method_call_send(conn, msg, unmarshal_device_get_all_properties, cb_func, free_device_get_all_properties, -1, data); + dbus_message_unref(msg); + return ret; +} + + +EAPI DBusPendingCall * +e_upower_hibernate(E_DBus_Connection *conn, E_DBus_Callback_Func cb_func, void *data) +{ + DBusMessage *msg; + DBusPendingCall *ret; + + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + + msg = e_ukit_call_new(E_UKIT_PATH, "Hibernate"); + + ret = e_dbus_method_call_send(conn, msg, NULL, cb_func, NULL, -1, data); + dbus_message_unref(msg); + return ret; +} + +EAPI DBusPendingCall * +e_upower_suspend(E_DBus_Connection *conn, E_DBus_Callback_Func cb_func, void *data) +{ + DBusMessage *msg; + DBusPendingCall *ret; + + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + + msg = e_ukit_call_new(E_UKIT_PATH, "Suspend"); + + ret = e_dbus_method_call_send(conn, msg, NULL, cb_func, NULL, -1, data); + dbus_message_unref(msg); + return ret; +} + +EAPI DBusPendingCall * +e_upower_hibernate_allowed(E_DBus_Connection *conn, E_DBus_Callback_Func cb_func, void *data) +{ + DBusMessage *msg; + DBusPendingCall *ret; + + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + + msg = e_ukit_call_new(E_UKIT_PATH, "HibernateAllowed"); + + ret = e_dbus_method_call_send(conn, msg, unmarshal_property, cb_func, free_property, -1, data); + dbus_message_unref(msg); + return ret; +} + +EAPI DBusPendingCall * +e_upower_suspend_allowed(E_DBus_Connection *conn, E_DBus_Callback_Func cb_func, void *data) +{ + DBusMessage *msg; + DBusPendingCall *ret; + + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + + msg = e_ukit_call_new(E_UKIT_PATH, "SuspendAllowed"); + + ret = e_dbus_method_call_send(conn, msg, unmarshal_property, cb_func, free_property, -1, data); + dbus_message_unref(msg); + return ret; +} + +/* EnumerateDevices */ +EAPI DBusPendingCall * +e_upower_get_all_devices(E_DBus_Connection *conn, E_DBus_Callback_Func cb_func, void *data) +{ + DBusMessage *msg; + DBusPendingCall *ret; + + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL); + + msg = e_ukit_call_new(E_UKIT_PATH, "EnumerateDevices"); + ret = e_dbus_method_call_send(conn, msg, unmarshal_string_list, cb_func, free_string_list, -1, data); + dbus_message_unref(msg); + return ret; +} -- 2.7.4