Merge lp:~ted/ubuntu-app-launch/app-object-signals into lp:ubuntu-app-launch

Proposed by Ted Gould
Status: Merged
Approved by: Ted Gould
Approved revision: 314
Merged at revision: 275
Proposed branch: lp:~ted/ubuntu-app-launch/app-object-signals
Merge into: lp:ubuntu-app-launch
Diff against target: 3388 lines (+1673/-939)
24 files modified
libubuntu-app-launch/application-impl-base.cpp (+15/-16)
libubuntu-app-launch/application.cpp (+5/-0)
libubuntu-app-launch/helper.h (+12/-0)
libubuntu-app-launch/libubuntu-app-launch.map (+1/-0)
libubuntu-app-launch/registry-impl.cpp (+565/-29)
libubuntu-app-launch/registry-impl.h (+88/-11)
libubuntu-app-launch/registry.cpp (+49/-6)
libubuntu-app-launch/registry.h (+123/-14)
libubuntu-app-launch/ubuntu-app-launch.cpp (+298/-436)
tests/CMakeLists.txt (+1/-0)
tests/failure-test.cc (+123/-96)
tests/libual-cpp-test.cc (+192/-141)
tools/CMakeLists.txt (+21/-2)
tools/ubuntu-app-info.cpp (+18/-9)
tools/ubuntu-app-launch.cpp (+56/-68)
tools/ubuntu-app-list-pids.cpp (+7/-3)
tools/ubuntu-app-list.cpp (+1/-1)
tools/ubuntu-app-pid.cpp (+8/-4)
tools/ubuntu-app-stop.cpp (+7/-3)
tools/ubuntu-app-triplet.cpp (+1/-1)
tools/ubuntu-app-watch.cpp (+61/-88)
tools/ubuntu-helper-list.cpp (+1/-1)
tools/ubuntu-helper-start.cpp (+10/-5)
tools/ubuntu-helper-stop.cpp (+10/-5)
To merge this branch: bzr merge lp:~ted/ubuntu-app-launch/app-object-signals
Reviewer Review Type Date Requested Status
dobey (community) Needs Fixing
Charles Kerr (community) Approve
unity-api-1-bot continuous-integration Needs Fixing
PS Jenkins bot continuous-integration Pending
Review via email: mp+310230@code.launchpad.net

This proposal supersedes a proposal from 2016-05-16.

Commit message

Change signals into C++ core::signal objects

Description of the change

This branch, on its own, is reshuffling the chairs on the deck of the Titanic. We're mostly setting up the branches to abstract out Upstart and Systemd but need to get the signals converted first. This is a "small" step on that road.

All of the signals now come from the Registry and then the C signals use the default registry to register against. The C signals also keep track of context as the previous C API was GLib based and that's what GLib callers would expect. The C++ signals require marshalling to the appropriate thread if needed.

There is also the addition of the manager interface, but we're not yet in this branch actually blocking the startup of apps based on it. Though the interface is designed to do that in the future.

Lastly, the C++ signals also have a space for the instance, but the signals would have to be changed. Saving that for the next branch to avoid complicating this one as little as possible. There are several TODOs in the code because of this.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:232
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https://code.launchpad.net/~ted/ubuntu-app-launch/app-object-signals/+merge/294807/+edit-commit-message

http://jenkins.qa.ubuntu.com/job/ubuntu-app-launch-ci/55/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/ubuntu-app-launch-wily-amd64-ci/55/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/ubuntu-app-launch-wily-armhf-ci/55/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/ubuntu-app-launch-wily-i386-ci/55/console

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/ubuntu-app-launch-ci/55/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:234
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https://code.launchpad.net/~ted/ubuntu-app-launch/app-object-signals/+merge/294807/+edit-commit-message

http://jenkins.qa.ubuntu.com/job/ubuntu-app-launch-ci/56/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-app-launch-wily-amd64-ci/56
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-app-launch-wily-armhf-ci/56
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-app-launch-wily-i386-ci/56

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/ubuntu-app-launch-ci/56/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:235
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https://code.launchpad.net/~ted/ubuntu-app-launch/app-object-signals/+merge/294807/+edit-commit-message

http://jenkins.qa.ubuntu.com/job/ubuntu-app-launch-ci/57/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-app-launch-wily-amd64-ci/57
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-app-launch-wily-armhf-ci/57
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-app-launch-wily-i386-ci/57

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/ubuntu-app-launch-ci/57/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :

FAILED: Continuous integration, rev:276
https://jenkins.canonical.com/unity-api-1/job/lp-ubuntu-app-launch-ci/112/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build/1020/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-0-fetch/1027
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/820/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/820/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/820/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/820/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/820/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/820/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/820/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/820/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/820/console

Click here to trigger a rebuild:
https://jenkins.canonical.com/unity-api-1/job/lp-ubuntu-app-launch-ci/112/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Terry (mterry) wrote :

Ted asked me to look at this from a "is this API suitable for Unity8" perspective.

It seems fine to me. Mostly we are interested in delaying/stopping an app from starting if we're not in the right context (like non-Touch app on a phone). This API looks sufficient for that.

A couple notes though:

- Application::Instance does not provide access to arguments. But *maybe* the arguments might matter to approving a startup? I'm not sure what the use case for that would be. The manager would have to have knowledge of what arguments meant to a specific app. And the argument would have to trigger something that the user couldn't just do anyway in the app if it were started without the arg. So maybe there is no use case there... But something to consider. Can easily be added later if we do find we want it.

- There are a few "singal" typos in doc strings.

- Also in doc strings, you might want to clarify behavior around delayed replies. Like if the manager doesn't immediately get back to UAL, is there a timeout? What if another request comes in while the manager is considering what to do -- I'm assuming UAL gives that to the manager and lets it decide, rather than denying the second one quietly? This information would help me know how carefully I have to write the signal handlers.

277. By Ted Gould

Make gcc6 happy

Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :

FAILED: Continuous integration, rev:277
https://jenkins.canonical.com/unity-api-1/job/lp-ubuntu-app-launch-ci/120/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build/1048/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-0-fetch/1055
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/844
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/844/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/844
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/844/artifact/output/*zip*/output.zip
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/844/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/844
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/844/artifact/output/*zip*/output.zip
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/844/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/844/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/844/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/844
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/844/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/844
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/844/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/unity-api-1/job/lp-ubuntu-app-launch-ci/120/rebuild

review: Needs Fixing (continuous-integration)
278. By Ted Gould

Putting additional checks in to make sure we don't use null pointers we don't get locks on.

Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :

FAILED: Continuous integration, rev:278
https://jenkins.canonical.com/unity-api-1/job/lp-ubuntu-app-launch-ci/121/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build/1060/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-0-fetch/1067
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/856
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/856/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/856
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/856/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/856
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/856/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/856
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/856/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/856
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/856/artifact/output/*zip*/output.zip
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/856/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/856
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/856/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/856
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/856/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/856
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/856/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/unity-api-1/job/lp-ubuntu-app-launch-ci/121/rebuild

review: Needs Fixing (continuous-integration)
279. By Ted Gould

Ensure the manager thread shutsdown before the registry to avoid a deadlock

Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :

PASSED: Continuous integration, rev:279
https://jenkins.canonical.com/unity-api-1/job/lp-ubuntu-app-launch-ci/124/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build/1063
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-0-fetch/1070
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/859
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/859/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/859
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/859/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/859
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/859/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/859
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/859/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/859
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/859/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/859
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/859/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/859
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/859/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/859
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/859/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/859
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/859/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/unity-api-1/job/lp-ubuntu-app-launch-ci/124/rebuild

review: Approve (continuous-integration)
280. By Ted Gould

Merge future trunk

Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Charles Kerr (charlesk) wrote :

Got about 1000 lines in, review part 1 inline

review: Needs Fixing
Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Ted Gould (ted) :
281. By Ted Gould

Remove some of the sing song part of the comments

282. By Ted Gould

Additional comment on lifecycle of replies

283. By Ted Gould

Zesty formatting tools diffs

284. By Ted Gould

Curly init

285. By Ted Gould

Make sure to check for a nullappid or error for g_variant_get()

286. By Ted Gould

Names of the parameters for clarity

287. By Ted Gould

Moar auto!

288. By Ted Gould

Don't specify returning void explicitly

289. By Ted Gould

Use static_cast() for void* casts

290. By Ted Gould

Don't spell well

291. By Ted Gould

Make sure to check for a valid registry

292. By Ted Gould

Comment out unused variables

293. By Ted Gould

Sometimes life would be better if it was more constant

294. By Ted Gould

Me no spell good

295. By Ted Gould

auto auto auto

296. By Ted Gould

Some 'NULL's crept in

297. By Ted Gould

More auto's with GVariants

298. By Ted Gould

Make params constant

299. By Ted Gould

Change setManager to have const& parameters

300. By Ted Gould

Fix the API so that the signal callbacks take pointers

Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Charles Kerr (charlesk) wrote :

Part 2/2

Revision history for this message
Ted Gould (ted) :
301. By Ted Gould

Note it is on the UAL thread only

302. By Ted Gould

Save some stack data with this context

303. By Ted Gould

Formatting fix

304. By Ted Gould

Use an ensure_cmanager() helper to remove duplicate code

305. By Ted Gould

Putting all the map handling code in a couple templates

306. By Ted Gould

Pull out the request code into individual functions

307. By Ted Gould

Rename a function and add comments

308. By Ted Gould

Expand the usage of observer_delete

309. By Ted Gould

Factor out pause/resume commonality

310. By Ted Gould

I've been overrided by charles

Revision history for this message
Ted Gould (ted) wrote :

$ bzr diff -r 281..310 | wc -l
1418

Revision history for this message
Ted Gould (ted) wrote :

I think that after this silo lands we need to put ubuntu-app-launch.cpp into the autoformatter. Right now that's a 3000 line diff by itself. But it's getting kinda out of control with some of these signal prototypes.

Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Charles Kerr (charlesk) wrote :

Whew, going through and looking at the full diff cold again this morning, I agree wrt (a) running this through the autoformatter, some of these lines are super wide (b) following standard procedure of doing that in a standalone branch with no other changes

Thanks for the changes. This branch is insane on its own but as a step to the followup branches it makes sense.

review: Approve
Revision history for this message
dobey (dobey) wrote :

Too many totally unrelated style changes, including addition of auto-formatting of unrelated code.
Introduces failing tests.
All actual new code in this branch appears to simply be moved around to other files in another branch which depends on this one.
Changed copyright years are mostly wrong.
Really needs to be broken up better, and have passing tests.

review: Needs Fixing
311. By Ted Gould

Update to trunk

312. By Ted Gould

Adding virtual destructors, acc says they're fine.

313. By Ted Gould

Putting this off for gcc 5.4

314. By Ted Gould

