aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean-Luc Lamadon <jean-luc.lamadon@nokia.com>2009-10-27 16:56:12 +0200
committerJean-Luc Lamadon <jean-luc.lamadon@nokia.com>2009-10-27 16:56:12 +0200
commitaf6acc5189cab7f130b9c40501fc570970ae0892 (patch)
treea09995cdee866f0a4c205ba340ffc7236fe86675
parentc340ac0e5d1358b0a4a355a67b1e8349a8290665 (diff)
parent39930b262b5b2f32964eb3fbd1228b6859dbfd05 (diff)
Merge branch 'protocol_chge_provider' of git@gitorious.org:maemo-af/contextkit into protocol_chge_provider
-rw-r--r--.gitignore2
-rw-r--r--HACKING19
-rw-r--r--Makefile.am2
-rw-r--r--am/tests.am4
-rw-r--r--configure.ac21
-rw-r--r--debian/changelog22
-rw-r--r--doc/context-arch.txt2
-rw-r--r--doc/demos.txt57
-rw-r--r--gtk-doc.make201
-rw-r--r--libcontextprovider/context-provide/Makefile.am17
-rw-r--r--libcontextprovider/context-provide/commandwatcher.cpp112
-rw-r--r--libcontextprovider/context-provide/commandwatcher.h23
-rw-r--r--libcontextprovider/context-provide/context-provide.cpp10
-rw-r--r--libcontextprovider/context-provide/propertyproxy.cpp49
-rw-r--r--libcontextprovider/context-provide/propertyproxy.h45
-rwxr-xr-xlibcontextprovider/customer-tests/runTests.sh2
-rw-r--r--libcontextprovider/customer-tests/service/.gitignore1
-rw-r--r--libcontextprovider/customer-tests/service/servicetest.cpp172
-rw-r--r--libcontextprovider/customer-tests/service/servicetest.h6
-rw-r--r--libcontextprovider/customer-tests/subscription/Makefile.am7
-rw-r--r--libcontextprovider/customer-tests/subscription/subscriptiontests.cpp13
-rw-r--r--libcontextprovider/customer-tests/types/Makefile.am5
-rw-r--r--libcontextprovider/customer-tests/types/typestests.cpp18
-rw-r--r--libcontextprovider/customer-tests/value-changes/Makefile.am5
-rw-r--r--libcontextprovider/customer-tests/value-changes/valuechangestests.cpp10
-rw-r--r--libcontextprovider/doc/Makefile.am4
-rw-r--r--libcontextprovider/man/context-provide-v2.125
-rw-r--r--libcontextprovider/src/Makefile.am4
-rw-r--r--libcontextprovider/src/property.cpp5
-rw-r--r--libcontextprovider/src/property.h2
-rw-r--r--libcontextprovider/src/propertyadaptor.cpp33
-rw-r--r--libcontextprovider/src/propertyprivate.cpp85
-rw-r--r--libcontextprovider/src/propertyprivate.h13
-rw-r--r--libcontextprovider/src/service.cpp36
-rw-r--r--libcontextprovider/src/service.h2
-rw-r--r--libcontextprovider/src/servicebackend.cpp61
-rw-r--r--libcontextprovider/src/servicebackend.h10
-rw-r--r--libcontextprovider/unit-tests/property/.gitignore1
-rw-r--r--libcontextprovider/unit-tests/property/Makefile.am2
-rw-r--r--libcontextprovider/unit-tests/property/propertyprivate.h19
-rw-r--r--libcontextprovider/unit-tests/property/propertyunittest.cpp104
-rw-r--r--libcontextprovider/unit-tests/property/service.h1
-rw-r--r--libcontextprovider/unit-tests/service/Makefile.am4
-rw-r--r--libcontextprovider/unit-tests/service/servicebackend.h8
-rw-r--r--libcontextprovider/unit-tests/service/serviceunittest.cpp52
-rw-r--r--libcontextprovider/unit-tests/servicebackend/Makefile.am2
-rw-r--r--libcontextprovider/unit-tests/servicebackend/propertyadaptor.h48
-rw-r--r--libcontextprovider/unit-tests/servicebackend/propertyprivate.h (renamed from libcontextprovider/unit-tests/service/property.h)27
-rw-r--r--libcontextprovider/unit-tests/servicebackend/servicebackendunittest.cpp80
-rw-r--r--libcontextsubscriber/.gitignore1
-rw-r--r--libcontextsubscriber/Makefile.am2
-rw-r--r--libcontextsubscriber/cli/Makefile.am7
-rw-r--r--libcontextsubscriber/cls/Makefile.am8
-rwxr-xr-xlibcontextsubscriber/customer-tests/asynchronicity/rapidchanges.py4
-rwxr-xr-xlibcontextsubscriber/customer-tests/commander/commander_disabled.py3
-rwxr-xr-xlibcontextsubscriber/customer-tests/commander/commander_nonexistent.py2
-rwxr-xr-xlibcontextsubscriber/customer-tests/registry/registry.py2
-rwxr-xr-xlibcontextsubscriber/customer-tests/subscription/multiprovider.py100
-rwxr-xr-xlibcontextsubscriber/customer-tests/subscription/multiprovider2.py90
-rwxr-xr-xlibcontextsubscriber/customer-tests/subscription/subscription.py1
-rw-r--r--libcontextsubscriber/customer-tests/testplugins/timeplugin1/Makefile.am4
-rw-r--r--libcontextsubscriber/customer-tests/testplugins/timeplugin2/Makefile.am4
-rw-r--r--libcontextsubscriber/customer-tests/tests.xml15
-rw-r--r--libcontextsubscriber/doc/Makefile.am4
-rw-r--r--libcontextsubscriber/multithreading-tests/wait-for-subscription-thread/Makefile.am17
-rw-r--r--libcontextsubscriber/src/Makefile.am6
-rw-r--r--libcontextsubscriber/src/contextkitplugin.cpp141
-rw-r--r--libcontextsubscriber/src/contextkitplugin.h34
-rw-r--r--libcontextsubscriber/src/handlesignalrouter.cpp4
-rw-r--r--libcontextsubscriber/src/handlesignalrouter.h4
-rw-r--r--libcontextsubscriber/src/iproviderplugin.h4
-rw-r--r--libcontextsubscriber/src/propertyhandle.cpp120
-rw-r--r--libcontextsubscriber/src/propertyhandle.h7
-rw-r--r--libcontextsubscriber/src/provider.cpp50
-rw-r--r--libcontextsubscriber/src/provider.h15
-rw-r--r--libcontextsubscriber/src/timedvalue.h53
-rw-r--r--libcontextsubscriber/unit-tests/handlesignalrouter/propertyhandle.h6
-rw-r--r--libcontextsubscriber/unit-tests/handlesignalrouter/testhandlesignalrouter.cpp22
-rw-r--r--libcontextsubscriber/unit-tests/propertyhandle/.gitignore2
-rw-r--r--libcontextsubscriber/unit-tests/propertyhandle/Makefile.am3
-rw-r--r--libcontextsubscriber/unit-tests/propertyhandle/provider.h11
-rw-r--r--libcontextsubscriber/unit-tests/propertyhandle/testpropertyhandle.cpp30
-rw-r--r--libcontextsubscriber/unit-tests/provider/.gitignore1
-rw-r--r--libcontextsubscriber/unit-tests/provider/Makefile.am2
-rw-r--r--libcontextsubscriber/unit-tests/provider/contextkitplugin.h6
-rw-r--r--libcontextsubscriber/unit-tests/provider/handlesignalrouter.h2
-rw-r--r--libcontextsubscriber/unit-tests/provider/testprovider.cpp13
-rw-r--r--libcontextsubscriber/update-contextkit-providers/Makefile.am4
-rw-r--r--m4/dolt.m4177
-rw-r--r--python/ContextKit/cltool.py16
-rwxr-xr-xsandbox/context-proxy112
-rw-r--r--sandbox/messaging-to-self/main.cpp (renamed from libcontextsubscriber/sandbox/messaging-to-self/main.cpp)0
-rw-r--r--sandbox/messaging-to-self/messaging-to-self.pro (renamed from libcontextsubscriber/sandbox/messaging-to-self/messaging-to-self.pro)0
-rw-r--r--sandbox/messaging-to-self/myobject.h (renamed from libcontextsubscriber/sandbox/messaging-to-self/myobject.h)0
-rw-r--r--sandbox/messaging-to-self/mythread.h (renamed from libcontextsubscriber/sandbox/messaging-to-self/mythread.h)0
-rw-r--r--sandbox/messaging-to-self/queuedinvoker.cpp (renamed from libcontextsubscriber/sandbox/messaging-to-self/queuedinvoker.cpp)0
-rw-r--r--sandbox/messaging-to-self/queuedinvoker.h (renamed from libcontextsubscriber/sandbox/messaging-to-self/queuedinvoker.h)0
-rw-r--r--sandbox/multithreading-tests/Makefile.am (renamed from libcontextsubscriber/multithreading-tests/Makefile.am)0
-rw-r--r--sandbox/multithreading-tests/new-property-in-thread/.gitignore (renamed from libcontextsubscriber/multithreading-tests/new-property-in-thread/.gitignore)0
-rw-r--r--sandbox/multithreading-tests/new-property-in-thread/Makefile.am (renamed from libcontextsubscriber/multithreading-tests/new-property-in-thread/Makefile.am)5
-rw-r--r--sandbox/multithreading-tests/new-property-in-thread/main.cpp (renamed from libcontextsubscriber/multithreading-tests/new-property-in-thread/main.cpp)0
-rw-r--r--sandbox/multithreading-tests/new-property-in-thread/thread.h (renamed from libcontextsubscriber/multithreading-tests/new-property-in-thread/thread.h)0
-rw-r--r--sandbox/multithreading-tests/old-property-in-thread/.gitignore (renamed from libcontextsubscriber/multithreading-tests/old-property-in-thread/.gitignore)0
-rw-r--r--sandbox/multithreading-tests/old-property-in-thread/Makefile.am (renamed from libcontextsubscriber/multithreading-tests/stress-test/Makefile.am)5
-rw-r--r--sandbox/multithreading-tests/old-property-in-thread/main.cpp (renamed from libcontextsubscriber/multithreading-tests/old-property-in-thread/main.cpp)0
-rw-r--r--sandbox/multithreading-tests/old-property-in-thread/thread.h (renamed from libcontextsubscriber/multithreading-tests/old-property-in-thread/thread.h)0
-rw-r--r--sandbox/multithreading-tests/single-thread/.gitignore (renamed from libcontextsubscriber/multithreading-tests/single-thread/.gitignore)0
-rw-r--r--sandbox/multithreading-tests/single-thread/Makefile.am (renamed from libcontextsubscriber/multithreading-tests/single-thread/Makefile.am)5
-rw-r--r--sandbox/multithreading-tests/single-thread/listener.h (renamed from libcontextsubscriber/multithreading-tests/single-thread/listener.h)0
-rw-r--r--sandbox/multithreading-tests/single-thread/main.cpp (renamed from libcontextsubscriber/multithreading-tests/single-thread/main.cpp)0
-rw-r--r--sandbox/multithreading-tests/stress-test/.gitignore (renamed from libcontextsubscriber/multithreading-tests/stress-test/.gitignore)0
-rw-r--r--sandbox/multithreading-tests/stress-test/1provider.cdb (renamed from libcontextsubscriber/multithreading-tests/stress-test/1provider.cdb)bin2727 -> 2727 bytes
-rw-r--r--sandbox/multithreading-tests/stress-test/2providers.cdb (renamed from libcontextsubscriber/multithreading-tests/stress-test/2providers.cdb)bin3423 -> 3423 bytes
-rw-r--r--sandbox/multithreading-tests/stress-test/Makefile.am (renamed from libcontextsubscriber/multithreading-tests/wait-for-subscription-only-in-thread/Makefile.am)5
-rw-r--r--sandbox/multithreading-tests/stress-test/main.cpp (renamed from libcontextsubscriber/multithreading-tests/stress-test/main.cpp)0
-rwxr-xr-xsandbox/multithreading-tests/stress-test/provider.py (renamed from libcontextsubscriber/multithreading-tests/stress-test/provider.py)0
-rwxr-xr-xsandbox/multithreading-tests/stress-test/runme.sh (renamed from libcontextsubscriber/multithreading-tests/stress-test/runme.sh)0
-rw-r--r--sandbox/multithreading-tests/stress-test/thread.h (renamed from libcontextsubscriber/multithreading-tests/stress-test/thread.h)0
-rw-r--r--sandbox/multithreading-tests/using-backend-from-thread/.gitignore (renamed from libcontextsubscriber/multithreading-tests/using-backend-from-thread/.gitignore)0
-rw-r--r--sandbox/multithreading-tests/using-backend-from-thread/Makefile.am (renamed from libcontextsubscriber/multithreading-tests/using-backend-from-thread/Makefile.am)5
-rw-r--r--sandbox/multithreading-tests/using-backend-from-thread/main.cpp (renamed from libcontextsubscriber/multithreading-tests/using-backend-from-thread/main.cpp)0
-rw-r--r--sandbox/multithreading-tests/using-backend-from-thread/thread.h (renamed from libcontextsubscriber/multithreading-tests/using-backend-from-thread/thread.h)0
-rw-r--r--sandbox/multithreading-tests/wait-for-subscription-only-in-thread/.gitignore (renamed from libcontextsubscriber/multithreading-tests/wait-for-subscription-only-in-thread/.gitignore)0
-rw-r--r--sandbox/multithreading-tests/wait-for-subscription-only-in-thread/Makefile.am (renamed from libcontextsubscriber/multithreading-tests/old-property-in-thread/Makefile.am)5
-rw-r--r--sandbox/multithreading-tests/wait-for-subscription-only-in-thread/main.cpp (renamed from libcontextsubscriber/multithreading-tests/wait-for-subscription-only-in-thread/main.cpp)0
-rw-r--r--sandbox/multithreading-tests/wait-for-subscription-only-in-thread/thread.h (renamed from libcontextsubscriber/multithreading-tests/wait-for-subscription-only-in-thread/thread.h)0
-rw-r--r--sandbox/multithreading-tests/wait-for-subscription-thread/.gitignore (renamed from libcontextsubscriber/multithreading-tests/wait-for-subscription-thread/.gitignore)0
-rw-r--r--sandbox/multithreading-tests/wait-for-subscription-thread/Makefile.am18
-rw-r--r--sandbox/multithreading-tests/wait-for-subscription-thread/main.cpp (renamed from libcontextsubscriber/multithreading-tests/wait-for-subscription-thread/main.cpp)0
-rw-r--r--sandbox/multithreading-tests/wait-for-subscription-thread/thread.h (renamed from libcontextsubscriber/multithreading-tests/wait-for-subscription-thread/thread.h)0
-rw-r--r--spec/ContextKit.xml41
-rw-r--r--spec/Makefile.am3
-rw-r--r--spec/all.xml1
-rw-r--r--spec/generic-types.xml6
134 files changed, 1751 insertions, 910 deletions
diff --git a/.gitignore b/.gitignore
index a333a903..a7c4bd86 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,3 +43,5 @@ flexi-properties.xml
mocs.cpp
moc_*
TAGS
+doltcompile
+doltlibtool
diff --git a/HACKING b/HACKING
index f2cae842..87ecc542 100644
--- a/HACKING
+++ b/HACKING
@@ -177,13 +177,17 @@ Here is the general procedure:
$ make maintainer-clean || make distclean
+ If you don't have any new files not commited yet, you can also use:
+
+ $ git clean -dfx
+
- Recreate the build cruft.
$ ./autogen.sh
- Configure your source tree as needed for making a release.
- $ ./configure --enable-maintainer-mode --enable-gtk-doc
+ $ ./configure --enable-doc
- Build the source tree and do a "make distcheck"
@@ -200,13 +204,12 @@ the contents of the created directory.
Building a debian package
-------------------------
-After a git clone, you first have to build vala C sources and
-documentation. If you have extracted a distribution tarball, then you
-already have these files. Otherwise just do a ./configure, let's
-double check that all of the documentation tools and the vala compiler
-is found and make. After that you are ready to run
-'dpkg-buildpackage -us -uc -rfakeroot -b' to get your shiny new debian
-packages.
+After a git clone, you first have to build documentation. If you have
+extracted a distribution tarball, then you already have these files.
+Otherwise just do a ./configure, let's double check that all of the
+documentation tools and the vala compiler is found and make. After
+that you are ready to run 'dpkg-buildpackage -us -uc -rfakeroot -b' to
+get your shiny new debian packages.
Making releases
---------------
diff --git a/Makefile.am b/Makefile.am
index f37e1164..4ed81457 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -12,7 +12,7 @@ SUBDIRS = \
tools \
common
-DISTCLEANFILES = lcov.info lcov.html
+DISTCLEANFILES = lcov.info lcov.html doltlibtool doltcompile
MAINTAINERCLEANFILES = INSTALL
check-sum:
diff --git a/am/tests.am b/am/tests.am
index 26c3ca26..51aca517 100644
--- a/am/tests.am
+++ b/am/tests.am
@@ -55,9 +55,9 @@ all-am:
$(MAKE) $(AM_MAKEFLAGS) $(check_LIBRARIES) $(check_PROGRAMS); \
fi
-$(top_builddir)/common/libcommon.la:
+$(top_builddir)/common/libcommon.la: FORCE
$(MAKE) -C $(top_builddir)/common libcommon.la
-.PHONY: coverage covdircheck $(top_builddir)/common/libcommon.la
+.PHONY: coverage covdircheck FORCE
include $(top_srcdir)/am/covoptioncheck.am
diff --git a/configure.ac b/configure.ac
index 6a67c160..1f6d090d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
AC_PREREQ([2.61])
-AC_INIT([ContextKit], [0.3.11~unreleased], [marius.vollmer@nokia.com], ContextKit)
+AC_INIT([ContextKit], [0.4~unreleased], [marius.vollmer@nokia.com], ContextKit)
AC_CONFIG_SRCDIR([Makefile.am])
AM_INIT_AUTOMAKE([-Wall -Werror foreign dist-bzip2 tar-ustar 1.9])
@@ -10,7 +10,8 @@ AC_PROG_CC
CFLAGS="$CXXFLAGS -Wall"
AC_PROG_CXX
CXXFLAGS="$CXXFLAGS -Wall"
-AC_PROG_LIBTOOL
+LT_INIT([disable-static])
+DOLT
AM_PATH_PYTHON
# check for libraries
@@ -30,12 +31,6 @@ AC_SUBST([CDB_LIBS])
# tools for documentation
AX_FEATURE_DISABLEABLE([doc], [BUILD_DOCS], [disable building of documentation])
-AC_MSG_CHECKING([for gtk-doc])
-PKG_CHECK_EXISTS([gtk-doc >= 1.9],[AC_MSG_RESULT([yes])],
- [AC_MSG_RESULT([no])
- missing_deps_BUILD_DOCS="$missing_deps_BUILD_DOCS, gtk-doc >= 1.9"])
-HTML_DIR="${datadir}/gtk-doc/html"
-AC_SUBST([HTML_DIR])
AX_DISABLE_FEATURE_ON_PROG([Dot drawing tool], [BUILD_DOCS], [DOT], [dot])
AX_DISABLE_FEATURE_ON_PROG([Asciidoc], [BUILD_DOCS], [ASCIIDOC], [asciidoc], [8.2.7])
AX_DISABLE_FEATURE_ON_PROG([source-highlight], [BUILD_DOCS], [SOURCE_HIGHLIGHT], [source-highlight])
@@ -43,8 +38,6 @@ AX_DISABLE_FEATURE_ON_PROG([xsltproc], [BUILD_DOCS], [XSLTPROC], [xsltproc])
AX_DISABLE_FEATURE_ON_PROG([xmllint], [BUILD_DOCS], [XMLLINT], [xmllint])
AX_DISABLE_FEATURE_ON_PROG([doxygen], [BUILD_DOCS], [DOXYGEN], [doxygen])
AM_CONDITIONAL([CONTEXTKIT_BUILD_DOCS], [test "$missing_deps_BUILD_DOCS" = ""])
-AM_CONDITIONAL([GTK_DOC_USE_LIBTOOL], [test "$missing_deps_BUILD_DOCS" = ""])
-AM_CONDITIONAL([ENABLE_GTK_DOC], [test "$missing_deps_BUILD_DOCS" = ""])
# coverage tools
AX_FEATURE_DISABLEABLE([coverage], [COVERAGE], [disable unittests' coverage support])
@@ -80,14 +73,6 @@ AC_CONFIG_FILES([
libcontextsubscriber/unit-tests/provider/Makefile
libcontextsubscriber/unit-tests/util/Makefile
libcontextsubscriber/unit-tests/nanoxml/Makefile
- libcontextsubscriber/multithreading-tests/Makefile
- libcontextsubscriber/multithreading-tests/new-property-in-thread/Makefile
- libcontextsubscriber/multithreading-tests/old-property-in-thread/Makefile
- libcontextsubscriber/multithreading-tests/single-thread/Makefile
- libcontextsubscriber/multithreading-tests/stress-test/Makefile
- libcontextsubscriber/multithreading-tests/using-backend-from-thread/Makefile
- libcontextsubscriber/multithreading-tests/wait-for-subscription-only-in-thread/Makefile
- libcontextsubscriber/multithreading-tests/wait-for-subscription-thread/Makefile
libcontextsubscriber/update-contextkit-providers/Makefile
libcontextprovider/contextprovider-1.0.pc
libcontextprovider/Makefile
diff --git a/debian/changelog b/debian/changelog
index 30e76721..1930cedc 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,8 +1,16 @@
-contextkit (0.3.11~unreleased) unstable; urgency=low
+contextkit (0.4~unreleased) unstable; urgency=low
- *
+ * Subscriber part of libcontextsubscriber now supports multiple
+ providers for one property.
+ * Support both the old and the new ContextKit D-Bus protocol in
+ libcontextsubscriber.
+ * The start command in context-provide is now able to reregister
+ the service name if needed as advertised in the help.
+ * context-provide supports proxying in Commander mode.
- -- Marja Hassinen <ext-marja.2.hassinen@nokia.com> Tue, 15 Oct 2009 16:25:04 +0300
+ Implemented: SWP#CntFr-349, SWP#CntFr-353
+
+ -- Gergely Risko <gergely+context@risko.hu> Thu, 22 Oct 2009 13:42:27 +0300
contextkit (0.3.10) unstable; urgency=low
@@ -10,7 +18,7 @@ contextkit (0.3.10) unstable; urgency=low
providers for one property.
* New tool: context-ls for listing available context properties.
- Implemented: CntFr-324, CntFr-318, CntFr-343
+ Implemented: SWP#CntFr-324, SWP#CntFr-318, SWP#CntFr-343
-- Marja Hassinen <ext-marja.2.hassinen@nokia.com> Tue, 15 Oct 2009 16:25:04 +0300
@@ -20,7 +28,7 @@ contextkit (0.3.9) unstable; urgency=low
* Removed complex types from core.context, since they're not
supported yet.
- Implemented: CntFr-337, CntFr-203
+ Implemented: SWP#CntFr-337, SWP#CntFr-203
-- Marja Hassinen <ext-marja.2.hassinen@nokia.com> Tue, 06 Oct 2009 15:00:00 +0300
@@ -32,7 +40,7 @@ contextkit (0.3.8) unstable; urgency=low
* New package libcontextprovider-tests added, containing customer tests
of the libcontextprovider.
- Implemented: CntFr-323
+ Implemented: SWP#CntFr-323
-- Gergely Risko <gergely+context@risko.hu> Mon, 05 Oct 2009 09:02:50 +0300
@@ -67,7 +75,7 @@ contextkit (0.3.5) unstable; urgency=low
stderr from the libcontextprovider library
* Fixes: NB#121556 - C API of libcontextprovider uses Glib types
- Implemented: CntFr-305
+ Implemented: SWP#CntFr-305
-- Gergely Risko <gergely+context@risko.hu> Mon, 22 Sep 2009 11:36:40 +0300
diff --git a/doc/context-arch.txt b/doc/context-arch.txt
index 767dcf35..bc8f77dc 100644
--- a/doc/context-arch.txt
+++ b/doc/context-arch.txt
@@ -394,7 +394,7 @@ Packages
.+contextkit+
-Languages:: C, C++, Vala
+Languages:: C, C++
License:: LGPL 2.1
diff --git a/doc/demos.txt b/doc/demos.txt
deleted file mode 100644
index 0add33bc..00000000
--- a/doc/demos.txt
+++ /dev/null
@@ -1,57 +0,0 @@
-Demo / manual test descrption
-
-Feature to be demonstrated:
-- Dropping the context prefix both on provider side and on client side, when doing the dbus traffic.
-
-
-0.
-
-Versions used in this tests:
-
-Latest versions (27.5.2009) of DuiValueSpace and libcontextprovider:
-libcontextprovider0_0.1.8~unreleased_armel.deb
-libduivaluespace0_0.9~unreleased_armel.deb
-
-1a. Build the packages from ContextKit repository (branch removecontextprefix) inside scratchbox.
-Outside scratchbox:
-./autogen.sh --enable-gtk-doc
-./configure
-make
-make clean
-
-Inside scratchbox:
-./configure
-make
-dpkg-buildpackage -b -us -uc -rfakeroot
-
-1b. Build the packages from DuiValueSpace repository (branch dropping-context) inside scratchbox.
-
-Inside scratchbox:
-./configure
-make
-dpkg-buildpackage -b -us -uc -rfakeroot
-
-Note: When these versions are released, they can be obtained directly from Harmattan repository.
-
-
-3. Copy the .deb packages to the device and install them.
-
-On the device:
-4. Install context-subscriber-example.
-
-5. Start monitoring DBus traffic on system bus.
-dbus-monitor --system
-
-6. Run context-subscriber-example.
-
-7. Outcome: Even though the context-provider-example and context-subscriber-example use the keys with "Context." prefix, they still work together, and dbus-monitor shows that the keys are transmitted without the "Context." prefix.
-
-signal sender=:1.15 -> dest=(null destination) serial=38 path=/org/freedesktop/ContextKit/Subscribers/6; interface=org.freedesktop.ContextKit.Subscriber; member=Changed
- array [
- dict entry(
- string "Example.EdgeUp"
- variant int32 3
- )
- ]
- array [
- ]
diff --git a/gtk-doc.make b/gtk-doc.make
deleted file mode 100644
index fa7fd6c4..00000000
--- a/gtk-doc.make
+++ /dev/null
@@ -1,201 +0,0 @@
-# -*- mode: makefile -*-
-
-####################################
-# Everything below here is generic #
-####################################
-
-if GTK_DOC_USE_LIBTOOL
-GTKDOC_CC = $(LIBTOOL) --mode=compile $(CC) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-GTKDOC_LD = $(LIBTOOL) --mode=link $(CC) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS)
-GTKDOC_RUN = $(LIBTOOL) --mode=execute
-else
-GTKDOC_CC = $(CC) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-GTKDOC_LD = $(CC) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS)
-GTKDOC_RUN = sh -c
-endif
-
-# We set GPATH here; this gives us semantics for GNU make
-# which are more like other make's VPATH, when it comes to
-# whether a source that is a target of one rule is then
-# searched for in VPATH/GPATH.
-#
-GPATH = $(srcdir)
-
-TARGET_DIR=$(HTML_DIR)/$(DOC_MODULE)
-
-EXTRA_DIST = \
- $(content_files) \
- $(HTML_IMAGES) \
- $(DOC_MAIN_SGML_FILE) \
- $(DOC_MODULE)-sections.txt \
- $(DOC_MODULE)-overrides.txt
-
-DOC_STAMPS=scan-build.stamp tmpl-build.stamp sgml-build.stamp html-build.stamp \
- $(srcdir)/tmpl.stamp $(srcdir)/sgml.stamp $(srcdir)/html.stamp
-
-SCANOBJ_FILES = \
- $(DOC_MODULE).args \
- $(DOC_MODULE).hierarchy \
- $(DOC_MODULE).interfaces \
- $(DOC_MODULE).prerequisites \
- $(DOC_MODULE).signals
-
-REPORT_FILES = \
- $(DOC_MODULE)-undocumented.txt \
- $(DOC_MODULE)-undeclared.txt \
- $(DOC_MODULE)-unused.txt
-
-CLEANFILES = $(SCANOBJ_FILES) $(REPORT_FILES) $(DOC_STAMPS)
-
-if ENABLE_GTK_DOC
-all-local: html-build.stamp
-else
-all-local:
-endif
-
-docs: html-build.stamp
-
-$(REPORT_FILES): sgml-build.stamp
-
-#### scan ####
-
-scan-build.stamp: $(HFILE_GLOB) $(CFILE_GLOB)
- @echo 'gtk-doc: Scanning header files'
- @-chmod -R u+w $(srcdir)
- cd $(srcdir) && \
- gtkdoc-scan --module=$(DOC_MODULE) --source-dir=$(DOC_SOURCE_DIR) --ignore-headers="$(IGNORE_HFILES)" $(SCAN_OPTIONS) $(EXTRA_HFILES)
- if grep -l '^..*$$' $(srcdir)/$(DOC_MODULE).types > /dev/null 2>&1 ; then \
- CC="$(GTKDOC_CC)" LD="$(GTKDOC_LD)" RUN="$(GTKDOC_RUN)" CFLAGS="$(GTKDOC_CFLAGS) $(CFLAGS)" LDFLAGS="$(GTKDOC_LIBS) $(LDFLAGS)" gtkdoc-scangobj $(SCANGOBJ_OPTIONS) --module=$(DOC_MODULE) --output-dir=$(srcdir) ; \
- else \
- cd $(srcdir) ; \
- for i in $(SCANOBJ_FILES) ; do \
- test -f $$i || touch $$i ; \
- done \
- fi
- touch scan-build.stamp
-
-$(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt: scan-build.stamp
- @true
-
-#### templates ####
-
-tmpl-build.stamp: $(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt
- @echo 'gtk-doc: Rebuilding template files'
- @-chmod -R u+w $(srcdir)
- cd $(srcdir) && gtkdoc-mktmpl --module=$(DOC_MODULE) $(MKTMPL_OPTIONS)
- touch tmpl-build.stamp
-
-tmpl.stamp: tmpl-build.stamp
- @true
-
-tmpl/*.sgml:
- @true
-
-
-#### xml ####
-
-sgml-build.stamp: tmpl.stamp $(HFILE_GLOB) $(CFILE_GLOB) $(DOC_MODULE)-sections.txt $(srcdir)/tmpl/*.sgml $(expand_content_files)
- @echo 'gtk-doc: Building XML'
- @-chmod -R u+w $(srcdir)
- cd $(srcdir) && \
- gtkdoc-mkdb --module=$(DOC_MODULE) --source-dir=$(DOC_SOURCE_DIR) --output-format=xml --expand-content-files="$(expand_content_files)" --main-sgml-file=$(DOC_MAIN_SGML_FILE) $(MKDB_OPTIONS)
- touch sgml-build.stamp
-
-sgml.stamp: sgml-build.stamp
- @true
-
-#### html ####
-
-html-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files)
- @echo 'gtk-doc: Building HTML'
- @-chmod -R u+w $(srcdir)
- rm -rf $(srcdir)/html
- mkdir $(srcdir)/html
- mkhtml_options=""; \
- gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-path"; \
- if test "$(?)" = "0"; then \
- mkhtml_options=--path="$(srcdir)"; \
- fi
- cd $(srcdir)/html && gtkdoc-mkhtml $(mkhtml_options) $(MKHTML_OPTIONS) $(DOC_MODULE) ../$(DOC_MAIN_SGML_FILE)
- test "x$(HTML_IMAGES)" = "x" || ( cd $(srcdir) && cp $(HTML_IMAGES) html )
- @echo 'gtk-doc: Fixing cross-references'
- cd $(srcdir) && gtkdoc-fixxref --module-dir=html --html-dir=$(HTML_DIR) $(FIXXREF_OPTIONS)
- touch html-build.stamp
-
-##############
-
-clean-local:
- rm -f *~ *.bak
- rm -rf .libs
-
-distclean-local:
- cd $(srcdir) && \
- rm -rf xml $(REPORT_FILES) \
- $(DOC_MODULE)-decl-list.txt $(DOC_MODULE)-decl.txt
-
-maintainer-clean-local: clean
- cd $(srcdir) && rm -rf xml html
-
-install-data-local:
- installfiles=`echo $(srcdir)/html/*`; \
- if test "$$installfiles" = '$(srcdir)/html/*'; \
- then echo '-- Nothing to install' ; \
- else \
- if test -n "$(DOC_MODULE_VERSION)"; then \
- installdir="$(DESTDIR)$(TARGET_DIR)-$(DOC_MODULE_VERSION)"; \
- else \
- installdir="$(DESTDIR)$(TARGET_DIR)"; \
- fi; \
- $(mkinstalldirs) $${installdir} ; \
- for i in $$installfiles; do \
- echo '-- Installing '$$i ; \
- $(INSTALL_DATA) $$i $${installdir}; \
- done; \
- if test -n "$(DOC_MODULE_VERSION)"; then \
- mv -f $${installdir}/$(DOC_MODULE).devhelp2 \
- $${installdir}/$(DOC_MODULE)-$(DOC_MODULE_VERSION).devhelp2; \
- mv -f $${installdir}/$(DOC_MODULE).devhelp \
- $${installdir}/$(DOC_MODULE)-$(DOC_MODULE_VERSION).devhelp; \
- fi; \
- ! which gtkdoc-rebase >/dev/null 2>&1 || \
- gtkdoc-rebase --relative --dest-dir=$(DESTDIR) --html-dir=$${installdir} ; \
- fi
-
-uninstall-local:
- if test -n "$(DOC_MODULE_VERSION)"; then \
- installdir="$(DESTDIR)$(TARGET_DIR)-$(DOC_MODULE_VERSION)"; \
- else \
- installdir="$(DESTDIR)$(TARGET_DIR)"; \
- fi; \
- rm -rf $${installdir}
-
-#
-# Require gtk-doc when making dist
-#
-#if ENABLE_GTK_DOC
-#dist-check-gtkdoc:
-#else
-#dist-check-gtkdoc:
-# @echo "*** gtk-doc must be installed and enabled in order to make dist"
-# @false
-#endif
-
-# contextkit note: instead of checking the availability of gtk-doc,
-# just make the procedure, without hesitation. gtk-doc + distcheck is
-# so hacky and messed up that we haven't found out a better way yet
-dist-check-gtkdoc: html-build.stamp
-
-dist-hook: dist-check-gtkdoc dist-hook-local
- mkdir $(distdir)/tmpl
- mkdir $(distdir)/xml
- mkdir $(distdir)/html
- -cp $(srcdir)/tmpl/*.sgml $(distdir)/tmpl
- -cp $(srcdir)/xml/*.xml $(distdir)/xml
- cp $(srcdir)/html/* $(distdir)/html
- -cp $(srcdir)/$(DOC_MODULE).types $(distdir)/
- -cp $(srcdir)/$(DOC_MODULE)-sections.txt $(distdir)/
- cd $(distdir) && rm -f $(DISTCLEANFILES)
- ! which gtkdoc-rebase >/dev/null 2>&1 || \
- gtkdoc-rebase --online --relative --html-dir=$(distdir)/html
-
-.PHONY : dist-hook-local docs
diff --git a/libcontextprovider/context-provide/Makefile.am b/libcontextprovider/context-provide/Makefile.am
index 571ecd16..4a559065 100644
--- a/libcontextprovider/context-provide/Makefile.am
+++ b/libcontextprovider/context-provide/Makefile.am
@@ -1,5 +1,7 @@
bin_PROGRAMS = context-provide-internal
-context_provide_internal_SOURCES = context-provide.cpp commandwatcher.cpp commandwatcher.h
+context_provide_internal_SOURCES = context-provide.cpp \
+ commandwatcher.cpp commandwatcher.h propertyproxy.h \
+ propertyproxy.cpp
AM_CXXFLAGS = $(QtCore_CFLAGS) $(QtDBus_CFLAGS) \
'-DCONTEXT_LOG_MODULE_NAME="context-provide"'
@@ -7,17 +9,18 @@ LIBS += $(QtCore_LIBS)
# library dependency hack for seamless make
AM_CXXFLAGS += -I$(srcdir)/../src \
- -I$(top_srcdir)/common
+ -I$(top_srcdir)/common \
+ -I$(top_srcdir)/libcontextsubscriber/src
-context_provide_internal_LDADD = ../src/libcontextprovider.la $(top_builddir)/common/libcommon.la
+context_provide_internal_LDADD = ../src/libcontextprovider.la $(top_builddir)/common/libcommon.la $(top_builddir)/libcontextsubscriber/src/libcontextsubscriber.la
-../src/libcontextprovider.la:
+../src/libcontextprovider.la: FORCE
$(MAKE) -C ../src libcontextprovider.la
-$(top_builddir)/common/libcommon.la:
- $(MAKE) -C $(top_builddir)/common libcommon.la
+../../libcontextsubscriber/src/libcontextsubscriber.la: FORCE
+ $(MAKE) -C ../../libcontextsubscriber/src libcontextsubscriber.la
-.PHONY: ../src/libcontextprovider.la $(top_builddir)/common/libcommon.la
+.PHONY: FORCE
# moccing
nodist_context_provide_internal_SOURCES = mocs.cpp
diff --git a/libcontextprovider/context-provide/commandwatcher.cpp b/libcontextprovider/context-provide/commandwatcher.cpp
index 25092948..9a44de1d 100644
--- a/libcontextprovider/context-provide/commandwatcher.cpp
+++ b/libcontextprovider/context-provide/commandwatcher.cpp
@@ -20,8 +20,12 @@
*/
#include "commandwatcher.h"
+#include "propertyproxy.h"
#include "sconnect.h"
+#include <contextpropertyinfo.h>
+#include <contextregistryinfo.h>
#include <service.h>
+
#include <QTextStream>
#include <QFile>
#include <QSocketNotifier>
@@ -33,18 +37,46 @@
#include <errno.h>
#include <QMap>
#include <QDir>
+#include <QSet>
+
+const QString CommandWatcher::commanderBusName = "org.freedesktop.ContextKit.Commander";
CommandWatcher::CommandWatcher(QString bn, QDBusConnection::BusType bt, int commandfd, QObject *parent) :
- QObject(parent), commandfd(commandfd), out(stdout), busName(bn), busType(bt)
+ QObject(parent), commandfd(commandfd),
+ registryInfo(ContextRegistryInfo::instance()),
+ out(stdout), busName(bn), busType(bt), started(false)
{
commandNotifier = new QSocketNotifier(commandfd, QSocketNotifier::Read, this);
sconnect(commandNotifier, SIGNAL(activated(int)), this, SLOT(onActivated()));
+ if (busName == commanderBusName) {
+ ContextProperty::ignoreCommander();
+ ContextProperty::setTypeCheck(true);
+ sconnect(registryInfo, SIGNAL(changed()), this, SLOT(onRegistryChanged()));
+ onRegistryChanged();
+ }
}
CommandWatcher::~CommandWatcher()
{
- foreach(Property* p, properties) {
+ foreach(Property *p, properties)
+ delete p;
+ foreach(PropertyProxy *p, proxies)
+ delete p;
+}
+
+void CommandWatcher::onRegistryChanged()
+{
+ qDebug() << "registry changed";
+ foreach (PropertyProxy *p, proxies)
delete p;
+ proxies.clear();
+ // (Re)create the proxies and restore the user's will of overriding.
+ foreach (QString key, registryInfo->listKeys()) {
+ if (ContextPropertyInfo(key).provided()) {
+ qDebug() << "creating proxy for" << key;
+ proxies.insert(key, new PropertyProxy(key, !properties.contains(key),
+ this));
+ }
}
}
@@ -77,7 +109,10 @@ void CommandWatcher::help()
qDebug() << "Available commands:\n";
qDebug() << " add TYPE KEY [VALUE] - create new key with the given type";
qDebug() << " KEY=VALUE - set KEY to the given VALUE";
+ qDebug() << " info KEY - prints the real (proxied) value of KEY";
qDebug() << " unset KEY - sets KEY to unknown";
+ qDebug() << " del KEY - delete KEY, useful if the tool is commander";
+ qDebug() << " list - same as calling info for all known keys";
qDebug() << " sleep INTERVAL - sleep the INTERVAL amount of seconds";
qDebug() << " dump [FILENAME] - dump the xml content of the defined props";
qDebug() << " start - (re)register everything on D-Bus";
@@ -101,6 +136,12 @@ void CommandWatcher::interpret(const QString& command)
// Interpret commands
if (QString("add").startsWith(commandName)) {
addCommand(args);
+ } else if (QString("del").startsWith(commandName)) {
+ delCommand(args);
+ } else if (QString("info").startsWith(commandName)) {
+ infoCommand(args);
+ } else if (QString("list").startsWith(commandName)) {
+ listCommand();
} else if (QString("sleep").startsWith(commandName)) {
sleepCommand(args);
} else if (QString("exit").startsWith(commandName)) {
@@ -152,6 +193,8 @@ void CommandWatcher::addCommand(const QStringList& args)
} else {
types.insert(keyName, keyType);
properties.insert(keyName, new Property(keyName));
+ if (busName == commanderBusName && proxies.contains(keyName))
+ proxies[keyName]->enable(false);
out << "Added key: " << keyName << " with type: " << keyType << endl;
out.flush();
}
@@ -159,6 +202,67 @@ void CommandWatcher::addCommand(const QStringList& args)
// handle default value
if (args.count() > 2)
setCommand(keyName + "=\"" + QStringList(args.mid(2)).join(" ") + "\"");
+ else
+ unsetCommand(QStringList(keyName));
+
+ // if service is already started then it has to be restarted after a property is added
+ if (started && busName != CommandWatcher::commanderBusName)
+ startCommand();
+}
+
+void CommandWatcher::delCommand(const QStringList &args)
+{
+ if (args.count() < 1) {
+ qDebug() << "ERROR: need to specify KEY";
+ return;
+ }
+
+ const QString keyName = unquote(args.at(0));
+
+ types.remove(keyName);
+ delete properties.take(keyName);
+ if (busName == commanderBusName && proxies.contains(keyName))
+ proxies[keyName]->enable(true);
+ out << "Deleted key: " << keyName << endl;
+ out.flush();
+}
+
+void CommandWatcher::listCommand()
+{
+ foreach (QString key,
+ QSet<QString>::fromList(properties.keys()) +
+ QSet<QString>::fromList(proxies.keys()))
+ infoCommand(QStringList(key));
+}
+
+void CommandWatcher::infoCommand(const QStringList &args)
+{
+ if (args.count() < 1) {
+ qDebug() << "ERROR: need to specify KEY";
+ return;
+ }
+ QString keyName = args.at(0);
+ if (properties.contains(keyName)) {
+ out << types[keyName] << " " << keyName;
+ if (properties[keyName]->value().isNull())
+ out << " is Unknown" << endl;
+ else
+ out << " = " << properties[keyName]->value().toString() << endl;
+ } else if (busName != commanderBusName) {
+ qDebug() << "ERROR:" << keyName << "not addd";
+ }
+ if (busName == commanderBusName) {
+ if (!proxies.contains(keyName))
+ qDebug() << "ERROR:" << keyName << "not known";
+ else {
+ out << "real: " << proxies[keyName]->type() << " "
+ << keyName;
+ if (proxies[keyName]->realValue().isNull())
+ out << " is Unknown" << endl;
+ else
+ out << " = " << proxies[keyName]->realValue().toString() << endl;
+ }
+ }
}
void CommandWatcher::sleepCommand(const QStringList& args)
@@ -279,10 +383,12 @@ void CommandWatcher::unsetCommand(const QStringList& args)
void CommandWatcher::startCommand()
{
Service service(busType, busName);
+ service.stop(); // this is harmless if we are not started yet, but useful if we are
if (!service.start()) {
- qDebug() << "Starting service failed";
+ qDebug() << "Starting service failed, no D-Bus or the service name is already taken";
exit(2);
}
out << "Service started" << endl;
out.flush();
+ started = true;
}
diff --git a/libcontextprovider/context-provide/commandwatcher.h b/libcontextprovider/context-provide/commandwatcher.h
index e8248599..114fa65b 100644
--- a/libcontextprovider/context-provide/commandwatcher.h
+++ b/libcontextprovider/context-provide/commandwatcher.h
@@ -31,6 +31,8 @@ using namespace ContextProvider;
class QFile;
class QSocketNotifier;
class QString;
+class PropertyProxy;
+class ContextRegistryInfo;
template <typename K, typename V> class QMap;
class CommandWatcher : public QObject
@@ -41,28 +43,37 @@ public:
CommandWatcher(QString busName, QDBusConnection::BusType busType, int commandfd, QObject *parent = 0);
~CommandWatcher();
void addCommand(const QStringList& args);
+ static const QString commanderBusName;
private:
- int commandfd;
- QSocketNotifier *commandNotifier;
void interpret(const QString& command);
- QMap <QString, QString> types; // key -> type
- QMap <QString, Property*> properties; // property index
void help();
- QString unquote(const QString& str);
void unsetCommand(const QStringList& args);
void setCommand(const QString& command);
void sleepCommand(const QStringList& args);
void flushCommand();
void dumpCommand(const QStringList& args);
+ void delCommand(const QStringList& args);
+ void infoCommand(const QStringList& args);
+ void listCommand();
void startCommand();
+ QString unquote(const QString& str);
+
+ int commandfd;
+ QSocketNotifier *commandNotifier;
+ QMap <QString, QString> types; // key -> type
+ QMap <QString, Property*> properties; // property index
+ QMap <QString, PropertyProxy*> proxies;
+ ContextRegistryInfo *registryInfo;
+
QTextStream out;
- bool silent;
QString busName;
QDBusConnection::BusType busType;
+ bool started;
private slots:
void onActivated();
+ void onRegistryChanged();
};
#endif
diff --git a/libcontextprovider/context-provide/context-provide.cpp b/libcontextprovider/context-provide/context-provide.cpp
index c95b6aa4..551b5a4b 100644
--- a/libcontextprovider/context-provide/context-provide.cpp
+++ b/libcontextprovider/context-provide/context-provide.cpp
@@ -19,6 +19,8 @@
*
*/
+#include "commandwatcher.h"
+
#include <QCoreApplication>
#include <QString>
#include <QStringList>
@@ -26,12 +28,9 @@
#include <QDebug>
#include <stdlib.h>
#include <service.h>
-#include "commandwatcher.h"
using namespace ContextProvider;
-#define COMMANDER "org.freedesktop.ContextKit.Commander"
-
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
@@ -55,7 +54,7 @@ int main(int argc, char **argv)
// Help? Show it and be gone.
// FIXME: has to replace this with argv[0]
out << "Usage: context-provide --v2 [--session | --system] [BUSNAME]\n";
- out << "BUSNAME is " COMMANDER " by default, and bus is session.\n";
+ out << "BUSNAME is " << CommandWatcher::commanderBusName << " by default, and bus is session.\n";
return 0;
}
@@ -77,7 +76,7 @@ int main(int argc, char **argv)
if (args.count() < 2) {
// No arguments at all? Use commander by default.
- args.push_back(COMMANDER);
+ args.push_back(CommandWatcher::commanderBusName);
}
// Parameter? Extract the session bus and type from it.
@@ -103,7 +102,6 @@ int main(int argc, char **argv)
qDebug() << "Service:" << busName.toLocal8Bit().data() << "on" <<
((busType == QDBusConnection::SessionBus) ? "session" : "system");
-
CommandWatcher commandWatcher(busName, busType, STDIN_FILENO, QCoreApplication::instance());
for (int i=2; i < args.count(); i+=3)
diff --git a/libcontextprovider/context-provide/propertyproxy.cpp b/libcontextprovider/context-provide/propertyproxy.cpp
new file mode 100644
index 00000000..9c012a24
--- /dev/null
+++ b/libcontextprovider/context-provide/propertyproxy.cpp
@@ -0,0 +1,49 @@
+#include "propertyproxy.h"
+#include "sconnect.h"
+#include "contextproperty.h"
+#include "contextpropertyinfo.h"
+#include "property.h"
+#include "logging.h"
+
+PropertyProxy::PropertyProxy(QString key, bool enabled, QObject *parent) :
+ QObject(parent), enabled(enabled)
+{
+ subscriber = new ContextProperty(key, this);
+ provider = new ContextProvider::Property(key, this);
+ sconnect(subscriber, SIGNAL(valueChanged()),
+ this, SLOT(onValueChanged()));
+ enable(enabled);
+}
+
+void PropertyProxy::enable(bool enable)
+{
+ qDebug() << (const char *)(enable ? "enabled" : "disabled") <<
+ "proxying" << subscriber->key();
+ enabled = enable;
+ if (enable)
+ provider->setValue(value);
+}
+
+QVariant PropertyProxy::realValue() const
+{
+ return value;
+}
+
+QString PropertyProxy::type() const
+{
+ return subscriber->info()->type();
+}
+
+void PropertyProxy::onValueChanged()
+{
+ value = subscriber->value();
+ if (enabled)
+ provider->setValue(value);
+ QTextStream out(stdout);
+ out << "real: " << type() << " " << subscriber->key();
+ if (realValue().isNull())
+ out << " is Unknown" << endl;
+ else
+ out << " = " << realValue().toString() << endl;
+ out.flush();
+}
diff --git a/libcontextprovider/context-provide/propertyproxy.h b/libcontextprovider/context-provide/propertyproxy.h
new file mode 100644
index 00000000..f185f2ad
--- /dev/null
+++ b/libcontextprovider/context-provide/propertyproxy.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2009 Nokia Corporation.
+ *
+ * Contact: Marius Vollmer <marius.vollmer@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef PROPERTYPROXY_H
+#define PROPERTYPROXY_H
+
+#include "contextproperty.h"
+#include "property.h"
+
+class PropertyProxy : public QObject
+{
+ Q_OBJECT;
+public:
+ PropertyProxy(QString key, bool enabled = true, QObject *parent = 0);
+ void enable(bool enable);
+ QVariant realValue() const;
+ QString type() const;
+private slots:
+ void onValueChanged();
+private:
+ ContextProvider::Property *provider;
+ ContextProperty *subscriber;
+ bool enabled;
+ QVariant value;
+};
+
+#endif
diff --git a/libcontextprovider/customer-tests/runTests.sh b/libcontextprovider/customer-tests/runTests.sh
index c1e458c7..075131eb 100755
--- a/libcontextprovider/customer-tests/runTests.sh
+++ b/libcontextprovider/customer-tests/runTests.sh
@@ -1,6 +1,6 @@
#!/bin/sh -e
-DIRS="subscription value-changes types c-api"
+DIRS="subscription value-changes types c-api service"
make -C client
diff --git a/libcontextprovider/customer-tests/service/.gitignore b/libcontextprovider/customer-tests/service/.gitignore
new file mode 100644
index 00000000..b1f9b3ec
--- /dev/null
+++ b/libcontextprovider/customer-tests/service/.gitignore
@@ -0,0 +1 @@
+servicetest \ No newline at end of file
diff --git a/libcontextprovider/customer-tests/service/servicetest.cpp b/libcontextprovider/customer-tests/service/servicetest.cpp
index 8ab4aea9..c27b80ac 100644
--- a/libcontextprovider/customer-tests/service/servicetest.cpp
+++ b/libcontextprovider/customer-tests/service/servicetest.cpp
@@ -30,21 +30,23 @@
#include <QtTest/QtTest>
#include <QProcess>
#include <QStringList>
+#include <string>
#define SERVICE_NAME "org.maemo.contextkit.testProvider"
+int serviceNameIx = 0;
namespace ContextProvider {
-void ServiceTests::initTestCase()
+void ServiceTest::initTestCase()
{
}
-void ServiceTests::cleanupTestCase()
+void ServiceTest::cleanupTestCase()
{
}
// Before each test
-void ServiceTests::init()
+void ServiceTest::init()
{
// Initialize test program state
isReadyToRead = false;
@@ -55,16 +57,10 @@ void ServiceTests::init()
client->start("client");
// Record whether the client was successfully started
clientStarted = client->waitForStarted();
-
- // Associate shorter names for the test services when communicating with the client
- if (clientStarted) {
- qDebug() << "Writing";
- writeToClient("assign session " SERVICE_NAME " service\n");
- }
}
// After each test
-void ServiceTests::cleanup()
+void ServiceTest::cleanup()
{
// Stop the client
if (clientStarted) {
@@ -74,14 +70,19 @@ void ServiceTests::cleanup()
delete client; client = NULL;
}
-void ServiceTests::startStopStart()
+void ServiceTest::startStopStart()
{
// Check that the initialization went well.
// Doing this only in init() is not enough; doesn't stop the test case.
QVERIFY(clientStarted);
+ // Use a different service name in each test; this way the ServiceBackends
+ QString serviceName = QString::number(serviceNameIx).prepend(SERVICE_NAME);
+ writeToClient(("assign session " + serviceName + " service\n").toStdString().c_str());
+ ++serviceNameIx;
+
// Test: create a Service and an associated Property
- Service* service = new Service(QDBusConnection::SessionBus, SERVICE_NAME);
+ Service* service = new Service(QDBusConnection::SessionBus, serviceName);
Property* property = new Property(*service, "Test.Property");
// Test: command client to subscribe
@@ -107,15 +108,156 @@ void ServiceTests::startStopStart()
expected = "Subscribe returned: Unknown";
QCOMPARE(actual.simplified(), expected.simplified());
+ delete service;
+ delete property;
+}
+
+void ServiceTest::recreate()
+{
+ // Check that the initialization went well.
+ // Doing this only in init() is not enough; doesn't stop the test case.
+ QVERIFY(clientStarted);
+
+ // Use a different service name in each test; this way the ServiceBackends
+ QString serviceName = QString::number(serviceNameIx).prepend(SERVICE_NAME);
+ writeToClient(("assign session " + serviceName + " service\n").toStdString().c_str());
+ ++serviceNameIx;
+
+ // Test: create a Service and an associated Property
+ Service* service = new Service(QDBusConnection::SessionBus, serviceName);
+ Property* property = new Property(*service, "Test.Property");
+
+ // Test: command client to subscribe
+ QString actual = writeToClient("subscribe service Test.Property\n");
+
+ // Expected result: service is started automatically and Subscribe works
+ QString expected = "Subscribe returned: Unknown";
+ QCOMPARE(actual.simplified(), expected.simplified());
+
+ // Test: delete the property and the service
+ delete property;
+ delete service;
+
+ actual = writeToClient("subscribe service Test.Property\n");
+
+ // Expected result: the client can no more subscribe
+ expected = "Subscribe error: org.freedesktop.DBus.Error.ServiceUnknown";
+ QCOMPARE(actual.simplified(), expected.simplified());
+
+ // Test: recreate the service and try to subscribe again
+ service = new Service(QDBusConnection::SessionBus, serviceName);
+ property = new Property(*service, "Test.Property");
+ actual = writeToClient("subscribe service Test.Property\n");
+
+ // Expected result: service is started and Subscribe works
+ expected = "Subscribe returned: Unknown";
+ QCOMPARE(actual.simplified(), expected.simplified());
+
+ delete service;
+ delete property;
+}
+
+void ServiceTest::multiStart()
+{
+ // Check that the initialization went well.
+ // Doing this only in init() is not enough; doesn't stop the test case.
+ QVERIFY(clientStarted);
+
+ // Use a different service name in each test; this way the ServiceBackends
+ QString serviceName = QString::number(serviceNameIx).prepend(SERVICE_NAME);
+ writeToClient(("assign session " + serviceName + " service\n").toStdString().c_str());
+ ++serviceNameIx;
+
+ // Test: create a Service and an associated Property
+ Service* service = new Service(QDBusConnection::SessionBus, serviceName);
+ Property* property = new Property(*service, "Test.Property");
+
+ // Test: command client to subscribe
+ QString actual = writeToClient("subscribe service Test.Property\n");
+
+ // Expected result: service is started automatically and Subscribe works
+ QString expected = "Subscribe returned: Unknown";
+ QCOMPARE(actual.simplified(), expected.simplified());
+
+ // Test: start the service again (even though it's started)
+ service->start();
+
+ // Expected result: the service is still there, and remembers the client
+ actual = writeToClient("subscribe service Test.Property\n");
+ expected = "Subscribe error: org.maemo.contextkit.Error.MultipleSubscribe";
+ QCOMPARE(actual.simplified(), expected.simplified());
+
+ delete service;
+ delete property;
+}
+
+void ServiceTest::defaultService()
+{
+ // Check that the initialization went well.
+ // Doing this only in init() is not enough; doesn't stop the test case.
+ QVERIFY(clientStarted);
+
+ // Use a different service name in each test; this way the ServiceBackends
+ QString serviceName = QString::number(serviceNameIx).prepend(SERVICE_NAME);
+ writeToClient(("assign session " + serviceName + " service\n").toStdString().c_str());
+ ++serviceNameIx;
+
+ // Test: create a Service. Set the Service as default. Then create
+ // a Property without a Service.
+ Service* service = new Service(QDBusConnection::SessionBus, serviceName);
+ service->setAsDefault();
+ Property* property = new Property("Test.Property");
+
+ // Expected result: the Property got associated with the default
+ // Service.
+
+ // Test: command client to subscribe
+ QString actual = writeToClient("subscribe service Test.Property\n");
+
+ // Expected result: Subscribe works
+ QString expected = "Subscribe returned: Unknown";
+ QCOMPARE(actual.simplified(), expected.simplified());
+
+ delete service;
+ delete property;
+}
+
+void ServiceTest::recreateProperty()
+{
+ // Check that the initialization went well.
+ // Doing this only in init() is not enough; doesn't stop the test case.
+ QVERIFY(clientStarted);
+
+ // Use a different service name in each test; this way the ServiceBackends
+ QString serviceName = QString::number(serviceNameIx).prepend(SERVICE_NAME);
+ writeToClient(("assign session " + serviceName + " service\n").toStdString().c_str());
+ ++serviceNameIx;
+
+ // Test: create a Service and an associated Property. Set a value
+ // to the property.
+ Service* service = new Service(QDBusConnection::SessionBus, serviceName);
+ Property* property = new Property(*service, "Test.Property");
+
+ property->setValue("keep this value");
+
+ // Test: delete the property and create it again
+ delete property;
+ property = new Property(*service, "Test.Property");
+
+ // Expeted result: the Property has kept its value
+ QCOMPARE(property->value(), QVariant("keep this value"));
+
+ delete property;
+ delete service;
}
-void ServiceTests::readStandardOutput()
+void ServiceTest::readStandardOutput()
{
isReadyToRead = true;
}
// Note: input must end with \n
-QString ServiceTests::writeToClient(const char* input)
+QString ServiceTest::writeToClient(const char* input)
{
isReadyToRead = false;
client->write(input);
@@ -134,4 +276,4 @@ QString ServiceTests::writeToClient(const char* input)
} // end namespace
-QTEST_MAIN(ContextProvider::ServiceTests);
+QTEST_MAIN(ContextProvider::ServiceTest);
diff --git a/libcontextprovider/customer-tests/service/servicetest.h b/libcontextprovider/customer-tests/service/servicetest.h
index 90e2a2e9..6756cf61 100644
--- a/libcontextprovider/customer-tests/service/servicetest.h
+++ b/libcontextprovider/customer-tests/service/servicetest.h
@@ -4,7 +4,7 @@ class QProcess;
namespace ContextProvider {
-class ServiceTests : public QObject
+class ServiceTest : public QObject
{
Q_OBJECT
@@ -18,6 +18,10 @@ private slots:
void cleanup();
void startStopStart();
+ void recreate();
+ void multiStart();
+ void defaultService();
+ void recreateProperty();
public slots:
void readStandardOutput();
diff --git a/libcontextprovider/customer-tests/subscription/Makefile.am b/libcontextprovider/customer-tests/subscription/Makefile.am
index 6d6dc42b..b4d69087 100644
--- a/libcontextprovider/customer-tests/subscription/Makefile.am
+++ b/libcontextprovider/customer-tests/subscription/Makefile.am
@@ -13,13 +13,10 @@ AM_CXXFLAGS = $(QtCore_CFLAGS) $(QtDBus_CFLAGS) $(QtTest_CFLAGS) -I$(srcdir)/..
LIBS += $(QtCore_LIBS) $(QtDBus_LIBS) $(QtTest_LIBS)
subscriptiontests_LDADD = ../../src/libcontextprovider.la $(top_builddir)/common/libcommon.la
-../../src/libcontextprovider.la:
+../../src/libcontextprovider.la: FORCE
$(MAKE) -C ../../src libcontextprovider.la
-$(top_builddir)/common/libcommon.la:
- $(MAKE) -C $(top_builddir)/common libcommon.la
-
-.PHONY: ../../libcontextprovider.la $(top_builddir)/libcommon.la
+.PHONY: FORCE
# moccing
nodist_subscriptiontests_SOURCES = mocs.cpp
diff --git a/libcontextprovider/customer-tests/subscription/subscriptiontests.cpp b/libcontextprovider/customer-tests/subscription/subscriptiontests.cpp
index 3f0f91c7..1e0737df 100644
--- a/libcontextprovider/customer-tests/subscription/subscriptiontests.cpp
+++ b/libcontextprovider/customer-tests/subscription/subscriptiontests.cpp
@@ -56,8 +56,12 @@ void SubscriptionTests::init()
test_string = new Property (*service2, "Test.String");
test_bool = new Property(*service2, "Test.Bool");
- // Process the events so that the services get started
- QCoreApplication::processEvents(QEventLoop::AllEvents);
+ // Nullify the values (we create the same Properties over and
+ // over, and they will keep their old values.
+ test_int->unsetValue();
+ test_double->unsetValue();
+ test_string->unsetValue();
+ test_bool->unsetValue();
// Initialize test program state
isReadyToRead = false;
@@ -86,7 +90,6 @@ void SubscriptionTests::cleanup()
}
delete client; client = NULL;
- // Note: we need to delete the properties before deleting the service
delete test_int; test_int = NULL;
delete test_double; test_double = NULL;
delete test_bool; test_bool = NULL;
@@ -94,10 +97,6 @@ void SubscriptionTests::cleanup()
delete service1; service1 = NULL;
delete service2; service2 = NULL;
-
- // ServiceBackedns are deleted in a deferred way, thus we need to
- // get them deleted
- QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
}
void SubscriptionTests::subscribeReturnValueForUnknownProperty()
diff --git a/libcontextprovider/customer-tests/types/Makefile.am b/libcontextprovider/customer-tests/types/Makefile.am
index 469920b9..e2ca07fd 100644
--- a/libcontextprovider/customer-tests/types/Makefile.am
+++ b/libcontextprovider/customer-tests/types/Makefile.am
@@ -9,9 +9,10 @@ check-customer: $(test_PROGRAMS)
AM_CXXFLAGS = $(QtCore_CFLAGS) $(QtDBus_CFLAGS) $(QtTest_CFLAGS) -I$(srcdir)/../../src -I$(top_srcdir)/common
LIBS += $(QtCore_LIBS) $(QtDBus_LIBS) $(QtTest_LIBS)
typestests_LDADD = ../../src/libcontextprovider.la
-../../src/libcontextprovider.la:
+
+../../src/libcontextprovider.la: FORCE
$(MAKE) -C ../../src libcontextprovider.la
-.PHONY: ../../libcontextprovider.la
+.PHONY: FORCE
# moccing
nodist_typestests_SOURCES = mocs.cpp
diff --git a/libcontextprovider/customer-tests/types/typestests.cpp b/libcontextprovider/customer-tests/types/typestests.cpp
index 76e77b1b..aad31142 100644
--- a/libcontextprovider/customer-tests/types/typestests.cpp
+++ b/libcontextprovider/customer-tests/types/typestests.cpp
@@ -55,8 +55,16 @@ void TypesTests::init()
dateItem = new Property(*service, "Test.Date");
timeItem = new Property (*service, "Test.Time");
- // Process the events so that the service gets started
- QCoreApplication::processEvents(QEventLoop::AllEvents);
+ // Nullify the values (we create the same Properties over and
+ // over, and they will keep their old values.
+ intItem->unsetValue();
+ stringItem->unsetValue();
+ boolItem->unsetValue();
+ doubleItem->unsetValue();
+ stringListItem->unsetValue();
+ charItem->unsetValue();
+ dateItem->unsetValue();
+ timeItem->unsetValue();
// Initialize test program state
isReadyToRead = false;
@@ -84,8 +92,6 @@ void TypesTests::cleanup()
}
delete client; client = NULL;
- // Note: we need to delete the properties before deleting the
- // service
delete intItem; intItem = NULL;
delete doubleItem; doubleItem = NULL;
delete boolItem; boolItem = NULL;
@@ -96,10 +102,6 @@ void TypesTests::cleanup()
delete dateItem; dateItem = NULL;
delete service; service = NULL;
-
- // PropertyPrivate and ServiceBackend object are deleted in a
- // deferred way, thus we need to get them deleted
- QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
}
void TypesTests::typesInReturnValueOfSubscribe()
diff --git a/libcontextprovider/customer-tests/value-changes/Makefile.am b/libcontextprovider/customer-tests/value-changes/Makefile.am
index 1ccf3215..6b104c16 100644
--- a/libcontextprovider/customer-tests/value-changes/Makefile.am
+++ b/libcontextprovider/customer-tests/value-changes/Makefile.am
@@ -9,9 +9,10 @@ check-customer: $(test_PROGRAMS)
AM_CXXFLAGS = $(QtCore_CFLAGS) $(QtDBus_CFLAGS) $(QtTest_CFLAGS) -I$(srcdir)/../../src -I$(top_srcdir)/common
LIBS += $(QtCore_LIBS) $(QtDBus_LIBS) $(QtTest_LIBS)
valuechangestests_LDADD = ../../src/libcontextprovider.la
-../../src/libcontextprovider.la:
+
+../../src/libcontextprovider.la: FORCE
$(MAKE) -C ../../src libcontextprovider.la
-.PHONY: ../../libcontextprovider.la
+.PHONY: FORCE
# moccing
nodist_valuechangestests_SOURCES = mocs.cpp
diff --git a/libcontextprovider/customer-tests/value-changes/valuechangestests.cpp b/libcontextprovider/customer-tests/value-changes/valuechangestests.cpp
index 62b3db11..f5da63e6 100644
--- a/libcontextprovider/customer-tests/value-changes/valuechangestests.cpp
+++ b/libcontextprovider/customer-tests/value-changes/valuechangestests.cpp
@@ -53,8 +53,10 @@ void ValueChangesTests::init()
test_int = new Property(*service, "Test.Int");
test_double = new Property(*service, "Test.Double");
- // Process the events so that the services get started
- QCoreApplication::processEvents(QEventLoop::AllEvents);
+ // Nullify the values (we create the same Properties over and
+ // over, and they will keep their old values.
+ test_int->unsetValue();
+ test_double->unsetValue();
// Initialize test program state
isReadyToRead = false;
@@ -82,15 +84,11 @@ void ValueChangesTests::cleanup()
}
delete client; client = NULL;
- // Note: we need to delete the properties before deleting the service
delete test_int; test_int = NULL;
delete test_double; test_double = NULL;
delete service; service = NULL;
- // PropertyPrivate and ServiceBackend objects are deleted in a
- // deferred way, thus we need to get them deleted
- QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
}
void ValueChangesTests::subscribedPropertyChanges()
diff --git a/libcontextprovider/doc/Makefile.am b/libcontextprovider/doc/Makefile.am
index 627b7778..1274e3c6 100644
--- a/libcontextprovider/doc/Makefile.am
+++ b/libcontextprovider/doc/Makefile.am
@@ -4,7 +4,9 @@ endif
DOXYCFG = $(srcdir)/doxy.cfg
-doxygen:
+doxygen: html/index.html
+
+html/index.html: ../src/*.cpp ../src/*.h
@if test x$(srcdir) = x. ; then \
echo srcdir=$(srcdir) $(DOXYGEN) $(DOXYCFG); \
srcdir=$(srcdir) $(DOXYGEN) $(DOXYCFG); \
diff --git a/libcontextprovider/man/context-provide-v2.1 b/libcontextprovider/man/context-provide-v2.1
index 855a5521..a4bbf63e 100644
--- a/libcontextprovider/man/context-provide-v2.1
+++ b/libcontextprovider/man/context-provide-v2.1
@@ -27,10 +27,16 @@ Properties can also be provided on the command line with \fITYPE\fR
If at least one property is specified on the command line the service
is autostarted and will start providing the properties right away,
-otherwise you have to use the \fBstart\fR command after all of the
-properties have been added by \fBadd\fR invocations. In both cases
+otherwise you have to use the \fB`start'\fR command after all of the
+properties have been added by \fB`add'\fR invocations. In both cases
the service name registered for the service on D-Bus will be \fIBUSNAME\fR.
+If no \fIBUSNAME\fR is given the tool acts as a Commander, taking control of
+all subscribers in the system. This mode by default proxies the real values
+of all properties. To control a property you have to \fB`add'\fR the
+property, which stops forwarding the corresponding property. You can undo
+this via the \fB`del'\fR command.
+
.SH OPTIONS
.TP
\fB--system\fR
@@ -47,6 +53,12 @@ provided properties. The initial value will be \fIINITVALUE\fR if
specified, otherwise unset (null). This has to be called before an
attempt is made to set a key value. \fITYPE\fR can be: STRING, INT,
BOOL, DOUBLE. Example: "add INT Battery.Status 20".
+
+In \fICommander\fR mode it also ceases proxying the real property.
+.TP
+\fBdel\fR \fIKEY\fR
+makes context-provide forget \fIKEY\fR. In \fICommander\fR mode, restores
+proxying of the real property.
.TP
\fIKEY\fB=\fIVALUE\fR
sets the given \fIKEY\fR value to the new \fIVALUE\fR. Example: "Battery.Status=99".
@@ -54,6 +66,13 @@ sets the given \fIKEY\fR value to the new \fIVALUE\fR. Example: "Battery.Status=
\fBunset\fR \fIKEY\fR
sets the given \fIKEY\fR to non specified (null).
.TP
+\fBinfo\fR \fIKEY\fR
+prints the type, name and current value of \fIKEY\fR. In \fICommander\fR
+mode the real value (from the original provider) is printed as well.
+.TP
+\fBlist\fR
+same as calling `info' for all known keys (both proxied and added ones)
+.TP
\fBsleep\fR \fIINTERVAL\fR
sleeps and blocks the main loop for the given amount of seconds. Used
mainly for internal testing purposes. Example: "sleep 10".
@@ -64,7 +83,7 @@ by the \fIFILENAME\fR, the default is 'context-provide.context' in the
current directory.
.TP
\fBstart\fR
-try publish the \fIBUSNAME\fR of the provider on D-Bus, exit if it fails.
+try to (re)publish the \fIBUSNAME\fR of the provider on D-Bus, exit if it fails.
.TP
\fBexit\fR
exit from the program.
diff --git a/libcontextprovider/src/Makefile.am b/libcontextprovider/src/Makefile.am
index b4feb297..eac30d7e 100644
--- a/libcontextprovider/src/Makefile.am
+++ b/libcontextprovider/src/Makefile.am
@@ -34,10 +34,10 @@ AM_CXXFLAGS = $(QtCore_CFLAGS) $(QtDBus_CFLAGS) $(DBUS_CFLAGS) \
LIBS += $(QtCore_LIBS) $(QtDBus_LIBS)
libcontextprovider_la_LIBADD=$(top_builddir)/common/libcommon.la
-$(top_builddir)/common/libcommon.la:
+$(top_builddir)/common/libcommon.la: FORCE
$(MAKE) -C $(top_builddir)/common libcommon.la
-.PHONY: $(top_builddir)/common/libcommon.la
+.PHONY: FORCE
# moccing
nodist_libcontextprovider_la_SOURCES = mocs.cpp
diff --git a/libcontextprovider/src/property.cpp b/libcontextprovider/src/property.cpp
index b35d0d68..02181fdd 100644
--- a/libcontextprovider/src/property.cpp
+++ b/libcontextprovider/src/property.cpp
@@ -62,6 +62,9 @@ Property::Property(const QString &k, QObject* parent)
init(ServiceBackend::defaultServiceBackend, k);
}
+/// Initialize the private implementation: find the corresponding
+/// PropertyPrivate (create it if it doesn't exist), and connect
+/// signals from it.
void Property::init(ServiceBackend *serviceBackend, const QString &key)
{
contextDebug() << F_PROPERTY << "Creating new Property for key:" << key;
@@ -75,7 +78,6 @@ void Property::init(ServiceBackend *serviceBackend, const QString &key)
priv = new PropertyPrivate(serviceBackend, key);
PropertyPrivate::propertyPrivateMap.insert(lookup, priv);
}
- priv->ref();
sconnect(priv, SIGNAL(firstSubscriberAppeared(const QString&)),
this, SIGNAL(firstSubscriberAppeared(const QString&)));
sconnect(priv, SIGNAL(lastSubscriberDisappeared(const QString&)),
@@ -118,7 +120,6 @@ QVariant Property::value()
Property::~Property()
{
contextDebug() << F_PROPERTY << F_DESTROY << "Destroying Property for key:" << priv->key;
- priv->unref();
}
} // end namespace
diff --git a/libcontextprovider/src/property.h b/libcontextprovider/src/property.h
index 837aa852..788a270d 100644
--- a/libcontextprovider/src/property.h
+++ b/libcontextprovider/src/property.h
@@ -53,7 +53,7 @@ public:
void unsetValue();
private:
- PropertyPrivate *priv;
+ PropertyPrivate *priv; ///< Private implementation
void init(ServiceBackend *serviceBackend, const QString &key);
signals:
diff --git a/libcontextprovider/src/propertyadaptor.cpp b/libcontextprovider/src/propertyadaptor.cpp
index 0d23accf..1751ab7e 100644
--- a/libcontextprovider/src/propertyadaptor.cpp
+++ b/libcontextprovider/src/propertyadaptor.cpp
@@ -30,6 +30,13 @@ namespace ContextProvider {
/*!
\class PropertyAdaptor
\brief A DBus adaptor for implementing the org.maemo.contextkit.Property
+
+ PropertyAdaptor represents the Property object on D-Bus. It also
+ keeps track of its clients and sets the PropertyPrivate to
+ subscribed or unsubscribed accordingly.
+
+ PropertyAdaptor also listens to values sent by other providers on
+ D-Bus and notifies the PropertyPrivate about them.
*/
/// Constructor. Creates new adaptor for the given manager with the given
@@ -43,6 +50,10 @@ PropertyAdaptor::PropertyAdaptor(PropertyPrivate* propertyPrivate, QDBusConnecti
sconnect(propertyPrivate, SIGNAL(valueChanged(const QVariantList&, const quint64&)),
this, SIGNAL(ValueChanged(const QVariantList&, const quint64&)));
+ // The same value can be provided on session and system bus by
+ // different providers. Unfortunately, each provider needs both a
+ // system bus connection and a session bus connection to listen to
+ // other providers.
QDBusConnection::sessionBus().connect("", objectPath(), DBUS_INTERFACE, "ValueChanged",
this, SLOT(onValueChanged(QVariantList, quint64)));
@@ -50,6 +61,7 @@ PropertyAdaptor::PropertyAdaptor(PropertyPrivate* propertyPrivate, QDBusConnecti
this, SLOT(onValueChanged(QVariantList, quint64)));
}
+/// Implementation of the D-Bus method Subscribe
void PropertyAdaptor::Subscribe(const QDBusMessage &msg, QVariantList& values, quint64& timestamp)
{
contextDebug() << "Subscribe called";
@@ -75,6 +87,7 @@ void PropertyAdaptor::Subscribe(const QDBusMessage &msg, QVariantList& values, q
Get(values, timestamp);
}
+/// Implementation of the D-Bus method Unsubscribe
void PropertyAdaptor::Unsubscribe(const QDBusMessage &msg)
{
contextDebug() << "Unsubscribe called";
@@ -94,6 +107,7 @@ void PropertyAdaptor::Unsubscribe(const QDBusMessage &msg)
}
}
+/// Implementation of the D-Bus method Get.
void PropertyAdaptor::Get(QVariantList& values, quint64& timestamp)
{
// Construct the return values
@@ -103,15 +117,16 @@ void PropertyAdaptor::Get(QVariantList& values, quint64& timestamp)
timestamp = propertyPrivate->timestamp;
}
+/// Called when a ValueChanged signal is overheard on D-Bus. Command
+/// PropertyPrivate to update its overheard value.
void PropertyAdaptor::onValueChanged(QVariantList values, quint64 timestamp)
{
propertyPrivate->updateOverheardValue(values, timestamp);
}
-
-/// Dbus interface slot. The PropertyAdaptor listens for dbus bus names changing
-/// to notify the managed Property that a bus name is gone. It does it through
-/// Property::busNameIsGone function.
+/// Called when a NameOwnerChanged signal is broadcast on D-Bus. If
+/// one of our clients has disappeared from D-Bus, update the client
+/// list.
void PropertyAdaptor::OnServiceOwnerChanged(const QString &name, const QString &oldName, const QString &newName)
{
@@ -126,6 +141,11 @@ void PropertyAdaptor::OnServiceOwnerChanged(const QString &name, const QString &
}
}
+/// Object path where the corresponding PropertyPrivate object should
+/// be registered at. For a core propertiy Property.Name (not starting
+/// with /), the path is /org/maemo/contextkit/Property/Name. For a
+/// non-core property /com/my/property, the object path is
+/// /com/my/property.
QString PropertyAdaptor::objectPath() const
{
if (!propertyPrivate->key.startsWith("/"))
@@ -133,11 +153,14 @@ QString PropertyAdaptor::objectPath() const
return QString(propertyPrivate->key);
}
+/// Called when the service is stopped and will disappear from
+/// D-Bus. If it appears again, the clients will resubscribe, and it
+/// shouldn't be a MultipleSubscribe error. Thus, we need to forget
+/// the clients when the service is stopped.
void PropertyAdaptor::forgetClients()
{
clientServiceNames.clear();
propertyPrivate->setUnsubscribed();
}
-
} // namespace ContextProvider
diff --git a/libcontextprovider/src/propertyprivate.cpp b/libcontextprovider/src/propertyprivate.cpp
index 80c480f6..48e3ca19 100644
--- a/libcontextprovider/src/propertyprivate.cpp
+++ b/libcontextprovider/src/propertyprivate.cpp
@@ -29,60 +29,34 @@
namespace ContextProvider {
+/*!
+ \class PropertyPrivate ContextProvider ContextProvider
+
+ \brief The private implementation of Property.
+
+ For each (ServiceBackend*, key) pair there exists only one
+ PropertyPrivate; multiple Property objects may share it.
+*/
+
+
QHash<QPair<ServiceBackend*,QString>, PropertyPrivate*> PropertyPrivate::propertyPrivateMap;
+/// Constructor. Register the PropertyPrivate to its ServiceBackend;
+/// this will make the Property object appear on D-Bus.
PropertyPrivate::PropertyPrivate(ServiceBackend* serviceBackend, const QString &key, QObject *parent)
: QObject(parent), refCount(0), serviceBackend(serviceBackend),
key(key), value(QVariant()), timestamp(currentTimestamp()), subscribed(false),
emittedValue(value), emittedTimestamp(timestamp), overheard(false)
{
+ // Associate the property to the service backend
+ serviceBackend->addProperty(key, this);
}
-PropertyPrivate::~PropertyPrivate()
-{
-}
-
-/// Increase the reference count by one. Property calls this.
-void PropertyPrivate::ref()
-{
- refCount++;
- if (refCount == 1) {
- // Increase the reference count of serviceBackend to ensure it
- // stays alive
- serviceBackend->ref();
- // Associate the property to the service backend
- serviceBackend->addProperty(key, this);
- }
-}
-
-/// Decrease the reference count by one. Property calls this. If the
-/// reference count goes to zero, schedule the PropertyPrivate
-/// instance to be deleted.
-void PropertyPrivate::unref()
-{
- refCount--;
-
- if (refCount == 0) {
- // Remove the property from the service backend
- serviceBackend->removeProperty(key);
- // Now serviceBackend can be deleted if nobody else needs it.
- serviceBackend->unref();
-
- // For now, remove the PropertyPrivate from the instance
- // map. If this is changed, we need to check carefully what
- // happens if a new ServiceBackend is created with the same
- // memory address as a previous ServiceBackend (which was
- // destructed), and having the old PropertyPrivate store the
- // ServiceBackend pointer to that memory address.
- QPair<ServiceBackend*, QString> key = propertyPrivateMap.key(this);
- if (key.second != "")
- propertyPrivateMap.remove(key);
- else
- contextCritical() << "PropertyPrivate couldn't find itself in the instance store";
- deleteLater(); // "delete this" would be probably unsafe
- }
-}
-
+/// Set value for the PropertyPrivate. Results in a valueChanged
+/// signal emission, if 1) the value was different than the current
+/// value of the PropertyPrivate, or 2) The provider has overheard
+/// another provider setting a different value having a more recent
+/// time stamp than our last emission.
void PropertyPrivate::setValue(const QVariant& v)
{
contextDebug() << F_PROPERTY << "Setting key:" << key << "to type:" << v.typeName();
@@ -106,9 +80,12 @@ void PropertyPrivate::setValue(const QVariant& v)
}
}
+/// Emit the valueChanged signal and update the emittedValue and
+/// emittedTimestamp. (If subscribed is false, no value is emitted.)
void PropertyPrivate::emitValue()
{
- // No difference between intention and emitted value, nothing happens
+ // No difference between intention and emitted value, nothing
+ // happens
if (emittedTimestamp == timestamp && emittedValue == value
&& emittedValue.isNull() == value.isNull())
return;
@@ -130,6 +107,9 @@ void PropertyPrivate::emitValue()
emit valueChanged(values, timestamp);
}
+/// Set the PropertyPrivate to subscribed state. If it was in the
+/// unsubscribed state, the firstSubscriberAppeared signal is
+/// emitted. (Property transmits the signal forward.)
void PropertyPrivate::setSubscribed()
{
if (subscribed == false) {
@@ -138,6 +118,9 @@ void PropertyPrivate::setSubscribed()
}
}
+/// Set the PropertyPrivate to unsubscribed state. If it was in the
+/// subscribed state, the lastSubscriberDisappeared signal is
+/// emitted. (Property transmits the signal forward.)
void PropertyPrivate::setUnsubscribed()
{
if (subscribed == true) {
@@ -146,6 +129,11 @@ void PropertyPrivate::setUnsubscribed()
}
}
+/// Called by PropertyAdaptor when it has overheard another provider
+/// sending a value on D-Bus. Check if the value is different and more
+/// recent than the value we've emitted last. If so, emit our value
+/// again. This way we ensure that the client gets the correct time
+/// stamp for our value.
void PropertyPrivate::updateOverheardValue(const QVariantList& v, const quint64& t)
{
contextDebug() << "Updating overheard value" << v << t;
@@ -158,7 +146,7 @@ void PropertyPrivate::updateOverheardValue(const QVariantList& v, const quint64&
else {
overheardValue = v[0];
}
- if (t > emittedTimestamp && overheardValue != emittedValue){
+ if (t > emittedTimestamp && overheardValue != emittedValue){
// record that a different and more recent value than the one
// emitted has been overheard
overheard = true;
@@ -168,6 +156,8 @@ void PropertyPrivate::updateOverheardValue(const QVariantList& v, const quint64&
}
}
+/// Compute a unique time stamp for our value. The time stamp is based
+/// on monotonic clock and its value is: seconds * 10e9 + nanoseconds.
quint64 PropertyPrivate::currentTimestamp()
{
struct timespec time;
@@ -177,6 +167,5 @@ quint64 PropertyPrivate::currentTimestamp()
return toReturn;
}
-
} // end namespace
diff --git a/libcontextprovider/src/propertyprivate.h b/libcontextprovider/src/propertyprivate.h
index 73be67c4..f8bb7c97 100644
--- a/libcontextprovider/src/propertyprivate.h
+++ b/libcontextprovider/src/propertyprivate.h
@@ -30,6 +30,7 @@
namespace ContextProvider {
class ServiceBackend;
+class Property;
class PropertyPrivate : public QObject
{
@@ -37,10 +38,6 @@ class PropertyPrivate : public QObject
public:
explicit PropertyPrivate(ServiceBackend* serviceBackend, const QString &key, QObject *parent = 0);
- virtual ~PropertyPrivate();
-
- void ref();
- void unref();
void setValue(const QVariant& v);
void updateOverheardValue(const QVariantList&, const quint64&);
@@ -56,16 +53,20 @@ private:
static quint64 currentTimestamp();
void emitValue();
- int refCount;
+ int refCount; ///< Number of Property instance sharing this PropertyPrivate
ServiceBackend* serviceBackend; ///< Pointer to the serviceBackend taking care of D-Bus related things
QString key; ///< Key of this property
QVariant value; ///< Current value of the property, set by this provider. QVariant() if null.
quint64 timestamp; ///< Time when the value was set
- bool subscribed;
+
+ bool subscribed; ///< True if this Property is subscribed to by the clients.
QVariant emittedValue; ///< Last value emitted by this provider.
quint64 emittedTimestamp; ///< Time when the emittedValue was emitted.
bool overheard; ///< True if provider overheard a value over D-Bus (must be different and more recent than emitted)
+
+ /// Map of PropertyPrivate instances
static QHash<QPair<ServiceBackend*, QString>, PropertyPrivate*> propertyPrivateMap;
+
friend class Property;
friend class PropertyAdaptor;
};
diff --git a/libcontextprovider/src/service.cpp b/libcontextprovider/src/service.cpp
index ad3e5d34..c015c4e9 100644
--- a/libcontextprovider/src/service.cpp
+++ b/libcontextprovider/src/service.cpp
@@ -21,7 +21,6 @@
#include "service.h"
#include "servicebackend.h"
-#include "property.h"
#include "logging.h"
#include "sconnect.h"
#include "loggingfeatures.h"
@@ -38,8 +37,8 @@ namespace ContextProvider {
protocol. It has both a C++ and a C interface, so you can choose
which you prefer. For the documentation of the C API, see \ref CApi.
- The C++ interface consists mainly of the three classes Service,
- Property, and Group in the namespace ContextProvider. They are
+ The C++ interface consists mainly of the three classes: Service,
+ Property, and Group in the namespace ContextProvider. They are
declared in the ContextProvider header file.
Thus, you would typically gain access to the classes like this
@@ -49,7 +48,7 @@ namespace ContextProvider {
using namespace ContextProvider;
- Service my_service (...);
+ Service myService (...);
\endcode
If you prefer not to have generic names like "Service" in your code,
@@ -62,14 +61,14 @@ namespace ContextProvider {
namespace CP = ContextProvider;
- CP::Service my_service (...);
+ CP::Service myService (...);
\endcode
The basic pattern to use this library is to create a Service
instance to represent you on D-Bus and then to add Property
instances to it for the keys that you want to provide. Once this is
- done, you can call Property::set() at any time to change the value
- of the property.
+ done, you can call Property::setValue() and Property::unsetValue()
+ at any time to change the value of the property.
Communication with clients happens asynchronously and this library
needs a running event loop for that.
@@ -85,18 +84,18 @@ namespace ContextProvider {
{
QApplication app(argc, argv);
- Service my_service(QDBusConnection::SessionBus, "com.example.simple");
- Property my_property(my_service, "Example.Simple");
+ Service myService(QDBusConnection::SessionBus, "com.example.simple");
+ Property myProperty(myService, "Example.Simple");
// set initial value of property
- my_property.set(100);
+ myProperty.set(100);
app.exec();
}
\endcode
- If you need to know when someone actually subscribes to one of
- your values, you can connect to the firstSubscriberAppeared and
+ If you need to know when someone actually subscribes to one of your
+ properties, you can connect to the firstSubscriberAppeared and
lastSubscriberDisappeared signals of the Property instances. You
can also use a Group if you are only interested in whether at least
one of a set of Property objects is subscribed to.
@@ -120,8 +119,8 @@ namespace ContextProvider {
interface to the real D-Bus service.
When the last instance of Service is destroyed, the real service
- is automatically terminated and destroyed (there is a simple ref
- counting mechanism involved to guarantee that).
+ is automatically stopped and it disappears from D-Bus (there is a
+ simple ref counting mechanism involved to guarantee that).
Consider the following examples:
@@ -176,7 +175,7 @@ namespace ContextProvider {
// Now myService will share the connection and won't register
// any service name
Service *myService = new Service(systemBus);
- Property* property = new Property(myService, "My.Property");
+ Property* myProperty = new Property(myService, "My.Property");
// Important: registering the service name should be done as
// late as possible, to prevent clients from connecting to us
@@ -203,7 +202,6 @@ Service::Service(QDBusConnection connection, QObject *parent)
backend->ref();
}
-
/// Creates a Service proxy object for \a busName on the bus indicated
/// by \a busType. The Service will register the given \a busName on
/// D-Bus. If the service is accessed for the first time it'll be
@@ -227,9 +225,9 @@ Service::Service(QDBusConnection::BusType busType, const QString &busName, QObje
}
/// Destroys this Service instance. The actual service on D-Bus is
-/// destroyed and stopped if this object is a last instance pointing
-/// at the actual service with the given constructor parameters
-/// (QDBusConnection or bus type and bus name).
+/// stopped if this object is a last instance pointing at the actual
+/// service with the given constructor parameters (QDBusConnection or
+/// bus type and bus name).
Service::~Service()
{
contextDebug() << F_SERVICE << F_DESTROY << "Destroying Service";
diff --git a/libcontextprovider/src/service.h b/libcontextprovider/src/service.h
index 0b47f715..7c502cfb 100644
--- a/libcontextprovider/src/service.h
+++ b/libcontextprovider/src/service.h
@@ -56,7 +56,7 @@ public:
void setConnection(const QDBusConnection &connection);
private:
- ServiceBackend *backend;
+ ServiceBackend *backend; ///< Private implementation of the Service
friend class Property;
friend class ::ServiceUnitTest;
diff --git a/libcontextprovider/src/servicebackend.cpp b/libcontextprovider/src/servicebackend.cpp
index ab4d92bf..caa2e4d2 100644
--- a/libcontextprovider/src/servicebackend.cpp
+++ b/libcontextprovider/src/servicebackend.cpp
@@ -18,12 +18,15 @@
* 02110-1301 USA
*
*/
-#include "propertyadaptor.h"
+
#include "servicebackend.h"
+#include "propertyprivate.h"
+#include "propertyadaptor.h"
#include "logging.h"
#include "sconnect.h"
#include "loggingfeatures.h"
-#include "propertyprivate.h"
+
+#include <QDBusError>
namespace ContextProvider {
@@ -32,9 +35,9 @@ namespace ContextProvider {
\brief A ServiceBackend is the real worker behind Service.
- Multiple Service instances can share same ServiceBackend.
- The backend is the actual worker that operates on D-Bus
- and registers properties.
+ Multiple Service instances can share same ServiceBackend. The
+ backend is the actual worker that operates on D-Bus (register
+ objects representing Property objects and possibly a bus name).
The Service class actually proxies all methods to the ServiceBackend.
*/
@@ -47,8 +50,8 @@ ServiceBackend *ServiceBackend::defaultServiceBackend;
/// program, and the ServiceBackend will not register any service
/// names.
ServiceBackend::ServiceBackend(QDBusConnection connection) :
- connection(connection),
refCount(0),
+ connection(connection),
busName("") // shared connection
{
contextDebug() << F_SERVICE_BACKEND << "Creating new ServiceBackend for" << busName;
@@ -58,8 +61,8 @@ ServiceBackend::ServiceBackend(QDBusConnection connection) :
/// service name to register. The connection will not be shared
/// between the Service and the provider program.
ServiceBackend::ServiceBackend(QDBusConnection connection, const QString &busName) :
- connection(connection),
refCount(0),
+ connection(connection),
busName(busName) // private connection
{
contextDebug() << F_SERVICE_BACKEND << "Creating new ServiceBackend for" << busName;
@@ -102,23 +105,11 @@ void ServiceBackend::addProperty(const QString& key, PropertyPrivate* property)
registerProperty(key, property);
}
-// Break the association of the PropertyPrivate object with this
-// ServiceBackend. The corresponding object will disappear from D-Bus.
-void ServiceBackend::removeProperty(const QString& key)
-{
- properties.remove(key);
-
- // Unregister the object on D-Bus
- PropertyAdaptor* adaptor = createdAdaptors[key];
- if (adaptor != 0) {
- connection.unregisterObject(adaptor->objectPath());
- }
-}
-
/// Register a Property with the given name on D-Bus. Returns true if
/// succeeded, false if failed.
bool ServiceBackend::registerProperty(const QString& key, PropertyPrivate* property)
{
+ // Check if there is an adaptor; if not, create it.
if (createdAdaptors.contains(key) == false) {
PropertyAdaptor* adaptor = new PropertyAdaptor(property, &connection);
createdAdaptors.insert(key, adaptor);
@@ -130,6 +121,7 @@ bool ServiceBackend::registerProperty(const QString& key, PropertyPrivate* prope
return true;
}
+ // Try to register the object.
if (!connection.registerObject(adaptor->objectPath(), property)) {
contextCritical() << F_SERVICE_BACKEND << "Failed to register the Property object for" << key;
contextCritical() << F_SERVICE_BACKEND << "Error:" << connection.lastError();
@@ -138,16 +130,17 @@ bool ServiceBackend::registerProperty(const QString& key, PropertyPrivate* prope
return true;
}
-
/// Start the Service again after it has been stopped. In the case of
/// shared connection, the objects will be registered to D-Bus. In the
/// case of non-shared connection, also the service name will be
/// registered on D-Bus. Returns true on success, false otherwise.
bool ServiceBackend::start()
{
- contextDebug() << createdAdaptors;
+ contextDebug() << F_SERVICE_BACKEND << "Starting service for bus:" << busName;
+
// Re-register existing Property objects on D-Bus
foreach (const QString& key, properties.keys()) {
+ contextDebug() << F_SERVICE_BACKEND << "Re-registering" << key;
if (!registerProperty(key, properties[key])) {
return false;
}
@@ -176,9 +169,9 @@ void ServiceBackend::stop()
if (!sharedConnection())
connection.unregisterService(busName);
- // Unregister Property objects and clean up PropertyAdaptors set.
- // Also, command Property objects to forget their subscriptions
- // (if the service is started again, clients will resubscribe).
+ // Unregister Property objects from D-Bus. Also, command
+ // PropertyAdaptor objects to forget their subscriptions (if the
+ // service is started again, clients will resubscribe).
foreach (PropertyAdaptor* adaptor, createdAdaptors) {
adaptor->forgetClients();
@@ -205,20 +198,15 @@ void ServiceBackend::ref()
}
/// Decrease the reference count by one. Service calls this. If the
-/// reference count goes to zero, stop the ServiceBackend instance,
-/// remove it from the instance store and schedule it to be deleted.
+/// reference count goes to zero, stop the ServiceBackend
+/// instance. The instance is not removed from the instance map and
+/// not deleted, though.
void ServiceBackend::unref()
{
refCount--;
if (refCount == 0) {
stop();
- QString key = instances.key(this);
- if (key != "")
- instances.remove(key);
- else
- contextCritical() << "Backend couldn't find itself in the instance store";
- deleteLater(); // "delete this" would be probably unsafe
}
}
@@ -251,9 +239,14 @@ ServiceBackend* ServiceBackend::instance(QDBusConnection::BusType busType,
ServiceBackend* backend = new ServiceBackend(
QDBusConnection::connectToBus(busType, busName),
busName);
- if (autoStart) backend->start();
instances.insert(lookup, backend);
}
+ // Autostart also if the instance wasn't newly created: it might
+ // be that the user has deleted the Service previously, and now
+ // recreates it. In that case, the ServiceBackend remains alive,
+ // and we should start it after retrieving it from the instance
+ // map.
+ if (autoStart) instances[lookup]->start();
return instances[lookup];
}
diff --git a/libcontextprovider/src/servicebackend.h b/libcontextprovider/src/servicebackend.h
index ef31c3f7..61f4431c 100644
--- a/libcontextprovider/src/servicebackend.h
+++ b/libcontextprovider/src/servicebackend.h
@@ -51,7 +51,6 @@ public:
void stop();
void addProperty(const QString& key, PropertyPrivate* property);
- void removeProperty(const QString& key);
void setAsDefault();
void setValue(const QString &key, const QVariant &val);
@@ -70,10 +69,17 @@ public:
private:
bool registerProperty(const QString& key, PropertyPrivate* property);
+ int refCount; ///< Number of Service objects using this as their backend
+
+ /// Shared or private QDBusConnection used for registering objects
+ /// (and possibly bus names)
QDBusConnection connection;
- int refCount;
+
+ /// The bus name that should be registered by this ServiceBackend;
+ /// "" if the ServiceBackend shouldn't register any.
QString busName;
+ /// Map storing the ServiceBackend instances (one instance for QString-ServiceBackend* pair).
static QHash <QString, ServiceBackend*> instances;
/// Properties associated with the current service
diff --git a/libcontextprovider/unit-tests/property/.gitignore b/libcontextprovider/unit-tests/property/.gitignore
index 87499d5a..e1526e46 100644
--- a/libcontextprovider/unit-tests/property/.gitignore
+++ b/libcontextprovider/unit-tests/property/.gitignore
@@ -1,5 +1,6 @@
propertyunittest
property.cpp
+propertyprivate.cpp
sconnect.h
logging.h
logging.cpp
diff --git a/libcontextprovider/unit-tests/property/Makefile.am b/libcontextprovider/unit-tests/property/Makefile.am
index 60400c39..790caccd 100644
--- a/libcontextprovider/unit-tests/property/Makefile.am
+++ b/libcontextprovider/unit-tests/property/Makefile.am
@@ -3,7 +3,7 @@ check_PROGRAMS = propertyunittest
# test's sources
propertyunittest_SOURCES = propertyunittest.cpp service.h servicebackend.h property.h propertyprivate.h
-COVERAGE_FILES = property.cpp propretyprivate.cpp
+COVERAGE_FILES = property.cpp propertyprivate.cpp
# do the testing, coverage, etc. stuff
# tests.am is using +=, so we have to set a value here for these four always
diff --git a/libcontextprovider/unit-tests/property/propertyprivate.h b/libcontextprovider/unit-tests/property/propertyprivate.h
index 1866889b..591045e9 100644
--- a/libcontextprovider/unit-tests/property/propertyprivate.h
+++ b/libcontextprovider/unit-tests/property/propertyprivate.h
@@ -41,6 +41,9 @@ public:
explicit PropertyPrivate(ServiceBackend* serviceBackend, const QString &key, QObject *parent = 0);
void setValue(const QVariant& v);
+ void updateOverheardValue(const QVariantList&, const quint64&);
+ void setSubscribed();
+ void setUnsubscribed();
signals:
void valueChanged(const QVariantList& values, const qlonglong& timestamp);
@@ -48,13 +51,23 @@ signals:
void lastSubscriberDisappeared(const QString& key);
private:
- static qlonglong currentTimestamp();
+ static quint64 currentTimestamp();
+ void emitValue();
+ int refCount; ///< Number of Property instance sharing this PropertyPrivate
ServiceBackend* serviceBackend; ///< Pointer to the serviceBackend taking care of D-Bus related things
QString key; ///< Key of this property
- QVariant value; ///< Current value of the property, QVariant() if null
- qlonglong timestamp; ///< Time when the value was set
+ QVariant value; ///< Current value of the property, set by this provider. QVariant() if null.
+ quint64 timestamp; ///< Time when the value was set
+
+ bool subscribed; ///< True if this Property is subscribed to by the clients.
+ QVariant emittedValue; ///< Last value emitted by this provider.
+ quint64 emittedTimestamp; ///< Time when the emittedValue was emitted.
+ bool overheard; ///< True if provider overheard a value over D-Bus (must be different and more recent than emitted)
+
+ /// Map of PropertyPrivate instances
static QHash<QPair<ServiceBackend*, QString>, PropertyPrivate*> propertyPrivateMap;
+
friend class Property;
friend class PropertyAdaptor;
diff --git a/libcontextprovider/unit-tests/property/propertyunittest.cpp b/libcontextprovider/unit-tests/property/propertyunittest.cpp
index be3402b3..9ba2de78 100644
--- a/libcontextprovider/unit-tests/property/propertyunittest.cpp
+++ b/libcontextprovider/unit-tests/property/propertyunittest.cpp
@@ -19,80 +19,19 @@
*
*/
+#include "service.h" // mock
+#include "servicebackend.h" // mock
+
+#include "property.h" // to be tested
+#include "propertyprivate.h" // to be tested
+
#include <QtTest/QtTest>
#include <QtCore>
#include <stdlib.h>
-#include "service.h"
-#include "property.h"
-#include "manager.h"
namespace ContextProvider {
-Manager *manager;
ServiceBackend *serviceBackend;
-QVariant *lastVariantSet = NULL;
-
-void resetVariants()
-{
- delete lastVariantSet;
- lastVariantSet = NULL;
-}
-
-bool Manager::keyIsValid(const QString &key) const
-{
- return (keys.contains(key));
-}
-
-void Manager::increaseSubscriptionCount(const QString &key)
-{
-}
-
-void Manager::decreaseSubscriptionCount(const QString &key)
-{
-}
-
-const QVariant Manager::keyValue(const QString &key)
-{
- return QVariant();
-}
-
-void Manager::setKeyValue(const QString &key, const QVariant &v)
-{
- delete lastVariantSet;
- lastVariantSet = new QVariant(v);
-}
-
-QStringList Manager::getKeys()
-{
- return keys;
-}
-
-QVariant Manager::getKeyValue(const QString &key)
-{
- if (lastVariantSet)
- return QVariant(*lastVariantSet);
- else
- return QVariant();
-}
-
-void Manager::fakeFirst(const QString &key)
-{
- emit firstSubscriberAppeared(key);
-}
-
-void Manager::fakeLast(const QString &key)
-{
- emit lastSubscriberDisappeared(key);
-}
-
-Manager::Manager()
-{
-}
-
-void Manager::addKey(const QString &key)
-{
- keys << key;
-}
// Mock implementation of Service
@@ -104,9 +43,8 @@ Service::Service()
// Mock implementation of ServiceBackend
ServiceBackend* ServiceBackend::defaultServiceBackend = NULL;
-Manager *ServiceBackend::manager()
+void ServiceBackend::addProperty(const QString& key, PropertyPrivate* prop)
{
- return ContextProvider::manager;
}
class PropertyUnitTest : public QObject
@@ -138,7 +76,6 @@ void PropertyUnitTest::initTestCase()
void PropertyUnitTest::init()
{
- manager = new Manager();
battery_voltage = new Property(service, "Battery.Voltage");
battery_is_charging = new Property(service, "Battery.IsCharging");
}
@@ -147,7 +84,6 @@ void PropertyUnitTest::cleanup()
{
delete battery_voltage; battery_voltage = NULL;
delete battery_is_charging; battery_is_charging = NULL;
- delete manager; manager = NULL;
}
void PropertyUnitTest::getProperty()
@@ -157,18 +93,17 @@ void PropertyUnitTest::getProperty()
void PropertyUnitTest::checkSignals()
{
- resetVariants();
-
QSignalSpy spy1(battery_voltage, SIGNAL(firstSubscriberAppeared(QString)));
QSignalSpy spy2(battery_voltage, SIGNAL(lastSubscriberDisappeared(QString)));
- manager->fakeFirst("Battery.Voltage");
+ QVERIFY(battery_voltage->priv);
+ emit battery_voltage->priv->firstSubscriberAppeared("Battery.Voltage");
QCOMPARE(spy1.count(), 1);
QList<QVariant> args1 = spy1.takeFirst();
QCOMPARE(args1.at(0).toString(), QString("Battery.Voltage"));
- manager->fakeLast("Battery.Voltage");
+ emit battery_voltage->priv->lastSubscriberDisappeared("Battery.Voltage");
QCOMPARE(spy2.count(), 1);
QList<QVariant> args2 = spy2.takeFirst();
@@ -177,57 +112,52 @@ void PropertyUnitTest::checkSignals()
void PropertyUnitTest::setGetBool()
{
- resetVariants();
battery_is_charging->setValue(true);
- QCOMPARE(*lastVariantSet, QVariant(true));
QCOMPARE(battery_is_charging->value(), QVariant(true));
+ QCOMPARE(battery_is_charging->priv->value, QVariant(true));
}
void PropertyUnitTest::setGetInt()
{
- resetVariants();
battery_voltage->setValue(666);
- QCOMPARE(*lastVariantSet, QVariant(666));
QCOMPARE(battery_voltage->value(), QVariant(666));
+ QCOMPARE(battery_voltage->priv->value, QVariant(666));
QVERIFY(battery_voltage->isSet());
}
void PropertyUnitTest::setGetDouble()
{
- resetVariants();
battery_voltage->setValue(0.456);
- QCOMPARE(*lastVariantSet, QVariant(0.456));
QCOMPARE(battery_voltage->value(), QVariant(0.456));
+ QCOMPARE(battery_voltage->priv->value, QVariant(0.456));
QVERIFY(battery_voltage->isSet());
}
void PropertyUnitTest::setGetString()
{
- resetVariants();
battery_voltage->setValue(QString("Hello!"));
- QCOMPARE(*lastVariantSet, QVariant(QString("Hello!")));
QCOMPARE(battery_voltage->value(), QVariant("Hello!"));
+ QCOMPARE(battery_voltage->priv->value, QVariant("Hello!"));
QVERIFY(battery_voltage->isSet());
}
void PropertyUnitTest::setGetQVariant()
{
- resetVariants();
battery_voltage->setValue(QVariant(123));
- QCOMPARE(*lastVariantSet, QVariant(123));
QCOMPARE(battery_voltage->value(), QVariant(123));
+ QCOMPARE(battery_voltage->priv->value, QVariant(123));
QVERIFY(battery_voltage->isSet());
}
void PropertyUnitTest::unset()
{
- resetVariants();
- QVERIFY(battery_voltage->isSet() == false);
battery_voltage->setValue(QVariant(123));
QVERIFY(battery_voltage->isSet());
QCOMPARE(battery_voltage->value(), QVariant(123));
+ QCOMPARE(battery_voltage->priv->value, QVariant(123));
battery_voltage->unsetValue();
QCOMPARE(battery_voltage->value(), QVariant());
+ QCOMPARE(battery_voltage->priv->value, QVariant());
QVERIFY(!battery_voltage->isSet());
}
diff --git a/libcontextprovider/unit-tests/property/service.h b/libcontextprovider/unit-tests/property/service.h
index cbb14c34..4947df02 100644
--- a/libcontextprovider/unit-tests/property/service.h
+++ b/libcontextprovider/unit-tests/property/service.h
@@ -24,7 +24,6 @@
namespace ContextProvider {
class Property;
-class Manager;
class Service : public QObject
{
diff --git a/libcontextprovider/unit-tests/service/Makefile.am b/libcontextprovider/unit-tests/service/Makefile.am
index 8e9d8ef8..a09a7778 100644
--- a/libcontextprovider/unit-tests/service/Makefile.am
+++ b/libcontextprovider/unit-tests/service/Makefile.am
@@ -1,7 +1,7 @@
check_PROGRAMS = serviceunittest
# test's sources
-serviceunittest_SOURCES = serviceunittest.cpp manager.h manageradaptor.h property.h servicebackend.h
+serviceunittest_SOURCES = serviceunittest.cpp servicebackend.h
COVERAGE_FILES = service.cpp
@@ -9,7 +9,7 @@ COVERAGE_FILES = service.cpp
# tests.am is using +=, so we have to set a value here for these four always
AM_CXXFLAGS = $(QtCore_CFLAGS) $(QtDBus_CFLAGS)
AM_LDFLAGS = $(QtCore_LIBS) $(QtDBus_LIBS)
-FROM_SOURCE = loggingfeatures.h service.cpp service.h queuedinvoker.h queuedinvoker.cpp
+FROM_SOURCE = loggingfeatures.h service.cpp service.h
FROM_SOURCE_DIR = $(srcdir)/../../src
LDADD =
include $(top_srcdir)/am/tests.am
diff --git a/libcontextprovider/unit-tests/service/servicebackend.h b/libcontextprovider/unit-tests/service/servicebackend.h
index d158d5be..8233abe4 100644
--- a/libcontextprovider/unit-tests/service/servicebackend.h
+++ b/libcontextprovider/unit-tests/service/servicebackend.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Nokia Corporation.
+ * Copyright (C) 2009 Nokia Corporation.
*
* Contact: Marius Vollmer <marius.vollmer@nokia.com>
*
@@ -24,14 +24,11 @@
#include <QObject>
#include <QString>
-#include <QStringList>
#include <QDBusConnection>
-#include <QHash>
#include <QVariant>
namespace ContextProvider {
-class Manager;
class ServiceBackend : QObject
{
@@ -39,7 +36,6 @@ class ServiceBackend : QObject
public:
explicit ServiceBackend(QDBusConnection connection, const QString &busName = "");
- Manager *manager();
static ServiceBackend *defaultServiceBackend;
void setAsDefault();
@@ -52,6 +48,8 @@ public:
static ServiceBackend* instance(QDBusConnection connection);
static ServiceBackend* instance(QDBusConnection::BusType busType,
const QString &busName, bool autoStart = true);
+
+ void setValue(const QString &key, const QVariant &val);
QDBusConnection connection;
};
diff --git a/libcontextprovider/unit-tests/service/serviceunittest.cpp b/libcontextprovider/unit-tests/service/serviceunittest.cpp
index 3247e20b..7659367f 100644
--- a/libcontextprovider/unit-tests/service/serviceunittest.cpp
+++ b/libcontextprovider/unit-tests/service/serviceunittest.cpp
@@ -19,14 +19,13 @@
*
*/
+#include "servicebackend.h" // mocked
+
+#include "service.h" // to be tested
+
#include <QtTest/QtTest>
#include <QtCore>
#include <stdlib.h>
-#include "manager.h"
-#include "service.h"
-#include "servicebackend.h"
-#include "property.h"
-#include "manageradaptor.h"
using namespace ContextProvider;
@@ -39,7 +38,6 @@ int lastState = STATE_UNDEFINED;
QString *lastKey = NULL;
QVariant *lastValue = NULL;
-Manager *lastManager = NULL;
QDBusConnection *lastConnection = NULL;
/* Mocked ServiceBackend */
@@ -52,11 +50,6 @@ ServiceBackend::ServiceBackend(QDBusConnection connection, const QString &busNam
lastState = STATE_UNDEFINED;
}
-Manager* ServiceBackend::manager()
-{
- return new Manager();
-}
-
void ServiceBackend::setAsDefault()
{
defaultServiceBackend = this;
@@ -104,46 +97,12 @@ ServiceBackend* ServiceBackend::instance(QDBusConnection::BusType busType,
return r;
}
-/* Mocked Manager */
-
-Manager::Manager()
-{
-}
-
-void Manager::addKey(const QString &key)
-{
-}
-
-void Manager::setKeyValue(const QString &key, const QVariant &v)
+void ServiceBackend::setValue(const QString &key, const QVariant &v)
{
lastValue = new QVariant(v);
lastKey = new QString(key);
}
-QVariant Manager::getKeyValue(const QString &key)
-{
- return QVariant();
-}
-
-/* Mocked manager adaptor */
-
-ManagerAdaptor::ManagerAdaptor(Manager *m, QDBusConnection *c)
-{
- lastManager = m;
- lastConnection = c;
-}
-
-/* Mocked Property */
-
-void Property::setManager(Manager *)
-{
-}
-
-QString Property::key()
-{
- return "XXX";
-}
-
/* Service unit test */
class ServiceUnitTest : public QObject
@@ -178,7 +137,6 @@ void ServiceUnitTest::cleanup()
lastKey = NULL;
lastValue = NULL;
- lastManager = NULL;
lastConnection = NULL;
}
diff --git a/libcontextprovider/unit-tests/servicebackend/Makefile.am b/libcontextprovider/unit-tests/servicebackend/Makefile.am
index 3ff4d395..0c35652c 100644
--- a/libcontextprovider/unit-tests/servicebackend/Makefile.am
+++ b/libcontextprovider/unit-tests/servicebackend/Makefile.am
@@ -1,7 +1,7 @@
check_PROGRAMS = servicebackendunittest
# test's sources
-servicebackendunittest_SOURCES = servicebackendunittest.cpp manager.h manageradaptor.h
+servicebackendunittest_SOURCES = servicebackendunittest.cpp propertyprivate.h propertyadaptor.h
COVERAGE_FILES = servicebackend.cpp
diff --git a/libcontextprovider/unit-tests/servicebackend/propertyadaptor.h b/libcontextprovider/unit-tests/servicebackend/propertyadaptor.h
new file mode 100644
index 00000000..393bf478
--- /dev/null
+++ b/libcontextprovider/unit-tests/servicebackend/propertyadaptor.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008, 2009 Nokia Corporation.
+ *
+ * Contact: Marius Vollmer <marius.vollmer@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+// This is a mock implementation
+
+#ifndef PROPERTYADAPTOR_H
+#define PROPERTYADAPTOR_H
+
+#include <QObject>
+#include <QDBusConnection>
+#include <QSet>
+#include <QString>
+
+namespace ContextProvider {
+
+class PropertyPrivate;
+
+class PropertyAdaptor : public QObject
+{
+ Q_OBJECT
+
+public:
+ PropertyAdaptor(PropertyPrivate* property, QDBusConnection *connection);
+ QString objectPath() const;
+ void forgetClients();
+};
+
+} // namespace ContextProvider
+
+#endif
diff --git a/libcontextprovider/unit-tests/service/property.h b/libcontextprovider/unit-tests/servicebackend/propertyprivate.h
index 5cb8a6dd..c6a82ff7 100644
--- a/libcontextprovider/unit-tests/service/property.h
+++ b/libcontextprovider/unit-tests/servicebackend/propertyprivate.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 Nokia Corporation.
+ * Copyright (C) 2008 Nokia Corporation.
*
* Contact: Marius Vollmer <marius.vollmer@nokia.com>
*
@@ -19,32 +19,27 @@
*
*/
-#ifndef PROPERTY_H
-#define PROPERTY_H
-
// This is a mock implementation
+#ifndef PROPERTYPRIVATE_H
+#define PROPERTYPRIVATE_H
+
#include <QObject>
+#include <QString>
+#include <QVariant>
+#include <QPair>
namespace ContextProvider {
-class Manager;
-class Service;
+class ServiceBackend;
-class Property : public QObject
+class PropertyPrivate : public QObject
{
Q_OBJECT
-public:
- Property(QString key, QObject* parent = 0);
- Property(Service &service, QString key, QObject* parent = 0);
- void setManager(Manager *);
-
- QString key();
+public:
+ void setValue(const QVariant& v);
-signals:
- void firstSubscriberAppeared(const QString &key);
- void lastSubscriberDisappeared(const QString &key);
};
} // end namespace
diff --git a/libcontextprovider/unit-tests/servicebackend/servicebackendunittest.cpp b/libcontextprovider/unit-tests/servicebackend/servicebackendunittest.cpp
index c1c05640..22cfcc35 100644
--- a/libcontextprovider/unit-tests/servicebackend/servicebackendunittest.cpp
+++ b/libcontextprovider/unit-tests/servicebackend/servicebackendunittest.cpp
@@ -19,58 +19,39 @@
*
*/
+#include "propertyadaptor.h" // mocked
+#include "propertyprivate.h" // mocked
+#include "servicebackend.h" // to be tested
+
#include <QtTest/QtTest>
#include <QtCore>
#include <stdlib.h>
-#include "manager.h"
-#include "servicebackend.h"
-#include "manageradaptor.h"
using namespace ContextProvider;
-struct ContextProvider::ServiceBackendPrivate {
- QDBusConnection::BusType busType;
- QString busName;
- Manager *manager;
- QDBusConnection *connection;
- QDBusConnection *implicitConnection;
- int refCount;
- bool registeredService;
-};
-
-QString *lastKey = NULL;
QVariant *lastValue = NULL;
-Manager *lastManager = NULL;
QDBusConnection *lastConnection = NULL;
+PropertyPrivate* mockProperty;
-/* Mocked manager adaptor */
+// Mock implementations
-ManagerAdaptor::ManagerAdaptor(Manager *m, QDBusConnection *c)
+PropertyAdaptor::PropertyAdaptor(PropertyPrivate*, QDBusConnection*)
{
- lastManager = m;
- lastConnection = c;
}
-/* Mocked Manager */
-
-Manager::Manager()
-{
- lastManager = this;
-}
-void Manager::addKey(const QString &key)
+void PropertyAdaptor::forgetClients()
{
}
-void Manager::setKeyValue(const QString &key, const QVariant &v)
+QString PropertyAdaptor::objectPath() const
{
- lastValue = new QVariant(v);
- lastKey = new QString(key);
+ return QString("/mock/object/path");
}
-QVariant Manager::getKeyValue(const QString &key)
+void PropertyPrivate::setValue(const QVariant& value)
{
- return QVariant();
+ lastValue = new QVariant(value);
}
class ServiceBackendUnitTest : public QObject
@@ -83,8 +64,6 @@ private slots:
void defaults();
void setValue();
void refCouting();
- void manager();
- void startAndStop();
private:
ServiceBackend *serviceBackend;
@@ -95,13 +74,7 @@ private:
// Before each test
void ServiceBackendUnitTest::init()
{
- serviceBackend = new ServiceBackend(QDBusConnection::sessionBus(), "test.com");
-}
-
-void ServiceBackendUnitTest::manager()
-{
- QVERIFY(lastManager != NULL);
- QVERIFY(serviceBackend->manager() == lastManager);
+ serviceBackend = new ServiceBackend(QDBusConnection::sessionBus(), "org.maemo.contextkit.test");
}
void ServiceBackendUnitTest::sanity()
@@ -133,31 +106,14 @@ void ServiceBackendUnitTest::refCouting()
QCOMPARE(serviceBackend->refCount, 0);
}
-void ServiceBackendUnitTest::startAndStop()
-{
- QVERIFY(serviceBackend->connection.objectRegisteredAt("/org/freedesktop/ContextKit/Manager") == 0);
- serviceBackend->start();
-
- QTest::qWait(100);
- QVERIFY(serviceBackend->connection.objectRegisteredAt("/org/freedesktop/ContextKit/Manager") != 0);
- serviceBackend->stop();
- serviceBackend->start();
- QTest::qWait(100);
- QVERIFY(serviceBackend->connection.objectRegisteredAt("/org/freedesktop/ContextKit/Manager") != 0);
-
- QTest::qWait(100);
- serviceBackend->stop();
- QVERIFY(serviceBackend->connection.objectRegisteredAt("/org/freedesktop/ContextKit/Manager") == 0);
-
- serviceBackend->stop();
- QTest::qWait(100);
- QVERIFY(serviceBackend->connection.objectRegisteredAt("/org/freedesktop/ContextKit/Manager") == 0);
-}
-
void ServiceBackendUnitTest::setValue()
{
+ mockProperty = new PropertyPrivate();
+ qDebug() << "add prop";
+ serviceBackend->addProperty("Battery.ChargeLevel", mockProperty);
+ qDebug() << "set val";
serviceBackend->setValue("Battery.ChargeLevel", 99);
- QCOMPARE(*lastKey, QString("Battery.ChargeLevel"));
+ qDebug() << "done";
QCOMPARE(lastValue->toInt(), 99);
}
diff --git a/libcontextsubscriber/.gitignore b/libcontextsubscriber/.gitignore
index 3ba1ffed..616c4958 100644
--- a/libcontextsubscriber/.gitignore
+++ b/libcontextsubscriber/.gitignore
@@ -42,6 +42,7 @@ coverage/
# Other binaries
/cli/context-listen
+/cls/context-ls
/update-tool/update-tool
/update-contextkit-providers/update-contextkit-providers
diff --git a/libcontextsubscriber/Makefile.am b/libcontextsubscriber/Makefile.am
index 35609d42..435fd078 100644
--- a/libcontextsubscriber/Makefile.am
+++ b/libcontextsubscriber/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = src unit-tests customer-tests multithreading-tests cli cls update-contextkit-providers doc man
+SUBDIRS = src unit-tests customer-tests cli cls update-contextkit-providers doc man
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = contextsubscriber-1.0.pc
diff --git a/libcontextsubscriber/cli/Makefile.am b/libcontextsubscriber/cli/Makefile.am
index 43fe4c95..21f6b543 100644
--- a/libcontextsubscriber/cli/Makefile.am
+++ b/libcontextsubscriber/cli/Makefile.am
@@ -13,13 +13,10 @@ AM_CXXFLAGS += -I$(srcdir)/../src \
context_listen_LDADD = ../src/libcontextsubscriber.la $(top_builddir)/common/libcommon.la
-../src/libcontextsubscriber.la:
+../src/libcontextsubscriber.la: FORCE
$(MAKE) -C ../src libcontextsubscriber.la
-$(top_builddir)/common/libcommon.la:
- $(MAKE) -C $(top_builddir)/common libcommon.la
-
-.PHONY: ../src/libcontextsubscriber.la $(top_builddir)/common/libcommon.la
+.PHONY: FORCE
# moccing
nodist_context_listen_SOURCES = mocs.cpp
diff --git a/libcontextsubscriber/cls/Makefile.am b/libcontextsubscriber/cls/Makefile.am
index 82fc46ce..72ad14e4 100644
--- a/libcontextsubscriber/cls/Makefile.am
+++ b/libcontextsubscriber/cls/Makefile.am
@@ -10,14 +10,10 @@ AM_CXXFLAGS += -I$(srcdir)/../src \
-I$(top_srcdir)/common
context_ls_LDADD = ../src/libcontextsubscriber.la $(top_builddir)/common/libcommon.la
-
-../src/libcontextsubscriber.la:
+../src/libcontextsubscriber.la: FORCE
$(MAKE) -C ../src libcontextsubscriber.la
-$(top_builddir)/common/libcommon.la:
- $(MAKE) -C $(top_builddir)/common libcommon.la
-
-.PHONY: ../src/libcontextsubscriber.la $(top_builddir)/common/libcommon.la
+.PHONY: FORCE
# moccing
# nodist_context_ls_SOURCES = mocs.cpp
diff --git a/libcontextsubscriber/customer-tests/asynchronicity/rapidchanges.py b/libcontextsubscriber/customer-tests/asynchronicity/rapidchanges.py
index f58aa887..cdb6d345 100755
--- a/libcontextsubscriber/customer-tests/asynchronicity/rapidchanges.py
+++ b/libcontextsubscriber/customer-tests/asynchronicity/rapidchanges.py
@@ -66,7 +66,9 @@ class RapidChanges(unittest.TestCase):
context_client.resume()
# /.^/ is a regexp that doesn't match anything
context_client.expect(CLTool.STDOUT, ".^", 3, wantdump=False)
- self.assertEqual(context_client.last_output, "test.fast = int:54\n",
+ if context_client.last_output != "test.fast = int:54\n":
+ context_client.printio()
+ self.assert_(False,
"expected a single valueChanged")
provider_fast.close()
diff --git a/libcontextsubscriber/customer-tests/commander/commander_disabled.py b/libcontextsubscriber/customer-tests/commander/commander_disabled.py
index 5e3a6648..0fffd634 100755
--- a/libcontextsubscriber/customer-tests/commander/commander_disabled.py
+++ b/libcontextsubscriber/customer-tests/commander/commander_disabled.py
@@ -46,9 +46,10 @@ class CommanderDisabled(unittest.TestCase):
commander.expect(CLTool.STDOUT, "Added", 10) # wait for it
os.environ["CONTEXT_CLI_IGNORE_COMMANDER"] = ""
listen = CLTool("context-listen", "test.int")
+ listen.expect(CLTool.STDERR, "Available commands", 10) # wait for it
self.assert_(listen.expect(CLTool.STDOUT,
CLTool.wanted("test.int", "int", "42"),
- 1),
+ 3),
"Provider provided value is wrong")
def runTests():
diff --git a/libcontextsubscriber/customer-tests/commander/commander_nonexistent.py b/libcontextsubscriber/customer-tests/commander/commander_nonexistent.py
index e892a7fd..6bdb8295 100755
--- a/libcontextsubscriber/customer-tests/commander/commander_nonexistent.py
+++ b/libcontextsubscriber/customer-tests/commander/commander_nonexistent.py
@@ -44,6 +44,8 @@ class CommanderNonExistent(unittest.TestCase):
provider.send("dump")
self.assert_(provider.expect(CLTool.STDOUT, "Wrote", 10)) # wait for it
listen = CLTool("context-listen", "test.int", "test.string")
+ listen.expect(CLTool.STDERR, "Available commands", 10) # wait for starting
+
commander = CLTool("context-provide", "--v2")
commander.send("add string test.int foobar")
commander.send("add string test.string barfoo")
diff --git a/libcontextsubscriber/customer-tests/registry/registry.py b/libcontextsubscriber/customer-tests/registry/registry.py
index 629946f3..12dbfbe0 100755
--- a/libcontextsubscriber/customer-tests/registry/registry.py
+++ b/libcontextsubscriber/customer-tests/registry/registry.py
@@ -67,7 +67,7 @@ class PrintingProperties(unittest.TestCase):
self.assert_(info_client.expectAll(CLTool.STDOUT,
expected_results,
- 1),
+ 10),
"Bad introspection result from context-ls")
def runTests():
diff --git a/libcontextsubscriber/customer-tests/subscription/multiprovider.py b/libcontextsubscriber/customer-tests/subscription/multiprovider.py
new file mode 100755
index 00000000..02013797
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/subscription/multiprovider.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+##
+## Copyright (C) 2008, 2009 Nokia. All rights reserved.
+##
+## Contact: Marius Vollmer <marius.vollmer@nokia.com>
+##
+## This library is free software; you can redistribute it and/or
+## modify it under the terms of the GNU Lesser General Public License
+## version 2.1 as published by the Free Software Foundation.
+##
+## This library is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+## Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public
+## License along with this library; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+## 02110-1301 USA
+
+import sys
+import os
+import unittest
+from ContextKit.cltool import CLTool
+
+class MultiProvider(unittest.TestCase):
+ def testMultipleProviders(self):
+ """
+ Description
+ This test verifies correct client behavior in the presence of
+ multiple providers.
+
+ Steps
+ 1. starts up a client
+ 2. starts two providers (X and Y) providing the same P property
+ 3. X sets P to V1 and verifies that the client got it
+ 4. Y sets P to V2 and likewise verifies in the client
+ 5. Y sets P to NULL, the client verifies that P goes back to V1
+ 6. Y sets P to V3, the client verifies P == V3
+ 7. Y is removed from the registry, client verifies that P == V1
+ 8. X is removed from the registry, client verifies that P == NULL
+ """
+ client = CLTool("context-listen", "test.prop")
+ client.expect(CLTool.STDERR, "Available commands", 3)
+
+ provider_x = CLTool("context-provide", "--v2", "test.X",
+ "int", "test.prop", "44")
+ provider_x.send("dump x.context")
+ provider_x.expect(CLTool.STDOUT, "Wrote", 10)
+
+ provider_y = CLTool("context-provide", "--v2", "test.Y",
+ "int", "test.prop", "22")
+ provider_y.send("dump y.context")
+ provider_y.expect(CLTool.STDOUT, "Wrote", 10)
+
+ provider_x.send("test.prop = 55");
+ provider_x.expect(CLTool.STDOUT, "Setting key", 10)
+ self.assert_(client.expect(CLTool.STDOUT,
+ CLTool.wanted("test.prop", "int", "55"),
+ 3))
+
+ provider_y.send("test.prop = 77");
+ provider_y.expect(CLTool.STDOUT, "Setting key", 10)
+ self.assert_(client.expect(CLTool.STDOUT,
+ CLTool.wanted("test.prop", "int", "77"),
+ 3))
+
+ provider_y.send("unset test.prop");
+ provider_y.expect(CLTool.STDOUT, "Setting key", 10)
+ self.assert_(client.expect(CLTool.STDOUT,
+ CLTool.wanted("test.prop", "int", "55"),
+ 3))
+
+ provider_y.send("test.prop = 99");
+ provider_y.expect(CLTool.STDOUT, "Setting key", 10)
+ self.assert_(client.expect(CLTool.STDOUT,
+ CLTool.wanted("test.prop", "int", "99"),
+ 3))
+
+ provider_y.close()
+ os.unlink("y.context")
+ self.assert_(client.expect(CLTool.STDOUT,
+ CLTool.wanted("test.prop", "int", "55"),
+ 3))
+
+ provider_x.close()
+ os.unlink("x.context")
+ self.assert_(client.expect(CLTool.STDOUT,
+ CLTool.wantedUnknown("test.prop"),
+ 3))
+ client.close()
+
+def runTests():
+ suiteInstallation = unittest.TestLoader().loadTestsFromTestCase(MultiProvider)
+ result = unittest.TextTestRunner(verbosity=2).run(suiteInstallation)
+ return len(result.errors + result.failures)
+
+if __name__ == "__main__":
+ sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1)
+ sys.exit(runTests())
diff --git a/libcontextsubscriber/customer-tests/subscription/multiprovider2.py b/libcontextsubscriber/customer-tests/subscription/multiprovider2.py
new file mode 100755
index 00000000..70e89a8f
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/subscription/multiprovider2.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+##
+## Copyright (C) 2008, 2009 Nokia. All rights reserved.
+##
+## Contact: Marius Vollmer <marius.vollmer@nokia.com>
+##
+## This library is free software; you can redistribute it and/or
+## modify it under the terms of the GNU Lesser General Public License
+## version 2.1 as published by the Free Software Foundation.
+##
+## This library is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+## Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public
+## License along with this library; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+## 02110-1301 USA
+
+import sys
+import os
+import unittest
+import time
+from ContextKit.cltool import CLTool
+
+class MultiProvider(unittest.TestCase):
+ def tearDown(self):
+ try:
+ os.unlink("x.context")
+ except:
+ pass
+ try:
+ os.unlink("y.context")
+ except:
+ pass
+
+ def testMultipleProviders2(self):
+ """
+ Description
+ This test verifies correct client behavior in the presence
+ of multiple providers. In this test we always start the
+ client after the providers have already been started and
+ values have been set.
+
+ Steps
+ 1. starts two providers (X and Y) providing the same P property
+ 2. X sets P to V1
+ 3. Y sets P to V2
+ 4. starts a client and check that value for P is V2
+ 5. starts a client with different provider order and
+ checks that value for P is still V2
+ """
+ # please reenable this test when new protocol on provider side is merged!
+ return
+
+ provider_x = CLTool("context-provide", "--v2", "test.x",
+ "int", "test.prop", "44")
+ provider_x.send("dump x.context")
+ provider_x.expect(CLTool.STDOUT, "Wrote", 10)
+
+ provider_y = CLTool("context-provide", "--v2", "test.y",
+ "int", "test.prop", "22")
+ provider_y.send("dump y.context")
+ provider_y.expect(CLTool.STDOUT, "Wrote", 10)
+
+ client = CLTool("context-listen")
+ client.expect(CLTool.STDERR, "Available commands", 3)
+ provider_x.send("sleep 2")
+ provider_x.expect(CLTool.STDOUT, "Sleeping", 10)
+ client.send("n test.prop")
+
+ time.sleep(4)
+
+ client.send("value test.prop")
+ self.assert_(client.expect(CLTool.STDOUT,
+ "\nvalue: int:22\n",
+ 3))
+ client.close()
+ provider_x.close()
+ provider_y.close()
+
+def runTests():
+ suiteInstallation = unittest.TestLoader().loadTestsFromTestCase(MultiProvider)
+ result = unittest.TextTestRunner(verbosity=2).run(suiteInstallation)
+ return len(result.errors + result.failures)
+
+if __name__ == "__main__":
+ sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1)
+ sys.exit(runTests())
diff --git a/libcontextsubscriber/customer-tests/subscription/subscription.py b/libcontextsubscriber/customer-tests/subscription/subscription.py
index 91e304bd..62b0c0c5 100755
--- a/libcontextsubscriber/customer-tests/subscription/subscription.py
+++ b/libcontextsubscriber/customer-tests/subscription/subscription.py
@@ -418,6 +418,7 @@ class MultipleSubscribers(unittest.TestCase):
self.context_client4.expect(CLTool.STDERR, "Available commands", 10) # wait for it
def tearDown(self):
+ self.flexiprovider.send("exit")
self.flexiprovider.close()
self.context_client1.close()
self.context_client2.close()
diff --git a/libcontextsubscriber/customer-tests/testplugins/timeplugin1/Makefile.am b/libcontextsubscriber/customer-tests/testplugins/timeplugin1/Makefile.am
index 1bfee787..9f8f97a5 100644
--- a/libcontextsubscriber/customer-tests/testplugins/timeplugin1/Makefile.am
+++ b/libcontextsubscriber/customer-tests/testplugins/timeplugin1/Makefile.am
@@ -19,13 +19,13 @@ AM_CXXFLAGS = -I$(top_srcdir)/common \
contextsubscribertime1_la_LDFLAGS = -avoid-version -module
-$(top_builddir)/common/libcommon.a:
+$(top_builddir)/common/libcommon.a: FORCE
$(MAKE) -C $(top_builddir)/common libcommon.a
LIBS += $(CDB_LIBS) $(QtCore_LIBS) $(QtDBus_LIBS)
contextsubscribertime1_la_LIBADD=$(top_builddir)/common/libcommon.la
-.PHONY: $(top_builddir)/common/libcommon.la
+.PHONY: FORCE
# moccing
nodist_contextsubscribertime1_la_SOURCES = mocs.cpp
diff --git a/libcontextsubscriber/customer-tests/testplugins/timeplugin2/Makefile.am b/libcontextsubscriber/customer-tests/testplugins/timeplugin2/Makefile.am
index 537159b0..fdbc492b 100644
--- a/libcontextsubscriber/customer-tests/testplugins/timeplugin2/Makefile.am
+++ b/libcontextsubscriber/customer-tests/testplugins/timeplugin2/Makefile.am
@@ -19,13 +19,13 @@ AM_CXXFLAGS = -I$(top_srcdir)/common \
contextsubscribertime2_la_LDFLAGS = -avoid-version -module
-$(top_builddir)/common/libcommon.a:
+$(top_builddir)/common/libcommon.a: FORCE
$(MAKE) -C $(top_builddir)/common libcommon.a
LIBS += $(CDB_LIBS) $(QtCore_LIBS) $(QtDBus_LIBS)
contextsubscribertime2_la_LIBADD=$(top_builddir)/common/libcommon.la
-.PHONY: $(top_builddir)/common/libcommon.la
+.PHONY: FORCE
# moccing
nodist_contextsubscribertime2_la_SOURCES = mocs.cpp
diff --git a/libcontextsubscriber/customer-tests/tests.xml b/libcontextsubscriber/customer-tests/tests.xml
index 91823244..5c114768 100644
--- a/libcontextsubscriber/customer-tests/tests.xml
+++ b/libcontextsubscriber/customer-tests/tests.xml
@@ -82,7 +82,20 @@
<case name="libsub016" description="Plugin loading of libcontextsubscriber" requirement="" timeout="20">
<step expected_result="0">. /tmp/session_bus_address.user;export CONTEXT_PROVIDERS=.; cd /usr/share/libcontextsubscriber-tests/pluginchanging;python /usr/share/libcontextsubscriber-tests/pluginchanging/pluginchanging.py</step>
</case>
-
+ <case name="libsub017" description="Multiple providers"
+ requirement="" timeout="20">
+ <step expected_result="0">. /tmp/session_bus_address.user;export CONTEXT_PROVIDERS=.; cd /usr/share/libcontextsubscriber-tests/subscription; python multiprovider.py</step>
+ </case>
+ <case name="libsub018" description="Rapid changes"
+ requirement="" timeout="20">
+ <step expected_result="0">. /tmp/session_bus_address.user;export CONTEXT_PROVIDERS=.; cd /usr/share/libcontextsubscriber-tests/asynchronicity; python rapidchanges.py</step>
+ </case>
+ <!-- Please add this as soon as the provider library supports the new protocol
+ <case name="libsub019" description="Multiprovider timestamp"
+ requirement="" timeout="20">
+ <step expected_result="0">. /tmp/session_bus_address.user;export CONTEXT_PROVIDERS=.; cd /usr/share/libcontextsubscriber-tests/subscription; python multiprovider2.py</step>
+ </case>
+ -->
<environments>
<scratchbox>false</scratchbox>
<hardware>true</hardware>
diff --git a/libcontextsubscriber/doc/Makefile.am b/libcontextsubscriber/doc/Makefile.am
index 627b7778..1274e3c6 100644
--- a/libcontextsubscriber/doc/Makefile.am
+++ b/libcontextsubscriber/doc/Makefile.am
@@ -4,7 +4,9 @@ endif
DOXYCFG = $(srcdir)/doxy.cfg
-doxygen:
+doxygen: html/index.html
+
+html/index.html: ../src/*.cpp ../src/*.h
@if test x$(srcdir) = x. ; then \
echo srcdir=$(srcdir) $(DOXYGEN) $(DOXYCFG); \
srcdir=$(srcdir) $(DOXYGEN) $(DOXYCFG); \
diff --git a/libcontextsubscriber/multithreading-tests/wait-for-subscription-thread/Makefile.am b/libcontextsubscriber/multithreading-tests/wait-for-subscription-thread/Makefile.am
deleted file mode 100644
index 6e80e6d8..00000000
--- a/libcontextsubscriber/multithreading-tests/wait-for-subscription-thread/Makefile.am
+++ /dev/null
@@ -1,17 +0,0 @@
-noinst_PROGRAMS = run-test
-run_test_SOURCES = main.cpp thread.h
-
-AM_CXXFLAGS = $(QtCore_CFLAGS)
-LIBS += $(QtCore_LIBS)
-
-# library dependency hack for seamless make in cli/
-AM_CXXFLAGS += -I$(srcdir)/../../src
-run_test_LDADD = ../../src/libcontextsubscriber.la
-../../src/libcontextsubscriber.la:
- $(MAKE) -C ../../src libcontextsubscriber.la
-.PHONY: ../../src/libcontextsubscriber.la
-
-# moccing
-nodist_run_test_SOURCES = mocs.cpp
-QT_TOMOC = $(filter %.h, $(run_test_SOURCES))
-include $(top_srcdir)/am/qt.am
diff --git a/libcontextsubscriber/src/Makefile.am b/libcontextsubscriber/src/Makefile.am
index e72bf620..05e5dc1d 100644
--- a/libcontextsubscriber/src/Makefile.am
+++ b/libcontextsubscriber/src/Makefile.am
@@ -12,7 +12,7 @@ libcontextsubscriber_la_SOURCES = contextproperty.cpp \
handlesignalrouter.h queuedinvoker.cpp queuedinvoker.h \
loggingfeatures.h contextkitplugin.h contextkitplugin.cpp \
iproviderplugin.h contextproviderinfo.h nanoxml.h nanoxml.cpp \
- asyncdbusinterface.cpp
+ asyncdbusinterface.cpp timedvalue.h
includecontextsubscriberdir=$(includedir)/contextsubscriber
includecontextsubscriber_HEADERS = contextproperty.h \
@@ -26,13 +26,13 @@ AM_CXXFLAGS = -I$(top_srcdir)/common \
'-DDEFAULT_CONTEXT_CORE_DECLARATIONS="@datadir@/contextkit/core.context"' \
'-DCONTEXT_LOG_MODULE_NAME="libcontextsubscriber"'
-$(top_builddir)/common/libcommon.la:
+$(top_builddir)/common/libcommon.la: FORCE
$(MAKE) -C $(top_builddir)/common libcommon.la
LIBS += $(CDB_LIBS) $(QtCore_LIBS) $(QtXml_LIBS) $(QtDBus_LIBS)
libcontextsubscriber_la_LIBADD=$(top_builddir)/common/libcommon.la
-.PHONY: $(top_builddir)/common/libcommon.la
+.PHONY: FORCE
# moccing
nodist_libcontextsubscriber_la_SOURCES = mocs.cpp
diff --git a/libcontextsubscriber/src/contextkitplugin.cpp b/libcontextsubscriber/src/contextkitplugin.cpp
index a4ed0738..ad34ac56 100644
--- a/libcontextsubscriber/src/contextkitplugin.cpp
+++ b/libcontextsubscriber/src/contextkitplugin.cpp
@@ -24,7 +24,9 @@
#include "subscriberinterface.h"
#include "sconnect.h"
#include <QStringList>
+#include <QDBusPendingCall>
#include <QTimer>
+#include <QDBusPendingReply>
/// Creates a new instance, the service to connect to has to be passed
/// in \c constructionString in the format <tt>[session|dbus]:servicename</tt>.
@@ -53,6 +55,34 @@ namespace ContextSubscriber {
const QString ContextKitPlugin::managerPath = "/org/freedesktop/ContextKit/Manager";
const QString ContextKitPlugin::managerIName = "org.freedesktop.ContextKit.Manager";
+const QString ContextKitPlugin::propertyIName = "org.maemo.contextkit.Property";
+const QString ContextKitPlugin::corePrefix = "/org/maemo/contextkit/";
+
+/// Converts a key name to a protocol level object path. There is a
+/// distinction, because core properties have the form
+/// <tt>/org/maemo/contextkit/Screen/TopEdge</tt> on D-Bus level, but
+/// on higher levels they are <tt>Screen.TopEdge</tt>. Non-core
+/// properties should simply have a name like
+/// /com/nokia/modem/Specific/Feature, so they can be used as object
+/// paths without further conversions.
+QString ContextKitPlugin::keyToPath(QString key)
+{
+ if (key.startsWith("/"))
+ return key;
+
+ return corePrefix + key.replace('.', '/');
+}
+
+/// Inverse of \c keyToPath.
+QString ContextKitPlugin::pathToKey(QString path)
+{
+ if (path.startsWith(corePrefix)) {
+ QString key = path.mid(corePrefix.size());
+ return key.replace('/', '.');
+ }
+
+ return path;
+}
/// Creates subscriber and manager interface, tries to get a
/// subscriber instance from the manager and starts listening for
@@ -64,6 +94,7 @@ ContextKitPlugin::ContextKitPlugin(const QDBusConnection bus, const QString& bus
connection(new QDBusConnection(bus)),
busName(busName)
{
+ reset();
// Notice if the provider on the dbus comes and goes
sconnect(providerListener, SIGNAL(nameAppeared()),
this, SLOT(onProviderAppeared()));
@@ -77,23 +108,31 @@ ContextKitPlugin::ContextKitPlugin(const QDBusConnection bus, const QString& bus
QMetaObject::invokeMethod(this, "onProviderAppeared", Qt::QueuedConnection);
}
+void ContextKitPlugin::reset()
+{
+ delete(subscriberInterface);
+ subscriberInterface = 0;
+ delete(managerInterface);
+ managerInterface = 0;
+ newProtocol = false;
+ connection->disconnect(busName, "", propertyIName, "ValueChanged",
+ this, SLOT(onNewValueChanged(QList<QVariant>,quint64,QDBusMessage)));
+}
+
/// Gets a new subscriber interface from manager when the provider
/// appears.
void ContextKitPlugin::onProviderAppeared()
{
contextDebug() << "ContextKitPlugin::onProviderAppeared";
- delete subscriberInterface;
- subscriberInterface = 0;
- delete managerInterface;
+ reset();
managerInterface = new AsyncDBusInterface(busName, managerPath, managerIName, *connection, this);
if (!managerInterface->callWithCallback("GetSubscriber",
QList<QVariant>(),
this,
SLOT(onDBusGetSubscriberFinished(QDBusObjectPath)),
SLOT(onDBusGetSubscriberFailed(QDBusError)))) {
- emit failed(QString("Wasn't able to call GetSubscriber on the managerinterface: ") +
- managerInterface->lastError().message());
+ onDBusGetSubscriberFailed(managerInterface->lastError());
}
}
@@ -101,8 +140,7 @@ void ContextKitPlugin::onProviderAppeared()
void ContextKitPlugin::onProviderDisappeared()
{
contextDebug() << "ContextKitPlugin::onProviderDisappeared";
- delete subscriberInterface;
- subscriberInterface = 0;
+ reset();
emit failed("Provider went away");
}
@@ -130,7 +168,21 @@ void ContextKitPlugin::onDBusGetSubscriberFinished(QDBusObjectPath objectPath)
void ContextKitPlugin::onDBusGetSubscriberFailed(QDBusError err)
{
- emit failed("Was unable to get subscriber object on dbus: " + err.message());
+ contextWarning() <<
+ "Trying new protocol, because were not able to get subscriber object on dbus: " +
+ err.message();
+ reset();
+ newProtocol = true;
+
+ // connect to dbus value changes too!
+ connection->connect(busName, "", propertyIName, "ValueChanged",
+ this, SLOT(onNewValueChanged(QList<QVariant>,quint64,QDBusMessage)));
+
+ // We queue the emitting of ready, because if subscribtions are
+ // already scheduled, they all will be tried in response and
+ // (apparently) we can't start a new pendingcall inside the error
+ // callback of an other one without a deadlock.
+ QMetaObject::invokeMethod(this, "ready", Qt::QueuedConnection);
}
/// Signals the Provider that the subscribe is finished.
@@ -150,13 +202,42 @@ void ContextKitPlugin::onDBusSubscribeFailed(QList<QString> keys, QString error)
/// Forwards the subscribe request to the wire.
void ContextKitPlugin::subscribe(QSet<QString> keys)
{
- subscriberInterface->subscribe(keys);
+ if (newProtocol)
+ foreach (QString key, keys) {
+ QDBusPendingCall pc = connection->asyncCall(QDBusMessage::createMethodCall(busName,
+ keyToPath(key),
+ propertyIName,
+ "Subscribe"));
+ PendingSubscribeWatcher *psw = new PendingSubscribeWatcher(pc, key, this);
+ sconnect(psw,
+ SIGNAL(subscribeFinished(QString)),
+ this,
+ SIGNAL(subscribeFinished(QString)));
+ sconnect(psw,
+ SIGNAL(subscribeFailed(QString,QString)),
+ this,
+ SIGNAL(subscribeFailed(QString,QString)));
+ sconnect(psw,
+ SIGNAL(valueChanged(QString,TimedValue)),
+ this,
+ SIGNAL(valueChanged(QString,TimedValue)));
+ }
+ else
+ subscriberInterface->subscribe(keys);
}
/// Forwards the unsubscribe request to the wire.
void ContextKitPlugin::unsubscribe(QSet<QString> keys)
{
- subscriberInterface->unsubscribe(keys);
+ if (newProtocol)
+ foreach (QString key, keys) {
+ connection->asyncCall(QDBusMessage::createMethodCall(busName,
+ keyToPath(key),
+ propertyIName,
+ "Unsubscribe"));
+ }
+ else
+ subscriberInterface->unsubscribe(keys);
}
/// Forwards value changes from the wire to the upper layer (Provider).
@@ -166,4 +247,44 @@ void ContextKitPlugin::onDBusValuesChanged(QMap<QString, QVariant> values)
emit valueChanged(key, values[key]);
}
+static TimedValue createTimedValue(const QList<QVariant> value, quint64 time)
+{
+ if (value.size() == 0)
+ return TimedValue(QVariant(), time);
+ else
+ return TimedValue(value.at(0), time);
+}
+
+void ContextKitPlugin::onNewValueChanged(QList<QVariant> value,
+ quint64 timestamp,
+ QDBusMessage message)
+{
+ emit valueChanged(pathToKey(message.path()), createTimedValue(value,
+ timestamp));
+}
+
+PendingSubscribeWatcher::PendingSubscribeWatcher(const QDBusPendingCall &call,
+ const QString &key,
+ QObject * parent) :
+ QDBusPendingCallWatcher(call, parent), key(key)
+{
+ sconnect(this, SIGNAL(finished(QDBusPendingCallWatcher *)),
+ this, SLOT(onFinished()));
+ sconnect(this, SIGNAL(finished(QDBusPendingCallWatcher *)),
+ this, SLOT(deleteLater()));
+}
+
+void PendingSubscribeWatcher::onFinished()
+{
+ QDBusPendingReply<QList<QVariant>, quint64> reply = *this;
+ if (reply.isError()) {
+ emit subscribeFailed(key, reply.error().message());
+ return;
+ }
+
+ emit valueChanged(key, createTimedValue(reply.argumentAt<0>(),
+ reply.argumentAt<1>()));
+ emit subscribeFinished(key);
+}
+
}
diff --git a/libcontextsubscriber/src/contextkitplugin.h b/libcontextsubscriber/src/contextkitplugin.h
index 5fd3b8d2..7566e3f5 100644
--- a/libcontextsubscriber/src/contextkitplugin.h
+++ b/libcontextsubscriber/src/contextkitplugin.h
@@ -27,8 +27,10 @@
#include "provider.h"
#include "iproviderplugin.h"
#include "asyncdbusinterface.h"
+#include "timedvalue.h"
#include <QString>
#include <QDBusConnection>
+#include <QDBusPendingCallWatcher>
#include <QDBusObjectPath>
#include <QSet>
#include <QVariant>
@@ -39,6 +41,26 @@ extern "C" {
}
namespace ContextSubscriber {
+class PendingSubscribeWatcher : public QDBusPendingCallWatcher
+{
+ Q_OBJECT;
+
+public:
+ PendingSubscribeWatcher(const QDBusPendingCall &call,
+ const QString &key,
+ QObject * parent = 0);
+private slots:
+ void onFinished();
+
+signals:
+ void subscribeFailed(QString, QString);
+ void valueChanged(QString, TimedValue);
+ void subscribeFinished(QString);
+
+private:
+ QString key;
+};
+
class ContextKitPlugin : public IProviderPlugin
{
Q_OBJECT
@@ -58,6 +80,9 @@ signals:
#endif
private slots:
+ void onNewValueChanged(QList<QVariant> value,
+ quint64 timestamp,
+ QDBusMessage message);
void onDBusValuesChanged(QMap<QString, QVariant> values);
void onDBusGetSubscriberFinished(QDBusObjectPath objectPath);
void onDBusGetSubscriberFailed(QDBusError err);
@@ -67,6 +92,11 @@ private slots:
void onProviderDisappeared();
private:
+ static QString keyToPath(QString key);
+ static QString pathToKey(QString key);
+
+ void reset();
+
QMap<QString, QVariant>& mergeNullsWithMap(QMap<QString, QVariant> &map, QStringList nulls) const;
DBusNameListener *providerListener; ///< Listens to provider's (dis)appearance over DBus
@@ -76,9 +106,13 @@ private:
QDBusConnection *connection; ///< The connection to DBus
QString busName; ///< The D-Bus service name of the ContextKit provider connected to
+ bool newProtocol; ///< The provider on D-Bus speaks the new protocol only.
+
static const QString managerIName; ///< org.freedesktop.ContextKit.Manager
static const QString subscriberIName; ///< org.freedesktop.ContextKit.Subscriber
static const QString managerPath; ///< /org/freedesktop/ContextKit/Manager
+ static const QString propertyIName; ///< org.maemo.contextkit.Property
+ static const QString corePrefix; ///< /org/maemo/contextkit/
};
diff --git a/libcontextsubscriber/src/handlesignalrouter.cpp b/libcontextsubscriber/src/handlesignalrouter.cpp
index 8d7da41c..aa9e6372 100644
--- a/libcontextsubscriber/src/handlesignalrouter.cpp
+++ b/libcontextsubscriber/src/handlesignalrouter.cpp
@@ -53,10 +53,10 @@ void HandleSignalRouter::onValueChanged(QString key)
handle->onValueChanged();
}
-void HandleSignalRouter::onSubscribeFinished(QString key)
+void HandleSignalRouter::onSubscribeFinished(Provider *provider, QString key)
{
PropertyHandle* handle = PropertyHandle::instance(key);
- handle->setSubscribeFinished();
+ handle->setSubscribeFinished(provider);
}
} // end namespace
diff --git a/libcontextsubscriber/src/handlesignalrouter.h b/libcontextsubscriber/src/handlesignalrouter.h
index 940ceb4c..59a45c6e 100644
--- a/libcontextsubscriber/src/handlesignalrouter.h
+++ b/libcontextsubscriber/src/handlesignalrouter.h
@@ -28,6 +28,8 @@
namespace ContextSubscriber {
+class Provider;
+
class HandleSignalRouter : public QObject
{
Q_OBJECT
@@ -36,7 +38,7 @@ public:
public slots:
void onValueChanged(QString key);
- void onSubscribeFinished(QString key);
+ void onSubscribeFinished(Provider *provider, QString key);
private:
HandleSignalRouter();
diff --git a/libcontextsubscriber/src/iproviderplugin.h b/libcontextsubscriber/src/iproviderplugin.h
index dcd4342c..d14ad48e 100644
--- a/libcontextsubscriber/src/iproviderplugin.h
+++ b/libcontextsubscriber/src/iproviderplugin.h
@@ -22,6 +22,8 @@
#ifndef IPROVIDERPLUGIN_H
#define IPROVIDERPLUGIN_H
+#include "timedvalue.h"
+
#include <QObject>
#include <QVariant>
@@ -43,8 +45,10 @@ signals:
void ready();
void failed(QString error);
void subscribeFinished(QString key);
+ void subscribeFinished(QString key, TimedValue timedvalue);
void subscribeFailed(QString failedKey, QString error);
void valueChanged(QString key, QVariant value);
+ void valueChanged(QString key, TimedValue timedvalue);
};
typedef IProviderPlugin* (*PluginFactoryFunc)(QString constructionString);
diff --git a/libcontextsubscriber/src/propertyhandle.cpp b/libcontextsubscriber/src/propertyhandle.cpp
index f0348e71..73a88877 100644
--- a/libcontextsubscriber/src/propertyhandle.cpp
+++ b/libcontextsubscriber/src/propertyhandle.cpp
@@ -73,12 +73,8 @@ bool PropertyHandle::typeCheckEnabled = false;
*/
PropertyHandle::PropertyHandle(const QString& key)
- : myProvider(0), myInfo(0), subscribeCount(0), subscribePending(true), myKey(key)
+ : myInfo(0), subscribeCount(0), myKey(key)
{
- // Note: We set subscribePending to true, assuming that the
- // intention of the upper layer is to subscribe construction time.
- // If this intention is changed, changes are needed here as well.
-
// Read the information about the provider. This needs to be
// done before calling updateProvider.
myInfo = new ContextPropertyInfo(myKey, this);
@@ -90,6 +86,9 @@ PropertyHandle::PropertyHandle(const QString& key)
// Start listening for the context commander, and also initiate a
// NameHasOwner check.
+ // Because of the waitForSubscription() feature, we immediately need to
+ // subscribe to the real providers when the commander presence becomes
+ // known. So, these connect()s need to be synchronous (not queued).
sconnect(commanderListener, SIGNAL(nameAppeared()),
this, SLOT(updateProvider()));
sconnect(commanderListener, SIGNAL(nameDisappeared()),
@@ -127,55 +126,43 @@ void PropertyHandle::setTypeCheck(bool typeCheck)
/// renews the subscriptions.
void PropertyHandle::updateProvider()
{
- Provider *newProvider;
+ QList<Provider*> newProviders;
contextDebug() << F_PLUGINS;
if (commandingEnabled && commanderListener->isServicePresent() == DBusNameListener::Present) {
// If commander is present it should be able to override the
// property, so connect to it.
- newProvider = Provider::instance(commanderInfo);
+ newProviders << Provider::instance(commanderInfo);
+ Provider::instance(commanderInfo)->clearValues();
} else {
// The myInfo object doesn't have to be re-created, because it
// just routes the function calls to a registry backend.
- if (myInfo->provided()) {
- // If myInfo knows the current provider which should be
- // connected to, connect to it.
- contextDebug() << F_PLUGINS << "Key exists";
- QList<ContextProviderInfo> providers = myInfo->providers();
- if (providers.size() > 1)
- contextCritical() << "multi-process not implemented yet";
- else if (providers.size() == 0)
- contextCritical() << "property provided() but no providers() is empty";
-
- newProvider = Provider::instance(providers[0]);
- } else {
- // Otherwise we keep the pointer to the old provider.
- // This way, we can still continue communicating with the
- // provider even though the key is no longer in the
- // registry.
- contextDebug() << F_PLUGINS << "Key doesn't exist -> keep old provider info";
-
- newProvider = myProvider;
-
- if (newProvider == 0) {
- // This is the first place where we can know that the
- // provider for this property doesn't exist. We
- // shouldn't block waiting for subscription for such a property.
- subscribePending = false;
- }
- }
+ foreach (ContextProviderInfo info, myInfo->providers())
+ newProviders << Provider::instance(info);
+ contextDebug() << newProviders.size() << "providers for" << myKey;
}
-
- if (newProvider != myProvider && subscribeCount > 0) {
- // The provider has changed and ContextProperty classes are subscribed to this handle.
- // Unsubscribe from the old provider
- if (myProvider) myProvider->unsubscribe(myKey);
- // And subscribe to the new provider
- if (newProvider) subscribePending = newProvider->subscribe(myKey);
+ if (subscribeCount > 0) {
+ // Unsubscribe from old providers and subscribe to the new ones.
+ foreach (Provider *oldprovider, myProviders)
+ oldprovider->unsubscribe(myKey);
+ pendingSubscriptions.clear();
+ foreach (Provider *newprovider, newProviders)
+ if (newprovider->subscribe(myKey))
+ pendingSubscriptions << newprovider;
}
+ myProviders = newProviders;
+ // If all subscriptions succeeded immediately, then we have to trigger
+ // recomputing the value now. Otherwise we rely on the
+ // subscribeFinished signal.
+ if (subscribeCount > 0 && pendingSubscriptions.empty())
+ onValueChanged();
+}
- myProvider = newProvider;
+/// Sets \c subscribePending to false.
+void PropertyHandle::setSubscribeFinished(Provider *provider)
+{
+ pendingSubscriptions.remove(provider);
}
/// Increase the \c subscribeCount of this context property and
@@ -186,8 +173,11 @@ void PropertyHandle::subscribe()
QMutexLocker locker(&subscribeCountLock);
++subscribeCount;
- if (subscribeCount == 1 && myProvider != 0) {
- subscribePending = myProvider->subscribe(myKey);
+ if (subscribeCount == 1) {
+ pendingSubscriptions.clear();
+ foreach (Provider *provider, myProviders)
+ if (provider->subscribe(myKey))
+ pendingSubscriptions << provider;
}
}
@@ -198,18 +188,13 @@ void PropertyHandle::unsubscribe()
{
QMutexLocker locker(&subscribeCountLock);
--subscribeCount;
- if (subscribeCount == 0 && myProvider != 0) {
- subscribePending = false;
- myProvider->unsubscribe(myKey);
+ if (subscribeCount == 0) {
+ pendingSubscriptions.clear();
+ foreach (Provider *provider, myProviders)
+ provider->unsubscribe(myKey);
}
}
-/// Sets \c subscribePending to false.
-void PropertyHandle::setSubscribeFinished()
-{
- subscribePending = false;
-}
-
QString PropertyHandle::key() const
{
return myKey;
@@ -223,7 +208,14 @@ QVariant PropertyHandle::value() const
bool PropertyHandle::isSubscribePending() const
{
- return subscribePending;
+ // We wait until commander presence is unknown ...
+ if (commanderListener->isServicePresent() == DBusNameListener::Unknown)
+ return true;
+ // ... or until we get some value ...
+ if (!myValue.isNull())
+ return false;
+ // ... or all pending subscriptions finished.
+ return pendingSubscriptions.size() != 0;
}
/// Used by the \c HandleSignalRouter to change the value of the
@@ -233,8 +225,22 @@ bool PropertyHandle::isSubscribePending() const
/// valueChanged() signal.
void PropertyHandle::onValueChanged()
{
- // FIXME: implement multiprocess here
- QVariant newValue = myProvider->get(myKey).value;
+ bool found = false;
+ TimedValue latest = QVariant();
+
+ foreach (Provider *provider, myProviders) {
+ TimedValue current = provider->get(myKey);
+ if (current.value.isNull())
+ continue;
+ if (!found) {
+ found = true;
+ latest = current;
+ } else if (latest < current)
+ latest = current;
+ }
+ QVariant newValue;
+ if (found)
+ newValue = latest.value;
if (typeCheckEnabled // type checks enabled
&& !newValue.isNull() // variable is non-null
@@ -253,7 +259,7 @@ void PropertyHandle::onValueChanged()
}
if (!checked) {
- contextCritical() << "Provider error, bad type for " << myKey <<
+ contextCritical() << "Provider error, bad type for " << myKey <<
"wanted:" << myType << "got:" << newValue.typeName();
return;
}
diff --git a/libcontextsubscriber/src/propertyhandle.h b/libcontextsubscriber/src/propertyhandle.h
index a6cfb587..e6f06897 100644
--- a/libcontextsubscriber/src/propertyhandle.h
+++ b/libcontextsubscriber/src/propertyhandle.h
@@ -52,7 +52,7 @@ public:
static PropertyHandle* instance(const QString& key);
void onValueChanged();
- void setSubscribeFinished();
+ void setSubscribeFinished(Provider *provider);
static void ignoreCommander();
static void setTypeCheck(bool typeCheck);
@@ -65,12 +65,11 @@ private slots:
private:
PropertyHandle(const QString& key);
- Provider *myProvider; ///< Provider of this property
+ QSet<Provider*> pendingSubscriptions; ///< Providers pending subscription
+ QList<Provider*> myProviders; ///< Providers of this property
ContextPropertyInfo *myInfo; ///< Metadata for this property
unsigned int subscribeCount; ///< Number of subscribed ContextProperty objects subscribed to this property
QMutex subscribeCountLock;
- bool subscribePending; ///< True when the subscription has been started, but hasn't been finished yet
- /// (used by the waitForSubscription() feature)
QString myKey; ///< Key of this property
mutable QReadWriteLock valueLock;
QVariant myValue; ///< Current value of this property
diff --git a/libcontextsubscriber/src/provider.cpp b/libcontextsubscriber/src/provider.cpp
index ef3ce984..dff7adb4 100644
--- a/libcontextsubscriber/src/provider.cpp
+++ b/libcontextsubscriber/src/provider.cpp
@@ -34,11 +34,6 @@
namespace ContextSubscriber {
-TimedValue::TimedValue(const QVariant &value) : value(value)
-{
- clock_gettime(CLOCK_MONOTONIC, &time);
-}
-
/*!
\class IProviderPlugin
\brief Interface for provider plugins.
@@ -175,6 +170,8 @@ void Provider::constructPlugin()
// Connect the signal of changing values to the class who handles it
HandleSignalRouter* handleSignalRouter = HandleSignalRouter::instance();
+ sconnect(plugin, SIGNAL(valueChanged(QString, TimedValue)),
+ this, SLOT(onPluginValueChanged(QString, TimedValue)));
sconnect(plugin, SIGNAL(valueChanged(QString, QVariant)),
this, SLOT(onPluginValueChanged(QString, QVariant)));
sconnect(this, SIGNAL(valueChanged(QString)),
@@ -185,12 +182,18 @@ void Provider::constructPlugin()
sconnect(plugin, SIGNAL(failed(QString)),
this, SLOT(onPluginFailed(QString)));
+ // The following signals are queued, because a plugin might emit
+ // subscribeFinished() right in the subscribe() call.
+ qRegisterMetaType<TimedValue>("TimedValue");
+ sconnect(plugin, SIGNAL(subscribeFinished(QString, TimedValue)),
+ this, SLOT(onPluginSubscribeFinished(QString, TimedValue)),
+ Qt::QueuedConnection);
sconnect(plugin, SIGNAL(subscribeFinished(QString)),
this, SLOT(onPluginSubscribeFinished(QString)), Qt::QueuedConnection);
sconnect(plugin, SIGNAL(subscribeFailed(QString, QString)),
this, SLOT(onPluginSubscribeFailed(QString, QString)), Qt::QueuedConnection);
- sconnect(this, SIGNAL(subscribeFinished(QString)),
- handleSignalRouter, SLOT(onSubscribeFinished(QString)));
+ sconnect(this, SIGNAL(subscribeFinished(Provider *,QString)),
+ handleSignalRouter, SLOT(onSubscribeFinished(Provider *,QString)));
}
/// Updates \c pluginState to \c READY and requests subscription for
@@ -210,6 +213,13 @@ void Provider::onPluginReady()
handleSubscribes();
}
+/// Clears the cached values for this provider. This is used when the
+/// provider instance is (re)connected to the commander.
+void Provider::clearValues()
+{
+ values.clear();
+}
+
/// Updates \c pluginState to \c FAILED and signals subscribeFinished
/// for keys we are trying to subscribe to.
void Provider::onPluginFailed(QString error)
@@ -229,10 +239,17 @@ void Provider::signalSubscribeFinished(QString key)
{
QMutexLocker lock(&subscribeLock);
if (subscribedKeys.contains(key))
- emit subscribeFinished(key);
+ emit subscribeFinished(this, key);
}
/// Forwards the call to \c signalSubscribeFinished.
+void Provider::onPluginSubscribeFinished(QString key, TimedValue value)
+{
+ signalSubscribeFinished(key);
+ onPluginValueChanged(key, value);
+}
+
+/// Deprecated.
void Provider::onPluginSubscribeFinished(QString key)
{
contextDebug() << key;
@@ -318,7 +335,7 @@ void Provider::handleSubscribes()
contextDebug() << "Plugin init has failed";
if (toSubscribe.size() > 0)
foreach (QString key, toSubscribe)
- emit subscribeFinished(key);
+ emit subscribeFinished(this, key);
toSubscribe.clear();
toUnsubscribe.clear();
break;
@@ -332,6 +349,21 @@ void Provider::handleSubscribes()
/// Forwards the \c newValue for \c key received from the plugin to
/// the upper layers via \c HandleSignalRouter.
+void Provider::onPluginValueChanged(QString key, TimedValue newValue)
+{
+ QMutexLocker lock(&subscribeLock);
+ if (subscribedKeys.contains(key)) {
+ // FIXME: try out if everything works with lock.unlock() here
+ values.insert(key, newValue);
+ emit valueChanged(key);
+ }
+ else
+ contextWarning() << "Received a property not subscribed to:" << key;
+}
+
+/// Deprecated: plugins should use the variant taking a TimedValue.
+/// Forwards the \c newValue for \c key received from the plugin to
+/// the upper layers via \c HandleSignalRouter.
void Provider::onPluginValueChanged(QString key, QVariant newValue)
{
QMutexLocker lock(&subscribeLock);
diff --git a/libcontextsubscriber/src/provider.h b/libcontextsubscriber/src/provider.h
index cacfc4d8..482d678b 100644
--- a/libcontextsubscriber/src/provider.h
+++ b/libcontextsubscriber/src/provider.h
@@ -24,12 +24,12 @@
#include "queuedinvoker.h"
#include "contextproviderinfo.h"
+#include "timedvalue.h"
#include <QObject>
#include <QDBusConnection>
#include <QSet>
#include <QMutex>
-#include <time.h>
class ContextPropertyInfo;
@@ -41,14 +41,6 @@ class DBusNameListener;
class ManagerInterface;
class IProviderPlugin;
-struct TimedValue
-{
- struct timespec time;
- QVariant value;
- TimedValue(const QVariant &value);
-// future bool operator<(const TimedValue &other);
-};
-
class Provider : public QueuedInvoker
{
Q_OBJECT
@@ -58,17 +50,20 @@ public:
bool subscribe(const QString &key);
void unsubscribe(const QString &key);
TimedValue get(const QString &key) const;
+ void clearValues();
signals:
- void subscribeFinished(QString key);
+ void subscribeFinished(Provider *provider, QString key);
void valueChanged(QString key);
private slots:
void onPluginReady();
void onPluginFailed(QString error);
void onPluginSubscribeFinished(QString key);
+ void onPluginSubscribeFinished(QString key, TimedValue value);
void onPluginSubscribeFailed(QString failedKey, QString error);
void onPluginValueChanged(QString key, QVariant newValue);
+ void onPluginValueChanged(QString key, TimedValue newValue);
private:
enum PluginState { INITIALIZING, READY, FAILED };
diff --git a/libcontextsubscriber/src/timedvalue.h b/libcontextsubscriber/src/timedvalue.h
new file mode 100644
index 00000000..15a9a404
--- /dev/null
+++ b/libcontextsubscriber/src/timedvalue.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008, 2009 Nokia Corporation.
+ *
+ * Contact: Marius Vollmer <marius.vollmer@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef TIMEDVALUE_H
+#define TIMEDVALUE_H
+
+#include <time.h>
+#include <QVariant>
+
+namespace ContextSubscriber {
+
+struct TimedValue
+{
+ quint64 time;
+ QVariant value;
+
+ TimedValue() : time(0), value(QVariant())
+ { }
+ TimedValue(const QVariant &value, quint64 time) : time(time), value(value)
+ { }
+ TimedValue(const QVariant &value) : value(value)
+ {
+ struct timespec t;
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ time = t.tv_sec * Q_UINT64_C(1000000000) + t.tv_nsec;
+ }
+ bool operator<(const TimedValue &other)
+ {
+ return time < other.time;
+ }
+};
+
+}
+
+#endif
diff --git a/libcontextsubscriber/unit-tests/handlesignalrouter/propertyhandle.h b/libcontextsubscriber/unit-tests/handlesignalrouter/propertyhandle.h
index bde228f6..24e243e2 100644
--- a/libcontextsubscriber/unit-tests/handlesignalrouter/propertyhandle.h
+++ b/libcontextsubscriber/unit-tests/handlesignalrouter/propertyhandle.h
@@ -31,6 +31,8 @@
namespace ContextSubscriber {
+class Provider;
+
class PropertyHandle : public QObject
{
Q_OBJECT
@@ -38,12 +40,12 @@ class PropertyHandle : public QObject
public:
static PropertyHandle* instance(const QString& key);
void onValueChanged();
- void setSubscribeFinished();
+ void setSubscribeFinished(Provider *);
signals:
// For tests
void onValueChangedCalled(QString);
- void setSubscribeFinishedCalled(QString);
+ void setSubscribeFinishedCalled(Provider *,QString);
public:
PropertyHandle(const QString& key);
diff --git a/libcontextsubscriber/unit-tests/handlesignalrouter/testhandlesignalrouter.cpp b/libcontextsubscriber/unit-tests/handlesignalrouter/testhandlesignalrouter.cpp
index a7073d34..3f674634 100644
--- a/libcontextsubscriber/unit-tests/handlesignalrouter/testhandlesignalrouter.cpp
+++ b/libcontextsubscriber/unit-tests/handlesignalrouter/testhandlesignalrouter.cpp
@@ -78,9 +78,9 @@ void PropertyHandle::onValueChanged()
emit onValueChangedCalled(myKey);
}
-void PropertyHandle::setSubscribeFinished()
+void PropertyHandle::setSubscribeFinished(Provider *prov)
{
- emit setSubscribeFinishedCalled(myKey);
+ emit setSubscribeFinishedCalled(prov, myKey);
}
PropertyHandle* PropertyHandle::instance(const QString& key)
@@ -145,14 +145,14 @@ void HandleSignalRouterUnitTests::routingSignals()
QSignalSpy spy1(mockHandleOne, SIGNAL(onValueChangedCalled(QString)));
QSignalSpy spy2(mockHandleTwo, SIGNAL(onValueChangedCalled(QString)));
QSignalSpy spy3(mockHandleThree, SIGNAL(onValueChangedCalled(QString)));
- QSignalSpy sspy1(mockHandleOne, SIGNAL(setSubscribeFinishedCalled(QString)));
- QSignalSpy sspy2(mockHandleTwo, SIGNAL(setSubscribeFinishedCalled(QString)));
- QSignalSpy sspy3(mockHandleThree, SIGNAL(setSubscribeFinishedCalled(QString)));
+ QSignalSpy sspy1(mockHandleOne, SIGNAL(setSubscribeFinishedCalled(Provider *, QString)));
+ QSignalSpy sspy2(mockHandleTwo, SIGNAL(setSubscribeFinishedCalled(Provider *, QString)));
+ QSignalSpy sspy3(mockHandleThree, SIGNAL(setSubscribeFinishedCalled(Provider *, QString)));
// Test:
// Send a signal to the HandleSignalRouter
handleSignalRouter->onValueChanged("Property.One");
- handleSignalRouter->onSubscribeFinished("Property.One");
+ handleSignalRouter->onSubscribeFinished(0, "Property.One");
// Expected results:
// The mockHandleOne.setValue was called
@@ -163,8 +163,8 @@ void HandleSignalRouterUnitTests::routingSignals()
QCOMPARE(sspy1.count(), 1);
parameters = sspy1.takeFirst();
- QCOMPARE(parameters.size(), 1);
- QCOMPARE(parameters.at(0), QVariant("Property.One"));
+ QCOMPARE(parameters.size(), 2);
+ QCOMPARE(parameters.at(1), QVariant("Property.One"));
// The setValue of other mock handles were not called
QCOMPARE(spy2.count(), 0);
@@ -175,7 +175,7 @@ void HandleSignalRouterUnitTests::routingSignals()
// Test:
// Send a signal to the HandleSignalRouter
handleSignalRouter->onValueChanged("Property.Two");
- handleSignalRouter->onSubscribeFinished("Property.Two");
+ handleSignalRouter->onSubscribeFinished(0, "Property.Two");
// Expected results:
// The mockHandleTwo.setValue was called
@@ -185,8 +185,8 @@ void HandleSignalRouterUnitTests::routingSignals()
QCOMPARE(parameters.size(), 1);
QCOMPARE(parameters.at(0), QVariant("Property.Two"));
parameters = sspy2.takeFirst();
- QCOMPARE(parameters.size(), 1);
- QCOMPARE(parameters.at(0), QVariant("Property.Two"));
+ QCOMPARE(parameters.size(), 2);
+ QCOMPARE(parameters.at(1), QVariant("Property.Two"));
// The setValue of other mock handles were not called
QCOMPARE(spy1.count(), 0);
QCOMPARE(spy3.count(), 0);
diff --git a/libcontextsubscriber/unit-tests/propertyhandle/.gitignore b/libcontextsubscriber/unit-tests/propertyhandle/.gitignore
index a0f647c6..e59a3396 100644
--- a/libcontextsubscriber/unit-tests/propertyhandle/.gitignore
+++ b/libcontextsubscriber/unit-tests/propertyhandle/.gitignore
@@ -5,3 +5,5 @@ logging.cpp
logging.h
loggingfeatures.h
contextproviderinfo.h
+timedvalue.h
+
diff --git a/libcontextsubscriber/unit-tests/propertyhandle/Makefile.am b/libcontextsubscriber/unit-tests/propertyhandle/Makefile.am
index 367b3c30..5fbc5acd 100644
--- a/libcontextsubscriber/unit-tests/propertyhandle/Makefile.am
+++ b/libcontextsubscriber/unit-tests/propertyhandle/Makefile.am
@@ -13,7 +13,8 @@ COVERAGE_FILES = propertyhandle.cpp
AM_CXXFLAGS = $(QtDBus_CFLAGS) '-DDEFAULT_CONTEXT_PROVIDERS="@datadir@/contextkit/providers/"'
AM_LDFLAGS = $(QtDBus_LIBS)
# copy these files from the real source
-FROM_SOURCE = propertyhandle.cpp propertyhandle.h loggingfeatures.h contextproviderinfo.h
+FROM_SOURCE = propertyhandle.cpp propertyhandle.h loggingfeatures.h \
+ contextproviderinfo.h timedvalue.h
FROM_SOURCE_DIR = $(srcdir)/../../src
LDADD =
include $(top_srcdir)/am/tests.am
diff --git a/libcontextsubscriber/unit-tests/propertyhandle/provider.h b/libcontextsubscriber/unit-tests/propertyhandle/provider.h
index 850e6cac..15da4f8c 100644
--- a/libcontextsubscriber/unit-tests/propertyhandle/provider.h
+++ b/libcontextsubscriber/unit-tests/propertyhandle/provider.h
@@ -25,23 +25,15 @@
#define PROVIDER_H
#include "contextproviderinfo.h"
+#include "timedvalue.h"
#include <QObject>
#include <QDBusConnection>
#include <QSet>
#include <QString>
-#include <time.h>
namespace ContextSubscriber {
-struct TimedValue
-{
- struct timespec time;
- QVariant value;
- TimedValue(const QVariant &value);
-// future bool operator<(const TimedValue &other);
-};
-
class Provider : public QObject
{
Q_OBJECT
@@ -51,6 +43,7 @@ public:
bool subscribe(const QString &key);
void unsubscribe(const QString &key);
TimedValue get(const QString &key) const;
+ void clearValues();
signals:
void subscribeFinished(QString key);
diff --git a/libcontextsubscriber/unit-tests/propertyhandle/testpropertyhandle.cpp b/libcontextsubscriber/unit-tests/propertyhandle/testpropertyhandle.cpp
index 9ccbaf8e..44f33264 100644
--- a/libcontextsubscriber/unit-tests/propertyhandle/testpropertyhandle.cpp
+++ b/libcontextsubscriber/unit-tests/propertyhandle/testpropertyhandle.cpp
@@ -100,12 +100,6 @@ Provider* mockProvider;
Provider* mockCommanderProvider;
DBusNameListener* mockDBusNameListener;
-// Mock implementation of TimedValue
-TimedValue::TimedValue(const QVariant &value) : value(value)
-{
- clock_gettime(CLOCK_MONOTONIC, &time);
-}
-
// Mock implementation of the Provider
int Provider::instanceCount = 0;
QStringList Provider::instancePluginNames;
@@ -153,6 +147,11 @@ void Provider::unsubscribe(const QString& key)
unsubscribeProviderNames << myName;
}
+void Provider::clearValues()
+{
+ cachedValue = TimedValue(QVariant());
+}
+
void Provider::setValue(const QString &key, const QVariant &value)
{
cachedValue = TimedValue(value);
@@ -374,7 +373,7 @@ void PropertyHandleUnitTests::subscriptionPendingAndFinished()
QVERIFY(propertyHandle->isSubscribePending());
// finished
- propertyHandle->setSubscribeFinished();
+ propertyHandle->setSubscribeFinished(mockProvider);
// Expected results:
// The subscription is no longer marked as pending
@@ -868,9 +867,12 @@ void PropertyHandleUnitTests::commandingDisabled()
emit mockDBusNameListener->nameAppeared();
// Expected results:
- // The PropertyHandle ignores the Commander
- QCOMPARE(Provider::unsubscribeCount, 0);
- QCOMPARE(Provider::subscribeCount, 0);
+ // The PropertyHandle unsubscribes from and resubscribes to the same key,
+ // practically ignoring commander appearance.
+ QCOMPARE(Provider::unsubscribeCount, 1);
+ QCOMPARE(Provider::subscribeCount, 1);
+ QVERIFY(Provider::subscribeKeys == Provider::unsubscribeKeys);
+ QVERIFY(Provider::subscribeProviderNames == Provider::unsubscribeProviderNames);
// Setup:
// Clear the logs from the subscription
@@ -882,9 +884,11 @@ void PropertyHandleUnitTests::commandingDisabled()
emit mockDBusNameListener->nameDisappeared();
// Expected results:
- // The PropertyHandle ignores the Commander
- QCOMPARE(Provider::unsubscribeCount, 0);
- QCOMPARE(Provider::subscribeCount, 0);
+ // Same thing as above.
+ QCOMPARE(Provider::unsubscribeCount, 1);
+ QCOMPARE(Provider::subscribeCount, 1);
+ QVERIFY(Provider::subscribeKeys == Provider::unsubscribeKeys);
+ QVERIFY(Provider::subscribeProviderNames == Provider::unsubscribeProviderNames);
}
} // end namespace
diff --git a/libcontextsubscriber/unit-tests/provider/.gitignore b/libcontextsubscriber/unit-tests/provider/.gitignore
index 6d1bc189..b9ab0625 100644
--- a/libcontextsubscriber/unit-tests/provider/.gitignore
+++ b/libcontextsubscriber/unit-tests/provider/.gitignore
@@ -6,3 +6,4 @@ sconnect.h
logging.cpp
logging.h
loggingfeatures.h
+timedvalue.h
diff --git a/libcontextsubscriber/unit-tests/provider/Makefile.am b/libcontextsubscriber/unit-tests/provider/Makefile.am
index d672092e..11f664ec 100644
--- a/libcontextsubscriber/unit-tests/provider/Makefile.am
+++ b/libcontextsubscriber/unit-tests/provider/Makefile.am
@@ -15,7 +15,7 @@ AM_CXXFLAGS = $(QtDBus_CFLAGS) \
AM_LDFLAGS = $(QtDBus_LIBS)
# copy these files from the real source
FROM_SOURCE = provider.cpp provider.h iproviderplugin.h \
- loggingfeatures.h contextproviderinfo.h
+ loggingfeatures.h contextproviderinfo.h timedvalue.h
FROM_SOURCE_DIR = $(srcdir)/../../src
LDADD =
include $(top_srcdir)/am/tests.am
diff --git a/libcontextsubscriber/unit-tests/provider/contextkitplugin.h b/libcontextsubscriber/unit-tests/provider/contextkitplugin.h
index 39159c35..5d91b360 100644
--- a/libcontextsubscriber/unit-tests/provider/contextkitplugin.h
+++ b/libcontextsubscriber/unit-tests/provider/contextkitplugin.h
@@ -24,11 +24,13 @@
#ifndef CONTEXTKITPLUGIN_H
#define CONTEXTKITPLUGIN_H
+#include "iproviderplugin.h"
+#include "timedvalue.h"
+
#include <QString>
#include <QDBusConnection>
#include <QSet>
#include <QVariant>
-#include "iproviderplugin.h"
extern "C" {
ContextSubscriber::IProviderPlugin* contextKitPluginFactory(QString constructionString);
@@ -47,8 +49,10 @@ signals:
void ready();
void failed(QString error);
void subscribeFinished(QString key);
+ void subscribeFinished(QString key, TimedValue value);
void subscribeFailed(QString failedKey, QString error);
void valueChanged(QString key, QVariant value);
+ void valueChanged(QString key, TimedValue value);
private:
QSet<QString> subscribeRequested;
diff --git a/libcontextsubscriber/unit-tests/provider/handlesignalrouter.h b/libcontextsubscriber/unit-tests/provider/handlesignalrouter.h
index 070fa440..75ae2628 100644
--- a/libcontextsubscriber/unit-tests/provider/handlesignalrouter.h
+++ b/libcontextsubscriber/unit-tests/provider/handlesignalrouter.h
@@ -38,7 +38,7 @@ public:
public slots:
void onValueChanged(QString key);
- void onSubscribeFinished(QString key);
+ void onSubscribeFinished(Provider *provider, QString key);
};
} // end namespace
diff --git a/libcontextsubscriber/unit-tests/provider/testprovider.cpp b/libcontextsubscriber/unit-tests/provider/testprovider.cpp
index d51c4b0a..c39d45cc 100644
--- a/libcontextsubscriber/unit-tests/provider/testprovider.cpp
+++ b/libcontextsubscriber/unit-tests/provider/testprovider.cpp
@@ -74,7 +74,7 @@ void HandleSignalRouter::onValueChanged(QString key)
{
}
-void HandleSignalRouter::onSubscribeFinished(QString key)
+void HandleSignalRouter::onSubscribeFinished(Provider *provider, QString key)
{
}
@@ -283,17 +283,18 @@ void ProviderUnitTests::pluginSubscriptionFinishes()
emit pluginInstances[conStr]->ready(); // set the plugin to ready
provider->callAllMethodsInQueue();
- QSignalSpy spy(provider, SIGNAL(subscribeFinished(QString)));
+ QSignalSpy spy(provider, SIGNAL(subscribeFinished(Provider *, QString)));
provider->subscribe("test.key1");
provider->subscribe("test.key2");
provider->callAllMethodsInQueue();
emit pluginInstances[conStr]->subscribeFinished("test.key1");
emit pluginInstances[conStr]->subscribeFailed("test.key2", "error");
QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents); // signal delivery is queued
- QCOMPARE((QList<QList<QVariant> >)spy,
- QList<QList<QVariant> >()
- << (QList<QVariant>() << "test.key1")
- << (QList<QVariant>() << "test.key2"));
+ QCOMPARE(spy.size(), 2);
+ QCOMPARE(spy[0].size(), 2);
+ QCOMPARE(spy[1].size(), 2);
+ QCOMPARE(spy[0][1], QVariant("test.key1"));
+ QCOMPARE(spy[1][1], QVariant("test.key2"));
}
void ProviderUnitTests::pluginValueChanges()
diff --git a/libcontextsubscriber/update-contextkit-providers/Makefile.am b/libcontextsubscriber/update-contextkit-providers/Makefile.am
index b0524b7e..4dde0332 100644
--- a/libcontextsubscriber/update-contextkit-providers/Makefile.am
+++ b/libcontextsubscriber/update-contextkit-providers/Makefile.am
@@ -10,9 +10,9 @@ AM_LDFLAGS = $(QtXml_LIBS) $(QtCore_LIBS)
# library dependency hack for seamless make in update-contextkit-providers/
update_contextkit_providers_LDADD = ../src/libcontextsubscriber.la
-../src/libcontextsubscriber.la:
+../src/libcontextsubscriber.la: FORCE
$(MAKE) -C ../src libcontextsubscriber.la
-.PHONY: ../src/libcontextsubscriber.la
+.PHONY: FORCE
# moccing
# Note: enable these 2 lines when there is something to mock
diff --git a/m4/dolt.m4 b/m4/dolt.m4
new file mode 100644
index 00000000..8f94582f
--- /dev/null
+++ b/m4/dolt.m4
@@ -0,0 +1,177 @@
+dnl dolt, a replacement for libtool
+dnl Copyright © 2007-2008 Josh Triplett <josh@freedesktop.org>
+dnl Copying and distribution of this file, with or without modification,
+dnl are permitted in any medium without royalty provided the copyright
+dnl notice and this notice are preserved.
+dnl
+dnl To use dolt, invoke the DOLT macro immediately after the libtool macros.
+dnl Optionally, copy this file into acinclude.m4, to avoid the need to have it
+dnl installed when running autoconf on your project.
+
+AC_DEFUN([DOLT], [
+AC_REQUIRE([AC_CANONICAL_HOST])
+# dolt, a replacement for libtool
+# Josh Triplett <josh@freedesktop.org>
+AC_PATH_PROG(DOLT_BASH, bash)
+AC_MSG_CHECKING([if dolt supports this host])
+dolt_supported=yes
+if test x$DOLT_BASH = x; then
+ dolt_supported=no
+fi
+if test x$GCC != xyes; then
+ dolt_supported=no
+fi
+case $host in
+i?86-*-linux*|x86_64-*-linux*|powerpc-*-linux* \
+|amd64-*-freebsd*|i?86-*-freebsd*|ia64-*-freebsd*)
+ pic_options='-fPIC'
+ ;;
+i?86-apple-darwin*)
+ pic_options='-fno-common'
+ ;;
+*)
+ dolt_supported=no
+ ;;
+esac
+if test x$dolt_supported = xno ; then
+ AC_MSG_RESULT([no, falling back to libtool])
+ LTCOMPILE='$(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(COMPILE)'
+ LTCXXCOMPILE='$(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXXCOMPILE)'
+else
+ AC_MSG_RESULT([yes, replacing libtool])
+
+dnl Start writing out doltcompile.
+ cat <<__DOLTCOMPILE__EOF__ >doltcompile
+#!$DOLT_BASH
+__DOLTCOMPILE__EOF__
+ cat <<'__DOLTCOMPILE__EOF__' >>doltcompile
+args=("$[]@")
+for ((arg=0; arg<${#args@<:@@@:>@}; arg++)) ; do
+ if test x"${args@<:@$arg@:>@}" = x-o ; then
+ objarg=$((arg+1))
+ break
+ fi
+done
+if test x$objarg = x ; then
+ echo 'Error: no -o on compiler command line' 1>&2
+ exit 1
+fi
+lo="${args@<:@$objarg@:>@}"
+obj="${lo%.lo}"
+if test x"$lo" = x"$obj" ; then
+ echo "Error: libtool object file name \"$lo\" does not end in .lo" 1>&2
+ exit 1
+fi
+objbase="${obj##*/}"
+__DOLTCOMPILE__EOF__
+
+dnl Write out shared compilation code.
+ if test x$enable_shared = xyes; then
+ cat <<'__DOLTCOMPILE__EOF__' >>doltcompile
+libobjdir="${obj%$objbase}.libs"
+if test ! -d "$libobjdir" ; then
+ mkdir_out="$(mkdir "$libobjdir" 2>&1)"
+ mkdir_ret=$?
+ if test "$mkdir_ret" -ne 0 && test ! -d "$libobjdir" ; then
+ echo "$mkdir_out" 1>&2
+ exit $mkdir_ret
+ fi
+fi
+pic_object="$libobjdir/$objbase.o"
+args@<:@$objarg@:>@="$pic_object"
+__DOLTCOMPILE__EOF__
+ cat <<__DOLTCOMPILE__EOF__ >>doltcompile
+"\${args@<:@@@:>@}" $pic_options -DPIC || exit \$?
+__DOLTCOMPILE__EOF__
+ fi
+
+dnl Write out static compilation code.
+dnl Avoid duplicate compiler output if also building shared objects.
+ if test x$enable_static = xyes; then
+ cat <<'__DOLTCOMPILE__EOF__' >>doltcompile
+non_pic_object="$obj.o"
+args@<:@$objarg@:>@="$non_pic_object"
+__DOLTCOMPILE__EOF__
+ if test x$enable_shared = xyes; then
+ cat <<'__DOLTCOMPILE__EOF__' >>doltcompile
+"${args@<:@@@:>@}" >/dev/null 2>&1 || exit $?
+__DOLTCOMPILE__EOF__
+ else
+ cat <<'__DOLTCOMPILE__EOF__' >>doltcompile
+"${args@<:@@@:>@}" || exit $?
+__DOLTCOMPILE__EOF__
+ fi
+ fi
+
+dnl Write out the code to write the .lo file.
+dnl The second line of the .lo file must match "^# Generated by .*libtool"
+ cat <<'__DOLTCOMPILE__EOF__' >>doltcompile
+{
+echo "# $lo - a libtool object file"
+echo "# Generated by doltcompile, not libtool"
+__DOLTCOMPILE__EOF__
+
+ if test x$enable_shared = xyes; then
+ cat <<'__DOLTCOMPILE__EOF__' >>doltcompile
+echo "pic_object='.libs/${objbase}.o'"
+__DOLTCOMPILE__EOF__
+ else
+ cat <<'__DOLTCOMPILE__EOF__' >>doltcompile
+echo pic_object=none
+__DOLTCOMPILE__EOF__
+ fi
+
+ if test x$enable_static = xyes; then
+ cat <<'__DOLTCOMPILE__EOF__' >>doltcompile
+echo "non_pic_object='${objbase}.o'"
+__DOLTCOMPILE__EOF__
+ else
+ cat <<'__DOLTCOMPILE__EOF__' >>doltcompile
+echo non_pic_object=none
+__DOLTCOMPILE__EOF__
+ fi
+
+ cat <<'__DOLTCOMPILE__EOF__' >>doltcompile
+} > "$lo"
+__DOLTCOMPILE__EOF__
+
+dnl Done writing out doltcompile; substitute it for libtool compilation.
+ chmod +x doltcompile
+ LTCOMPILE='$(top_builddir)/doltcompile $(COMPILE)'
+ LTCXXCOMPILE='$(top_builddir)/doltcompile $(CXXCOMPILE)'
+
+dnl automake ignores LTCOMPILE and LTCXXCOMPILE when it has separate CFLAGS for
+dnl a target, so write out a libtool wrapper to handle that case.
+dnl Note that doltlibtool does not handle inferred tags or option arguments
+dnl without '=', because automake does not use them.
+ cat <<__DOLTLIBTOOL__EOF__ > doltlibtool
+#!$DOLT_BASH
+__DOLTLIBTOOL__EOF__
+ cat <<'__DOLTLIBTOOL__EOF__' >>doltlibtool
+top_builddir_slash="${0%%doltlibtool}"
+: ${top_builddir_slash:=./}
+args=()
+modeok=false
+tagok=false
+for arg in "$[]@"; do
+ case "$arg" in
+ --mode=compile) modeok=true ;;
+ --tag=CC|--tag=CXX) tagok=true ;;
+ *) args@<:@${#args[@]}@:>@="$arg" ;;
+ esac
+done
+if $modeok && $tagok ; then
+ . ${top_builddir_slash}doltcompile "${args@<:@@@:>@}"
+else
+ exec ${top_builddir_slash}libtool "$[]@"
+fi
+__DOLTLIBTOOL__EOF__
+
+dnl Done writing out doltlibtool; substitute it for libtool.
+ chmod +x doltlibtool
+ LIBTOOL='$(top_builddir)/doltlibtool'
+fi
+AC_SUBST(LTCOMPILE)
+AC_SUBST(LTCXXCOMPILE)
+# end dolt
+])
diff --git a/python/ContextKit/cltool.py b/python/ContextKit/cltool.py
index 0535b88c..fc0fcc42 100644
--- a/python/ContextKit/cltool.py
+++ b/python/ContextKit/cltool.py
@@ -49,9 +49,19 @@ class CLTool:
def expectAll(self, fileno, _exp_l, timeout, wantdump = True):
exp_l = list(_exp_l)
stream = 0
- if fileno == self.STDOUT: stream = self.__process.stdout
- if fileno == self.STDERR: stream = self.__process.stderr
- if stream == 0: return False
+ if fileno == self.STDOUT:
+ stream = self.__process.stdout
+ elif fileno == self.STDERR:
+ stream = self.__process.stderr
+ else: return False
+
+ if not stream:
+ self.__io.append((fileno, "----- WAS NOT ABLE TO START THE PROGRAM -----"))
+ if wantdump:
+ self.printio()
+ print "Expected:"
+ pprint(exp_l)
+ return False
# set the stream to nonblocking
fd = stream.fileno()
diff --git a/sandbox/context-proxy b/sandbox/context-proxy
new file mode 100755
index 00000000..2b6a366b
--- /dev/null
+++ b/sandbox/context-proxy
@@ -0,0 +1,112 @@
+#!/usr/bin/python
+
+from sys import stderr, stdin
+from subprocess import Popen, PIPE
+from select import select
+import re
+import os
+
+class Program:
+ def __init__(self, cline):
+ d = dict(os.environ)
+ d.update({"CONTEXT_CLI_DISABLE_TYPE_CHECK": "1",
+ "CONTEXT_CLI_IGNORE_COMMANDER": "1"})
+
+ self.__process = Popen(cline, stdin=PIPE, stdout=PIPE, stderr=PIPE,
+ env = d)
+
+ def send(self, string):
+ print >>self.__process.stdin, string
+ self.__process.stdin.flush()
+
+ def outfd(self):
+ return self.__process.stdout.fileno()
+
+ def readline(self):
+ return self.__process.stdout.readline()
+
+ def ready(self):
+ raise NotImplementedError
+
+class Listen(Program):
+ def __init__(self, *properties):
+ Program.__init__(self, ["context-listen"] + list(properties))
+
+ def ready(self):
+ global provide
+ line = self.readline()
+ if line:
+ print >>stderr, "LISTEN:", line,
+ match = re.match("(.*?) = (.*?):(.*)\n", line)
+ if match:
+ property = match.group(1)
+ type = ""
+ if match.group(2) == "QString":
+ type = "string"
+ elif match.group(2) == "int":
+ type = "int"
+ elif match.group(2) == "bool":
+ type = "truth"
+ elif match.group(2) == "double":
+ type = "double"
+ else:
+ raise RuntimeError("unknown type from client: " + match.group(2))
+ value = match.group(3)
+ provide.send("add " + type + " " + property + " " + value)
+ match = re.match("(.*?) is Unknown\n", line)
+ if match:
+ property = match.group(1)
+ provide.send("add " + type + " " + property)
+ provide.send("unset " + property)
+
+ return True
+ else:
+ raise RuntimeError("context-listen terminated")
+
+class Provide(Program):
+ def __init__(self):
+ Program.__init__(self, ["context-provide-internal"])
+
+ def ready(self):
+ line = self.readline()
+ if line:
+ print "PROVIDE:", line,
+ return True
+ else:
+ raise RuntimeError("context-provide terminated")
+
+class UserInput():
+ def outfd(self):
+ return stdin.fileno()
+
+ def ready(self):
+ line = self.readline()
+ if line:
+ match = re.match("(.*?) (.*)\n", line)
+ command = match.group(1)
+ return True
+ else:
+ exit(0)
+
+class Select:
+ def __init__(self, *tools):
+ self.map = dict(map(lambda t: (t.outfd(), t), tools))
+ self.rlist = map(lambda t: t.outfd(), tools)
+
+ def select(self):
+ ret = select(self.rlist, [], [])[0]
+ for i in ret:
+ stderr.flush()
+ if not self.map[i].ready():
+ self.rlist.remove(i)
+ del self.map[i]
+
+listen = Listen("test.a", "test.b")
+provide = Provide()
+provide.send("start")
+s = Select(listen, provide)
+
+while True:
+ s.select()
+ if not s.rlist:
+ break
diff --git a/libcontextsubscriber/sandbox/messaging-to-self/main.cpp b/sandbox/messaging-to-self/main.cpp
index aafda83c..aafda83c 100644
--- a/libcontextsubscriber/sandbox/messaging-to-self/main.cpp
+++ b/sandbox/messaging-to-self/main.cpp
diff --git a/libcontextsubscriber/sandbox/messaging-to-self/messaging-to-self.pro b/sandbox/messaging-to-self/messaging-to-self.pro
index bafbb027..bafbb027 100644
--- a/libcontextsubscriber/sandbox/messaging-to-self/messaging-to-self.pro
+++ b/sandbox/messaging-to-self/messaging-to-self.pro
diff --git a/libcontextsubscriber/sandbox/messaging-to-self/myobject.h b/sandbox/messaging-to-self/myobject.h
index 155ebf3b..155ebf3b 100644
--- a/libcontextsubscriber/sandbox/messaging-to-self/myobject.h
+++ b/sandbox/messaging-to-self/myobject.h
diff --git a/libcontextsubscriber/sandbox/messaging-to-self/mythread.h b/sandbox/messaging-to-self/mythread.h
index 4170ccf8..4170ccf8 100644
--- a/libcontextsubscriber/sandbox/messaging-to-self/mythread.h
+++ b/sandbox/messaging-to-self/mythread.h
diff --git a/libcontextsubscriber/sandbox/messaging-to-self/queuedinvoker.cpp b/sandbox/messaging-to-self/queuedinvoker.cpp
index 85dbb7d1..85dbb7d1 100644
--- a/libcontextsubscriber/sandbox/messaging-to-self/queuedinvoker.cpp
+++ b/sandbox/messaging-to-self/queuedinvoker.cpp
diff --git a/libcontextsubscriber/sandbox/messaging-to-self/queuedinvoker.h b/sandbox/messaging-to-self/queuedinvoker.h
index 8f81f26e..8f81f26e 100644
--- a/libcontextsubscriber/sandbox/messaging-to-self/queuedinvoker.h
+++ b/sandbox/messaging-to-self/queuedinvoker.h
diff --git a/libcontextsubscriber/multithreading-tests/Makefile.am b/sandbox/multithreading-tests/Makefile.am
index 8da6191d..8da6191d 100644
--- a/libcontextsubscriber/multithreading-tests/Makefile.am
+++ b/sandbox/multithreading-tests/Makefile.am
diff --git a/libcontextsubscriber/multithreading-tests/new-property-in-thread/.gitignore b/sandbox/multithreading-tests/new-property-in-thread/.gitignore
index 4390dc85..4390dc85 100644
--- a/libcontextsubscriber/multithreading-tests/new-property-in-thread/.gitignore
+++ b/sandbox/multithreading-tests/new-property-in-thread/.gitignore
diff --git a/libcontextsubscriber/multithreading-tests/new-property-in-thread/Makefile.am b/sandbox/multithreading-tests/new-property-in-thread/Makefile.am
index 6e80e6d8..59dc63a7 100644
--- a/libcontextsubscriber/multithreading-tests/new-property-in-thread/Makefile.am
+++ b/sandbox/multithreading-tests/new-property-in-thread/Makefile.am
@@ -7,9 +7,10 @@ LIBS += $(QtCore_LIBS)
# library dependency hack for seamless make in cli/
AM_CXXFLAGS += -I$(srcdir)/../../src
run_test_LDADD = ../../src/libcontextsubscriber.la
-../../src/libcontextsubscriber.la:
+
+../../src/libcontextsubscriber.la: FORCE
$(MAKE) -C ../../src libcontextsubscriber.la
-.PHONY: ../../src/libcontextsubscriber.la
+.PHONY: FORCE
# moccing
nodist_run_test_SOURCES = mocs.cpp
diff --git a/libcontextsubscriber/multithreading-tests/new-property-in-thread/main.cpp b/sandbox/multithreading-tests/new-property-in-thread/main.cpp
index 3ab015fb..3ab015fb 100644
--- a/libcontextsubscriber/multithreading-tests/new-property-in-thread/main.cpp
+++ b/sandbox/multithreading-tests/new-property-in-thread/main.cpp
diff --git a/libcontextsubscriber/multithreading-tests/new-property-in-thread/thread.h b/sandbox/multithreading-tests/new-property-in-thread/thread.h
index dd9951b3..dd9951b3 100644
--- a/libcontextsubscriber/multithreading-tests/new-property-in-thread/thread.h
+++ b/sandbox/multithreading-tests/new-property-in-thread/thread.h
diff --git a/libcontextsubscriber/multithreading-tests/old-property-in-thread/.gitignore b/sandbox/multithreading-tests/old-property-in-thread/.gitignore
index 4390dc85..4390dc85 100644
--- a/libcontextsubscriber/multithreading-tests/old-property-in-thread/.gitignore
+++ b/sandbox/multithreading-tests/old-property-in-thread/.gitignore
diff --git a/libcontextsubscriber/multithreading-tests/stress-test/Makefile.am b/sandbox/multithreading-tests/old-property-in-thread/Makefile.am
index 6e80e6d8..59dc63a7 100644
--- a/libcontextsubscriber/multithreading-tests/stress-test/Makefile.am
+++ b/sandbox/multithreading-tests/old-property-in-thread/Makefile.am
@@ -7,9 +7,10 @@ LIBS += $(QtCore_LIBS)
# library dependency hack for seamless make in cli/
AM_CXXFLAGS += -I$(srcdir)/../../src
run_test_LDADD = ../../src/libcontextsubscriber.la
-../../src/libcontextsubscriber.la:
+
+../../src/libcontextsubscriber.la: FORCE
$(MAKE) -C ../../src libcontextsubscriber.la
-.PHONY: ../../src/libcontextsubscriber.la
+.PHONY: FORCE
# moccing
nodist_run_test_SOURCES = mocs.cpp
diff --git a/libcontextsubscriber/multithreading-tests/old-property-in-thread/main.cpp b/sandbox/multithreading-tests/old-property-in-thread/main.cpp
index c21c9d7f..c21c9d7f 100644
--- a/libcontextsubscriber/multithreading-tests/old-property-in-thread/main.cpp
+++ b/sandbox/multithreading-tests/old-property-in-thread/main.cpp
diff --git a/libcontextsubscriber/multithreading-tests/old-property-in-thread/thread.h b/sandbox/multithreading-tests/old-property-in-thread/thread.h
index ec84afdf..ec84afdf 100644
--- a/libcontextsubscriber/multithreading-tests/old-property-in-thread/thread.h
+++ b/sandbox/multithreading-tests/old-property-in-thread/thread.h
diff --git a/libcontextsubscriber/multithreading-tests/single-thread/.gitignore b/sandbox/multithreading-tests/single-thread/.gitignore
index 4390dc85..4390dc85 100644
--- a/libcontextsubscriber/multithreading-tests/single-thread/.gitignore
+++ b/sandbox/multithreading-tests/single-thread/.gitignore
diff --git a/libcontextsubscriber/multithreading-tests/single-thread/Makefile.am b/sandbox/multithreading-tests/single-thread/Makefile.am
index 3cec50b8..010bc826 100644
--- a/libcontextsubscriber/multithreading-tests/single-thread/Makefile.am
+++ b/sandbox/multithreading-tests/single-thread/Makefile.am
@@ -7,9 +7,10 @@ LIBS += $(QtCore_LIBS)
# library dependency hack for seamless make in cli/
AM_CXXFLAGS += -I$(srcdir)/../../src
run_test_LDADD = ../../src/libcontextsubscriber.la
-../../src/libcontextsubscriber.la:
+
+../../src/libcontextsubscriber.la: FORCE
$(MAKE) -C ../../src libcontextsubscriber.la
-.PHONY: ../../src/libcontextsubscriber.la
+.PHONY: FORCE
# moccing
nodist_run_test_SOURCES = mocs.cpp
diff --git a/libcontextsubscriber/multithreading-tests/single-thread/listener.h b/sandbox/multithreading-tests/single-thread/listener.h
index 9980f62a..9980f62a 100644
--- a/libcontextsubscriber/multithreading-tests/single-thread/listener.h
+++ b/sandbox/multithreading-tests/single-thread/listener.h
diff --git a/libcontextsubscriber/multithreading-tests/single-thread/main.cpp b/sandbox/multithreading-tests/single-thread/main.cpp
index 16dd2f65..16dd2f65 100644
--- a/libcontextsubscriber/multithreading-tests/single-thread/main.cpp
+++ b/sandbox/multithreading-tests/single-thread/main.cpp
diff --git a/libcontextsubscriber/multithreading-tests/stress-test/.gitignore b/sandbox/multithreading-tests/stress-test/.gitignore
index e08dedad..e08dedad 100644
--- a/libcontextsubscriber/multithreading-tests/stress-test/.gitignore
+++ b/sandbox/multithreading-tests/stress-test/.gitignore
diff --git a/libcontextsubscriber/multithreading-tests/stress-test/1provider.cdb b/sandbox/multithreading-tests/stress-test/1provider.cdb
index 58655ef3..58655ef3 100644
--- a/libcontextsubscriber/multithreading-tests/stress-test/1provider.cdb
+++ b/sandbox/multithreading-tests/stress-test/1provider.cdb
Binary files differ
diff --git a/libcontextsubscriber/multithreading-tests/stress-test/2providers.cdb b/sandbox/multithreading-tests/stress-test/2providers.cdb
index 98324d4c..98324d4c 100644
--- a/libcontextsubscriber/multithreading-tests/stress-test/2providers.cdb
+++ b/sandbox/multithreading-tests/stress-test/2providers.cdb
Binary files differ
diff --git a/libcontextsubscriber/multithreading-tests/wait-for-subscription-only-in-thread/Makefile.am b/sandbox/multithreading-tests/stress-test/Makefile.am
index 6e80e6d8..59dc63a7 100644
--- a/libcontextsubscriber/multithreading-tests/wait-for-subscription-only-in-thread/Makefile.am
+++ b/sandbox/multithreading-tests/stress-test/Makefile.am
@@ -7,9 +7,10 @@ LIBS += $(QtCore_LIBS)
# library dependency hack for seamless make in cli/
AM_CXXFLAGS += -I$(srcdir)/../../src
run_test_LDADD = ../../src/libcontextsubscriber.la
-../../src/libcontextsubscriber.la:
+
+../../src/libcontextsubscriber.la: FORCE
$(MAKE) -C ../../src libcontextsubscriber.la
-.PHONY: ../../src/libcontextsubscriber.la
+.PHONY: FORCE
# moccing
nodist_run_test_SOURCES = mocs.cpp
diff --git a/libcontextsubscriber/multithreading-tests/stress-test/main.cpp b/sandbox/multithreading-tests/stress-test/main.cpp
index d7c334bd..d7c334bd 100644
--- a/libcontextsubscriber/multithreading-tests/stress-test/main.cpp
+++ b/sandbox/multithreading-tests/stress-test/main.cpp
diff --git a/libcontextsubscriber/multithreading-tests/stress-test/provider.py b/sandbox/multithreading-tests/stress-test/provider.py
index 748929a4..748929a4 100755
--- a/libcontextsubscriber/multithreading-tests/stress-test/provider.py
+++ b/sandbox/multithreading-tests/stress-test/provider.py
diff --git a/libcontextsubscriber/multithreading-tests/stress-test/runme.sh b/sandbox/multithreading-tests/stress-test/runme.sh
index a2aa73d6..a2aa73d6 100755
--- a/libcontextsubscriber/multithreading-tests/stress-test/runme.sh
+++ b/sandbox/multithreading-tests/stress-test/runme.sh
diff --git a/libcontextsubscriber/multithreading-tests/stress-test/thread.h b/sandbox/multithreading-tests/stress-test/thread.h
index 44cbf7c8..44cbf7c8 100644
--- a/libcontextsubscriber/multithreading-tests/stress-test/thread.h
+++ b/sandbox/multithreading-tests/stress-test/thread.h
diff --git a/libcontextsubscriber/multithreading-tests/using-backend-from-thread/.gitignore b/sandbox/multithreading-tests/using-backend-from-thread/.gitignore
index 4390dc85..4390dc85 100644
--- a/libcontextsubscriber/multithreading-tests/using-backend-from-thread/.gitignore
+++ b/sandbox/multithreading-tests/using-backend-from-thread/.gitignore
diff --git a/libcontextsubscriber/multithreading-tests/using-backend-from-thread/Makefile.am b/sandbox/multithreading-tests/using-backend-from-thread/Makefile.am
index b5883842..b4cf1a5d 100644
--- a/libcontextsubscriber/multithreading-tests/using-backend-from-thread/Makefile.am
+++ b/sandbox/multithreading-tests/using-backend-from-thread/Makefile.am
@@ -7,9 +7,10 @@ LIBS += $(QtCore_LIBS) $(QtDBus_LIBS)
# library dependency hack for seamless make in cli/
AM_CXXFLAGS += -I$(srcdir)/../../src
run_test_LDADD = ../../src/libcontextsubscriber.la
-../../src/libcontextsubscriber.la:
+
+../../src/libcontextsubscriber.la: FORCE
$(MAKE) -C ../../src libcontextsubscriber.la
-.PHONY: ../../src/libcontextsubscriber.la
+.PHONY: FORCE
# moccing
nodist_run_test_SOURCES = mocs.cpp
diff --git a/libcontextsubscriber/multithreading-tests/using-backend-from-thread/main.cpp b/sandbox/multithreading-tests/using-backend-from-thread/main.cpp
index a6a7c8aa..a6a7c8aa 100644
--- a/libcontextsubscriber/multithreading-tests/using-backend-from-thread/main.cpp
+++ b/sandbox/multithreading-tests/using-backend-from-thread/main.cpp
diff --git a/libcontextsubscriber/multithreading-tests/using-backend-from-thread/thread.h b/sandbox/multithreading-tests/using-backend-from-thread/thread.h
index cd77a298..cd77a298 100644
--- a/libcontextsubscriber/multithreading-tests/using-backend-from-thread/thread.h
+++ b/sandbox/multithreading-tests/using-backend-from-thread/thread.h
diff --git a/libcontextsubscriber/multithreading-tests/wait-for-subscription-only-in-thread/.gitignore b/sandbox/multithreading-tests/wait-for-subscription-only-in-thread/.gitignore
index 4390dc85..4390dc85 100644
--- a/libcontextsubscriber/multithreading-tests/wait-for-subscription-only-in-thread/.gitignore
+++ b/sandbox/multithreading-tests/wait-for-subscription-only-in-thread/.gitignore
diff --git a/libcontextsubscriber/multithreading-tests/old-property-in-thread/Makefile.am b/sandbox/multithreading-tests/wait-for-subscription-only-in-thread/Makefile.am
index 6e80e6d8..59dc63a7 100644
--- a/libcontextsubscriber/multithreading-tests/old-property-in-thread/Makefile.am
+++ b/sandbox/multithreading-tests/wait-for-subscription-only-in-thread/Makefile.am
@@ -7,9 +7,10 @@ LIBS += $(QtCore_LIBS)
# library dependency hack for seamless make in cli/
AM_CXXFLAGS += -I$(srcdir)/../../src
run_test_LDADD = ../../src/libcontextsubscriber.la
-../../src/libcontextsubscriber.la:
+
+../../src/libcontextsubscriber.la: FORCE
$(MAKE) -C ../../src libcontextsubscriber.la
-.PHONY: ../../src/libcontextsubscriber.la
+.PHONY: FORCE
# moccing
nodist_run_test_SOURCES = mocs.cpp
diff --git a/libcontextsubscriber/multithreading-tests/wait-for-subscription-only-in-thread/main.cpp b/sandbox/multithreading-tests/wait-for-subscription-only-in-thread/main.cpp
index 1a1727b7..1a1727b7 100644
--- a/libcontextsubscriber/multithreading-tests/wait-for-subscription-only-in-thread/main.cpp
+++ b/sandbox/multithreading-tests/wait-for-subscription-only-in-thread/main.cpp
diff --git a/libcontextsubscriber/multithreading-tests/wait-for-subscription-only-in-thread/thread.h b/sandbox/multithreading-tests/wait-for-subscription-only-in-thread/thread.h
index e92af6f2..e92af6f2 100644
--- a/libcontextsubscriber/multithreading-tests/wait-for-subscription-only-in-thread/thread.h
+++ b/sandbox/multithreading-tests/wait-for-subscription-only-in-thread/thread.h
diff --git a/libcontextsubscriber/multithreading-tests/wait-for-subscription-thread/.gitignore b/sandbox/multithreading-tests/wait-for-subscription-thread/.gitignore
index 4390dc85..4390dc85 100644
--- a/libcontextsubscriber/multithreading-tests/wait-for-subscription-thread/.gitignore
+++ b/sandbox/multithreading-tests/wait-for-subscription-thread/.gitignore
diff --git a/sandbox/multithreading-tests/wait-for-subscription-thread/Makefile.am b/sandbox/multithreading-tests/wait-for-subscription-thread/Makefile.am
new file mode 100644
index 00000000..59dc63a7
--- /dev/null
+++ b/sandbox/multithreading-tests/wait-for-subscription-thread/Makefile.am
@@ -0,0 +1,18 @@
+noinst_PROGRAMS = run-test
+run_test_SOURCES = main.cpp thread.h
+
+AM_CXXFLAGS = $(QtCore_CFLAGS)
+LIBS += $(QtCore_LIBS)
+
+# library dependency hack for seamless make in cli/
+AM_CXXFLAGS += -I$(srcdir)/../../src
+run_test_LDADD = ../../src/libcontextsubscriber.la
+
+../../src/libcontextsubscriber.la: FORCE
+ $(MAKE) -C ../../src libcontextsubscriber.la
+.PHONY: FORCE
+
+# moccing
+nodist_run_test_SOURCES = mocs.cpp
+QT_TOMOC = $(filter %.h, $(run_test_SOURCES))
+include $(top_srcdir)/am/qt.am
diff --git a/libcontextsubscriber/multithreading-tests/wait-for-subscription-thread/main.cpp b/sandbox/multithreading-tests/wait-for-subscription-thread/main.cpp
index 44390e2d..44390e2d 100644
--- a/libcontextsubscriber/multithreading-tests/wait-for-subscription-thread/main.cpp
+++ b/sandbox/multithreading-tests/wait-for-subscription-thread/main.cpp
diff --git a/libcontextsubscriber/multithreading-tests/wait-for-subscription-thread/thread.h b/sandbox/multithreading-tests/wait-for-subscription-thread/thread.h
index 7766fe68..7766fe68 100644
--- a/libcontextsubscriber/multithreading-tests/wait-for-subscription-thread/thread.h
+++ b/sandbox/multithreading-tests/wait-for-subscription-thread/thread.h
diff --git a/spec/ContextKit.xml b/spec/ContextKit.xml
new file mode 100644
index 00000000..da6a70ba
--- /dev/null
+++ b/spec/ContextKit.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<node name="/org/maemo/contextkit/Some/Property"
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+ <interface name="org.maemo.contextkit.Property">
+ <method name="Subscribe">
+ <tp:docstring>
+ Subscribes to the context property.
+ </tp:docstring>
+ <arg name="value" type="av" tp:type="Maybe_Variant" direction="out">
+ <tp:docstring>
+ The actual value at the of subscription.
+ </tp:docstring>
+ </arg>
+ <arg name="timestamp" type="t" direction="out">
+ <tp:docstring>
+ The timestamp of the value.
+ </tp:docstring>
+ </arg>
+ </method>
+ <method name="Unsubscribe">
+ <tp:docstring>
+ Unsubscribes from the property.
+ </tp:docstring>
+ </method>
+ <method name="Get">
+ <tp:docstring>
+ Returns the actual value from the provider without subscribing to it.
+ </tp:docstring>
+ <arg name="value" type="av" tp:type="Maybe_Variant" direction="out"/>
+ <arg name="timestamp" type="t" direction="out"/>
+ </method>
+ <signal name="Changed">
+ <tp:docstring>
+ Emitted when the value changed and there are at least one subscribed client.
+ </tp:docstring>
+ <arg name="value" type="av" tp:type="Maybe_Variant"/>
+ <arg name="timestamp" type="t"/>
+ </signal>
+ </interface>
+</node>
diff --git a/spec/Makefile.am b/spec/Makefile.am
index e22b2413..6450fcac 100644
--- a/spec/Makefile.am
+++ b/spec/Makefile.am
@@ -17,7 +17,8 @@ SPEC_TOOLS = spec-to-introspect.xsl \
INTERFACES = \
Manager.xml \
- Subscriber.xml
+ Subscriber.xml \
+ ContextKit.xml
SPEC_FILES = \
$(INTERFACES) \
diff --git a/spec/all.xml b/spec/all.xml
index a73b1dcb..c2132a60 100644
--- a/spec/all.xml
+++ b/spec/all.xml
@@ -25,5 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</
<xi:include href="Manager.xml"/>
<xi:include href="Subscriber.xml"/>
+<xi:include href="ContextKit.xml"/>
<xi:include href="generic-types.xml"/>
</tp:spec>
diff --git a/spec/generic-types.xml b/spec/generic-types.xml
index f2bd4940..f3ab3227 100644
--- a/spec/generic-types.xml
+++ b/spec/generic-types.xml
@@ -7,6 +7,12 @@
<tp:member type="v" name="Value"/>
</tp:mapping>
+ <tp:simple-type name="Maybe_Variant" type="av">
+ <tp:docstring>A list of variants where empty list represents
+ unknown, otherwise it represents the same variant as the contained
+ first variant. This hack is needed because D-Bus doesn't support
+ null values on the wire.</tp:docstring>
+ </tp:simple-type>
</tp:generic-types>