Block off more API breaks this time

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'libubuntu-app-launch/application-impl-base.cpp'
2--- libubuntu-app-launch/application-impl-base.cpp 2016-11-10 15:34:52 +0000
3+++ libubuntu-app-launch/application-impl-base.cpp 2017-01-24 04:21:42 +0000
4@@ -556,22 +556,21 @@
5 std::array<const char*, 4> args = {OOM_HELPER, pidstr.c_str(), oomstr.c_str(), nullptr};
6
7 g_debug("Excuting OOM Helper (pid: %d, score: %d): %s", int(pid), int(oomvalue),
8- std::accumulate(args.begin(), args.end(), std::string{},
9- [](const std::string& instr, const char* output) -> std::string {
10- if (instr.empty())
11- {
12- return output;
13- }
14- else if (output != nullptr)
15- {
16- return instr + " " + std::string(output);
17- }
18- else
19- {
20- return instr;
21- }
22- })
23- .c_str());
24+ std::accumulate(args.begin(), args.end(), std::string{}, [](const std::string& instr,
25+ const char* output) -> std::string {
26+ if (instr.empty())
27+ {
28+ return output;
29+ }
30+ else if (output != nullptr)
31+ {
32+ return instr + " " + std::string(output);
33+ }
34+ else
35+ {
36+ return instr;
37+ }
38+ }).c_str());
39
40 g_spawn_async(nullptr, /* working dir */
41 (char**)(args.data()), /* args */
42
43=== modified file 'libubuntu-app-launch/application.cpp'
44--- libubuntu-app-launch/application.cpp 2016-08-26 17:33:34 +0000
45+++ libubuntu-app-launch/application.cpp 2017-01-24 04:21:42 +0000
46@@ -46,6 +46,11 @@
47 throw std::runtime_error("AppID is empty");
48 }
49
50+ if (!registry || !registry->impl)
51+ {
52+ throw std::runtime_error("Invalid registry object");
53+ }
54+
55 if (app_impls::Click::hasAppId(appid, registry))
56 {
57 return std::make_shared<app_impls::Click>(appid, registry);
58
59=== modified file 'libubuntu-app-launch/helper.h'
60--- libubuntu-app-launch/helper.h 2016-06-09 14:55:34 +0000
61+++ libubuntu-app-launch/helper.h 2017-01-24 04:21:42 +0000
62@@ -62,6 +62,12 @@
63 */
64 class Helper
65 {
66+/*
67+protected:
68+ Helper() = default;
69+ virtual ~Helper() = default;
70+ TODO: Next ABI break */
71+
72 public:
73 /** \private */
74 struct TypeTag;
75@@ -87,6 +93,12 @@
76 /** Running instance of a a Helper */
77 class Instance
78 {
79+/*
80+ protected:
81+ Instance() = default;
82+ virtual ~Instance() = default;
83+ TODO: Next ABI break */
84+
85 public:
86 /** Check to see if this instance is running */
87 virtual bool isRunning() = 0;
88
89=== modified file 'libubuntu-app-launch/libubuntu-app-launch.map'
90--- libubuntu-app-launch/libubuntu-app-launch.map 2016-05-19 16:24:11 +0000
91+++ libubuntu-app-launch/libubuntu-app-launch.map 2017-01-24 04:21:42 +0000
92@@ -14,6 +14,7 @@
93 ubuntu::app_launch::Helper::*;
94 typeinfo?for?ubuntu::app_launch::Helper;
95 typeinfo?name?for?ubuntu::app_launch::Helper;
96+ ubuntu::app_launch::operator*;
97 ubuntu::app_launch::oom::*;
98 };
99 local:
100
101=== modified file 'libubuntu-app-launch/registry-impl.cpp'
102--- libubuntu-app-launch/registry-impl.cpp 2016-11-10 15:38:50 +0000
103+++ libubuntu-app-launch/registry-impl.cpp 2017-01-24 04:21:42 +0000
104@@ -20,6 +20,7 @@
105 #include "registry-impl.h"
106 #include "application-icon-finder.h"
107 #include <cgmanager/cgmanager.h>
108+#include <regex>
109 #include <upstart.h>
110
111 namespace ubuntu
112@@ -36,13 +37,29 @@
113 zgLog_.reset();
114 cgManager_.reset();
115
116+ auto dohandle = [&](guint& handle) {
117+ if (handle != 0)
118+ {
119+ g_dbus_connection_signal_unsubscribe(_dbus.get(), handle);
120+ handle = 0;
121+ }
122+ };
123+
124+ dohandle(handle_appStarted);
125+ dohandle(handle_appStopped);
126+ dohandle(handle_appFailed);
127+ dohandle(handle_appPaused);
128+ dohandle(handle_appResumed);
129+ dohandle(handle_managerSignalFocus);
130+ dohandle(handle_managerSignalResume);
131+ dohandle(handle_managerSignalStarting);
132+
133 if (_dbus)
134 g_dbus_connection_flush_sync(_dbus.get(), nullptr, nullptr);
135 _dbus.reset();
136 })
137- , _registry(registry)
138+ , _registry{registry}
139 , _iconFinders()
140-// _manager(nullptr)
141 {
142 auto cancel = thread.getCancellable();
143 _dbus = thread.executeOnThread<std::shared_ptr<GDBusConnection>>([cancel]() {
144@@ -139,6 +156,7 @@
145
146 auto node = json_node_alloc();
147 json_node_init_object(node, mani);
148+ json_object_unref(mani);
149
150 auto retval = std::shared_ptr<JsonObject>(json_node_dup_object(node), json_object_unref);
151
152@@ -168,9 +186,9 @@
153 }
154
155 std::list<AppID::Package> list;
156- for (GList* item = pkgs; item != NULL; item = g_list_next(item))
157+ for (GList* item = pkgs; item != nullptr; item = g_list_next(item))
158 {
159- auto pkgobj = reinterpret_cast<gchar*>(item->data);
160+ auto pkgobj = static_cast<gchar*>(item->data);
161 if (pkgobj)
162 {
163 list.emplace_back(AppID::Package::from_raw(pkgobj));
164@@ -269,7 +287,7 @@
165
166 GVariant* vtpids = g_dbus_connection_call_sync(
167 lmanager.get(), /* connection */
168- name, /* bus name for direct connection is NULL */
169+ name, /* bus name for direct connection is nullptr */
170 "/org/linuxcontainers/cgmanager", /* object */
171 "org.linuxcontainers.cgmanager0_0", /* interface */
172 "GetTasksRecursive", /* method */
173@@ -287,7 +305,7 @@
174 return {};
175 }
176
177- GVariant* vpids = g_variant_get_child_value(vtpids, 0);
178+ auto vpids = g_variant_get_child_value(vtpids, 0);
179 GVariantIter iter;
180 g_variant_iter_init(&iter, vpids);
181 gint32 pid;
182@@ -394,7 +412,7 @@
183 return {};
184 }
185
186- GVariant* instance_list = g_variant_get_child_value(instance_tuple, 0);
187+ auto instance_list = g_variant_get_child_value(instance_tuple, 0);
188 g_variant_unref(instance_tuple);
189
190 GVariantIter instance_iter;
191@@ -425,12 +443,12 @@
192 continue;
193 }
194
195- GVariant* props_dict = g_variant_get_child_value(props_tuple, 0);
196+ auto props_dict = g_variant_get_child_value(props_tuple, 0);
197
198- GVariant* namev = g_variant_lookup_value(props_dict, "name", G_VARIANT_TYPE_STRING);
199+ auto namev = g_variant_lookup_value(props_dict, "name", G_VARIANT_TYPE_STRING);
200 if (namev != nullptr)
201 {
202- auto name = g_variant_get_string(namev, NULL);
203+ auto name = g_variant_get_string(namev, nullptr);
204 g_debug("Adding instance for job '%s': %s", job.c_str(), name);
205 instances.push_back(name);
206 g_variant_unref(namev);
207@@ -487,7 +505,7 @@
208 zeitgeist_log_insert_event(zgLog_.get(), /* log */
209 event, /* event */
210 nullptr, /* cancellable */
211- [](GObject* obj, GAsyncResult* res, gpointer user_data) -> void {
212+ [](GObject* obj, GAsyncResult* res, gpointer user_data) {
213 GError* error = nullptr;
214 GArray* result = nullptr;
215
216@@ -517,24 +535,207 @@
217 return _iconFinders[basePath];
218 }
219
220-#if 0
221-void
222-Registry::Impl::setManager (Registry::Manager* manager)
223-{
224- if (_manager != nullptr)
225- {
226- throw std::runtime_error("Already have a manager and trying to set another");
227- }
228-
229- _manager = manager;
230-}
231-
232-void
233-Registry::Impl::clearManager ()
234-{
235- _manager = nullptr;
236-}
237-#endif
238+/** Structure to track the data needed for upstart events. This cleans
239+ up the lifecycle as we're passing this as a pointer through the
240+ GLib calls. */
241+struct upstartEventData
242+{
243+ /** Keeping a weak pointer because the handle is held by
244+ the registry implementation. */
245+ std::weak_ptr<Registry> weakReg;
246+};
247+
248+/** Take the GVariant of parameters and turn them into an application and
249+ and instance. Easier to read in the smaller function */
250+std::tuple<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> Registry::Impl::managerParams(
251+ const std::shared_ptr<GVariant>& params, const std::shared_ptr<Registry>& reg)
252+{
253+ std::shared_ptr<Application> app;
254+ std::shared_ptr<Application::Instance> instance;
255+
256+ const gchar* cappid = nullptr;
257+ g_variant_get(params.get(), "(&s)", &cappid);
258+
259+ if (cappid != nullptr)
260+ {
261+ auto appid = ubuntu::app_launch::AppID::find(reg, cappid);
262+ app = ubuntu::app_launch::Application::create(appid, reg);
263+ }
264+
265+ return std::make_tuple(app, instance);
266+}
267+
268+/** Used to store data for manager based signal handlers. Has a link to the
269+ registry and the callback to use in a C++ style. */
270+struct managerEventData
271+{
272+ /* Keeping a weak pointer because the handle is held by
273+ the registry implementation. */
274+ std::weak_ptr<Registry> weakReg;
275+ std::function<void(const std::shared_ptr<Registry>& reg,
276+ const std::shared_ptr<Application>& app,
277+ const std::shared_ptr<Application::Instance>& instance,
278+ const std::shared_ptr<GDBusConnection>& dbus,
279+ const std::string& sender,
280+ const std::shared_ptr<GVariant>& params)>
281+ func;
282+};
283+
284+/** Register for a signal for the manager. All of the signals needed this same
285+ code so it got pulled out into a function. Takes the same of the signal, the registry
286+ that we're using and a function to call after we've messaged all the parameters
287+ into being something C++-ish. */
288+guint Registry::Impl::managerSignalHelper(const std::shared_ptr<Registry>& reg,
289+ const std::string& signalname,
290+ std::function<void(const std::shared_ptr<Registry>& reg,
291+ const std::shared_ptr<Application>& app,
292+ const std::shared_ptr<Application::Instance>& instance,
293+ const std::shared_ptr<GDBusConnection>& dbus,
294+ const std::string& sender,
295+ const std::shared_ptr<GVariant>& params)> responsefunc)
296+{
297+ auto focusdata = new managerEventData{reg, responsefunc};
298+
299+ return g_dbus_connection_signal_subscribe(
300+ reg->impl->_dbus.get(), /* bus */
301+ nullptr, /* sender */
302+ "com.canonical.UbuntuAppLaunch", /* interface */
303+ signalname.c_str(), /* signal */
304+ "/", /* path */
305+ nullptr, /* arg0 */
306+ G_DBUS_SIGNAL_FLAGS_NONE,
307+ [](GDBusConnection* cconn, const gchar* csender, const gchar*, const gchar*, const gchar*, GVariant* params,
308+ gpointer user_data) {
309+ auto data = static_cast<managerEventData*>(user_data);
310+ auto reg = data->weakReg.lock();
311+
312+ if (!reg)
313+ {
314+ g_warning("Registry object invalid!");
315+ return;
316+ }
317+
318+ /* If we're still connected and the manager has been cleared
319+ we'll just be a no-op */
320+ if (!reg->impl->manager_)
321+ {
322+ return;
323+ }
324+
325+ auto vparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref);
326+ auto conn = std::shared_ptr<GDBusConnection>(reinterpret_cast<GDBusConnection*>(g_object_ref(cconn)),
327+ [](GDBusConnection* con) { g_clear_object(&con); });
328+ std::string sender = csender;
329+ std::shared_ptr<Application> app;
330+ std::shared_ptr<Application::Instance> instance;
331+
332+ std::tie(app, instance) = managerParams(vparams, reg);
333+
334+ data->func(reg, app, instance, conn, sender, vparams);
335+ },
336+ focusdata,
337+ [](gpointer user_data) {
338+ auto data = static_cast<managerEventData*>(user_data);
339+ delete data;
340+ }); /* user data destroy */
341+}
342+
343+/** Set the manager for the registry. This includes tracking the pointer
344+ as well as setting up the signals to call back into the manager. The
345+ signals are only setup once per registry even if the manager is cleared
346+ and changed again. They will just be no-op's in those cases.
347+*/
348+void Registry::Impl::setManager(const std::shared_ptr<Registry::Manager>& manager, const std::shared_ptr<Registry>& reg)
349+{
350+ if (!reg)
351+ {
352+ throw std::invalid_argument("Passed null registry to setManager()");
353+ }
354+
355+ if (reg->impl->manager_)
356+ {
357+ throw std::logic_error("Already have a manager and trying to set another");
358+ }
359+
360+ g_debug("Setting a new manager");
361+ reg->impl->manager_ = manager;
362+
363+ std::call_once(reg->impl->flag_managerSignals, [reg]() {
364+ if (!reg->impl->thread.executeOnThread<bool>([reg]() {
365+ reg->impl->handle_managerSignalFocus = managerSignalHelper(
366+ reg, "UnityFocusRequest",
367+ [](const std::shared_ptr<Registry>& reg, const std::shared_ptr<Application>& app,
368+ const std::shared_ptr<Application::Instance>& instance,
369+ const std::shared_ptr<GDBusConnection>& /* conn */, const std::string& /* sender */,
370+ const std::shared_ptr<GVariant>& /* params */) {
371+ /* Nothing to do today */
372+ reg->impl->manager_->focusRequest(app, instance, [](bool response) {
373+ /* NOTE: We have no clue what thread this is gonna be
374+ executed on, but since we're just talking to the GDBus
375+ thread it isn't an issue today. Be careful in changing
376+ this code. */
377+ });
378+ });
379+ reg->impl->handle_managerSignalStarting = managerSignalHelper(
380+ reg, "UnityStartingBroadcast",
381+ [](const std::shared_ptr<Registry>& reg, const std::shared_ptr<Application>& app,
382+ const std::shared_ptr<Application::Instance>& instance,
383+ const std::shared_ptr<GDBusConnection>& conn, const std::string& sender,
384+ const std::shared_ptr<GVariant>& params) {
385+
386+ reg->impl->manager_->startingRequest(app, instance, [conn, sender, params](bool response) {
387+ /* NOTE: We have no clue what thread this is gonna be
388+ executed on, but since we're just talking to the GDBus
389+ thread it isn't an issue today. Be careful in changing
390+ this code. */
391+ if (response)
392+ {
393+ g_dbus_connection_emit_signal(conn.get(), sender.c_str(), /* destination */
394+ "/", /* path */
395+ "com.canonical.UbuntuAppLaunch", /* interface */
396+ "UnityStartingSignal", /* signal */
397+ params.get(), /* params, the same */
398+ nullptr); /* error */
399+ }
400+ });
401+ });
402+ reg->impl->handle_managerSignalResume = managerSignalHelper(
403+ reg, "UnityResumeRequest",
404+ [](const std::shared_ptr<Registry>& reg, const std::shared_ptr<Application>& app,
405+ const std::shared_ptr<Application::Instance>& instance,
406+ const std::shared_ptr<GDBusConnection>& conn, const std::string& sender,
407+ const std::shared_ptr<GVariant>& params) {
408+ reg->impl->manager_->resumeRequest(app, instance, [conn, sender, params](bool response) {
409+ /* NOTE: We have no clue what thread this is gonna be
410+ executed on, but since we're just talking to the GDBus
411+ thread it isn't an issue today. Be careful in changing
412+ this code. */
413+ if (response)
414+ {
415+ g_dbus_connection_emit_signal(conn.get(), sender.c_str(), /* destination */
416+ "/", /* path */
417+ "com.canonical.UbuntuAppLaunch", /* interface */
418+ "UnityResumeResponse", /* signal */
419+ params.get(), /* params, the same */
420+ nullptr); /* error */
421+ }
422+ });
423+ });
424+
425+ return true;
426+ }))
427+ {
428+ g_warning("Unable to install manager signals");
429+ }
430+ });
431+}
432+
433+/** Clear the manager pointer */
434+void Registry::Impl::clearManager()
435+{
436+ g_debug("Clearing the manager");
437+ manager_.reset();
438+}
439
440 /** App start watching, if we're registered for the signal we
441 can't wait on it. We are making this static right now because
442@@ -557,5 +758,340 @@
443 return watchingAppStarting_;
444 }
445
446+/** Regex to parse the JOB environment variable from Upstart */
447+const std::regex jobenv_regex{"^JOB=(application\\-(?:click|snap|legacy))$"};
448+/** Regex to parse the INSTANCE environment variable from Upstart */
449+const std::regex instanceenv_regex{"^INSTANCE=(.*?)(?:\\-([0-9]*))?+$"};
450+
451+/** Core of most of the events that come from Upstart directly. Includes parsing of the
452+ Upstart event environment and calling the appropriate signal with the right Application
453+ object and eventually its instance */
454+void Registry::Impl::upstartEventEmitted(
455+ core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>& signal,
456+ const std::shared_ptr<GVariant>& params,
457+ const std::shared_ptr<Registry>& reg)
458+{
459+ std::string jobname;
460+ std::string sappid;
461+ std::string instance;
462+
463+ gchar* env = nullptr;
464+ auto envs = g_variant_get_child_value(params.get(), 1);
465+ GVariantIter iter;
466+ g_variant_iter_init(&iter, envs);
467+
468+ while (g_variant_iter_loop(&iter, "s", &env))
469+ {
470+ std::smatch match;
471+ std::string senv = env;
472+
473+ if (std::regex_match(senv, match, jobenv_regex))
474+ {
475+ jobname = match[1].str();
476+ }
477+ else if (std::regex_match(senv, match, instanceenv_regex))
478+ {
479+ sappid = match[1].str();
480+ instance = match[2].str();
481+ }
482+ }
483+
484+ g_variant_unref(envs);
485+
486+ if (jobname.empty())
487+ {
488+ return;
489+ }
490+
491+ g_debug("Upstart Event for job '%s' appid '%s' instance '%s'", jobname.c_str(), sappid.c_str(), instance.c_str());
492+
493+ auto appid = AppID::find(reg, sappid);
494+ auto app = Application::create(appid, reg);
495+
496+ // TODO: Figure out creating instances
497+
498+ signal(app, {});
499+}
500+
501+/** Grab the signal object for application startup. If we're not already listing for
502+ those signals this sets up a listener for them. */
503+core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>&
504+ Registry::Impl::appStarted(const std::shared_ptr<Registry>& reg)
505+{
506+ std::call_once(flag_appStarted, [reg]() {
507+ reg->impl->thread.executeOnThread<bool>([reg]() {
508+ auto data = new upstartEventData{reg};
509+
510+ reg->impl->handle_appStarted = g_dbus_connection_signal_subscribe(
511+ reg->impl->_dbus.get(), /* bus */
512+ nullptr, /* sender */
513+ DBUS_INTERFACE_UPSTART, /* interface */
514+ "EventEmitted", /* signal */
515+ DBUS_PATH_UPSTART, /* path */
516+ "started", /* arg0 */
517+ G_DBUS_SIGNAL_FLAGS_NONE,
518+ [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params,
519+ gpointer user_data) {
520+ auto data = static_cast<upstartEventData*>(user_data);
521+ auto reg = data->weakReg.lock();
522+
523+ if (!reg)
524+ {
525+ g_warning("Registry object invalid!");
526+ return;
527+ }
528+
529+ auto sparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref);
530+ reg->impl->upstartEventEmitted(reg->impl->sig_appStarted, sparams, reg);
531+ }, /* callback */
532+ data, /* user data */
533+ [](gpointer user_data) {
534+ auto data = static_cast<upstartEventData*>(user_data);
535+ delete data;
536+ }); /* user data destroy */
537+
538+ return true;
539+ });
540+ });
541+
542+ return sig_appStarted;
543+}
544+
545+/** Grab the signal object for application stopping. If we're not already listing for
546+ those signals this sets up a listener for them. */
547+core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>&
548+ Registry::Impl::appStopped(const std::shared_ptr<Registry>& reg)
549+{
550+ std::call_once(flag_appStopped, [reg]() {
551+ reg->impl->thread.executeOnThread<bool>([reg]() {
552+ auto data = new upstartEventData{reg};
553+
554+ reg->impl->handle_appStopped = g_dbus_connection_signal_subscribe(
555+ reg->impl->_dbus.get(), /* bus */
556+ nullptr, /* sender */
557+ DBUS_INTERFACE_UPSTART, /* interface */
558+ "EventEmitted", /* signal */
559+ DBUS_PATH_UPSTART, /* path */
560+ "stopped", /* arg0 */
561+ G_DBUS_SIGNAL_FLAGS_NONE,
562+ [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params,
563+ gpointer user_data) {
564+ auto data = static_cast<upstartEventData*>(user_data);
565+ auto reg = data->weakReg.lock();
566+
567+ if (!reg)
568+ {
569+ g_warning("Registry object invalid!");
570+ return;
571+ }
572+
573+ auto sparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref);
574+ reg->impl->upstartEventEmitted(reg->impl->sig_appStopped, sparams, reg);
575+ }, /* callback */
576+ data, /* user data */
577+ [](gpointer user_data) {
578+ auto data = static_cast<upstartEventData*>(user_data);
579+ delete data;
580+ }); /* user data destroy */
581+
582+ return true;
583+ });
584+ });
585+
586+ return sig_appStopped;
587+}
588+
589+/** Grab the signal object for application failing. If we're not already listing for
590+ those signals this sets up a listener for them. */
591+core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&, Registry::FailureType>&
592+ Registry::Impl::appFailed(const std::shared_ptr<Registry>& reg)
593+{
594+ std::call_once(flag_appFailed, [reg]() {
595+ reg->impl->thread.executeOnThread<bool>([reg]() {
596+ auto data = new upstartEventData{reg};
597+
598+ reg->impl->handle_appFailed = g_dbus_connection_signal_subscribe(
599+ reg->impl->_dbus.get(), /* bus */
600+ nullptr, /* sender */
601+ "com.canonical.UbuntuAppLaunch", /* interface */
602+ "ApplicationFailed", /* signal */
603+ "/", /* path */
604+ nullptr, /* arg0 */
605+ G_DBUS_SIGNAL_FLAGS_NONE,
606+ [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params,
607+ gpointer user_data) {
608+ auto data = static_cast<upstartEventData*>(user_data);
609+ auto reg = data->weakReg.lock();
610+
611+ if (!reg)
612+ {
613+ g_warning("Registry object invalid!");
614+ return;
615+ }
616+
617+ const gchar* sappid = nullptr;
618+ const gchar* typestr = nullptr;
619+
620+ Registry::FailureType type = Registry::FailureType::CRASH;
621+ g_variant_get(params, "(&s&s)", &sappid, &typestr);
622+
623+ if (g_strcmp0("crash", typestr) == 0)
624+ {
625+ type = Registry::FailureType::CRASH;
626+ }
627+ else if (g_strcmp0("start-failure", typestr) == 0)
628+ {
629+ type = Registry::FailureType::START_FAILURE;
630+ }
631+ else
632+ {
633+ g_warning("Application failure type '%s' unknown, reporting as a crash", typestr);
634+ }
635+
636+ auto appid = AppID::find(reg, sappid);
637+ auto app = Application::create(appid, reg);
638+
639+ /* TODO: Instance issues */
640+
641+ reg->impl->sig_appFailed(app, {}, type);
642+ }, /* callback */
643+ data, /* user data */
644+ [](gpointer user_data) {
645+ auto data = static_cast<upstartEventData*>(user_data);
646+ delete data;
647+ }); /* user data destroy */
648+
649+ return true;
650+ });
651+ });
652+
653+ return sig_appFailed;
654+}
655+
656+/** Core handler for pause and resume events. Includes turning the GVariant
657+ pid list into a std::vector and getting the application object. */
658+void Registry::Impl::pauseEventEmitted(core::Signal<const std::shared_ptr<Application>&,
659+ const std::shared_ptr<Application::Instance>&,
660+ const std::vector<pid_t>&>& signal,
661+ const std::shared_ptr<GVariant>& params,
662+ const std::shared_ptr<Registry>& reg)
663+{
664+ std::vector<pid_t> pids;
665+ auto vappid = g_variant_get_child_value(params.get(), 0);
666+ auto vpids = g_variant_get_child_value(params.get(), 1);
667+ guint64 pid;
668+ GVariantIter thispid;
669+ g_variant_iter_init(&thispid, vpids);
670+
671+ while (g_variant_iter_loop(&thispid, "t", &pid))
672+ {
673+ pids.emplace_back(pid);
674+ }
675+
676+ auto cappid = g_variant_get_string(vappid, nullptr);
677+ auto appid = ubuntu::app_launch::AppID::find(reg, cappid);
678+ auto app = Application::create(appid, reg);
679+
680+ /* TODO: Instance */
681+ signal(app, {}, pids);
682+
683+ g_variant_unref(vappid);
684+ g_variant_unref(vpids);
685+
686+ return;
687+}
688+
689+/** Grab the signal object for application paused. If we're not already listing for
690+ those signals this sets up a listener for them. */
691+core::Signal<const std::shared_ptr<Application>&,
692+ const std::shared_ptr<Application::Instance>&,
693+ const std::vector<pid_t>&>&
694+ Registry::Impl::appPaused(const std::shared_ptr<Registry>& reg)
695+{
696+ std::call_once(flag_appPaused, [&]() {
697+ reg->impl->thread.executeOnThread<bool>([reg]() {
698+ auto data = new upstartEventData{reg};
699+
700+ reg->impl->handle_appPaused = g_dbus_connection_signal_subscribe(
701+ reg->impl->_dbus.get(), /* bus */
702+ nullptr, /* sender */
703+ "com.canonical.UbuntuAppLaunch", /* interface */
704+ "ApplicationPaused", /* signal */
705+ "/", /* path */
706+ nullptr, /* arg0 */
707+ G_DBUS_SIGNAL_FLAGS_NONE,
708+ [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params,
709+ gpointer user_data) {
710+ auto data = static_cast<upstartEventData*>(user_data);
711+ auto reg = data->weakReg.lock();
712+
713+ if (!reg)
714+ {
715+ g_warning("Registry object invalid!");
716+ return;
717+ }
718+
719+ auto sparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref);
720+ reg->impl->pauseEventEmitted(reg->impl->sig_appPaused, sparams, reg);
721+ }, /* callback */
722+ data, /* user data */
723+ [](gpointer user_data) {
724+ auto data = static_cast<upstartEventData*>(user_data);
725+ delete data;
726+ }); /* user data destroy */
727+
728+ return true;
729+ });
730+ });
731+
732+ return sig_appPaused;
733+}
734+
735+/** Grab the signal object for application resumed. If we're not already listing for
736+ those signals this sets up a listener for them. */
737+core::Signal<const std::shared_ptr<Application>&,
738+ const std::shared_ptr<Application::Instance>&,
739+ const std::vector<pid_t>&>&
740+ Registry::Impl::appResumed(const std::shared_ptr<Registry>& reg)
741+{
742+ std::call_once(flag_appResumed, [&]() {
743+ reg->impl->thread.executeOnThread<bool>([reg]() {
744+ auto data = new upstartEventData{reg};
745+
746+ reg->impl->handle_appResumed = g_dbus_connection_signal_subscribe(
747+ reg->impl->_dbus.get(), /* bus */
748+ nullptr, /* sender */
749+ "com.canonical.UbuntuAppLaunch", /* interface */
750+ "ApplicationResumed", /* signal */
751+ "/", /* path */
752+ nullptr, /* arg0 */
753+ G_DBUS_SIGNAL_FLAGS_NONE,
754+ [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params,
755+ gpointer user_data) {
756+ auto data = static_cast<upstartEventData*>(user_data);
757+ auto reg = data->weakReg.lock();
758+
759+ if (!reg)
760+ {
761+ g_warning("Registry object invalid!");
762+ return;
763+ }
764+
765+ auto sparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref);
766+ reg->impl->pauseEventEmitted(reg->impl->sig_appResumed, sparams, reg);
767+ }, /* callback */
768+ data, /* user data */
769+ [](gpointer user_data) {
770+ auto data = static_cast<upstartEventData*>(user_data);
771+ delete data;
772+ }); /* user data destroy */
773+
774+ return true;
775+ });
776+ });
777+
778+ return sig_appResumed;
779+}
780+
781 } // namespace app_launch
782 } // namespace ubuntu
783
784=== modified file 'libubuntu-app-launch/registry-impl.h'
785--- libubuntu-app-launch/registry-impl.h 2016-09-23 22:30:51 +0000
786+++ libubuntu-app-launch/registry-impl.h 2017-01-24 04:21:42 +0000
787@@ -53,10 +53,9 @@
788 std::list<AppID::Package> getClickPackages();
789 std::string getClickDir(const std::string& package);
790
791-#if 0
792- void setManager (Registry::Manager* manager);
793- void clearManager ();
794-#endif
795+ static void setManager(const std::shared_ptr<Registry::Manager>& manager,
796+ const std::shared_ptr<Registry>& registry);
797+ void clearManager();
798
799 /** Shared context thread for events and background tasks
800 that UAL subtasks are doing */
801@@ -82,6 +81,22 @@
802 static std::string printJson(std::shared_ptr<JsonObject> jsonobj);
803 static std::string printJson(std::shared_ptr<JsonNode> jsonnode);
804
805+ /* Signals to discover what is happening to apps */
806+ core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>& appStarted(
807+ const std::shared_ptr<Registry>& reg);
808+ core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>& appStopped(
809+ const std::shared_ptr<Registry>& reg);
810+ core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&, FailureType>&
811+ appFailed(const std::shared_ptr<Registry>& reg);
812+ core::Signal<const std::shared_ptr<Application>&,
813+ const std::shared_ptr<Application::Instance>&,
814+ const std::vector<pid_t>&>&
815+ appPaused(const std::shared_ptr<Registry>& reg);
816+ core::Signal<const std::shared_ptr<Application>&,
817+ const std::shared_ptr<Application::Instance>&,
818+ const std::vector<pid_t>&>&
819+ appResumed(const std::shared_ptr<Registry>& reg);
820+
821 /* Signal Hints */
822 /* NOTE: Static because we don't have registry instances in the C
823 code right now. We want these to not be static in the future */
824@@ -89,22 +104,84 @@
825 static bool isWatchingAppStarting();
826
827 private:
828- Registry* _registry;
829-#if 0
830- Registry::Manager* _manager;
831-#endif
832-
833- std::shared_ptr<ClickDB> _clickDB;
834- std::shared_ptr<ClickUser> _clickUser;
835+ Registry* _registry; /**< The Registry that we're spawned from */
836+ std::shared_ptr<Registry::Manager> manager_; /**< Application manager if registered */
837+
838+ std::shared_ptr<ClickDB> _clickDB; /**< Shared instance of the Click Database */
839+ std::shared_ptr<ClickUser> _clickUser; /**< Click database filtered by the current user */
840+
841+ /** Signal object for applications started */
842+ core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&> sig_appStarted;
843+ /** Signal object for applications stopped */
844+ core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&> sig_appStopped;
845+ /** Signal object for applications failed */
846+ core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&, FailureType>
847+ sig_appFailed;
848+ /** Signal object for applications paused */
849+ core::Signal<const std::shared_ptr<Application>&,
850+ const std::shared_ptr<Application::Instance>&,
851+ const std::vector<pid_t>&>
852+ sig_appPaused;
853+ /** Signal object for applications resumed */
854+ core::Signal<const std::shared_ptr<Application>&,
855+ const std::shared_ptr<Application::Instance>&,
856+ const std::vector<pid_t>&>
857+ sig_appResumed;
858+
859+ guint handle_appStarted{0}; /**< GDBus signal watcher handle for app started signal */
860+ guint handle_appStopped{0}; /**< GDBus signal watcher handle for app stopped signal */
861+ guint handle_appFailed{0}; /**< GDBus signal watcher handle for app failed signal */
862+ guint handle_appPaused{0}; /**< GDBus signal watcher handle for app paused signal */
863+ guint handle_appResumed{0}; /**< GDBus signal watcher handle for app resumed signal */
864+ guint handle_managerSignalFocus{0}; /**< GDBus signal watcher handle for app focused signal */
865+ guint handle_managerSignalResume{0}; /**< GDBus signal watcher handle for app resumed signal */
866+ guint handle_managerSignalStarting{0}; /**< GDBus signal watcher handle for app starting signal */
867+
868+ std::once_flag flag_appStarted; /**< Variable to track to see if signal handlers are installed for application
869+ started */
870+ std::once_flag flag_appStopped; /**< Variable to track to see if signal handlers are installed for application
871+ stopped */
872+ std::once_flag
873+ flag_appFailed; /**< Variable to track to see if signal handlers are installed for application failed */
874+ std::once_flag
875+ flag_appPaused; /**< Variable to track to see if signal handlers are installed for application paused */
876+ std::once_flag flag_appResumed; /**< Variable to track to see if signal handlers are installed for application
877+ resumed */
878+ std::once_flag flag_managerSignals; /**< Variable to track to see if signal handlers are installed for the manager
879+ signals of focused, resumed and starting */
880+
881+ void upstartEventEmitted(
882+ core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>& signal,
883+ const std::shared_ptr<GVariant>& params,
884+ const std::shared_ptr<Registry>& reg);
885+ void pauseEventEmitted(core::Signal<const std::shared_ptr<Application>&,
886+ const std::shared_ptr<Application::Instance>&,
887+ const std::vector<pid_t>&>& signal,
888+ const std::shared_ptr<GVariant>& params,
889+ const std::shared_ptr<Registry>& reg);
890+ static std::tuple<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> managerParams(
891+ const std::shared_ptr<GVariant>& params, const std::shared_ptr<Registry>& reg);
892+ static guint managerSignalHelper(const std::shared_ptr<Registry>& reg,
893+ const std::string& signalname,
894+ std::function<void(const std::shared_ptr<Registry>& reg,
895+ const std::shared_ptr<Application>& app,
896+ const std::shared_ptr<Application::Instance>& instance,
897+ const std::shared_ptr<GDBusConnection>&,
898+ const std::string&,
899+ const std::shared_ptr<GVariant>&)> responsefunc);
900
901 void initClick();
902
903+ /** Shared instance of the Zeitgeist Log */
904 std::shared_ptr<ZeitgeistLog> zgLog_;
905
906+ /** Shared connection to CGManager */
907 std::shared_ptr<GDBusConnection> cgManager_;
908
909 void initCGManager();
910
911+ /** All of our icon finders based on the path that they're looking
912+ into */
913 std::unordered_map<std::string, std::shared_ptr<IconFinder>> _iconFinders;
914
915 /** Getting the Upstart job path is relatively expensive in
916
917=== modified file 'libubuntu-app-launch/registry.cpp'
918--- libubuntu-app-launch/registry.cpp 2016-08-26 17:33:34 +0000
919+++ libubuntu-app-launch/registry.cpp 2017-01-24 04:21:42 +0000
920@@ -57,7 +57,7 @@
921 instances.splice(instances.begin(), connection->impl->upstartInstancesForJob("application-snap"));
922
923 /* Remove the instance ID */
924- std::transform(instances.begin(), instances.end(), instances.begin(), [](std::string &instancename) -> std::string {
925+ std::transform(instances.begin(), instances.end(), instances.begin(), [](std::string& instancename) -> std::string {
926 static const std::regex instanceregex("^(.*)-[0-9]*$");
927 std::smatch match;
928 if (std::regex_match(instancename, match, instanceregex))
929@@ -86,11 +86,10 @@
930 }
931
932 g_debug("Overall there are %d instances: %s", int(instanceset.size()),
933- std::accumulate(instanceset.begin(), instanceset.end(), std::string{},
934- [](const std::string &instr, std::string instance) {
935- return instr.empty() ? instance : instr + ", " + instance;
936- })
937- .c_str());
938+ std::accumulate(instanceset.begin(), instanceset.end(), std::string{}, [](const std::string& instr,
939+ std::string instance) {
940+ return instr.empty() ? instance : instr + ", " + instance;
941+ }).c_str());
942
943 /* Convert to Applications */
944 std::list<std::shared_ptr<Application>> apps;
945@@ -127,6 +126,16 @@
946 return list;
947 }
948
949+void Registry::setManager(const std::shared_ptr<Manager>& manager, const std::shared_ptr<Registry>& registry)
950+{
951+ Registry::Impl::setManager(manager, registry);
952+}
953+
954+void Registry::clearManager()
955+{
956+ impl->clearManager();
957+}
958+
959 std::shared_ptr<Registry> defaultRegistry;
960 std::shared_ptr<Registry> Registry::getDefault()
961 {
962@@ -143,5 +152,39 @@
963 defaultRegistry.reset();
964 }
965
966+core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>& Registry::appStarted(
967+ const std::shared_ptr<Registry>& reg)
968+{
969+ return reg->impl->appStarted(reg);
970+}
971+
972+core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>& Registry::appStopped(
973+ const std::shared_ptr<Registry>& reg)
974+{
975+ return reg->impl->appStopped(reg);
976+}
977+
978+core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&, Registry::FailureType>&
979+ Registry::appFailed(const std::shared_ptr<Registry>& reg)
980+{
981+ return reg->impl->appFailed(reg);
982+}
983+
984+core::Signal<const std::shared_ptr<Application>&,
985+ const std::shared_ptr<Application::Instance>&,
986+ const std::vector<pid_t>&>&
987+ Registry::appPaused(const std::shared_ptr<Registry>& reg)
988+{
989+ return reg->impl->appPaused(reg);
990+}
991+
992+core::Signal<const std::shared_ptr<Application>&,
993+ const std::shared_ptr<Application::Instance>&,
994+ const std::vector<pid_t>&>&
995+ Registry::appResumed(const std::shared_ptr<Registry>& reg)
996+{
997+ return reg->impl->appResumed(reg);
998+}
999+
1000 } // namespace app_launch
1001 } // namespace ubuntu
1002
1003=== modified file 'libubuntu-app-launch/registry.h'
1004--- libubuntu-app-launch/registry.h 2016-06-09 14:55:34 +0000
1005+++ libubuntu-app-launch/registry.h 2017-01-24 04:21:42 +0000
1006@@ -73,28 +73,137 @@
1007 */
1008 static std::list<std::shared_ptr<Application>> installedApps(std::shared_ptr<Registry> registry = getDefault());
1009
1010-#if 0 /* TODO -- In next MR */
1011 /* Signals to discover what is happening to apps */
1012- core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> appStarted;
1013- core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> appStopped;
1014- core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, FailureType> appFailed;
1015- core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> appPaused;
1016- core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> appResumed;
1017-
1018- /* The Application Manager, almost always if you're not Unity8, don't
1019- use this API. Testing is a special case. */
1020+ /** Get the signal object that is signaled when an application has been
1021+ started.
1022+
1023+ \note This signal handler is activated on the UAL thread
1024+
1025+ \param reg Registry to get the handler from
1026+ */
1027+ static core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>& appStarted(
1028+ const std::shared_ptr<Registry>& reg = getDefault());
1029+
1030+ /** Get the signal object that is signaled when an application has stopped.
1031+
1032+ \note This signal handler is activated on the UAL thread
1033+
1034+ \param reg Registry to get the handler from
1035+ */
1036+ static core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>& appStopped(
1037+ const std::shared_ptr<Registry>& reg = getDefault());
1038+
1039+ /** Get the signal object that is signaled when an application has failed.
1040+
1041+ \note This signal handler is activated on the UAL thread
1042+
1043+ \param reg Registry to get the handler from
1044+ */
1045+ static core::
1046+ Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&, FailureType>&
1047+ appFailed(const std::shared_ptr<Registry>& reg = getDefault());
1048+
1049+ /** Get the signal object that is signaled when an application has been
1050+ paused.
1051+
1052+ \note This signal handler is activated on the UAL thread
1053+
1054+ \param reg Registry to get the handler from
1055+ */
1056+ static core::Signal<const std::shared_ptr<Application>&,
1057+ const std::shared_ptr<Application::Instance>&,
1058+ const std::vector<pid_t>&>&
1059+ appPaused(const std::shared_ptr<Registry>& reg = getDefault());
1060+
1061+ /** Get the signal object that is signaled when an application has been
1062+ resumed.
1063+
1064+ \note This signal handler is activated on the UAL thread
1065+
1066+ \param reg Registry to get the handler from
1067+ */
1068+ static core::Signal<const std::shared_ptr<Application>&,
1069+ const std::shared_ptr<Application::Instance>&,
1070+ const std::vector<pid_t>&>&
1071+ appResumed(const std::shared_ptr<Registry>& reg = getDefault());
1072+
1073+ /** The Application Manager, almost always if you're not Unity8, don't
1074+ use this API. Testing is a special case. Subclass this interface and
1075+ implement these functions.
1076+
1077+ Each function here is being passed a function object that takes a boolean
1078+ to reply. This will accept or reject the request. The function object
1079+ can be copied to another thread and executed if needed.
1080+
1081+ The reply is required for the application to start. It will block (not
1082+ currently implemented) until approval is given. If there are multiple requests
1083+ sent they may be replied out of order if desired.
1084+ */
1085 class Manager
1086 {
1087- virtual bool focusRequest (std::shared_ptr<Application> app, std::shared_ptr<Application::Instance> instance) = 0;
1088- virtual bool startingRequest (std::shared_ptr<Application> app, std::shared_ptr<Application::Instance> instance) = 0;
1089+ public:
1090+ /** Application wishes to startup
1091+
1092+ \note This signal handler is activated on the UAL thread
1093+
1094+ \param app Application requesting startup
1095+ \param instance Instance of the app, always valid but not useful
1096+ unless mulit-instance app.
1097+ \param reply Function object to reply if it is allowed to start
1098+ */
1099+ virtual void startingRequest(const std::shared_ptr<Application>& app,
1100+ const std::shared_ptr<Application::Instance>& instance,
1101+ std::function<void(bool)> reply) = 0;
1102+
1103+ /** Application wishes to have focus. Usually this occurs when
1104+ a URL for the application is activated and the running app is
1105+ requested.
1106+
1107+ \note This signal handler is activated on the UAL thread
1108+
1109+ \param app Application requesting focus
1110+ \param instance Instance of the app, always valid but not useful
1111+ unless mulit-instance app.
1112+ \param reply Function object to reply if it is allowed to focus
1113+ */
1114+ virtual void focusRequest(const std::shared_ptr<Application>& app,
1115+ const std::shared_ptr<Application::Instance>& instance,
1116+ std::function<void(bool)> reply) = 0;
1117+
1118+ /** Application wishes to resume. Usually this occurs when
1119+ a URL for the application is activated and the running app is
1120+ requested.
1121+
1122+ \note This signal handler is activated on the UAL thread
1123+
1124+ \param app Application requesting resume
1125+ \param instance Instance of the app, always valid but not useful
1126+ unless mulit-instance app.
1127+ \param reply Function object to reply if it is allowed to resume
1128+ */
1129+ virtual void resumeRequest(const std::shared_ptr<Application>& app,
1130+ const std::shared_ptr<Application::Instance>& instance,
1131+ std::function<void(bool)> reply) = 0;
1132
1133 protected:
1134 Manager() = default;
1135+ /* virtual ~Manager() = default;
1136+ TODO: Next ABI break */
1137 };
1138
1139- void setManager (Manager* manager);
1140- void clearManager ();
1141-#endif
1142+ /** Set the manager of applications, which gives permissions for them to
1143+ start and gain focus. In almost all cases this should be Unity8 as it
1144+ will be controlling applications.
1145+
1146+ This function will failure if there is already a manager set.
1147+
1148+ \param manager A reference to the Manager object to call
1149+ \param registry Registry to register the manager on
1150+ */
1151+ static void setManager(const std::shared_ptr<Manager>& manager, const std::shared_ptr<Registry>& registry);
1152+
1153+ /** Remove the current manager on the registry */
1154+ void clearManager();
1155
1156 /* Helper Lists */
1157 /** Get a list of all the helpers for a given helper type
1158
1159=== modified file 'libubuntu-app-launch/ubuntu-app-launch.cpp'
1160--- libubuntu-app-launch/ubuntu-app-launch.cpp 2016-11-10 15:39:08 +0000
1161+++ libubuntu-app-launch/ubuntu-app-launch.cpp 2017-01-24 04:21:42 +0000
1162@@ -39,6 +39,7 @@
1163 #include "appid.h"
1164 #include "registry.h"
1165 #include "registry-impl.h"
1166+#include <algorithm>
1167
1168 static void free_helper (gpointer value);
1169 int kill (pid_t pid, int signal) noexcept;
1170@@ -271,504 +272,365 @@
1171 gpointer user_data;
1172 };
1173
1174-/* The data we keep for each failed observer */
1175-typedef struct _paused_resumed_observer_t paused_resumed_observer_t;
1176-struct _paused_resumed_observer_t {
1177- GDBusConnection * conn;
1178- guint sighandle;
1179- UbuntuAppLaunchAppPausedResumedObserver func;
1180- gpointer user_data;
1181- const gchar * lttng_signal;
1182-};
1183-
1184-/* The lists of Observers */
1185-static GList * starting_array = NULL;
1186-static GList * started_array = NULL;
1187-static GList * stop_array = NULL;
1188-static GList * focus_array = NULL;
1189-static GList * resume_array = NULL;
1190-static GList * failed_array = NULL;
1191-static GList * paused_array = NULL;
1192-static GList * resumed_array = NULL;
1193-
1194-static void
1195-observer_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
1196-{
1197- observer_t * observer = (observer_t *)user_data;
1198-
1199- const gchar * signalname = NULL;
1200- g_variant_get_child(params, 0, "&s", &signalname);
1201-
1202- ual_tracepoint(observer_start, signalname);
1203-
1204- gchar * env = NULL;
1205- GVariant * envs = g_variant_get_child_value(params, 1);
1206- GVariantIter iter;
1207- g_variant_iter_init(&iter, envs);
1208-
1209- gboolean job_found = FALSE;
1210- gboolean job_legacy = FALSE;
1211- gchar * instance = NULL;
1212-
1213- while (g_variant_iter_loop(&iter, "s", &env)) {
1214- if (g_strcmp0(env, "JOB=application-click") == 0) {
1215- job_found = TRUE;
1216- } else if (g_strcmp0(env, "JOB=application-legacy") == 0) {
1217- job_found = TRUE;
1218- job_legacy = TRUE;
1219- } else if (g_strcmp0(env, "JOB=application-snap") == 0) {
1220- job_found = TRUE;
1221- job_legacy = TRUE;
1222- } else if (g_str_has_prefix(env, "INSTANCE=")) {
1223- instance = g_strdup(env + strlen("INSTANCE="));
1224- }
1225- }
1226-
1227- g_variant_unref(envs);
1228-
1229- if (job_legacy && instance != NULL) {
1230- gchar * dash = g_strrstr(instance, "-");
1231- if (dash != NULL) {
1232- dash[0] = '\0';
1233- }
1234- }
1235-
1236- if (job_found && instance != NULL) {
1237- observer->func(instance, observer->user_data);
1238- }
1239-
1240- ual_tracepoint(observer_finish, signalname);
1241-
1242- g_free(instance);
1243-}
1244-
1245-/* Creates the observer structure and registers for the signal with
1246- GDBus so that we can get a callback */
1247-static gboolean
1248-add_app_generic (UbuntuAppLaunchAppObserver observer, gpointer user_data, const gchar * signal, GList ** list)
1249-{
1250- GDBusConnection * conn = gdbus_upstart_ref();
1251-
1252- if (conn == NULL) {
1253+/* Function to take a work function and have it execute on a given
1254+ GMainContext */
1255+static void executeOnContext (const std::shared_ptr<GMainContext>& context, std::function<void()> work)
1256+{
1257+ if (!context) {
1258+ work();
1259+ return;
1260+ }
1261+
1262+ auto heapWork = new std::function<void()>(work);
1263+
1264+ auto source = std::shared_ptr<GSource>(g_idle_source_new(), [](GSource* src) { g_clear_pointer(&src, g_source_unref); });
1265+ g_source_set_callback(source.get(),
1266+ [](gpointer data) {
1267+ auto heapWork = static_cast<std::function<void()>*>(data);
1268+ (*heapWork)();
1269+ return G_SOURCE_REMOVE;
1270+ },
1271+ heapWork,
1272+ [](gpointer data) {
1273+ auto heapWork = static_cast<std::function<void()>*>(data);
1274+ delete heapWork;
1275+ });
1276+
1277+ g_source_attach(source.get(), context.get());
1278+}
1279+
1280+/** A handy helper function that is based of a function to get
1281+ a signal and put it into a map. */
1282+template <core::Signal<const std::shared_ptr<ubuntu::app_launch::Application>&, const std::shared_ptr<ubuntu::app_launch::Application::Instance>&>& (*getSignal)(const std::shared_ptr<ubuntu::app_launch::Registry>&)>
1283+static gboolean
1284+observer_add (UbuntuAppLaunchAppObserver observer, gpointer user_data, std::map<std::pair<UbuntuAppLaunchAppObserver, gpointer>, core::ScopedConnection> &observers)
1285+{
1286+ auto context = std::shared_ptr<GMainContext>(g_main_context_ref_thread_default(), [](GMainContext * context) { g_clear_pointer(&context, g_main_context_unref); });
1287+
1288+ observers.emplace(std::make_pair(
1289+ std::make_pair(observer, user_data),
1290+ core::ScopedConnection(
1291+ getSignal(ubuntu::app_launch::Registry::getDefault())
1292+ .connect([context, observer, user_data](std::shared_ptr<ubuntu::app_launch::Application> app, std::shared_ptr<ubuntu::app_launch::Application::Instance> instance) {
1293+ std::string appid = app->appId();
1294+ executeOnContext(context, [appid, observer, user_data]() {
1295+ observer(appid.c_str(), user_data);
1296+ });
1297+ })
1298+ )
1299+ ));
1300+
1301+ return TRUE;
1302+}
1303+
1304+/** A handy helper to delete items from an observer map */
1305+template<typename observertype>
1306+static gboolean
1307+observer_delete (observertype observer, gpointer user_data, std::map<std::pair<observertype, gpointer>, core::ScopedConnection> &observers)
1308+{
1309+ auto iter = observers.find(std::make_pair(observer, user_data));
1310+
1311+ if (iter == observers.end()) {
1312 return FALSE;
1313 }
1314
1315- observer_t * observert = g_new0(observer_t, 1);
1316-
1317- observert->conn = conn;
1318- observert->func = observer;
1319- observert->user_data = user_data;
1320-
1321- *list = g_list_prepend(*list, observert);
1322-
1323- observert->sighandle = g_dbus_connection_signal_subscribe(conn,
1324- NULL, /* sender */
1325- DBUS_INTERFACE_UPSTART, /* interface */
1326- "EventEmitted", /* signal */
1327- DBUS_PATH_UPSTART, /* path */
1328- signal, /* arg0 */
1329- G_DBUS_SIGNAL_FLAGS_NONE,
1330- observer_cb,
1331- observert,
1332- NULL); /* user data destroy */
1333-
1334+ observers.erase(iter);
1335 return TRUE;
1336 }
1337
1338+/** Map of all the observers listening for app started */
1339+static std::map<std::pair<UbuntuAppLaunchAppObserver, gpointer>, core::ScopedConnection> appStartedObservers;
1340+
1341 gboolean
1342 ubuntu_app_launch_observer_add_app_started (UbuntuAppLaunchAppObserver observer, gpointer user_data)
1343 {
1344- return add_app_generic(observer, user_data, "started", &started_array);
1345-}
1346+ return observer_add<&ubuntu::app_launch::Registry::appStarted>(observer, user_data, appStartedObservers);
1347+}
1348+
1349+gboolean
1350+ubuntu_app_launch_observer_delete_app_started (UbuntuAppLaunchAppObserver observer, gpointer user_data)
1351+{
1352+ return observer_delete<UbuntuAppLaunchAppObserver>(observer, user_data, appStartedObservers);
1353+}
1354+
1355+/* Map of all the observers listening for app stopped */
1356+static std::map<std::pair<UbuntuAppLaunchAppObserver, gpointer>, core::ScopedConnection> appStoppedObservers;
1357
1358 gboolean
1359 ubuntu_app_launch_observer_add_app_stop (UbuntuAppLaunchAppObserver observer, gpointer user_data)
1360 {
1361- return add_app_generic(observer, user_data, "stopped", &stop_array);
1362-}
1363-
1364-/* Creates the observer structure and registers for the signal with
1365- GDBus so that we can get a callback */
1366-static gboolean
1367-add_session_generic (UbuntuAppLaunchAppObserver observer, gpointer user_data, const gchar * signal, GList ** list, GDBusSignalCallback session_cb)
1368-{
1369- GDBusConnection * conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
1370-
1371- if (conn == NULL) {
1372- return FALSE;
1373- }
1374-
1375- observer_t * observert = g_new0(observer_t, 1);
1376-
1377- observert->conn = conn;
1378- observert->func = observer;
1379- observert->user_data = user_data;
1380-
1381- *list = g_list_prepend(*list, observert);
1382-
1383- observert->sighandle = g_dbus_connection_signal_subscribe(conn,
1384- NULL, /* sender */
1385- "com.canonical.UbuntuAppLaunch", /* interface */
1386- signal, /* signal */
1387- "/", /* path */
1388- NULL, /* arg0 */
1389- G_DBUS_SIGNAL_FLAGS_NONE,
1390- session_cb,
1391- observert,
1392- NULL); /* user data destroy */
1393-
1394- return TRUE;
1395-}
1396-
1397-/* Generic handler for a bunch of our signals */
1398-static inline void
1399-generic_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
1400-{
1401- observer_t * observer = (observer_t *)user_data;
1402- const gchar * appid = NULL;
1403-
1404- if (observer->func != NULL) {
1405- g_variant_get(params, "(&s)", &appid);
1406- observer->func(appid, observer->user_data);
1407- }
1408-}
1409-
1410-/* Handle the focus signal when it occurs, call the observer */
1411-static void
1412-focus_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
1413-{
1414- ual_tracepoint(observer_start, "focus");
1415-
1416- generic_signal_cb(conn, sender, object, interface, signal, params, user_data);
1417-
1418- ual_tracepoint(observer_finish, "focus");
1419+ return observer_add<&ubuntu::app_launch::Registry::appStopped>(observer, user_data, appStoppedObservers);
1420+}
1421+
1422+gboolean
1423+ubuntu_app_launch_observer_delete_app_stop (UbuntuAppLaunchAppObserver observer, gpointer user_data)
1424+{
1425+ return observer_delete<UbuntuAppLaunchAppObserver>(observer, user_data, appStoppedObservers);
1426+}
1427+
1428+/** Class to implement the Registry::Manager interface for the C code
1429+ using a GLib mainloop. */
1430+class CManager : public ubuntu::app_launch::Registry::Manager
1431+{
1432+public:
1433+ CManager () {
1434+ g_debug("Creating the CManager object");
1435+ }
1436+ virtual ~CManager() {
1437+ g_debug("Removing the shared the CManager object");
1438+ }
1439+
1440+ void startingRequest(const std::shared_ptr<ubuntu::app_launch::Application>& app,
1441+ const std::shared_ptr<ubuntu::app_launch::Application::Instance>& instance,
1442+ std::function<void(bool)> reply) override {
1443+ requestImpl(app, instance, reply, "starting", startingList);
1444+ }
1445+
1446+ void focusRequest(const std::shared_ptr<ubuntu::app_launch::Application>& app,
1447+ const std::shared_ptr<ubuntu::app_launch::Application::Instance>& instance,
1448+ std::function<void(bool)> reply) override {
1449+ requestImpl(app, instance, reply, "focus", focusList);
1450+ }
1451+
1452+ void resumeRequest(const std::shared_ptr<ubuntu::app_launch::Application> &app,
1453+ const std::shared_ptr<ubuntu::app_launch::Application::Instance> &instance,
1454+ std::function<void(bool)> reply) override {
1455+ requestImpl(app, instance, reply, "resume", resumeList);
1456+ }
1457+
1458+private:
1459+ /** The Data that we track on an observer. It is the functions to
1460+ call, the user data and the context to call it on. */
1461+ struct ObserverData {
1462+ UbuntuAppLaunchAppObserver observer;
1463+ gpointer user_data;
1464+ std::shared_ptr<GMainContext> context;
1465+
1466+ /** Handy constructor to get the context in one place */
1467+ ObserverData(UbuntuAppLaunchAppObserver obs, gpointer ud)
1468+ : observer(obs)
1469+ , user_data(ud) {
1470+ context = std::shared_ptr<GMainContext>(g_main_context_ref_thread_default(), [](GMainContext * context) { g_clear_pointer(&context, g_main_context_unref); });
1471+ }
1472+ };
1473+
1474+ std::list<ObserverData> focusList; /**< List of observers on the focus signal */
1475+ std::list<ObserverData> resumeList; /**< List of observers on the resume signal */
1476+ std::list<ObserverData> startingList; /**< List of observers on the starting signal */
1477+
1478+ /** Removes an observer from a specified list */
1479+ bool removeObserver (std::list<ObserverData> &list, UbuntuAppLaunchAppObserver observer, gpointer user_data) {
1480+ auto iter = std::find_if(list.begin(), list.end(), [observer, user_data](const ObserverData &data) {
1481+ return data.observer == observer && data.user_data == user_data;
1482+ });
1483+
1484+ if (iter == list.end()) {
1485+ return false;
1486+ }
1487+
1488+ list.erase(iter);
1489+ return true;
1490+ }
1491+
1492+ /** Implements a request for a specified list by calling each observer and then the reply */
1493+ inline void requestImpl ( const std::shared_ptr<ubuntu::app_launch::Application> &app,
1494+ const std::shared_ptr<ubuntu::app_launch::Application::Instance> &instance,
1495+ std::function<void(bool)> reply,
1496+ const std::string& name,
1497+ std::list<ObserverData>& list) {
1498+ std::string sappid = app->appId();
1499+ g_debug("CManager %s: %s", name.c_str(), sappid.c_str());
1500+
1501+ for (const auto &data : list) {
1502+ executeOnContext(data.context, [data, sappid]() {
1503+ data.observer(sappid.c_str(), data.user_data);
1504+ });
1505+ }
1506+
1507+ reply(true);
1508+ }
1509+
1510+public:
1511+ void addFocus (UbuntuAppLaunchAppObserver observer, gpointer user_data) {
1512+ focusList.emplace_back(ObserverData(observer, user_data));
1513+ }
1514+ void addResume (UbuntuAppLaunchAppObserver observer, gpointer user_data) {
1515+ resumeList.emplace_back(ObserverData(observer, user_data));
1516+ }
1517+ void addStarting (UbuntuAppLaunchAppObserver observer, gpointer user_data) {
1518+ startingList.emplace_back(ObserverData(observer, user_data));
1519+ }
1520+ bool deleteFocus (UbuntuAppLaunchAppObserver observer, gpointer user_data) {
1521+ return removeObserver(focusList, observer, user_data);
1522+ }
1523+ bool deleteResume (UbuntuAppLaunchAppObserver observer, gpointer user_data) {
1524+ return removeObserver(resumeList, observer, user_data);
1525+ }
1526+ bool deleteStarting (UbuntuAppLaunchAppObserver observer, gpointer user_data) {
1527+ return removeObserver(startingList, observer, user_data);
1528+ }
1529+};
1530+
1531+/** Weak pointer to the CManager if it is still in use. If it gets free'd by
1532+ the registry we're okay with that. */
1533+static std::weak_ptr<CManager> cmanager;
1534+
1535+/** Function to create the CManager if it doesn't currently exist. Otherwise
1536+ just return a lock to it */
1537+static std::shared_ptr<CManager>
1538+ensure_cmanager ()
1539+{
1540+ auto retval = cmanager.lock();
1541+
1542+ if (!retval) {
1543+ retval = std::make_shared<CManager>();
1544+ ubuntu::app_launch::Registry::setManager(retval, ubuntu::app_launch::Registry::getDefault());
1545+ cmanager = retval;
1546+ }
1547+
1548+ return retval;
1549 }
1550
1551 gboolean
1552 ubuntu_app_launch_observer_add_app_focus (UbuntuAppLaunchAppObserver observer, gpointer user_data)
1553 {
1554- return add_session_generic(observer, user_data, "UnityFocusRequest", &focus_array, focus_signal_cb);
1555+ auto manager = ensure_cmanager();
1556+ manager->addFocus(observer, user_data);
1557+ return TRUE;
1558 }
1559
1560-/* Handle the resume signal when it occurs, call the observer, then send a signal back when we're done */
1561-static void
1562-resume_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
1563+gboolean
1564+ubuntu_app_launch_observer_delete_app_focus (UbuntuAppLaunchAppObserver observer, gpointer user_data)
1565 {
1566- ual_tracepoint(observer_start, "resume");
1567-
1568- generic_signal_cb(conn, sender, object, interface, signal, params, user_data);
1569-
1570- GError * error = NULL;
1571- g_dbus_connection_emit_signal(conn,
1572- sender, /* destination */
1573- "/", /* path */
1574- "com.canonical.UbuntuAppLaunch", /* interface */
1575- "UnityResumeResponse", /* signal */
1576- params, /* params, the same */
1577- &error);
1578-
1579- if (error != NULL) {
1580- g_warning("Unable to emit response signal: %s", error->message);
1581- g_error_free(error);
1582- }
1583-
1584- ual_tracepoint(observer_finish, "resume");
1585+ auto manager = ensure_cmanager();
1586+ return manager->deleteFocus(observer, user_data) ? TRUE : FALSE;
1587 }
1588
1589 gboolean
1590 ubuntu_app_launch_observer_add_app_resume (UbuntuAppLaunchAppObserver observer, gpointer user_data)
1591 {
1592- return add_session_generic(observer, user_data, "UnityResumeRequest", &resume_array, resume_signal_cb);
1593+ auto manager = ensure_cmanager();
1594+ manager->addResume(observer, user_data);
1595+ return TRUE;
1596 }
1597
1598-/* Handle the starting signal when it occurs, call the observer, then send a signal back when we're done */
1599-static void
1600-starting_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
1601+gboolean
1602+ubuntu_app_launch_observer_delete_app_resume (UbuntuAppLaunchAppObserver observer, gpointer user_data)
1603 {
1604- ual_tracepoint(observer_start, "starting");
1605-
1606- generic_signal_cb(conn, sender, object, interface, signal, params, user_data);
1607-
1608- GError * error = NULL;
1609- g_dbus_connection_emit_signal(conn,
1610- sender, /* destination */
1611- "/", /* path */
1612- "com.canonical.UbuntuAppLaunch", /* interface */
1613- "UnityStartingSignal", /* signal */
1614- params, /* params, the same */
1615- &error);
1616-
1617- if (error != NULL) {
1618- g_warning("Unable to emit response signal: %s", error->message);
1619- g_error_free(error);
1620- }
1621-
1622- ual_tracepoint(observer_finish, "starting");
1623+ auto manager = ensure_cmanager();
1624+ return manager->deleteResume(observer, user_data) ? TRUE : FALSE;
1625 }
1626
1627 gboolean
1628 ubuntu_app_launch_observer_add_app_starting (UbuntuAppLaunchAppObserver observer, gpointer user_data)
1629 {
1630+ auto manager = ensure_cmanager();
1631 ubuntu::app_launch::Registry::Impl::watchingAppStarting(true);
1632- return add_session_generic(observer, user_data, "UnityStartingBroadcast", &starting_array, starting_signal_cb);
1633-}
1634-
1635-/* Handle the failed signal when it occurs, call the observer */
1636-static void
1637-failed_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
1638-{
1639- failed_observer_t * observer = (failed_observer_t *)user_data;
1640- const gchar * appid = NULL;
1641- const gchar * typestr = NULL;
1642-
1643- ual_tracepoint(observer_start, "failed");
1644-
1645- if (observer->func != NULL) {
1646- UbuntuAppLaunchAppFailed type = UBUNTU_APP_LAUNCH_APP_FAILED_CRASH;
1647- g_variant_get(params, "(&s&s)", &appid, &typestr);
1648-
1649- if (g_strcmp0("crash", typestr) == 0) {
1650- type = UBUNTU_APP_LAUNCH_APP_FAILED_CRASH;
1651- } else if (g_strcmp0("start-failure", typestr) == 0) {
1652- type = UBUNTU_APP_LAUNCH_APP_FAILED_START_FAILURE;
1653- } else {
1654- g_warning("Application failure type '%s' unknown, reporting as a crash", typestr);
1655- }
1656-
1657- observer->func(appid, type, observer->user_data);
1658- }
1659-
1660- ual_tracepoint(observer_finish, "failed");
1661-}
1662-
1663-gboolean
1664-ubuntu_app_launch_observer_add_app_failed (UbuntuAppLaunchAppFailedObserver observer, gpointer user_data)
1665-{
1666- GDBusConnection * conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
1667-
1668- if (conn == NULL) {
1669- return FALSE;
1670- }
1671-
1672- failed_observer_t * observert = g_new0(failed_observer_t, 1);
1673-
1674- observert->conn = conn;
1675- observert->func = observer;
1676- observert->user_data = user_data;
1677-
1678- failed_array = g_list_prepend(failed_array, observert);
1679-
1680- observert->sighandle = g_dbus_connection_signal_subscribe(conn,
1681- NULL, /* sender */
1682- "com.canonical.UbuntuAppLaunch", /* interface */
1683- "ApplicationFailed", /* signal */
1684- "/", /* path */
1685- NULL, /* arg0 */
1686- G_DBUS_SIGNAL_FLAGS_NONE,
1687- failed_signal_cb,
1688- observert,
1689- NULL); /* user data destroy */
1690-
1691- return TRUE;
1692-}
1693-
1694-/* Handle the paused signal when it occurs, call the observer */
1695-static void
1696-paused_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
1697-{
1698- paused_resumed_observer_t * observer = (paused_resumed_observer_t *)user_data;
1699-
1700- ual_tracepoint(observer_start, observer->lttng_signal);
1701-
1702- if (observer->func != NULL) {
1703- GArray * pidarray = g_array_new(TRUE, TRUE, sizeof(GPid));
1704- GVariant * appid = g_variant_get_child_value(params, 0);
1705- GVariant * pids = g_variant_get_child_value(params, 1);
1706- guint64 pid;
1707- GVariantIter thispid;
1708- g_variant_iter_init(&thispid, pids);
1709-
1710- while (g_variant_iter_loop(&thispid, "t", &pid)) {
1711- GPid gpid = (GPid)pid; /* Should be a no-op for most architectures, but just in case */
1712- g_array_append_val(pidarray, gpid);
1713- }
1714-
1715- observer->func(g_variant_get_string(appid, NULL), (GPid *)pidarray->data, observer->user_data);
1716-
1717- g_array_free(pidarray, TRUE);
1718- g_variant_unref(appid);
1719- g_variant_unref(pids);
1720- }
1721-
1722- ual_tracepoint(observer_finish, observer->lttng_signal);
1723-}
1724-
1725-static gboolean
1726-paused_resumed_generic (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data, GList ** queue, const gchar * signal_name, const gchar * lttng_signal)
1727-{
1728- GDBusConnection * conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
1729-
1730- if (conn == NULL) {
1731- return FALSE;
1732- }
1733-
1734- paused_resumed_observer_t * observert = g_new0(paused_resumed_observer_t, 1);
1735-
1736- observert->conn = conn;
1737- observert->func = observer;
1738- observert->user_data = user_data;
1739- observert->lttng_signal = lttng_signal;
1740-
1741- *queue = g_list_prepend(*queue, observert);
1742-
1743- observert->sighandle = g_dbus_connection_signal_subscribe(conn,
1744- NULL, /* sender */
1745- "com.canonical.UbuntuAppLaunch", /* interface */
1746- signal_name, /* signal */
1747- "/", /* path */
1748- NULL, /* arg0 */
1749- G_DBUS_SIGNAL_FLAGS_NONE,
1750- paused_signal_cb,
1751- observert,
1752- NULL); /* user data destroy */
1753-
1754- return TRUE;
1755-}
1756-
1757-gboolean
1758-ubuntu_app_launch_observer_add_app_paused (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data)
1759-{
1760- return paused_resumed_generic(observer, user_data, &paused_array, "ApplicationPaused", "paused");
1761-}
1762-
1763-gboolean
1764-ubuntu_app_launch_observer_add_app_resumed (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data)
1765-{
1766- return paused_resumed_generic(observer, user_data, &resumed_array, "ApplicationResumed", "resumed");
1767-}
1768-
1769-static gboolean
1770-delete_app_generic (UbuntuAppLaunchAppObserver observer, gpointer user_data, GList ** list)
1771-{
1772- observer_t * observert = NULL;
1773- GList * look;
1774-
1775- for (look = *list; look != NULL; look = g_list_next(look)) {
1776- observert = (observer_t *)look->data;
1777-
1778- if (observert->func == observer && observert->user_data == user_data) {
1779- break;
1780- }
1781- }
1782-
1783- if (look == NULL) {
1784- return FALSE;
1785- }
1786-
1787- g_dbus_connection_signal_unsubscribe(observert->conn, observert->sighandle);
1788- g_object_unref(observert->conn);
1789-
1790- g_free(observert);
1791- *list = g_list_delete_link(*list, look);
1792-
1793- return TRUE;
1794-}
1795-
1796-gboolean
1797-ubuntu_app_launch_observer_delete_app_started (UbuntuAppLaunchAppObserver observer, gpointer user_data)
1798-{
1799- return delete_app_generic(observer, user_data, &started_array);
1800-}
1801-
1802-gboolean
1803-ubuntu_app_launch_observer_delete_app_stop (UbuntuAppLaunchAppObserver observer, gpointer user_data)
1804-{
1805- return delete_app_generic(observer, user_data, &stop_array);
1806-}
1807-
1808-gboolean
1809-ubuntu_app_launch_observer_delete_app_resume (UbuntuAppLaunchAppObserver observer, gpointer user_data)
1810-{
1811- return delete_app_generic(observer, user_data, &resume_array);
1812-}
1813-
1814-gboolean
1815-ubuntu_app_launch_observer_delete_app_focus (UbuntuAppLaunchAppObserver observer, gpointer user_data)
1816-{
1817- return delete_app_generic(observer, user_data, &focus_array);
1818+ manager->addStarting(observer, user_data);
1819+ return TRUE;
1820 }
1821
1822 gboolean
1823 ubuntu_app_launch_observer_delete_app_starting (UbuntuAppLaunchAppObserver observer, gpointer user_data)
1824 {
1825+ auto manager = ensure_cmanager();
1826 ubuntu::app_launch::Registry::Impl::watchingAppStarting(false);
1827- return delete_app_generic(observer, user_data, &starting_array);
1828+ return manager->deleteStarting(observer, user_data) ? TRUE : FALSE;
1829+}
1830+
1831+/* Map of all the observers listening for app stopped */
1832+static std::map<std::pair<UbuntuAppLaunchAppFailedObserver, gpointer>, core::ScopedConnection> appFailedObservers;
1833+
1834+gboolean
1835+ubuntu_app_launch_observer_add_app_failed (UbuntuAppLaunchAppFailedObserver observer, gpointer user_data)
1836+{
1837+ auto context = std::shared_ptr<GMainContext>(g_main_context_ref_thread_default(), [](GMainContext * context) { g_clear_pointer(&context, g_main_context_unref); });
1838+
1839+ appFailedObservers.emplace(std::make_pair(
1840+ std::make_pair(observer, user_data),
1841+ core::ScopedConnection(
1842+ ubuntu::app_launch::Registry::appFailed().connect([context, observer, user_data](std::shared_ptr<ubuntu::app_launch::Application> app, std::shared_ptr<ubuntu::app_launch::Application::Instance> instance, ubuntu::app_launch::Registry::FailureType type) {
1843+ std::string appid = app->appId();
1844+ executeOnContext(context, [appid, type, observer, user_data]() {
1845+ UbuntuAppLaunchAppFailed ctype{UBUNTU_APP_LAUNCH_APP_FAILED_CRASH};
1846+
1847+ switch (type) {
1848+ case ubuntu::app_launch::Registry::FailureType::CRASH:
1849+ ctype = UBUNTU_APP_LAUNCH_APP_FAILED_CRASH;
1850+ break;
1851+ case ubuntu::app_launch::Registry::FailureType::START_FAILURE:
1852+ ctype = UBUNTU_APP_LAUNCH_APP_FAILED_START_FAILURE;
1853+ break;
1854+ }
1855+
1856+ observer(appid.c_str(), ctype, user_data);
1857+ });
1858+ })
1859+ )
1860+ ));
1861+
1862+ return TRUE;
1863 }
1864
1865 gboolean
1866 ubuntu_app_launch_observer_delete_app_failed (UbuntuAppLaunchAppFailedObserver observer, gpointer user_data)
1867 {
1868- failed_observer_t * observert = NULL;
1869- GList * look;
1870-
1871- for (look = failed_array; look != NULL; look = g_list_next(look)) {
1872- observert = (failed_observer_t *)look->data;
1873-
1874- if (observert->func == observer && observert->user_data == user_data) {
1875- break;
1876- }
1877- }
1878-
1879- if (look == NULL) {
1880- return FALSE;
1881- }
1882-
1883- g_dbus_connection_signal_unsubscribe(observert->conn, observert->sighandle);
1884- g_object_unref(observert->conn);
1885-
1886- g_free(observert);
1887- failed_array = g_list_delete_link(failed_array, look);
1888-
1889- return TRUE;
1890+ return observer_delete<UbuntuAppLaunchAppFailedObserver>(observer, user_data, appFailedObservers);
1891 }
1892
1893+/** Handy helper for pause and resume here */
1894+template <core::Signal<const std::shared_ptr<ubuntu::app_launch::Application>&, const std::shared_ptr<ubuntu::app_launch::Application::Instance>&, const std::vector<pid_t>&>& (*getSignal)(const std::shared_ptr<ubuntu::app_launch::Registry>&)>
1895 static gboolean
1896-paused_resumed_delete (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data, GList ** list)
1897+observer_add_pause (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data, std::map<std::pair<UbuntuAppLaunchAppPausedResumedObserver, gpointer>, core::ScopedConnection> &observers)
1898 {
1899- paused_resumed_observer_t * observert = NULL;
1900- GList * look;
1901-
1902- for (look = *list; look != NULL; look = g_list_next(look)) {
1903- observert = (paused_resumed_observer_t *)look->data;
1904-
1905- if (observert->func == observer && observert->user_data == user_data) {
1906- break;
1907- }
1908- }
1909-
1910- if (look == NULL) {
1911- return FALSE;
1912- }
1913-
1914- g_dbus_connection_signal_unsubscribe(observert->conn, observert->sighandle);
1915- g_object_unref(observert->conn);
1916-
1917- g_free(observert);
1918- *list = g_list_delete_link(*list, look);
1919+ auto context = std::shared_ptr<GMainContext>(g_main_context_ref_thread_default(), [](GMainContext * context) { g_clear_pointer(&context, g_main_context_unref); });
1920+
1921+ observers.emplace(std::make_pair(
1922+ std::make_pair(observer, user_data),
1923+ core::ScopedConnection(
1924+ getSignal(ubuntu::app_launch::Registry::getDefault())
1925+ .connect([context, observer, user_data](std::shared_ptr<ubuntu::app_launch::Application> app, std::shared_ptr<ubuntu::app_launch::Application::Instance> instance, const std::vector<pid_t> &pids) {
1926+ std::vector<pid_t> lpids = pids;
1927+ lpids.emplace_back(0);
1928+
1929+ std::string appid = app->appId();
1930+
1931+ executeOnContext(context, [appid, observer, user_data, lpids]() {
1932+ observer(appid.c_str(), (int *)(lpids.data()), user_data);
1933+ });
1934+ })
1935+ )
1936+ ));
1937
1938 return TRUE;
1939 }
1940
1941+static std::map<std::pair<UbuntuAppLaunchAppPausedResumedObserver, gpointer>, core::ScopedConnection> appPausedObservers;
1942+
1943+gboolean
1944+ubuntu_app_launch_observer_add_app_paused (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data)
1945+{
1946+ return observer_add_pause<&ubuntu::app_launch::Registry::appPaused>(observer, user_data, appPausedObservers);
1947+}
1948+
1949 gboolean
1950 ubuntu_app_launch_observer_delete_app_paused (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data)
1951 {
1952- return paused_resumed_delete(observer, user_data, &paused_array);
1953+ return observer_delete<UbuntuAppLaunchAppPausedResumedObserver>(observer, user_data, appPausedObservers);
1954+}
1955+
1956+static std::map<std::pair<UbuntuAppLaunchAppPausedResumedObserver, gpointer>, core::ScopedConnection> appResumedObservers;
1957+
1958+gboolean
1959+ubuntu_app_launch_observer_add_app_resumed (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data)
1960+{
1961+ return observer_add_pause<&ubuntu::app_launch::Registry::appResumed>(observer, user_data, appResumedObservers);
1962 }
1963
1964 gboolean
1965 ubuntu_app_launch_observer_delete_app_resumed (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data)
1966 {
1967- return paused_resumed_delete(observer, user_data, &resumed_array);
1968+ return observer_delete<UbuntuAppLaunchAppPausedResumedObserver>(observer, user_data, appResumedObservers);
1969 }
1970
1971 typedef void (*per_instance_func_t) (GDBusConnection * con, GVariant * prop_dict, gpointer user_data);
1972
1973=== modified file 'tests/CMakeLists.txt'
1974--- tests/CMakeLists.txt 2016-09-14 16:43:36 +0000
1975+++ tests/CMakeLists.txt 2017-01-24 04:21:42 +0000
1976@@ -157,6 +157,7 @@
1977
1978 add_custom_target(format-tests
1979 COMMAND clang-format -i -style=file
1980+ failure-test.cc
1981 application-info-desktop.cpp
1982 libual-cpp-test.cc
1983 list-apps.cpp
1984
1985=== modified file 'tests/failure-test.cc'
1986--- tests/failure-test.cc 2016-08-25 18:13:44 +0000
1987+++ tests/failure-test.cc 2017-01-24 04:21:42 +0000
1988@@ -17,120 +17,147 @@
1989 * Ted Gould <ted.gould@canonical.com>
1990 */
1991
1992+#include "eventually-fixture.h"
1993+#include "registry.h"
1994+#include <gio/gio.h>
1995+#include <glib/gstdio.h>
1996 #include <gtest/gtest.h>
1997-#include <glib/gstdio.h>
1998-#include <gio/gio.h>
1999-#include <ubuntu-app-launch.h>
2000-#include "eventually-fixture.h"
2001
2002 class FailureTest : public EventuallyFixture
2003 {
2004- private:
2005- GTestDBus * testbus = NULL;
2006-
2007- protected:
2008- virtual void SetUp() {
2009- testbus = g_test_dbus_new(G_TEST_DBUS_NONE);
2010- g_test_dbus_up(testbus);
2011- }
2012-
2013- virtual void TearDown() {
2014- g_test_dbus_down(testbus);
2015- g_clear_object(&testbus);
2016- }
2017+private:
2018+ GTestDBus* testbus = NULL;
2019+
2020+protected:
2021+ std::shared_ptr<ubuntu::app_launch::Registry> registry;
2022+
2023+ virtual void SetUp() override
2024+ {
2025+ /* Click DB test mode */
2026+ g_setenv("TEST_CLICK_DB", "click-db-dir", TRUE);
2027+ g_setenv("TEST_CLICK_USER", "test-user", TRUE);
2028+
2029+ gchar* linkfarmpath = g_build_filename(CMAKE_SOURCE_DIR, "link-farm", NULL);
2030+ g_setenv("UBUNTU_APP_LAUNCH_LINK_FARM", linkfarmpath, TRUE);
2031+ g_free(linkfarmpath);
2032+
2033+ g_setenv("XDG_DATA_DIRS", CMAKE_SOURCE_DIR, TRUE);
2034+ g_setenv("XDG_CACHE_HOME", CMAKE_SOURCE_DIR "/libertine-data", TRUE);
2035+ g_setenv("XDG_DATA_HOME", CMAKE_SOURCE_DIR "/libertine-home", TRUE);
2036+
2037+ testbus = g_test_dbus_new(G_TEST_DBUS_NONE);
2038+ g_test_dbus_up(testbus);
2039+
2040+ registry = std::make_shared<ubuntu::app_launch::Registry>();
2041+ }
2042+
2043+ virtual void TearDown() override
2044+ {
2045+ registry.reset();
2046+
2047+ g_test_dbus_down(testbus);
2048+ g_clear_object(&testbus);
2049+ }
2050 };
2051
2052-static void
2053-failed_observer (const gchar * appid, UbuntuAppLaunchAppFailed reason, gpointer user_data)
2054-{
2055- if (reason == UBUNTU_APP_LAUNCH_APP_FAILED_CRASH) {
2056- std::string * last = static_cast<std::string *>(user_data);
2057- *last = appid;
2058- }
2059-}
2060-
2061 TEST_F(FailureTest, CrashTest)
2062 {
2063- g_setenv("EXIT_STATUS", "-100", TRUE);
2064- g_setenv("JOB", "application-click", TRUE);
2065- g_setenv("INSTANCE", "foo", TRUE);
2066-
2067- std::string last_observer;
2068- ASSERT_TRUE(ubuntu_app_launch_observer_add_app_failed(failed_observer, &last_observer));
2069-
2070- /* Status based */
2071- ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL));
2072-
2073- EXPECT_EVENTUALLY_EQ("foo", last_observer);
2074-
2075- last_observer.clear();
2076- g_unsetenv("EXIT_STATUS");
2077- g_setenv("EXIT_SIGNAL", "KILL", TRUE);
2078-
2079- /* Signal based */
2080- ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL));
2081-
2082- EXPECT_EVENTUALLY_EQ("foo", last_observer);
2083-
2084- ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_failed(failed_observer, &last_observer));
2085+ g_setenv("EXIT_STATUS", "-100", TRUE);
2086+ g_setenv("JOB", "application-click", TRUE);
2087+ g_setenv("INSTANCE", "foo", TRUE);
2088+
2089+ std::string last_observer;
2090+ ubuntu::app_launch::Registry::appFailed(registry).connect(
2091+ [&last_observer](std::shared_ptr<ubuntu::app_launch::Application> app,
2092+ std::shared_ptr<ubuntu::app_launch::Application::Instance> instance,
2093+ ubuntu::app_launch::Registry::FailureType type) {
2094+ if (type == ubuntu::app_launch::Registry::FailureType::CRASH)
2095+ {
2096+ last_observer = app->appId();
2097+ }
2098+ });
2099+
2100+ /* Status based */
2101+ ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL));
2102+
2103+ EXPECT_EVENTUALLY_EQ("foo", last_observer);
2104+
2105+ last_observer.clear();
2106+ g_unsetenv("EXIT_STATUS");
2107+ g_setenv("EXIT_SIGNAL", "KILL", TRUE);
2108+
2109+ /* Signal based */
2110+ ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL));
2111+
2112+ EXPECT_EVENTUALLY_EQ("foo", last_observer);
2113 }
2114
2115 TEST_F(FailureTest, LegacyTest)
2116 {
2117- g_setenv("EXIT_STATUS", "-100", TRUE);
2118- g_setenv("JOB", "application-legacy", TRUE);
2119- g_setenv("INSTANCE", "foo-1234", TRUE);
2120-
2121- std::string last_observer;
2122- ASSERT_TRUE(ubuntu_app_launch_observer_add_app_failed(failed_observer, &last_observer));
2123-
2124- /* Status based */
2125- ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL));
2126-
2127- EXPECT_EVENTUALLY_EQ("foo", last_observer);
2128-
2129- ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_failed(failed_observer, &last_observer));
2130+ g_setenv("EXIT_STATUS", "-100", TRUE);
2131+ g_setenv("JOB", "application-legacy", TRUE);
2132+ g_setenv("INSTANCE", "foo-1234", TRUE);
2133+
2134+ std::string last_observer;
2135+ ubuntu::app_launch::Registry::appFailed(registry).connect(
2136+ [&last_observer](std::shared_ptr<ubuntu::app_launch::Application> app,
2137+ std::shared_ptr<ubuntu::app_launch::Application::Instance> instance,
2138+ ubuntu::app_launch::Registry::FailureType type) {
2139+ g_debug("Signal handler called");
2140+ if (type == ubuntu::app_launch::Registry::FailureType::CRASH)
2141+ {
2142+ last_observer = app->appId();
2143+ }
2144+ });
2145+
2146+ /* Status based */
2147+ ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL));
2148+
2149+ EXPECT_EVENTUALLY_EQ("foo", last_observer);
2150 }
2151
2152 TEST_F(FailureTest, SnapTest)
2153 {
2154- g_setenv("EXIT_STATUS", "-100", TRUE);
2155- g_setenv("JOB", "application-snap", TRUE);
2156- g_setenv("INSTANCE", "foo_bar_x123-1234", TRUE);
2157-
2158- std::string last_observer;
2159- ASSERT_TRUE(ubuntu_app_launch_observer_add_app_failed(failed_observer, &last_observer));
2160-
2161- /* Status based */
2162- ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL));
2163-
2164- EXPECT_EVENTUALLY_EQ("foo_bar_x123", last_observer);
2165-
2166- ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_failed(failed_observer, &last_observer));
2167-}
2168-
2169-static void
2170-failed_start_observer (const gchar * appid, UbuntuAppLaunchAppFailed reason, gpointer user_data)
2171-{
2172- if (reason == UBUNTU_APP_LAUNCH_APP_FAILED_START_FAILURE) {
2173- std::string * last = static_cast<std::string *>(user_data);
2174- *last = appid;
2175- }
2176+ g_setenv("EXIT_STATUS", "-100", TRUE);
2177+ g_setenv("JOB", "application-snap", TRUE);
2178+ g_setenv("INSTANCE", "com.test.good_application_1.2.3-1234", TRUE);
2179+
2180+ std::string last_observer;
2181+ ubuntu::app_launch::Registry::appFailed(registry).connect(
2182+ [&last_observer](std::shared_ptr<ubuntu::app_launch::Application> app,
2183+ std::shared_ptr<ubuntu::app_launch::Application::Instance> instance,
2184+ ubuntu::app_launch::Registry::FailureType type) {
2185+ if (type == ubuntu::app_launch::Registry::FailureType::CRASH)
2186+ {
2187+ last_observer = app->appId();
2188+ }
2189+ });
2190+
2191+ /* Status based */
2192+ ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL));
2193+
2194+ EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", last_observer);
2195 }
2196
2197 TEST_F(FailureTest, StartTest)
2198 {
2199- g_setenv("JOB", "application-click", TRUE);
2200- g_setenv("INSTANCE", "foo", TRUE);
2201- g_unsetenv("EXIT_STATUS");
2202- g_unsetenv("EXIT_SIGNAL");
2203-
2204- std::string last_observer;
2205- ASSERT_TRUE(ubuntu_app_launch_observer_add_app_failed(failed_start_observer, &last_observer));
2206-
2207- ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL));
2208-
2209- EXPECT_EVENTUALLY_EQ("foo", last_observer);
2210-
2211- ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_failed(failed_start_observer, &last_observer));
2212+ g_setenv("JOB", "application-click", TRUE);
2213+ g_setenv("INSTANCE", "foo", TRUE);
2214+ g_unsetenv("EXIT_STATUS");
2215+ g_unsetenv("EXIT_SIGNAL");
2216+
2217+ std::string last_observer;
2218+ ubuntu::app_launch::Registry::appFailed(registry).connect(
2219+ [&last_observer](std::shared_ptr<ubuntu::app_launch::Application> app,
2220+ std::shared_ptr<ubuntu::app_launch::Application::Instance> instance,
2221+ ubuntu::app_launch::Registry::FailureType type) {
2222+ if (type == ubuntu::app_launch::Registry::FailureType::START_FAILURE)
2223+ {
2224+ last_observer = app->appId();
2225+ }
2226+ });
2227+
2228+ ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL));
2229+
2230+ EXPECT_EVENTUALLY_EQ("foo", last_observer);
2231 }
2232
2233=== modified file 'tests/libual-cpp-test.cc'
2234--- tests/libual-cpp-test.cc 2017-01-10 18:41:25 +0000
2235+++ tests/libual-cpp-test.cc 2017-01-24 04:21:42 +0000
2236@@ -49,32 +49,73 @@
2237 DbusTestDbusMock* mock = NULL;
2238 DbusTestDbusMock* cgmock = NULL;
2239 GDBusConnection* bus = NULL;
2240- std::string last_focus_appid;
2241- std::string last_resume_appid;
2242 guint resume_timeout = 0;
2243 std::shared_ptr<ubuntu::app_launch::Registry> registry;
2244
2245-private:
2246- static void focus_cb(const gchar* appid, gpointer user_data)
2247- {
2248- g_debug("Focus Callback: %s", appid);
2249- LibUAL* _this = static_cast<LibUAL*>(user_data);
2250- _this->last_focus_appid = appid;
2251- }
2252-
2253- static void resume_cb(const gchar* appid, gpointer user_data)
2254- {
2255- g_debug("Resume Callback: %s", appid);
2256- LibUAL* _this = static_cast<LibUAL*>(user_data);
2257- _this->last_resume_appid = appid;
2258-
2259- if (_this->resume_timeout > 0)
2260- {
2261- _this->pause(_this->resume_timeout);
2262- }
2263- }
2264-
2265-protected:
2266+ class ManagerMock : public ubuntu::app_launch::Registry::Manager
2267+ {
2268+ GLib::ContextThread thread;
2269+
2270+ public:
2271+ ManagerMock()
2272+ {
2273+ g_debug("Building a Manager Mock");
2274+ }
2275+
2276+ ~ManagerMock()
2277+ {
2278+ g_debug("Freeing a Manager Mock");
2279+ }
2280+
2281+ void quit()
2282+ {
2283+ thread.quit();
2284+ }
2285+
2286+ ubuntu::app_launch::AppID lastStartedApp;
2287+ ubuntu::app_launch::AppID lastFocusedApp;
2288+ ubuntu::app_launch::AppID lastResumedApp;
2289+
2290+ bool startingResponse{true};
2291+ bool focusResponse{true};
2292+ bool resumeResponse{true};
2293+
2294+ std::chrono::milliseconds startingTimeout{0};
2295+ std::chrono::milliseconds focusTimeout{0};
2296+ std::chrono::milliseconds resumeTimeout{0};
2297+
2298+ void startingRequest(const std::shared_ptr<ubuntu::app_launch::Application>& app,
2299+ const std::shared_ptr<ubuntu::app_launch::Application::Instance>& instance,
2300+ std::function<void(bool)> reply) override
2301+ {
2302+ thread.timeout(startingTimeout, [this, app, instance, reply]() {
2303+ lastStartedApp = app->appId();
2304+ reply(startingResponse);
2305+ });
2306+ }
2307+
2308+ void focusRequest(const std::shared_ptr<ubuntu::app_launch::Application>& app,
2309+ const std::shared_ptr<ubuntu::app_launch::Application::Instance>& instance,
2310+ std::function<void(bool)> reply) override
2311+ {
2312+ thread.timeout(focusTimeout, [this, app, instance, reply]() {
2313+ lastFocusedApp = app->appId();
2314+ reply(focusResponse);
2315+ });
2316+ }
2317+
2318+ void resumeRequest(const std::shared_ptr<ubuntu::app_launch::Application>& app,
2319+ const std::shared_ptr<ubuntu::app_launch::Application::Instance>& instance,
2320+ std::function<void(bool)> reply) override
2321+ {
2322+ thread.timeout(resumeTimeout, [this, app, instance, reply]() {
2323+ lastResumedApp = app->appId();
2324+ reply(resumeResponse);
2325+ });
2326+ }
2327+ };
2328+ std::shared_ptr<ManagerMock> manager;
2329+
2330 /* Useful debugging stuff, but not on by default. You really want to
2331 not get all this noise typically */
2332 void debugConnection()
2333@@ -127,13 +168,13 @@
2334
2335 dbus_test_dbus_mock_object_add_method(mock, obj, "GetJobByName", G_VARIANT_TYPE("s"), G_VARIANT_TYPE("o"),
2336 "if args[0] == 'application-click':\n"
2337- " ret = dbus.ObjectPath('/com/test/application_click')\n"
2338+ " ret = dbus.ObjectPath('/com/test/application_click')\n"
2339 "elif args[0] == 'application-snap':\n"
2340- " ret = dbus.ObjectPath('/com/test/application_snap')\n"
2341+ " ret = dbus.ObjectPath('/com/test/application_snap')\n"
2342 "elif args[0] == 'application-legacy':\n"
2343- " ret = dbus.ObjectPath('/com/test/application_legacy')\n"
2344+ " ret = dbus.ObjectPath('/com/test/application_legacy')\n"
2345 "elif args[0] == 'untrusted-helper':\n"
2346- " ret = dbus.ObjectPath('/com/test/untrusted/helper')\n",
2347+ " ret = dbus.ObjectPath('/com/test/untrusted/helper')\n",
2348 NULL);
2349
2350 dbus_test_dbus_mock_object_add_method(mock, obj, "SetEnv", G_VARIANT_TYPE("(assb)"), NULL, "", NULL);
2351@@ -272,18 +313,23 @@
2352 /* Make sure we pretend the CG manager is just on our bus */
2353 g_setenv("UBUNTU_APP_LAUNCH_CG_MANAGER_SESSION_BUS", "YES", TRUE);
2354
2355- ASSERT_TRUE(ubuntu_app_launch_observer_add_app_focus(focus_cb, this));
2356- ASSERT_TRUE(ubuntu_app_launch_observer_add_app_resume(resume_cb, this));
2357-
2358 registry = std::make_shared<ubuntu::app_launch::Registry>();
2359+
2360+ manager = std::make_shared<ManagerMock>();
2361+ ubuntu::app_launch::Registry::setManager(manager, registry);
2362 }
2363
2364 virtual void TearDown()
2365 {
2366- ubuntu_app_launch_observer_delete_app_focus(focus_cb, this);
2367- ubuntu_app_launch_observer_delete_app_resume(resume_cb, this);
2368-
2369+ manager->quit();
2370 registry.reset();
2371+ manager.reset();
2372+
2373+ // NOTE: This should generally always be commented out, but
2374+ // it is useful for debugging common errors, so leaving it
2375+ // as a comment to make debugging those eaiser.
2376+ //
2377+ // ubuntu::app_launch::Registry::clearDefault();
2378
2379 g_clear_object(&mock);
2380 g_clear_object(&cgmock);
2381@@ -832,33 +878,30 @@
2382 #endif
2383 }
2384
2385-typedef struct
2386-{
2387- unsigned int count;
2388- const gchar* name;
2389-} observer_data_t;
2390-
2391-static void observer_cb(const gchar* appid, gpointer user_data)
2392-{
2393- observer_data_t* data = (observer_data_t*)user_data;
2394-
2395- if (data->name == NULL)
2396- {
2397- data->count++;
2398- }
2399- else if (g_strcmp0(data->name, appid) == 0)
2400- {
2401- data->count++;
2402- }
2403-}
2404-
2405 TEST_F(LibUAL, StartStopObserver)
2406 {
2407- observer_data_t start_data = {.count = 0, .name = nullptr};
2408- observer_data_t stop_data = {.count = 0, .name = nullptr};
2409-
2410- ASSERT_TRUE(ubuntu_app_launch_observer_add_app_started(observer_cb, &start_data));
2411- ASSERT_TRUE(ubuntu_app_launch_observer_add_app_stop(observer_cb, &stop_data));
2412+ int start_count = 0;
2413+ int stop_count = 0;
2414+ ubuntu::app_launch::AppID start_appid;
2415+ ubuntu::app_launch::AppID stop_appid;
2416+
2417+ ubuntu::app_launch::Registry::appStarted(registry).connect(
2418+ [&start_count, &start_appid](std::shared_ptr<ubuntu::app_launch::Application> app,
2419+ std::shared_ptr<ubuntu::app_launch::Application::Instance> instance) {
2420+ if (!start_appid.empty() && !(start_appid == app->appId()))
2421+ return;
2422+
2423+ start_count++;
2424+ });
2425+
2426+ ubuntu::app_launch::Registry::appStopped(registry).connect(
2427+ [&stop_count, &stop_appid](std::shared_ptr<ubuntu::app_launch::Application> app,
2428+ std::shared_ptr<ubuntu::app_launch::Application::Instance> instance) {
2429+ if (!stop_appid.empty() && !(stop_appid == app->appId()))
2430+ return;
2431+
2432+ stop_count++;
2433+ });
2434
2435 DbusTestDbusMockObject* obj =
2436 dbus_test_dbus_mock_get_object(mock, "/com/ubuntu/Upstart", "com.ubuntu.Upstart0_6", NULL);
2437@@ -869,7 +912,7 @@
2438 g_variant_new_parsed("('started', ['JOB=application-click', 'INSTANCE=com.test.good_application_1.2.3'])"),
2439 NULL);
2440
2441- EXPECT_EVENTUALLY_EQ(1, start_data.count);
2442+ EXPECT_EVENTUALLY_EQ(1, start_count);
2443
2444 /* Basic stop */
2445 dbus_test_dbus_mock_object_emit_signal(
2446@@ -877,33 +920,41 @@
2447 g_variant_new_parsed("('stopped', ['JOB=application-click', 'INSTANCE=com.test.good_application_1.2.3'])"),
2448 NULL);
2449
2450- EXPECT_EVENTUALLY_EQ(1, stop_data.count);
2451+ EXPECT_EVENTUALLY_EQ(1, stop_count);
2452
2453 /* Start legacy */
2454- start_data.count = 0;
2455- start_data.name = "multiple";
2456+ start_count = 0;
2457+ start_appid = ubuntu::app_launch::AppID{ubuntu::app_launch::AppID::Package::from_raw({}),
2458+ ubuntu::app_launch::AppID::AppName::from_raw("multiple"),
2459+ ubuntu::app_launch::AppID::Version::from_raw({})};
2460
2461 dbus_test_dbus_mock_object_emit_signal(
2462 mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"),
2463 g_variant_new_parsed("('started', ['JOB=application-legacy', 'INSTANCE=multiple-234235'])"), NULL);
2464
2465- EXPECT_EVENTUALLY_EQ(1, start_data.count);
2466+ EXPECT_EVENTUALLY_EQ(1, start_count);
2467
2468 /* Legacy stop */
2469- stop_data.count = 0;
2470- stop_data.name = "bar";
2471+ stop_count = 0;
2472+ stop_appid = ubuntu::app_launch::AppID{ubuntu::app_launch::AppID::Package::from_raw({}),
2473+ ubuntu::app_launch::AppID::AppName::from_raw("foo"),
2474+ ubuntu::app_launch::AppID::Version::from_raw({})};
2475
2476 dbus_test_dbus_mock_object_emit_signal(
2477 mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"),
2478- g_variant_new_parsed("('stopped', ['JOB=application-legacy', 'INSTANCE=bar-9344321'])"), NULL);
2479+ g_variant_new_parsed("('stopped', ['JOB=application-legacy', 'INSTANCE=foo-9344321'])"), NULL);
2480
2481- EXPECT_EVENTUALLY_EQ(1, stop_data.count);
2482+ EXPECT_EVENTUALLY_EQ(1, stop_count);
2483
2484 /* Test Noise Start */
2485- start_data.count = 0;
2486- start_data.name = "com.test.good_application_1.2.3";
2487- stop_data.count = 0;
2488- stop_data.name = "com.test.good_application_1.2.3";
2489+ start_count = 0;
2490+ start_appid = ubuntu::app_launch::AppID{ubuntu::app_launch::AppID::Package::from_raw("com.test.good"),
2491+ ubuntu::app_launch::AppID::AppName::from_raw("application"),
2492+ ubuntu::app_launch::AppID::Version::from_raw("1.2.3")};
2493+ stop_count = 0;
2494+ stop_appid = ubuntu::app_launch::AppID{ubuntu::app_launch::AppID::Package::from_raw("com.test.good"),
2495+ ubuntu::app_launch::AppID::AppName::from_raw("application"),
2496+ ubuntu::app_launch::AppID::Version::from_raw("1.2.3")};
2497
2498 /* A full lifecycle */
2499 dbus_test_dbus_mock_object_emit_signal(
2500@@ -924,46 +975,33 @@
2501 NULL);
2502
2503 /* Ensure we just signaled once for each */
2504- EXPECT_EVENTUALLY_EQ(1, start_data.count);
2505- EXPECT_EVENTUALLY_EQ(1, stop_data.count);
2506-
2507- /* Remove */
2508- ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_started(observer_cb, &start_data));
2509- ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_stop(observer_cb, &stop_data));
2510-}
2511-
2512-static GDBusMessage* filter_starting(GDBusConnection* conn,
2513- GDBusMessage* message,
2514- gboolean incomming,
2515- gpointer user_data)
2516-{
2517- if (g_strcmp0(g_dbus_message_get_member(message), "UnityStartingSignal") == 0)
2518- {
2519- unsigned int* count = static_cast<unsigned int*>(user_data);
2520- (*count)++;
2521- g_object_unref(message);
2522- return NULL;
2523- }
2524-
2525- return message;
2526-}
2527-
2528-static void starting_observer(const gchar* appid, gpointer user_data)
2529-{
2530- std::string* last = static_cast<std::string*>(user_data);
2531- *last = appid;
2532- return;
2533+ EXPECT_EVENTUALLY_EQ(1, start_count);
2534+ EXPECT_EVENTUALLY_EQ(1, stop_count);
2535 }
2536
2537 TEST_F(LibUAL, StartingResponses)
2538 {
2539- std::string last_observer;
2540+ /* Get Bus */
2541+ GDBusConnection* session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
2542+
2543+ /* Setup filter to count signals out */
2544 unsigned int starting_count = 0;
2545- GDBusConnection* session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
2546- guint filter = g_dbus_connection_add_filter(session, filter_starting, &starting_count, NULL);
2547-
2548- EXPECT_TRUE(ubuntu_app_launch_observer_add_app_starting(starting_observer, &last_observer));
2549-
2550+ guint filter = g_dbus_connection_add_filter(
2551+ session,
2552+ [](GDBusConnection* conn, GDBusMessage* message, gboolean incomming, gpointer user_data) -> GDBusMessage* {
2553+ if (g_strcmp0(g_dbus_message_get_member(message), "UnityStartingSignal") == 0)
2554+ {
2555+ unsigned int* count = static_cast<unsigned int*>(user_data);
2556+ (*count)++;
2557+ g_object_unref(message);
2558+ return NULL;
2559+ }
2560+
2561+ return message;
2562+ },
2563+ &starting_count, NULL);
2564+
2565+ /* Emit a signal */
2566 g_dbus_connection_emit_signal(session, NULL, /* destination */
2567 "/", /* path */
2568 "com.canonical.UbuntuAppLaunch", /* interface */
2569@@ -971,11 +1009,15 @@
2570 g_variant_new("(s)", "com.test.good_application_1.2.3"), /* params, the same */
2571 NULL);
2572
2573- EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", last_observer);
2574+ /* Make sure we run our observer */
2575+ EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID(ubuntu::app_launch::AppID::Package::from_raw("com.test.good"),
2576+ ubuntu::app_launch::AppID::AppName::from_raw("application"),
2577+ ubuntu::app_launch::AppID::Version::from_raw("1.2.3")),
2578+ manager->lastStartedApp);
2579+
2580+ /* Make sure we return */
2581 EXPECT_EVENTUALLY_EQ(1, starting_count);
2582
2583- EXPECT_TRUE(ubuntu_app_launch_observer_delete_app_starting(starting_observer, &last_observer));
2584-
2585 g_dbus_connection_remove_filter(session, filter);
2586 g_object_unref(session);
2587 }
2588@@ -986,8 +1028,10 @@
2589 auto app = ubuntu::app_launch::Application::create(appid, registry);
2590 app->launch();
2591
2592- EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_focus_appid);
2593- EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_resume_appid);
2594+ EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
2595+ this->manager->lastFocusedApp);
2596+ EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
2597+ this->manager->lastResumedApp);
2598 }
2599
2600 GDBusMessage* filter_func_good(GDBusConnection* conn, GDBusMessage* message, gboolean incomming, gpointer user_data)
2601@@ -1021,8 +1065,10 @@
2602
2603 app->launch(uris);
2604
2605- EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_focus_appid);
2606- EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_resume_appid);
2607+ EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
2608+ this->manager->lastFocusedApp);
2609+ EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
2610+ this->manager->lastResumedApp);
2611
2612 g_dbus_connection_remove_filter(session, filter);
2613
2614@@ -1054,8 +1100,10 @@
2615
2616 app->launch(uris);
2617
2618- EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_focus_appid);
2619- EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_resume_appid);
2620+ EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
2621+ this->manager->lastFocusedApp);
2622+ EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
2623+ this->manager->lastResumedApp);
2624 }
2625
2626 TEST_F(LibUAL, UnityTimeoutTest)
2627@@ -1067,8 +1115,10 @@
2628
2629 app->launch();
2630
2631- EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_resume_appid);
2632- EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_focus_appid);
2633+ EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
2634+ this->manager->lastResumedApp);
2635+ EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
2636+ this->manager->lastFocusedApp);
2637 }
2638
2639 TEST_F(LibUAL, UnityTimeoutUriTest)
2640@@ -1082,8 +1132,10 @@
2641
2642 app->launch(uris);
2643
2644- EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_focus_appid);
2645- EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_resume_appid);
2646+ EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
2647+ this->manager->lastFocusedApp);
2648+ EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
2649+ this->manager->lastResumedApp);
2650 }
2651
2652 GDBusMessage* filter_respawn(GDBusConnection* conn, GDBusMessage* message, gboolean incomming, gpointer user_data)
2653@@ -1116,8 +1168,10 @@
2654 g_debug("Start call time: %d ms", (end - start) / 1000);
2655 EXPECT_LT(end - start, 2000 * 1000);
2656
2657- EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_focus_appid);
2658- EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_resume_appid);
2659+ EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
2660+ this->manager->lastFocusedApp);
2661+ EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
2662+ this->manager->lastResumedApp);
2663
2664 g_dbus_connection_remove_filter(session, filter);
2665 g_object_unref(session);
2666@@ -1177,23 +1231,21 @@
2667 g_variant_unref(env);
2668 }
2669
2670-static void failed_observer(const gchar* appid, UbuntuAppLaunchAppFailed reason, gpointer user_data)
2671-{
2672- if (reason == UBUNTU_APP_LAUNCH_APP_FAILED_CRASH)
2673- {
2674- std::string* last = static_cast<std::string*>(user_data);
2675- *last = appid;
2676- }
2677- return;
2678-}
2679-
2680 TEST_F(LibUAL, FailingObserver)
2681 {
2682- std::string last_observer;
2683+ ubuntu::app_launch::AppID lastFailedApp;
2684+ ubuntu::app_launch::Registry::FailureType lastFailedType;
2685+
2686+ ubuntu::app_launch::Registry::appFailed(registry).connect(
2687+ [&lastFailedApp, &lastFailedType](std::shared_ptr<ubuntu::app_launch::Application> app,
2688+ std::shared_ptr<ubuntu::app_launch::Application::Instance> instance,
2689+ ubuntu::app_launch::Registry::FailureType type) {
2690+ lastFailedApp = app->appId();
2691+ lastFailedType = type;
2692+ });
2693+
2694 GDBusConnection* session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
2695
2696- EXPECT_TRUE(ubuntu_app_launch_observer_add_app_failed(failed_observer, &last_observer));
2697-
2698 g_dbus_connection_emit_signal(
2699 session, NULL, /* destination */
2700 "/", /* path */
2701@@ -1202,9 +1254,10 @@
2702 g_variant_new("(ss)", "com.test.good_application_1.2.3", "crash"), /* params, the same */
2703 NULL);
2704
2705- EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", last_observer);
2706+ EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"), lastFailedApp);
2707+ EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::Registry::FailureType::CRASH, lastFailedType);
2708
2709- last_observer.clear();
2710+ lastFailedApp = ubuntu::app_launch::AppID();
2711
2712 g_dbus_connection_emit_signal(
2713 session, NULL, /* destination */
2714@@ -1214,9 +1267,9 @@
2715 g_variant_new("(ss)", "com.test.good_application_1.2.3", "blahblah"), /* params, the same */
2716 NULL);
2717
2718- EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", last_observer);
2719+ EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"), lastFailedApp);
2720
2721- last_observer.clear();
2722+ lastFailedApp = ubuntu::app_launch::AppID();
2723
2724 g_dbus_connection_emit_signal(
2725 session, NULL, /* destination */
2726@@ -1226,9 +1279,7 @@
2727 g_variant_new("(ss)", "com.test.good_application_1.2.3", "start-failure"), /* params, the same */
2728 NULL);
2729
2730- EXPECT_EVENTUALLY_EQ(true, last_observer.empty());
2731-
2732- EXPECT_TRUE(ubuntu_app_launch_observer_delete_app_failed(failed_observer, &last_observer));
2733+ EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::Registry::FailureType::START_FAILURE, lastFailedType);
2734
2735 g_object_unref(session);
2736 }
2737
2738=== modified file 'tools/CMakeLists.txt'
2739--- tools/CMakeLists.txt 2016-06-18 18:24:27 +0000
2740+++ tools/CMakeLists.txt 2017-01-24 04:21:42 +0000
2741@@ -39,7 +39,7 @@
2742 # ubuntu-app-launch
2743 ########################
2744
2745-add_executable(ubuntu-app-launch ubuntu-app-launch.c)
2746+add_executable(ubuntu-app-launch ubuntu-app-launch.cpp)
2747 set_target_properties(ubuntu-app-launch PROPERTIES OUTPUT_NAME "ubuntu-app-launch")
2748 target_link_libraries(ubuntu-app-launch ubuntu-launcher)
2749 install(TARGETS ubuntu-app-launch RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}")
2750@@ -48,7 +48,7 @@
2751 # ubuntu-app-watch
2752 ########################
2753
2754-add_executable(ubuntu-app-watch ubuntu-app-watch.c)
2755+add_executable(ubuntu-app-watch ubuntu-app-watch.cpp)
2756 set_target_properties(ubuntu-app-watch PROPERTIES OUTPUT_NAME "ubuntu-app-watch")
2757 target_link_libraries(ubuntu-app-watch ubuntu-launcher)
2758 install(TARGETS ubuntu-app-watch RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}")
2759@@ -116,3 +116,22 @@
2760 target_link_libraries(ubuntu-app-usage ubuntu-launcher)
2761 install(TARGETS ubuntu-app-usage RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}")
2762
2763+########################
2764+# Formatting
2765+########################
2766+
2767+add_custom_target(format-tools
2768+ COMMAND clang-format -i -style=file
2769+ ubuntu-app-info.cpp
2770+ ubuntu-app-launch-appids.cpp
2771+ ubuntu-app-launch.cpp
2772+ ubuntu-app-list.cpp
2773+ ubuntu-app-list-pids.cpp
2774+ ubuntu-app-pid.cpp
2775+ ubuntu-app-stop.cpp
2776+ ubuntu-app-triplet.cpp
2777+ ubuntu-app-watch.cpp
2778+ ubuntu-helper-list.cpp
2779+ ubuntu-helper-start.cpp
2780+ ubuntu-helper-stop.cpp
2781+)
2782
2783=== modified file 'tools/ubuntu-app-info.cpp'
2784--- tools/ubuntu-app-info.cpp 2016-05-04 14:09:10 +0000
2785+++ tools/ubuntu-app-info.cpp 2017-01-24 04:21:42 +0000
2786@@ -17,34 +17,40 @@
2787 * Ted Gould <ted.gould@canonical.com>
2788 */
2789
2790-#include <iostream>
2791 #include "libubuntu-app-launch/application.h"
2792 #include "libubuntu-app-launch/registry.h"
2793+#include <iostream>
2794
2795-int main(int argc, char* argv[])
2796+int main(int argc, char *argv[])
2797 {
2798- if (argc != 2) {
2799+ if (argc != 2)
2800+ {
2801 std::cerr << "Usage: " << argv[0] << " (appid)" << std::endl;
2802 exit(1);
2803 }
2804
2805 auto appid = ubuntu::app_launch::AppID::find(argv[1]);
2806- if (appid.empty()) {
2807+ if (appid.empty())
2808+ {
2809 std::cerr << "Unable to find app for appid: " << argv[1] << std::endl;
2810 return 1;
2811 }
2812
2813 std::shared_ptr<ubuntu::app_launch::Application> app;
2814- try {
2815+ try
2816+ {
2817 app = ubuntu::app_launch::Application::create(appid, ubuntu::app_launch::Registry::getDefault());
2818 if (!app)
2819 throw std::runtime_error("Application object is nullptr");
2820- } catch (std::runtime_error &e) {
2821+ }
2822+ catch (std::runtime_error &e)
2823+ {
2824 std::cerr << "Unable to find application for AppID: " << argv[1] << std::endl;
2825 exit(1);
2826 }
2827
2828- try {
2829+ try
2830+ {
2831 auto info = app->info();
2832
2833 std::cout << "Name: " << info->name().value() << std::endl;
2834@@ -64,8 +70,11 @@
2835 std::cout << " Inv Landscape: " << info->supportedOrientations().invertedLandscape << std::endl;
2836 std::cout << "Rotates: " << info->rotatesWindowContents().value() << std::endl;
2837 std::cout << "Ubuntu Lifecycle: " << info->supportsUbuntuLifecycle().value() << std::endl;
2838- } catch (std::runtime_error &e) {
2839- std::cerr << "Unable to parse Application info for application '" << std::string(appid) << "': " << e.what() << std::endl;
2840+ }
2841+ catch (std::runtime_error &e)
2842+ {
2843+ std::cerr << "Unable to parse Application info for application '" << std::string(appid) << "': " << e.what()
2844+ << std::endl;
2845 exit(1);
2846 }
2847
2848
2849=== renamed file 'tools/ubuntu-app-launch.c' => 'tools/ubuntu-app-launch.cpp'
2850--- tools/ubuntu-app-launch.c 2016-02-08 19:03:31 +0000
2851+++ tools/ubuntu-app-launch.cpp 2017-01-24 04:21:42 +0000
2852@@ -1,5 +1,5 @@
2853 /*
2854- * Copyright 2013 Canonical Ltd.
2855+ * Copyright © 2016 Canonical Ltd.
2856 *
2857 * This program is free software: you can redistribute it and/or modify it
2858 * under the terms of the GNU General Public License version 3, as published
2859@@ -17,71 +17,59 @@
2860 * Ted Gould <ted.gould@canonical.com>
2861 */
2862
2863-#include <glib.h>
2864-#include "libubuntu-app-launch/ubuntu-app-launch.h"
2865-
2866-const gchar * global_appid = NULL;
2867-int retval = 0;
2868-
2869-static void
2870-good_observer (const gchar * appid, gpointer user_data)
2871-{
2872- if (g_strcmp0(appid, global_appid) != 0) {
2873- return;
2874- }
2875-
2876- g_debug("Application '%s' running", appid);
2877- g_main_loop_quit((GMainLoop *)user_data);
2878-}
2879-
2880-static void
2881-bad_observer (const gchar * appid, UbuntuAppLaunchAppFailed failure_type, gpointer user_data)
2882-{
2883- if (g_strcmp0(appid, global_appid) != 0) {
2884- return;
2885- }
2886-
2887- g_debug("Application '%s' failed: %s", appid, failure_type == UBUNTU_APP_LAUNCH_APP_FAILED_CRASH ? "crash" : "startup failure");
2888- retval = -1;
2889- g_main_loop_quit((GMainLoop *)user_data);
2890-}
2891-
2892-int
2893-main (int argc, gchar * argv[]) {
2894- if (argc < 2) {
2895- g_printerr("Usage: %s <app id> [uris]\n", argv[0]);
2896- return 1;
2897- }
2898-
2899- global_appid = argv[1];
2900- GMainLoop * mainloop = g_main_loop_new(NULL, FALSE);
2901-
2902- gchar ** uris = NULL;
2903- if (argc > 2) {
2904- int i;
2905-
2906- uris = g_new0(gchar *, argc - 1);
2907-
2908- for (i = 2; i < argc; i++) {
2909- uris[i - 2] = argv[i];
2910- }
2911- }
2912-
2913- ubuntu_app_launch_observer_add_app_started(good_observer, mainloop);
2914- ubuntu_app_launch_observer_add_app_focus(good_observer, mainloop);
2915-
2916- ubuntu_app_launch_observer_add_app_failed(bad_observer, mainloop);
2917-
2918- ubuntu_app_launch_start_application(global_appid, (const gchar * const *)uris);
2919-
2920- g_main_loop_run(mainloop);
2921-
2922- ubuntu_app_launch_observer_delete_app_started(good_observer, mainloop);
2923- ubuntu_app_launch_observer_delete_app_focus(good_observer, mainloop);
2924- ubuntu_app_launch_observer_delete_app_failed(bad_observer, mainloop);
2925-
2926- g_main_loop_unref(mainloop);
2927- g_free(uris);
2928-
2929- return retval;
2930+#include "libubuntu-app-launch/application.h"
2931+#include "libubuntu-app-launch/registry.h"
2932+#include <csignal>
2933+#include <future>
2934+#include <iostream>
2935+
2936+ubuntu::app_launch::AppID global_appid;
2937+std::promise<int> retval;
2938+
2939+int main(int argc, char* argv[])
2940+{
2941+ if (argc < 2)
2942+ {
2943+ std::cerr << "Usage: " << argv[0] << " <app id> [uris]" << std::endl;
2944+ return 1;
2945+ }
2946+
2947+ global_appid = ubuntu::app_launch::AppID::find(argv[1]);
2948+
2949+ std::vector<ubuntu::app_launch::Application::URL> urls;
2950+ for (int i = 2; i < argc; i++)
2951+ {
2952+ urls.push_back(ubuntu::app_launch::Application::URL::from_raw(argv[i]));
2953+ }
2954+
2955+ ubuntu::app_launch::Registry::appStarted().connect(
2956+ [](std::shared_ptr<ubuntu::app_launch::Application> app,
2957+ std::shared_ptr<ubuntu::app_launch::Application::Instance> instance) {
2958+ if (app->appId() != global_appid)
2959+ {
2960+ return;
2961+ }
2962+
2963+ std::cout << "Started: " << (std::string)app->appId() << std::endl;
2964+ retval.set_value(EXIT_SUCCESS);
2965+ });
2966+
2967+ ubuntu::app_launch::Registry::appFailed().connect(
2968+ [](std::shared_ptr<ubuntu::app_launch::Application> app,
2969+ std::shared_ptr<ubuntu::app_launch::Application::Instance> instance,
2970+ ubuntu::app_launch::Registry::FailureType type) {
2971+ if (app->appId() != global_appid)
2972+ {
2973+ return;
2974+ }
2975+
2976+ std::cout << "Failed: " << (std::string)app->appId() << std::endl;
2977+ retval.set_value(EXIT_FAILURE);
2978+ });
2979+
2980+ auto app = ubuntu::app_launch::Application::create(global_appid, ubuntu::app_launch::Registry::getDefault());
2981+ app->launch(urls);
2982+
2983+ std::signal(SIGTERM, [](int signal) -> void { retval.set_value(EXIT_SUCCESS); });
2984+ return retval.get_future().get();
2985 }
2986
2987=== modified file 'tools/ubuntu-app-list-pids.cpp'
2988--- tools/ubuntu-app-list-pids.cpp 2016-05-03 01:46:27 +0000
2989+++ tools/ubuntu-app-list-pids.cpp 2017-01-24 04:21:42 +0000
2990@@ -30,12 +30,14 @@
2991 }
2992
2993 auto appid = ubuntu::app_launch::AppID::find(argv[1]);
2994- if (appid.empty()) {
2995+ if (appid.empty())
2996+ {
2997 std::cerr << "Unable to find app for appid: " << argv[1] << std::endl;
2998 return 1;
2999 }
3000
3001- try {
3002+ try
3003+ {
3004 auto app = ubuntu::app_launch::Application::create(appid, ubuntu::app_launch::Registry::getDefault());
3005
3006 for (auto instance : app->instances())
3007@@ -45,7 +47,9 @@
3008 std::cout << pid << std::endl;
3009 }
3010 }
3011- } catch (std::runtime_error &e) {
3012+ }
3013+ catch (std::runtime_error& e)
3014+ {
3015 std::cerr << "Unable to find application for '" << std::string(appid) << "': " << e.what() << std::endl;
3016 return 1;
3017 }
3018
3019=== modified file 'tools/ubuntu-app-list.cpp'
3020--- tools/ubuntu-app-list.cpp 2016-02-09 21:12:54 +0000
3021+++ tools/ubuntu-app-list.cpp 2017-01-24 04:21:42 +0000
3022@@ -17,8 +17,8 @@
3023 * Ted Gould <ted.gould@canonical.com>
3024 */
3025
3026+#include "libubuntu-app-launch/registry.h"
3027 #include <iostream>
3028-#include "libubuntu-app-launch/registry.h"
3029
3030 int main(int argc, char* argv[])
3031 {
3032
3033=== modified file 'tools/ubuntu-app-pid.cpp'
3034--- tools/ubuntu-app-pid.cpp 2016-05-03 01:46:51 +0000
3035+++ tools/ubuntu-app-pid.cpp 2017-01-24 04:21:42 +0000
3036@@ -18,8 +18,8 @@
3037 */
3038
3039 #include <iostream>
3040+#include <libubuntu-app-launch/application.h>
3041 #include <libubuntu-app-launch/registry.h>
3042-#include <libubuntu-app-launch/application.h>
3043
3044 int main(int argc, char* argv[])
3045 {
3046@@ -31,12 +31,14 @@
3047 }
3048
3049 auto appid = ubuntu::app_launch::AppID::find(argv[1]);
3050- if (appid.empty()) {
3051+ if (appid.empty())
3052+ {
3053 std::cerr << "Unable to find app for appid: " << argv[1] << std::endl;
3054 return 1;
3055 }
3056
3057- try {
3058+ try
3059+ {
3060 auto app = ubuntu::app_launch::Application::create(appid, ubuntu::app_launch::Registry::getDefault());
3061 auto pid = app->instances()[0]->primaryPid();
3062
3063@@ -47,7 +49,9 @@
3064
3065 std::cout << pid << std::endl;
3066 return 0;
3067- } catch (std::runtime_error &e) {
3068+ }
3069+ catch (std::runtime_error& e)
3070+ {
3071 std::cerr << "Unable to find application for '" << std::string(appid) << "': " << e.what() << std::endl;
3072 return 1;
3073 }
3074
3075=== modified file 'tools/ubuntu-app-stop.cpp'
3076--- tools/ubuntu-app-stop.cpp 2016-05-03 01:46:27 +0000
3077+++ tools/ubuntu-app-stop.cpp 2017-01-24 04:21:42 +0000
3078@@ -30,19 +30,23 @@
3079 }
3080
3081 auto appid = ubuntu::app_launch::AppID::find(argv[1]);
3082- if (appid.empty()) {
3083+ if (appid.empty())
3084+ {
3085 std::cerr << "Unable to find app for appid: " << argv[1] << std::endl;
3086 return 1;
3087 }
3088
3089- try {
3090+ try
3091+ {
3092 auto app = ubuntu::app_launch::Application::create(appid, ubuntu::app_launch::Registry::getDefault());
3093
3094 for (auto instance : app->instances())
3095 {
3096 instance->stop();
3097 }
3098- } catch (std::runtime_error &e) {
3099+ }
3100+ catch (std::runtime_error& e)
3101+ {
3102 std::cerr << "Unable to find application for '" << std::string(appid) << "': " << e.what() << std::endl;
3103 return 1;
3104 }
3105
3106=== modified file 'tools/ubuntu-app-triplet.cpp'
3107--- tools/ubuntu-app-triplet.cpp 2016-02-09 21:12:54 +0000
3108+++ tools/ubuntu-app-triplet.cpp 2017-01-24 04:21:42 +0000
3109@@ -17,8 +17,8 @@
3110 * Ted Gould <ted.gould@canonical.com>
3111 */
3112
3113+#include "libubuntu-app-launch/application.h"
3114 #include <iostream>
3115-#include "libubuntu-app-launch/application.h"
3116
3117 int main(int argc, char* argv[])
3118 {
3119
3120=== renamed file 'tools/ubuntu-app-watch.c' => 'tools/ubuntu-app-watch.cpp'
3121--- tools/ubuntu-app-watch.c 2016-02-08 19:03:31 +0000
3122+++ tools/ubuntu-app-watch.cpp 2017-01-24 04:21:42 +0000
3123@@ -1,5 +1,5 @@
3124 /*
3125- * Copyright 2013 Canonical Ltd.
3126+ * Copyright © 2015 Canonical Ltd.
3127 *
3128 * This program is free software: you can redistribute it and/or modify it
3129 * under the terms of the GNU General Public License version 3, as published
3130@@ -17,91 +17,64 @@
3131 * Ted Gould <ted.gould@canonical.com>
3132 */
3133
3134-#include "libubuntu-app-launch/ubuntu-app-launch.h"
3135-
3136-void
3137-starting (const gchar * appid, gpointer user_data)
3138-{
3139- g_print("Starting %s\n", appid);
3140- return;
3141-}
3142-
3143-void
3144-started (const gchar * appid, gpointer user_data)
3145-{
3146- g_print("Started %s\n", appid);
3147- return;
3148-}
3149-
3150-void
3151-stopped (const gchar * appid, gpointer user_data)
3152-{
3153- g_print("Stop %s\n", appid);
3154- return;
3155-}
3156-
3157-void
3158-resumed (const gchar * appid, GPid * pids, gpointer user_data)
3159-{
3160- g_print("Resumed %s\n", appid);
3161- return;
3162-}
3163-
3164-void
3165-paused (const gchar * appid, GPid * pids, gpointer user_data)
3166-{
3167- g_print("Paused %s\n", appid);
3168- return;
3169-}
3170-
3171-void
3172-focus (const gchar * appid, gpointer user_data)
3173-{
3174- g_print("Focus %s\n", appid);
3175- return;
3176-}
3177-
3178-void
3179-fail (const gchar * appid, UbuntuAppLaunchAppFailed failhow, gpointer user_data)
3180-{
3181- const gchar * failstr = "unknown";
3182- switch (failhow) {
3183- case UBUNTU_APP_LAUNCH_APP_FAILED_CRASH:
3184- failstr = "crashed";
3185- break;
3186- case UBUNTU_APP_LAUNCH_APP_FAILED_START_FAILURE:
3187- failstr = "startup";
3188- break;
3189- }
3190-
3191- g_print("Fail %s (%s)\n", appid, failstr);
3192- return;
3193-}
3194-
3195-
3196-int
3197-main (int argc, gchar * argv[])
3198-{
3199- ubuntu_app_launch_observer_add_app_starting(starting, NULL);
3200- ubuntu_app_launch_observer_add_app_started(started, NULL);
3201- ubuntu_app_launch_observer_add_app_stop(stopped, NULL);
3202- ubuntu_app_launch_observer_add_app_focus(focus, NULL);
3203- ubuntu_app_launch_observer_add_app_resumed(resumed, NULL);
3204- ubuntu_app_launch_observer_add_app_paused(paused, NULL);
3205- ubuntu_app_launch_observer_add_app_failed(fail, NULL);
3206-
3207- GMainLoop * mainloop = g_main_loop_new(NULL, FALSE);
3208- g_main_loop_run(mainloop);
3209-
3210- ubuntu_app_launch_observer_delete_app_starting(starting, NULL);
3211- ubuntu_app_launch_observer_delete_app_started(started, NULL);
3212- ubuntu_app_launch_observer_delete_app_stop(stopped, NULL);
3213- ubuntu_app_launch_observer_delete_app_focus(focus, NULL);
3214- ubuntu_app_launch_observer_delete_app_resumed(resumed, NULL);
3215- ubuntu_app_launch_observer_delete_app_paused(paused, NULL);
3216- ubuntu_app_launch_observer_delete_app_failed(fail, NULL);
3217-
3218- g_main_loop_unref(mainloop);
3219-
3220- return 0;
3221+#include "libubuntu-app-launch/registry.h"
3222+#include <csignal>
3223+#include <future>
3224+
3225+std::promise<int> retval;
3226+
3227+int main(int argc, char* argv[])
3228+{
3229+ ubuntu::app_launch::Registry registry;
3230+
3231+ registry.appStarted().connect([](const std::shared_ptr<ubuntu::app_launch::Application>& app,
3232+ const std::shared_ptr<ubuntu::app_launch::Application::Instance>& instance) {
3233+ std::cout << "Started: " << (std::string)app->appId() << std::endl;
3234+ });
3235+ registry.appStopped().connect([](const std::shared_ptr<ubuntu::app_launch::Application>& app,
3236+ const std::shared_ptr<ubuntu::app_launch::Application::Instance>& instance) {
3237+ std::cout << "Stopped: " << (std::string)app->appId() << std::endl;
3238+ });
3239+ registry.appPaused().connect([](const std::shared_ptr<ubuntu::app_launch::Application>& app,
3240+ const std::shared_ptr<ubuntu::app_launch::Application::Instance>& instance,
3241+ const std::vector<pid_t>& pids) {
3242+ std::cout << "Paused: " << (std::string)app->appId() << " (";
3243+
3244+ for (auto pid : pids)
3245+ {
3246+ std::cout << std::to_string(pid) << " ";
3247+ }
3248+
3249+ std::cout << ")" << std::endl;
3250+ });
3251+ registry.appResumed().connect([](const std::shared_ptr<ubuntu::app_launch::Application>& app,
3252+ const std::shared_ptr<ubuntu::app_launch::Application::Instance>& instance,
3253+ const std::vector<pid_t>& pids) {
3254+ std::cout << "Resumed: " << (std::string)app->appId() << " (";
3255+
3256+ for (auto pid : pids)
3257+ {
3258+ std::cout << std::to_string(pid) << " ";
3259+ }
3260+
3261+ std::cout << ")" << std::endl;
3262+ });
3263+ registry.appFailed().connect([](const std::shared_ptr<ubuntu::app_launch::Application>& app,
3264+ const std::shared_ptr<ubuntu::app_launch::Application::Instance>& instance,
3265+ ubuntu::app_launch::Registry::FailureType type) {
3266+ std::cout << "Failed: " << (std::string)app->appId();
3267+ switch (type)
3268+ {
3269+ case ubuntu::app_launch::Registry::FailureType::CRASH:
3270+ std::cout << " (crash)";
3271+ break;
3272+ case ubuntu::app_launch::Registry::FailureType::START_FAILURE:
3273+ std::cout << " (start failure)";
3274+ break;
3275+ }
3276+ std::cout << std::endl;
3277+ });
3278+
3279+ std::signal(SIGTERM, [](int signal) -> void { retval.set_value(EXIT_SUCCESS); });
3280+ return retval.get_future().get();
3281 }
3282
3283=== modified file 'tools/ubuntu-helper-list.cpp'
3284--- tools/ubuntu-helper-list.cpp 2016-02-09 21:12:54 +0000
3285+++ tools/ubuntu-helper-list.cpp 2017-01-24 04:21:42 +0000
3286@@ -17,8 +17,8 @@
3287 * Ted Gould <ted.gould@canonical.com>
3288 */
3289
3290+#include "libubuntu-app-launch/registry.h"
3291 #include <iostream>
3292-#include "libubuntu-app-launch/registry.h"
3293
3294 int main(int argc, char* argv[])
3295 {
3296
3297=== modified file 'tools/ubuntu-helper-start.cpp'
3298--- tools/ubuntu-helper-start.cpp 2016-05-03 01:46:27 +0000
3299+++ tools/ubuntu-helper-start.cpp 2017-01-24 04:21:42 +0000
3300@@ -17,9 +17,9 @@
3301 * Ted Gould <ted.gould@canonical.com>
3302 */
3303
3304-#include <iostream>
3305 #include "libubuntu-app-launch/helper.h"
3306 #include "libubuntu-app-launch/registry.h"
3307+#include <iostream>
3308
3309 int main(int argc, char* argv[])
3310 {
3311@@ -31,20 +31,25 @@
3312
3313 auto type = ubuntu::app_launch::Helper::Type::from_raw(argv[1]);
3314 auto appid = ubuntu::app_launch::AppID::find(argv[2]);
3315- if (appid.empty()) {
3316+ if (appid.empty())
3317+ {
3318 std::cerr << "Unable to find helper for appid: " << argv[1] << std::endl;
3319 return 1;
3320 }
3321
3322 auto registry = std::make_shared<ubuntu::app_launch::Registry>();
3323
3324- try {
3325+ try
3326+ {
3327 auto helper = ubuntu::app_launch::Helper::create(type, appid, registry);
3328
3329 helper->launch();
3330 return 0;
3331- } catch (std::runtime_error &e) {
3332- std::cerr << "Unable to find helper for '" << std::string(appid) << "' type '" << type.value() << "': " << e.what() << std::endl;
3333+ }
3334+ catch (std::runtime_error& e)
3335+ {
3336+ std::cerr << "Unable to find helper for '" << std::string(appid) << "' type '" << type.value()
3337+ << "': " << e.what() << std::endl;
3338 return 1;
3339 }
3340 }
3341
3342=== modified file 'tools/ubuntu-helper-stop.cpp'
3343--- tools/ubuntu-helper-stop.cpp 2016-05-03 01:46:27 +0000
3344+++ tools/ubuntu-helper-stop.cpp 2017-01-24 04:21:42 +0000
3345@@ -17,9 +17,9 @@
3346 * Ted Gould <ted.gould@canonical.com>
3347 */
3348
3349-#include <iostream>
3350 #include "libubuntu-app-launch/helper.h"
3351 #include "libubuntu-app-launch/registry.h"
3352+#include <iostream>
3353
3354 int main(int argc, char* argv[])
3355 {
3356@@ -31,14 +31,16 @@
3357
3358 auto type = ubuntu::app_launch::Helper::Type::from_raw(argv[1]);
3359 auto appid = ubuntu::app_launch::AppID::find(argv[2]);
3360- if (appid.empty()) {
3361+ if (appid.empty())
3362+ {
3363 std::cerr << "Unable to find helper for appid: " << argv[1] << std::endl;
3364 return 1;
3365 }
3366
3367 auto registry = std::make_shared<ubuntu::app_launch::Registry>();
3368
3369- try {
3370+ try
3371+ {
3372 auto helper = ubuntu::app_launch::Helper::create(type, appid, registry);
3373
3374 for (auto instance : helper->instances())
3375@@ -47,8 +49,11 @@
3376 }
3377
3378 return 0;
3379- } catch (std::runtime_error &e) {
3380- std::cerr << "Unable to find helper for '" << std::string(appid) << "' type '" << type.value() << "': " << e.what() << std::endl;
3381+ }
3382+ catch (std::runtime_error& e)
3383+ {
3384+ std::cerr << "Unable to find helper for '" << std::string(appid) << "' type '" << type.value()
3385+ << "': " << e.what() << std::endl;
3386 return 1;
3387 }
3388 }

Subscribers

People subscribed via source and target branches