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
=== modified file 'libubuntu-app-launch/application-impl-base.cpp'
--- libubuntu-app-launch/application-impl-base.cpp 2016-11-10 15:34:52 +0000
+++ libubuntu-app-launch/application-impl-base.cpp 2017-01-24 04:21:42 +0000
@@ -556,22 +556,21 @@
556 std::array<const char*, 4> args = {OOM_HELPER, pidstr.c_str(), oomstr.c_str(), nullptr};556 std::array<const char*, 4> args = {OOM_HELPER, pidstr.c_str(), oomstr.c_str(), nullptr};
557557
558 g_debug("Excuting OOM Helper (pid: %d, score: %d): %s", int(pid), int(oomvalue),558 g_debug("Excuting OOM Helper (pid: %d, score: %d): %s", int(pid), int(oomvalue),
559 std::accumulate(args.begin(), args.end(), std::string{},559 std::accumulate(args.begin(), args.end(), std::string{}, [](const std::string& instr,
560 [](const std::string& instr, const char* output) -> std::string {560 const char* output) -> std::string {
561 if (instr.empty())561 if (instr.empty())
562 {562 {
563 return output;563 return output;
564 }564 }
565 else if (output != nullptr)565 else if (output != nullptr)
566 {566 {
567 return instr + " " + std::string(output);567 return instr + " " + std::string(output);
568 }568 }
569 else569 else
570 {570 {
571 return instr;571 return instr;
572 }572 }
573 })573 }).c_str());
574 .c_str());
575574
576 g_spawn_async(nullptr, /* working dir */575 g_spawn_async(nullptr, /* working dir */
577 (char**)(args.data()), /* args */576 (char**)(args.data()), /* args */
578577
=== modified file 'libubuntu-app-launch/application.cpp'
--- libubuntu-app-launch/application.cpp 2016-08-26 17:33:34 +0000
+++ libubuntu-app-launch/application.cpp 2017-01-24 04:21:42 +0000
@@ -46,6 +46,11 @@
46 throw std::runtime_error("AppID is empty");46 throw std::runtime_error("AppID is empty");
47 }47 }
4848
49 if (!registry || !registry->impl)
50 {
51 throw std::runtime_error("Invalid registry object");
52 }
53
49 if (app_impls::Click::hasAppId(appid, registry))54 if (app_impls::Click::hasAppId(appid, registry))
50 {55 {
51 return std::make_shared<app_impls::Click>(appid, registry);56 return std::make_shared<app_impls::Click>(appid, registry);
5257
=== modified file 'libubuntu-app-launch/helper.h'
--- libubuntu-app-launch/helper.h 2016-06-09 14:55:34 +0000
+++ libubuntu-app-launch/helper.h 2017-01-24 04:21:42 +0000
@@ -62,6 +62,12 @@
62*/62*/
63class Helper63class Helper
64{64{
65/*
66protected:
67 Helper() = default;
68 virtual ~Helper() = default;
69 TODO: Next ABI break */
70
65public:71public:
66 /** \private */72 /** \private */
67 struct TypeTag;73 struct TypeTag;
@@ -87,6 +93,12 @@
87 /** Running instance of a a Helper */93 /** Running instance of a a Helper */
88 class Instance94 class Instance
89 {95 {
96/*
97 protected:
98 Instance() = default;
99 virtual ~Instance() = default;
100 TODO: Next ABI break */
101
90 public:102 public:
91 /** Check to see if this instance is running */103 /** Check to see if this instance is running */
92 virtual bool isRunning() = 0;104 virtual bool isRunning() = 0;
93105
=== modified file 'libubuntu-app-launch/libubuntu-app-launch.map'
--- libubuntu-app-launch/libubuntu-app-launch.map 2016-05-19 16:24:11 +0000
+++ libubuntu-app-launch/libubuntu-app-launch.map 2017-01-24 04:21:42 +0000
@@ -14,6 +14,7 @@
14 ubuntu::app_launch::Helper::*;14 ubuntu::app_launch::Helper::*;
15 typeinfo?for?ubuntu::app_launch::Helper;15 typeinfo?for?ubuntu::app_launch::Helper;
16 typeinfo?name?for?ubuntu::app_launch::Helper;16 typeinfo?name?for?ubuntu::app_launch::Helper;
17 ubuntu::app_launch::operator*;
17 ubuntu::app_launch::oom::*;18 ubuntu::app_launch::oom::*;
18 };19 };
19local:20local:
2021
=== modified file 'libubuntu-app-launch/registry-impl.cpp'
--- libubuntu-app-launch/registry-impl.cpp 2016-11-10 15:38:50 +0000
+++ libubuntu-app-launch/registry-impl.cpp 2017-01-24 04:21:42 +0000
@@ -20,6 +20,7 @@
20#include "registry-impl.h"20#include "registry-impl.h"
21#include "application-icon-finder.h"21#include "application-icon-finder.h"
22#include <cgmanager/cgmanager.h>22#include <cgmanager/cgmanager.h>
23#include <regex>
23#include <upstart.h>24#include <upstart.h>
2425
25namespace ubuntu26namespace ubuntu
@@ -36,13 +37,29 @@
36 zgLog_.reset();37 zgLog_.reset();
37 cgManager_.reset();38 cgManager_.reset();
3839
40 auto dohandle = [&](guint& handle) {
41 if (handle != 0)
42 {
43 g_dbus_connection_signal_unsubscribe(_dbus.get(), handle);
44 handle = 0;
45 }
46 };
47
48 dohandle(handle_appStarted);
49 dohandle(handle_appStopped);
50 dohandle(handle_appFailed);
51 dohandle(handle_appPaused);
52 dohandle(handle_appResumed);
53 dohandle(handle_managerSignalFocus);
54 dohandle(handle_managerSignalResume);
55 dohandle(handle_managerSignalStarting);
56
39 if (_dbus)57 if (_dbus)
40 g_dbus_connection_flush_sync(_dbus.get(), nullptr, nullptr);58 g_dbus_connection_flush_sync(_dbus.get(), nullptr, nullptr);
41 _dbus.reset();59 _dbus.reset();
42 })60 })
43 , _registry(registry)61 , _registry{registry}
44 , _iconFinders()62 , _iconFinders()
45// _manager(nullptr)
46{63{
47 auto cancel = thread.getCancellable();64 auto cancel = thread.getCancellable();
48 _dbus = thread.executeOnThread<std::shared_ptr<GDBusConnection>>([cancel]() {65 _dbus = thread.executeOnThread<std::shared_ptr<GDBusConnection>>([cancel]() {
@@ -139,6 +156,7 @@
139156
140 auto node = json_node_alloc();157 auto node = json_node_alloc();
141 json_node_init_object(node, mani);158 json_node_init_object(node, mani);
159 json_object_unref(mani);
142160
143 auto retval = std::shared_ptr<JsonObject>(json_node_dup_object(node), json_object_unref);161 auto retval = std::shared_ptr<JsonObject>(json_node_dup_object(node), json_object_unref);
144162
@@ -168,9 +186,9 @@
168 }186 }
169187
170 std::list<AppID::Package> list;188 std::list<AppID::Package> list;
171 for (GList* item = pkgs; item != NULL; item = g_list_next(item))189 for (GList* item = pkgs; item != nullptr; item = g_list_next(item))
172 {190 {
173 auto pkgobj = reinterpret_cast<gchar*>(item->data);191 auto pkgobj = static_cast<gchar*>(item->data);
174 if (pkgobj)192 if (pkgobj)
175 {193 {
176 list.emplace_back(AppID::Package::from_raw(pkgobj));194 list.emplace_back(AppID::Package::from_raw(pkgobj));
@@ -269,7 +287,7 @@
269287
270 GVariant* vtpids = g_dbus_connection_call_sync(288 GVariant* vtpids = g_dbus_connection_call_sync(
271 lmanager.get(), /* connection */289 lmanager.get(), /* connection */
272 name, /* bus name for direct connection is NULL */290 name, /* bus name for direct connection is nullptr */
273 "/org/linuxcontainers/cgmanager", /* object */291 "/org/linuxcontainers/cgmanager", /* object */
274 "org.linuxcontainers.cgmanager0_0", /* interface */292 "org.linuxcontainers.cgmanager0_0", /* interface */
275 "GetTasksRecursive", /* method */293 "GetTasksRecursive", /* method */
@@ -287,7 +305,7 @@
287 return {};305 return {};
288 }306 }
289307
290 GVariant* vpids = g_variant_get_child_value(vtpids, 0);308 auto vpids = g_variant_get_child_value(vtpids, 0);
291 GVariantIter iter;309 GVariantIter iter;
292 g_variant_iter_init(&iter, vpids);310 g_variant_iter_init(&iter, vpids);
293 gint32 pid;311 gint32 pid;
@@ -394,7 +412,7 @@
394 return {};412 return {};
395 }413 }
396414
397 GVariant* instance_list = g_variant_get_child_value(instance_tuple, 0);415 auto instance_list = g_variant_get_child_value(instance_tuple, 0);
398 g_variant_unref(instance_tuple);416 g_variant_unref(instance_tuple);
399417
400 GVariantIter instance_iter;418 GVariantIter instance_iter;
@@ -425,12 +443,12 @@
425 continue;443 continue;
426 }444 }
427445
428 GVariant* props_dict = g_variant_get_child_value(props_tuple, 0);446 auto props_dict = g_variant_get_child_value(props_tuple, 0);
429447
430 GVariant* namev = g_variant_lookup_value(props_dict, "name", G_VARIANT_TYPE_STRING);448 auto namev = g_variant_lookup_value(props_dict, "name", G_VARIANT_TYPE_STRING);
431 if (namev != nullptr)449 if (namev != nullptr)
432 {450 {
433 auto name = g_variant_get_string(namev, NULL);451 auto name = g_variant_get_string(namev, nullptr);
434 g_debug("Adding instance for job '%s': %s", job.c_str(), name);452 g_debug("Adding instance for job '%s': %s", job.c_str(), name);
435 instances.push_back(name);453 instances.push_back(name);
436 g_variant_unref(namev);454 g_variant_unref(namev);
@@ -487,7 +505,7 @@
487 zeitgeist_log_insert_event(zgLog_.get(), /* log */505 zeitgeist_log_insert_event(zgLog_.get(), /* log */
488 event, /* event */506 event, /* event */
489 nullptr, /* cancellable */507 nullptr, /* cancellable */
490 [](GObject* obj, GAsyncResult* res, gpointer user_data) -> void {508 [](GObject* obj, GAsyncResult* res, gpointer user_data) {
491 GError* error = nullptr;509 GError* error = nullptr;
492 GArray* result = nullptr;510 GArray* result = nullptr;
493511
@@ -517,24 +535,207 @@
517 return _iconFinders[basePath];535 return _iconFinders[basePath];
518}536}
519537
520#if 0538/** Structure to track the data needed for upstart events. This cleans
521void539 up the lifecycle as we're passing this as a pointer through the
522Registry::Impl::setManager (Registry::Manager* manager)540 GLib calls. */
523{541struct upstartEventData
524 if (_manager != nullptr)542{
525 {543 /** Keeping a weak pointer because the handle is held by
526 throw std::runtime_error("Already have a manager and trying to set another");544 the registry implementation. */
527 }545 std::weak_ptr<Registry> weakReg;
528546};
529 _manager = manager;547
530}548/** Take the GVariant of parameters and turn them into an application and
531549 and instance. Easier to read in the smaller function */
532void550std::tuple<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> Registry::Impl::managerParams(
533Registry::Impl::clearManager ()551 const std::shared_ptr<GVariant>& params, const std::shared_ptr<Registry>& reg)
534{552{
535 _manager = nullptr;553 std::shared_ptr<Application> app;
536}554 std::shared_ptr<Application::Instance> instance;
537#endif555
556 const gchar* cappid = nullptr;
557 g_variant_get(params.get(), "(&s)", &cappid);
558
559 if (cappid != nullptr)
560 {
561 auto appid = ubuntu::app_launch::AppID::find(reg, cappid);
562 app = ubuntu::app_launch::Application::create(appid, reg);
563 }
564
565 return std::make_tuple(app, instance);
566}
567
568/** Used to store data for manager based signal handlers. Has a link to the
569 registry and the callback to use in a C++ style. */
570struct managerEventData
571{
572 /* Keeping a weak pointer because the handle is held by
573 the registry implementation. */
574 std::weak_ptr<Registry> weakReg;
575 std::function<void(const std::shared_ptr<Registry>& reg,
576 const std::shared_ptr<Application>& app,
577 const std::shared_ptr<Application::Instance>& instance,
578 const std::shared_ptr<GDBusConnection>& dbus,
579 const std::string& sender,
580 const std::shared_ptr<GVariant>& params)>
581 func;
582};
583
584/** Register for a signal for the manager. All of the signals needed this same
585 code so it got pulled out into a function. Takes the same of the signal, the registry
586 that we're using and a function to call after we've messaged all the parameters
587 into being something C++-ish. */
588guint Registry::Impl::managerSignalHelper(const std::shared_ptr<Registry>& reg,
589 const std::string& signalname,
590 std::function<void(const std::shared_ptr<Registry>& reg,
591 const std::shared_ptr<Application>& app,
592 const std::shared_ptr<Application::Instance>& instance,
593 const std::shared_ptr<GDBusConnection>& dbus,
594 const std::string& sender,
595 const std::shared_ptr<GVariant>& params)> responsefunc)
596{
597 auto focusdata = new managerEventData{reg, responsefunc};
598
599 return g_dbus_connection_signal_subscribe(
600 reg->impl->_dbus.get(), /* bus */
601 nullptr, /* sender */
602 "com.canonical.UbuntuAppLaunch", /* interface */
603 signalname.c_str(), /* signal */
604 "/", /* path */
605 nullptr, /* arg0 */
606 G_DBUS_SIGNAL_FLAGS_NONE,
607 [](GDBusConnection* cconn, const gchar* csender, const gchar*, const gchar*, const gchar*, GVariant* params,
608 gpointer user_data) {
609 auto data = static_cast<managerEventData*>(user_data);
610 auto reg = data->weakReg.lock();
611
612 if (!reg)
613 {
614 g_warning("Registry object invalid!");
615 return;
616 }
617
618 /* If we're still connected and the manager has been cleared
619 we'll just be a no-op */
620 if (!reg->impl->manager_)
621 {
622 return;
623 }
624
625 auto vparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref);
626 auto conn = std::shared_ptr<GDBusConnection>(reinterpret_cast<GDBusConnection*>(g_object_ref(cconn)),
627 [](GDBusConnection* con) { g_clear_object(&con); });
628 std::string sender = csender;
629 std::shared_ptr<Application> app;
630 std::shared_ptr<Application::Instance> instance;
631
632 std::tie(app, instance) = managerParams(vparams, reg);
633
634 data->func(reg, app, instance, conn, sender, vparams);
635 },
636 focusdata,
637 [](gpointer user_data) {
638 auto data = static_cast<managerEventData*>(user_data);
639 delete data;
640 }); /* user data destroy */
641}
642
643/** Set the manager for the registry. This includes tracking the pointer
644 as well as setting up the signals to call back into the manager. The
645 signals are only setup once per registry even if the manager is cleared
646 and changed again. They will just be no-op's in those cases.
647*/
648void Registry::Impl::setManager(const std::shared_ptr<Registry::Manager>& manager, const std::shared_ptr<Registry>& reg)
649{
650 if (!reg)
651 {
652 throw std::invalid_argument("Passed null registry to setManager()");
653 }
654
655 if (reg->impl->manager_)
656 {
657 throw std::logic_error("Already have a manager and trying to set another");
658 }
659
660 g_debug("Setting a new manager");
661 reg->impl->manager_ = manager;
662
663 std::call_once(reg->impl->flag_managerSignals, [reg]() {
664 if (!reg->impl->thread.executeOnThread<bool>([reg]() {
665 reg->impl->handle_managerSignalFocus = managerSignalHelper(
666 reg, "UnityFocusRequest",
667 [](const std::shared_ptr<Registry>& reg, const std::shared_ptr<Application>& app,
668 const std::shared_ptr<Application::Instance>& instance,
669 const std::shared_ptr<GDBusConnection>& /* conn */, const std::string& /* sender */,
670 const std::shared_ptr<GVariant>& /* params */) {
671 /* Nothing to do today */
672 reg->impl->manager_->focusRequest(app, instance, [](bool response) {
673 /* NOTE: We have no clue what thread this is gonna be
674 executed on, but since we're just talking to the GDBus
675 thread it isn't an issue today. Be careful in changing
676 this code. */
677 });
678 });
679 reg->impl->handle_managerSignalStarting = managerSignalHelper(
680 reg, "UnityStartingBroadcast",
681 [](const std::shared_ptr<Registry>& reg, const std::shared_ptr<Application>& app,
682 const std::shared_ptr<Application::Instance>& instance,
683 const std::shared_ptr<GDBusConnection>& conn, const std::string& sender,
684 const std::shared_ptr<GVariant>& params) {
685
686 reg->impl->manager_->startingRequest(app, instance, [conn, sender, params](bool response) {
687 /* NOTE: We have no clue what thread this is gonna be
688 executed on, but since we're just talking to the GDBus
689 thread it isn't an issue today. Be careful in changing
690 this code. */
691 if (response)
692 {
693 g_dbus_connection_emit_signal(conn.get(), sender.c_str(), /* destination */
694 "/", /* path */
695 "com.canonical.UbuntuAppLaunch", /* interface */
696 "UnityStartingSignal", /* signal */
697 params.get(), /* params, the same */
698 nullptr); /* error */
699 }
700 });
701 });
702 reg->impl->handle_managerSignalResume = managerSignalHelper(
703 reg, "UnityResumeRequest",
704 [](const std::shared_ptr<Registry>& reg, const std::shared_ptr<Application>& app,
705 const std::shared_ptr<Application::Instance>& instance,
706 const std::shared_ptr<GDBusConnection>& conn, const std::string& sender,
707 const std::shared_ptr<GVariant>& params) {
708 reg->impl->manager_->resumeRequest(app, instance, [conn, sender, params](bool response) {
709 /* NOTE: We have no clue what thread this is gonna be
710 executed on, but since we're just talking to the GDBus
711 thread it isn't an issue today. Be careful in changing
712 this code. */
713 if (response)
714 {
715 g_dbus_connection_emit_signal(conn.get(), sender.c_str(), /* destination */
716 "/", /* path */
717 "com.canonical.UbuntuAppLaunch", /* interface */
718 "UnityResumeResponse", /* signal */
719 params.get(), /* params, the same */
720 nullptr); /* error */
721 }
722 });
723 });
724
725 return true;
726 }))
727 {
728 g_warning("Unable to install manager signals");
729 }
730 });
731}
732
733/** Clear the manager pointer */
734void Registry::Impl::clearManager()
735{
736 g_debug("Clearing the manager");
737 manager_.reset();
738}
538739
539/** App start watching, if we're registered for the signal we740/** App start watching, if we're registered for the signal we
540 can't wait on it. We are making this static right now because741 can't wait on it. We are making this static right now because
@@ -557,5 +758,340 @@
557 return watchingAppStarting_;758 return watchingAppStarting_;
558}759}
559760
761/** Regex to parse the JOB environment variable from Upstart */
762const std::regex jobenv_regex{"^JOB=(application\\-(?:click|snap|legacy))$"};
763/** Regex to parse the INSTANCE environment variable from Upstart */
764const std::regex instanceenv_regex{"^INSTANCE=(.*?)(?:\\-([0-9]*))?+$"};
765
766/** Core of most of the events that come from Upstart directly. Includes parsing of the
767 Upstart event environment and calling the appropriate signal with the right Application
768 object and eventually its instance */
769void Registry::Impl::upstartEventEmitted(
770 core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>& signal,
771 const std::shared_ptr<GVariant>& params,
772 const std::shared_ptr<Registry>& reg)
773{
774 std::string jobname;
775 std::string sappid;
776 std::string instance;
777
778 gchar* env = nullptr;
779 auto envs = g_variant_get_child_value(params.get(), 1);
780 GVariantIter iter;
781 g_variant_iter_init(&iter, envs);
782
783 while (g_variant_iter_loop(&iter, "s", &env))
784 {
785 std::smatch match;
786 std::string senv = env;
787
788 if (std::regex_match(senv, match, jobenv_regex))
789 {
790 jobname = match[1].str();
791 }
792 else if (std::regex_match(senv, match, instanceenv_regex))
793 {
794 sappid = match[1].str();
795 instance = match[2].str();
796 }
797 }
798
799 g_variant_unref(envs);
800
801 if (jobname.empty())
802 {
803 return;
804 }
805
806 g_debug("Upstart Event for job '%s' appid '%s' instance '%s'", jobname.c_str(), sappid.c_str(), instance.c_str());
807
808 auto appid = AppID::find(reg, sappid);
809 auto app = Application::create(appid, reg);
810
811 // TODO: Figure out creating instances
812
813 signal(app, {});
814}
815
816/** Grab the signal object for application startup. If we're not already listing for
817 those signals this sets up a listener for them. */
818core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>&
819 Registry::Impl::appStarted(const std::shared_ptr<Registry>& reg)
820{
821 std::call_once(flag_appStarted, [reg]() {
822 reg->impl->thread.executeOnThread<bool>([reg]() {
823 auto data = new upstartEventData{reg};
824
825 reg->impl->handle_appStarted = g_dbus_connection_signal_subscribe(
826 reg->impl->_dbus.get(), /* bus */
827 nullptr, /* sender */
828 DBUS_INTERFACE_UPSTART, /* interface */
829 "EventEmitted", /* signal */
830 DBUS_PATH_UPSTART, /* path */
831 "started", /* arg0 */
832 G_DBUS_SIGNAL_FLAGS_NONE,
833 [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params,
834 gpointer user_data) {
835 auto data = static_cast<upstartEventData*>(user_data);
836 auto reg = data->weakReg.lock();
837
838 if (!reg)
839 {
840 g_warning("Registry object invalid!");
841 return;
842 }
843
844 auto sparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref);
845 reg->impl->upstartEventEmitted(reg->impl->sig_appStarted, sparams, reg);
846 }, /* callback */
847 data, /* user data */
848 [](gpointer user_data) {
849 auto data = static_cast<upstartEventData*>(user_data);
850 delete data;
851 }); /* user data destroy */
852
853 return true;
854 });
855 });
856
857 return sig_appStarted;
858}
859
860/** Grab the signal object for application stopping. If we're not already listing for
861 those signals this sets up a listener for them. */
862core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>&
863 Registry::Impl::appStopped(const std::shared_ptr<Registry>& reg)
864{
865 std::call_once(flag_appStopped, [reg]() {
866 reg->impl->thread.executeOnThread<bool>([reg]() {
867 auto data = new upstartEventData{reg};
868
869 reg->impl->handle_appStopped = g_dbus_connection_signal_subscribe(
870 reg->impl->_dbus.get(), /* bus */
871 nullptr, /* sender */
872 DBUS_INTERFACE_UPSTART, /* interface */
873 "EventEmitted", /* signal */
874 DBUS_PATH_UPSTART, /* path */
875 "stopped", /* arg0 */
876 G_DBUS_SIGNAL_FLAGS_NONE,
877 [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params,
878 gpointer user_data) {
879 auto data = static_cast<upstartEventData*>(user_data);
880 auto reg = data->weakReg.lock();
881
882 if (!reg)
883 {
884 g_warning("Registry object invalid!");
885 return;
886 }
887
888 auto sparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref);
889 reg->impl->upstartEventEmitted(reg->impl->sig_appStopped, sparams, reg);
890 }, /* callback */
891 data, /* user data */
892 [](gpointer user_data) {
893 auto data = static_cast<upstartEventData*>(user_data);
894 delete data;
895 }); /* user data destroy */
896
897 return true;
898 });
899 });
900
901 return sig_appStopped;
902}
903
904/** Grab the signal object for application failing. If we're not already listing for
905 those signals this sets up a listener for them. */
906core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&, Registry::FailureType>&
907 Registry::Impl::appFailed(const std::shared_ptr<Registry>& reg)
908{
909 std::call_once(flag_appFailed, [reg]() {
910 reg->impl->thread.executeOnThread<bool>([reg]() {
911 auto data = new upstartEventData{reg};
912
913 reg->impl->handle_appFailed = g_dbus_connection_signal_subscribe(
914 reg->impl->_dbus.get(), /* bus */
915 nullptr, /* sender */
916 "com.canonical.UbuntuAppLaunch", /* interface */
917 "ApplicationFailed", /* signal */
918 "/", /* path */
919 nullptr, /* arg0 */
920 G_DBUS_SIGNAL_FLAGS_NONE,
921 [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params,
922 gpointer user_data) {
923 auto data = static_cast<upstartEventData*>(user_data);
924 auto reg = data->weakReg.lock();
925
926 if (!reg)
927 {
928 g_warning("Registry object invalid!");
929 return;
930 }
931
932 const gchar* sappid = nullptr;
933 const gchar* typestr = nullptr;
934
935 Registry::FailureType type = Registry::FailureType::CRASH;
936 g_variant_get(params, "(&s&s)", &sappid, &typestr);
937
938 if (g_strcmp0("crash", typestr) == 0)
939 {
940 type = Registry::FailureType::CRASH;
941 }
942 else if (g_strcmp0("start-failure", typestr) == 0)
943 {
944 type = Registry::FailureType::START_FAILURE;
945 }
946 else
947 {
948 g_warning("Application failure type '%s' unknown, reporting as a crash", typestr);
949 }
950
951 auto appid = AppID::find(reg, sappid);
952 auto app = Application::create(appid, reg);
953
954 /* TODO: Instance issues */
955
956 reg->impl->sig_appFailed(app, {}, type);
957 }, /* callback */
958 data, /* user data */
959 [](gpointer user_data) {
960 auto data = static_cast<upstartEventData*>(user_data);
961 delete data;
962 }); /* user data destroy */
963
964 return true;
965 });
966 });
967
968 return sig_appFailed;
969}
970
971/** Core handler for pause and resume events. Includes turning the GVariant
972 pid list into a std::vector and getting the application object. */
973void Registry::Impl::pauseEventEmitted(core::Signal<const std::shared_ptr<Application>&,
974 const std::shared_ptr<Application::Instance>&,
975 const std::vector<pid_t>&>& signal,
976 const std::shared_ptr<GVariant>& params,
977 const std::shared_ptr<Registry>& reg)
978{
979 std::vector<pid_t> pids;
980 auto vappid = g_variant_get_child_value(params.get(), 0);
981 auto vpids = g_variant_get_child_value(params.get(), 1);
982 guint64 pid;
983 GVariantIter thispid;
984 g_variant_iter_init(&thispid, vpids);
985
986 while (g_variant_iter_loop(&thispid, "t", &pid))
987 {
988 pids.emplace_back(pid);
989 }
990
991 auto cappid = g_variant_get_string(vappid, nullptr);
992 auto appid = ubuntu::app_launch::AppID::find(reg, cappid);
993 auto app = Application::create(appid, reg);
994
995 /* TODO: Instance */
996 signal(app, {}, pids);
997
998 g_variant_unref(vappid);
999 g_variant_unref(vpids);
1000
1001 return;
1002}
1003
1004/** Grab the signal object for application paused. If we're not already listing for
1005 those signals this sets up a listener for them. */
1006core::Signal<const std::shared_ptr<Application>&,
1007 const std::shared_ptr<Application::Instance>&,
1008 const std::vector<pid_t>&>&
1009 Registry::Impl::appPaused(const std::shared_ptr<Registry>& reg)
1010{
1011 std::call_once(flag_appPaused, [&]() {
1012 reg->impl->thread.executeOnThread<bool>([reg]() {
1013 auto data = new upstartEventData{reg};
1014
1015 reg->impl->handle_appPaused = g_dbus_connection_signal_subscribe(
1016 reg->impl->_dbus.get(), /* bus */
1017 nullptr, /* sender */
1018 "com.canonical.UbuntuAppLaunch", /* interface */
1019 "ApplicationPaused", /* signal */
1020 "/", /* path */
1021 nullptr, /* arg0 */
1022 G_DBUS_SIGNAL_FLAGS_NONE,
1023 [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params,
1024 gpointer user_data) {
1025 auto data = static_cast<upstartEventData*>(user_data);
1026 auto reg = data->weakReg.lock();
1027
1028 if (!reg)
1029 {
1030 g_warning("Registry object invalid!");
1031 return;
1032 }
1033
1034 auto sparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref);
1035 reg->impl->pauseEventEmitted(reg->impl->sig_appPaused, sparams, reg);
1036 }, /* callback */
1037 data, /* user data */
1038 [](gpointer user_data) {
1039 auto data = static_cast<upstartEventData*>(user_data);
1040 delete data;
1041 }); /* user data destroy */
1042
1043 return true;
1044 });
1045 });
1046
1047 return sig_appPaused;
1048}
1049
1050/** Grab the signal object for application resumed. If we're not already listing for
1051 those signals this sets up a listener for them. */
1052core::Signal<const std::shared_ptr<Application>&,
1053 const std::shared_ptr<Application::Instance>&,
1054 const std::vector<pid_t>&>&
1055 Registry::Impl::appResumed(const std::shared_ptr<Registry>& reg)
1056{
1057 std::call_once(flag_appResumed, [&]() {
1058 reg->impl->thread.executeOnThread<bool>([reg]() {
1059 auto data = new upstartEventData{reg};
1060
1061 reg->impl->handle_appResumed = g_dbus_connection_signal_subscribe(
1062 reg->impl->_dbus.get(), /* bus */
1063 nullptr, /* sender */
1064 "com.canonical.UbuntuAppLaunch", /* interface */
1065 "ApplicationResumed", /* signal */
1066 "/", /* path */
1067 nullptr, /* arg0 */
1068 G_DBUS_SIGNAL_FLAGS_NONE,
1069 [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params,
1070 gpointer user_data) {
1071 auto data = static_cast<upstartEventData*>(user_data);
1072 auto reg = data->weakReg.lock();
1073
1074 if (!reg)
1075 {
1076 g_warning("Registry object invalid!");
1077 return;
1078 }
1079
1080 auto sparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref);
1081 reg->impl->pauseEventEmitted(reg->impl->sig_appResumed, sparams, reg);
1082 }, /* callback */
1083 data, /* user data */
1084 [](gpointer user_data) {
1085 auto data = static_cast<upstartEventData*>(user_data);
1086 delete data;
1087 }); /* user data destroy */
1088
1089 return true;
1090 });
1091 });
1092
1093 return sig_appResumed;
1094}
1095
560} // namespace app_launch1096} // namespace app_launch
561} // namespace ubuntu1097} // namespace ubuntu
5621098
=== modified file 'libubuntu-app-launch/registry-impl.h'
--- libubuntu-app-launch/registry-impl.h 2016-09-23 22:30:51 +0000
+++ libubuntu-app-launch/registry-impl.h 2017-01-24 04:21:42 +0000
@@ -53,10 +53,9 @@
53 std::list<AppID::Package> getClickPackages();53 std::list<AppID::Package> getClickPackages();
54 std::string getClickDir(const std::string& package);54 std::string getClickDir(const std::string& package);
5555
56#if 056 static void setManager(const std::shared_ptr<Registry::Manager>& manager,
57 void setManager (Registry::Manager* manager);57 const std::shared_ptr<Registry>& registry);
58 void clearManager ();58 void clearManager();
59#endif
6059
61 /** Shared context thread for events and background tasks60 /** Shared context thread for events and background tasks
62 that UAL subtasks are doing */61 that UAL subtasks are doing */
@@ -82,6 +81,22 @@
82 static std::string printJson(std::shared_ptr<JsonObject> jsonobj);81 static std::string printJson(std::shared_ptr<JsonObject> jsonobj);
83 static std::string printJson(std::shared_ptr<JsonNode> jsonnode);82 static std::string printJson(std::shared_ptr<JsonNode> jsonnode);
8483
84 /* Signals to discover what is happening to apps */
85 core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>& appStarted(
86 const std::shared_ptr<Registry>& reg);
87 core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>& appStopped(
88 const std::shared_ptr<Registry>& reg);
89 core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&, FailureType>&
90 appFailed(const std::shared_ptr<Registry>& reg);
91 core::Signal<const std::shared_ptr<Application>&,
92 const std::shared_ptr<Application::Instance>&,
93 const std::vector<pid_t>&>&
94 appPaused(const std::shared_ptr<Registry>& reg);
95 core::Signal<const std::shared_ptr<Application>&,
96 const std::shared_ptr<Application::Instance>&,
97 const std::vector<pid_t>&>&
98 appResumed(const std::shared_ptr<Registry>& reg);
99
85 /* Signal Hints */100 /* Signal Hints */
86 /* NOTE: Static because we don't have registry instances in the C101 /* NOTE: Static because we don't have registry instances in the C
87 code right now. We want these to not be static in the future */102 code right now. We want these to not be static in the future */
@@ -89,22 +104,84 @@
89 static bool isWatchingAppStarting();104 static bool isWatchingAppStarting();
90105
91private:106private:
92 Registry* _registry;107 Registry* _registry; /**< The Registry that we're spawned from */
93#if 0108 std::shared_ptr<Registry::Manager> manager_; /**< Application manager if registered */
94 Registry::Manager* _manager;109
95#endif110 std::shared_ptr<ClickDB> _clickDB; /**< Shared instance of the Click Database */
96111 std::shared_ptr<ClickUser> _clickUser; /**< Click database filtered by the current user */
97 std::shared_ptr<ClickDB> _clickDB;112
98 std::shared_ptr<ClickUser> _clickUser;113 /** Signal object for applications started */
114 core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&> sig_appStarted;
115 /** Signal object for applications stopped */
116 core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&> sig_appStopped;
117 /** Signal object for applications failed */
118 core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&, FailureType>
119 sig_appFailed;
120 /** Signal object for applications paused */
121 core::Signal<const std::shared_ptr<Application>&,
122 const std::shared_ptr<Application::Instance>&,
123 const std::vector<pid_t>&>
124 sig_appPaused;
125 /** Signal object for applications resumed */
126 core::Signal<const std::shared_ptr<Application>&,
127 const std::shared_ptr<Application::Instance>&,
128 const std::vector<pid_t>&>
129 sig_appResumed;
130
131 guint handle_appStarted{0}; /**< GDBus signal watcher handle for app started signal */
132 guint handle_appStopped{0}; /**< GDBus signal watcher handle for app stopped signal */
133 guint handle_appFailed{0}; /**< GDBus signal watcher handle for app failed signal */
134 guint handle_appPaused{0}; /**< GDBus signal watcher handle for app paused signal */
135 guint handle_appResumed{0}; /**< GDBus signal watcher handle for app resumed signal */
136 guint handle_managerSignalFocus{0}; /**< GDBus signal watcher handle for app focused signal */
137 guint handle_managerSignalResume{0}; /**< GDBus signal watcher handle for app resumed signal */
138 guint handle_managerSignalStarting{0}; /**< GDBus signal watcher handle for app starting signal */
139
140 std::once_flag flag_appStarted; /**< Variable to track to see if signal handlers are installed for application
141 started */
142 std::once_flag flag_appStopped; /**< Variable to track to see if signal handlers are installed for application
143 stopped */
144 std::once_flag
145 flag_appFailed; /**< Variable to track to see if signal handlers are installed for application failed */
146 std::once_flag
147 flag_appPaused; /**< Variable to track to see if signal handlers are installed for application paused */
148 std::once_flag flag_appResumed; /**< Variable to track to see if signal handlers are installed for application
149 resumed */
150 std::once_flag flag_managerSignals; /**< Variable to track to see if signal handlers are installed for the manager
151 signals of focused, resumed and starting */
152
153 void upstartEventEmitted(
154 core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>& signal,
155 const std::shared_ptr<GVariant>& params,
156 const std::shared_ptr<Registry>& reg);
157 void pauseEventEmitted(core::Signal<const std::shared_ptr<Application>&,
158 const std::shared_ptr<Application::Instance>&,
159 const std::vector<pid_t>&>& signal,
160 const std::shared_ptr<GVariant>& params,
161 const std::shared_ptr<Registry>& reg);
162 static std::tuple<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> managerParams(
163 const std::shared_ptr<GVariant>& params, const std::shared_ptr<Registry>& reg);
164 static guint managerSignalHelper(const std::shared_ptr<Registry>& reg,
165 const std::string& signalname,
166 std::function<void(const std::shared_ptr<Registry>& reg,
167 const std::shared_ptr<Application>& app,
168 const std::shared_ptr<Application::Instance>& instance,
169 const std::shared_ptr<GDBusConnection>&,
170 const std::string&,
171 const std::shared_ptr<GVariant>&)> responsefunc);
99172
100 void initClick();173 void initClick();
101174
175 /** Shared instance of the Zeitgeist Log */
102 std::shared_ptr<ZeitgeistLog> zgLog_;176 std::shared_ptr<ZeitgeistLog> zgLog_;
103177
178 /** Shared connection to CGManager */
104 std::shared_ptr<GDBusConnection> cgManager_;179 std::shared_ptr<GDBusConnection> cgManager_;
105180
106 void initCGManager();181 void initCGManager();
107182
183 /** All of our icon finders based on the path that they're looking
184 into */
108 std::unordered_map<std::string, std::shared_ptr<IconFinder>> _iconFinders;185 std::unordered_map<std::string, std::shared_ptr<IconFinder>> _iconFinders;
109186
110 /** Getting the Upstart job path is relatively expensive in187 /** Getting the Upstart job path is relatively expensive in
111188
=== modified file 'libubuntu-app-launch/registry.cpp'
--- libubuntu-app-launch/registry.cpp 2016-08-26 17:33:34 +0000
+++ libubuntu-app-launch/registry.cpp 2017-01-24 04:21:42 +0000
@@ -57,7 +57,7 @@
57 instances.splice(instances.begin(), connection->impl->upstartInstancesForJob("application-snap"));57 instances.splice(instances.begin(), connection->impl->upstartInstancesForJob("application-snap"));
5858
59 /* Remove the instance ID */59 /* Remove the instance ID */
60 std::transform(instances.begin(), instances.end(), instances.begin(), [](std::string &instancename) -> std::string {60 std::transform(instances.begin(), instances.end(), instances.begin(), [](std::string& instancename) -> std::string {
61 static const std::regex instanceregex("^(.*)-[0-9]*$");61 static const std::regex instanceregex("^(.*)-[0-9]*$");
62 std::smatch match;62 std::smatch match;
63 if (std::regex_match(instancename, match, instanceregex))63 if (std::regex_match(instancename, match, instanceregex))
@@ -86,11 +86,10 @@
86 }86 }
8787
88 g_debug("Overall there are %d instances: %s", int(instanceset.size()),88 g_debug("Overall there are %d instances: %s", int(instanceset.size()),
89 std::accumulate(instanceset.begin(), instanceset.end(), std::string{},89 std::accumulate(instanceset.begin(), instanceset.end(), std::string{}, [](const std::string& instr,
90 [](const std::string &instr, std::string instance) {90 std::string instance) {
91 return instr.empty() ? instance : instr + ", " + instance;91 return instr.empty() ? instance : instr + ", " + instance;
92 })92 }).c_str());
93 .c_str());
9493
95 /* Convert to Applications */94 /* Convert to Applications */
96 std::list<std::shared_ptr<Application>> apps;95 std::list<std::shared_ptr<Application>> apps;
@@ -127,6 +126,16 @@
127 return list;126 return list;
128}127}
129128
129void Registry::setManager(const std::shared_ptr<Manager>& manager, const std::shared_ptr<Registry>& registry)
130{
131 Registry::Impl::setManager(manager, registry);
132}
133
134void Registry::clearManager()
135{
136 impl->clearManager();
137}
138
130std::shared_ptr<Registry> defaultRegistry;139std::shared_ptr<Registry> defaultRegistry;
131std::shared_ptr<Registry> Registry::getDefault()140std::shared_ptr<Registry> Registry::getDefault()
132{141{
@@ -143,5 +152,39 @@
143 defaultRegistry.reset();152 defaultRegistry.reset();
144}153}
145154
155core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>& Registry::appStarted(
156 const std::shared_ptr<Registry>& reg)
157{
158 return reg->impl->appStarted(reg);
159}
160
161core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>& Registry::appStopped(
162 const std::shared_ptr<Registry>& reg)
163{
164 return reg->impl->appStopped(reg);
165}
166
167core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&, Registry::FailureType>&
168 Registry::appFailed(const std::shared_ptr<Registry>& reg)
169{
170 return reg->impl->appFailed(reg);
171}
172
173core::Signal<const std::shared_ptr<Application>&,
174 const std::shared_ptr<Application::Instance>&,
175 const std::vector<pid_t>&>&
176 Registry::appPaused(const std::shared_ptr<Registry>& reg)
177{
178 return reg->impl->appPaused(reg);
179}
180
181core::Signal<const std::shared_ptr<Application>&,
182 const std::shared_ptr<Application::Instance>&,
183 const std::vector<pid_t>&>&
184 Registry::appResumed(const std::shared_ptr<Registry>& reg)
185{
186 return reg->impl->appResumed(reg);
187}
188
146} // namespace app_launch189} // namespace app_launch
147} // namespace ubuntu190} // namespace ubuntu
148191
=== modified file 'libubuntu-app-launch/registry.h'
--- libubuntu-app-launch/registry.h 2016-06-09 14:55:34 +0000
+++ libubuntu-app-launch/registry.h 2017-01-24 04:21:42 +0000
@@ -73,28 +73,137 @@
73 */73 */
74 static std::list<std::shared_ptr<Application>> installedApps(std::shared_ptr<Registry> registry = getDefault());74 static std::list<std::shared_ptr<Application>> installedApps(std::shared_ptr<Registry> registry = getDefault());
7575
76#if 0 /* TODO -- In next MR */
77 /* Signals to discover what is happening to apps */76 /* Signals to discover what is happening to apps */
78 core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> appStarted;77 /** Get the signal object that is signaled when an application has been
79 core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> appStopped;78 started.
80 core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, FailureType> appFailed;79
81 core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> appPaused;80 \note This signal handler is activated on the UAL thread
82 core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> appResumed;81
8382 \param reg Registry to get the handler from
84 /* The Application Manager, almost always if you're not Unity8, don't83 */
85 use this API. Testing is a special case. */84 static core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>& appStarted(
85 const std::shared_ptr<Registry>& reg = getDefault());
86
87 /** Get the signal object that is signaled when an application has stopped.
88
89 \note This signal handler is activated on the UAL thread
90
91 \param reg Registry to get the handler from
92 */
93 static core::Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&>& appStopped(
94 const std::shared_ptr<Registry>& reg = getDefault());
95
96 /** Get the signal object that is signaled when an application has failed.
97
98 \note This signal handler is activated on the UAL thread
99
100 \param reg Registry to get the handler from
101 */
102 static core::
103 Signal<const std::shared_ptr<Application>&, const std::shared_ptr<Application::Instance>&, FailureType>&
104 appFailed(const std::shared_ptr<Registry>& reg = getDefault());
105
106 /** Get the signal object that is signaled when an application has been
107 paused.
108
109 \note This signal handler is activated on the UAL thread
110
111 \param reg Registry to get the handler from
112 */
113 static core::Signal<const std::shared_ptr<Application>&,
114 const std::shared_ptr<Application::Instance>&,
115 const std::vector<pid_t>&>&
116 appPaused(const std::shared_ptr<Registry>& reg = getDefault());
117
118 /** Get the signal object that is signaled when an application has been
119 resumed.
120
121 \note This signal handler is activated on the UAL thread
122
123 \param reg Registry to get the handler from
124 */
125 static core::Signal<const std::shared_ptr<Application>&,
126 const std::shared_ptr<Application::Instance>&,
127 const std::vector<pid_t>&>&
128 appResumed(const std::shared_ptr<Registry>& reg = getDefault());
129
130 /** The Application Manager, almost always if you're not Unity8, don't
131 use this API. Testing is a special case. Subclass this interface and
132 implement these functions.
133
134 Each function here is being passed a function object that takes a boolean
135 to reply. This will accept or reject the request. The function object
136 can be copied to another thread and executed if needed.
137
138 The reply is required for the application to start. It will block (not
139 currently implemented) until approval is given. If there are multiple requests
140 sent they may be replied out of order if desired.
141 */
86 class Manager142 class Manager
87 {143 {
88 virtual bool focusRequest (std::shared_ptr<Application> app, std::shared_ptr<Application::Instance> instance) = 0;144 public:
89 virtual bool startingRequest (std::shared_ptr<Application> app, std::shared_ptr<Application::Instance> instance) = 0;145 /** Application wishes to startup
146
147 \note This signal handler is activated on the UAL thread
148
149 \param app Application requesting startup
150 \param instance Instance of the app, always valid but not useful
151 unless mulit-instance app.
152 \param reply Function object to reply if it is allowed to start
153 */
154 virtual void startingRequest(const std::shared_ptr<Application>& app,
155 const std::shared_ptr<Application::Instance>& instance,
156 std::function<void(bool)> reply) = 0;
157
158 /** Application wishes to have focus. Usually this occurs when
159 a URL for the application is activated and the running app is
160 requested.
161
162 \note This signal handler is activated on the UAL thread
163
164 \param app Application requesting focus
165 \param instance Instance of the app, always valid but not useful
166 unless mulit-instance app.
167 \param reply Function object to reply if it is allowed to focus
168 */
169 virtual void focusRequest(const std::shared_ptr<Application>& app,
170 const std::shared_ptr<Application::Instance>& instance,
171 std::function<void(bool)> reply) = 0;
172
173 /** Application wishes to resume. Usually this occurs when
174 a URL for the application is activated and the running app is
175 requested.
176
177 \note This signal handler is activated on the UAL thread
178
179 \param app Application requesting resume
180 \param instance Instance of the app, always valid but not useful
181 unless mulit-instance app.
182 \param reply Function object to reply if it is allowed to resume
183 */
184 virtual void resumeRequest(const std::shared_ptr<Application>& app,
185 const std::shared_ptr<Application::Instance>& instance,
186 std::function<void(bool)> reply) = 0;
90187
91 protected:188 protected:
92 Manager() = default;189 Manager() = default;
190 /* virtual ~Manager() = default;
191 TODO: Next ABI break */
93 };192 };
94193
95 void setManager (Manager* manager);194 /** Set the manager of applications, which gives permissions for them to
96 void clearManager ();195 start and gain focus. In almost all cases this should be Unity8 as it
97#endif196 will be controlling applications.
197
198 This function will failure if there is already a manager set.
199
200 \param manager A reference to the Manager object to call
201 \param registry Registry to register the manager on
202 */
203 static void setManager(const std::shared_ptr<Manager>& manager, const std::shared_ptr<Registry>& registry);
204
205 /** Remove the current manager on the registry */
206 void clearManager();
98207
99 /* Helper Lists */208 /* Helper Lists */
100 /** Get a list of all the helpers for a given helper type209 /** Get a list of all the helpers for a given helper type
101210
=== modified file 'libubuntu-app-launch/ubuntu-app-launch.cpp'
--- libubuntu-app-launch/ubuntu-app-launch.cpp 2016-11-10 15:39:08 +0000
+++ libubuntu-app-launch/ubuntu-app-launch.cpp 2017-01-24 04:21:42 +0000
@@ -39,6 +39,7 @@
39#include "appid.h"39#include "appid.h"
40#include "registry.h"40#include "registry.h"
41#include "registry-impl.h"41#include "registry-impl.h"
42#include <algorithm>
4243
43static void free_helper (gpointer value);44static void free_helper (gpointer value);
44int kill (pid_t pid, int signal) noexcept;45int kill (pid_t pid, int signal) noexcept;
@@ -271,504 +272,365 @@
271 gpointer user_data;272 gpointer user_data;
272};273};
273274
274/* The data we keep for each failed observer */275/* Function to take a work function and have it execute on a given
275typedef struct _paused_resumed_observer_t paused_resumed_observer_t;276 GMainContext */
276struct _paused_resumed_observer_t {277static void executeOnContext (const std::shared_ptr<GMainContext>& context, std::function<void()> work)
277 GDBusConnection * conn;278{
278 guint sighandle;279 if (!context) {
279 UbuntuAppLaunchAppPausedResumedObserver func;280 work();
280 gpointer user_data;281 return;
281 const gchar * lttng_signal;282 }
282};283
283284 auto heapWork = new std::function<void()>(work);
284/* The lists of Observers */285
285static GList * starting_array = NULL;286 auto source = std::shared_ptr<GSource>(g_idle_source_new(), [](GSource* src) { g_clear_pointer(&src, g_source_unref); });
286static GList * started_array = NULL;287 g_source_set_callback(source.get(),
287static GList * stop_array = NULL;288 [](gpointer data) {
288static GList * focus_array = NULL;289 auto heapWork = static_cast<std::function<void()>*>(data);
289static GList * resume_array = NULL;290 (*heapWork)();
290static GList * failed_array = NULL;291 return G_SOURCE_REMOVE;
291static GList * paused_array = NULL;292 },
292static GList * resumed_array = NULL;293 heapWork,
293294 [](gpointer data) {
294static void295 auto heapWork = static_cast<std::function<void()>*>(data);
295observer_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)296 delete heapWork;
296{297 });
297 observer_t * observer = (observer_t *)user_data;298
298299 g_source_attach(source.get(), context.get());
299 const gchar * signalname = NULL;300}
300 g_variant_get_child(params, 0, "&s", &signalname);301
301302/** A handy helper function that is based of a function to get
302 ual_tracepoint(observer_start, signalname);303 a signal and put it into a map. */
303304template <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>&)>
304 gchar * env = NULL;305static gboolean
305 GVariant * envs = g_variant_get_child_value(params, 1);306observer_add (UbuntuAppLaunchAppObserver observer, gpointer user_data, std::map<std::pair<UbuntuAppLaunchAppObserver, gpointer>, core::ScopedConnection> &observers)
306 GVariantIter iter;307{
307 g_variant_iter_init(&iter, envs);308 auto context = std::shared_ptr<GMainContext>(g_main_context_ref_thread_default(), [](GMainContext * context) { g_clear_pointer(&context, g_main_context_unref); });
308309
309 gboolean job_found = FALSE;310 observers.emplace(std::make_pair(
310 gboolean job_legacy = FALSE;311 std::make_pair(observer, user_data),
311 gchar * instance = NULL;312 core::ScopedConnection(
312313 getSignal(ubuntu::app_launch::Registry::getDefault())
313 while (g_variant_iter_loop(&iter, "s", &env)) {314 .connect([context, observer, user_data](std::shared_ptr<ubuntu::app_launch::Application> app, std::shared_ptr<ubuntu::app_launch::Application::Instance> instance) {
314 if (g_strcmp0(env, "JOB=application-click") == 0) {315 std::string appid = app->appId();
315 job_found = TRUE;316 executeOnContext(context, [appid, observer, user_data]() {
316 } else if (g_strcmp0(env, "JOB=application-legacy") == 0) {317 observer(appid.c_str(), user_data);
317 job_found = TRUE;318 });
318 job_legacy = TRUE;319 })
319 } else if (g_strcmp0(env, "JOB=application-snap") == 0) {320 )
320 job_found = TRUE;321 ));
321 job_legacy = TRUE;322
322 } else if (g_str_has_prefix(env, "INSTANCE=")) {323 return TRUE;
323 instance = g_strdup(env + strlen("INSTANCE="));324}
324 }325
325 }326/** A handy helper to delete items from an observer map */
326327template<typename observertype>
327 g_variant_unref(envs);328static gboolean
328329observer_delete (observertype observer, gpointer user_data, std::map<std::pair<observertype, gpointer>, core::ScopedConnection> &observers)
329 if (job_legacy && instance != NULL) {330{
330 gchar * dash = g_strrstr(instance, "-");331 auto iter = observers.find(std::make_pair(observer, user_data));
331 if (dash != NULL) {332
332 dash[0] = '\0';333 if (iter == observers.end()) {
333 }
334 }
335
336 if (job_found && instance != NULL) {
337 observer->func(instance, observer->user_data);
338 }
339
340 ual_tracepoint(observer_finish, signalname);
341
342 g_free(instance);
343}
344
345/* Creates the observer structure and registers for the signal with
346 GDBus so that we can get a callback */
347static gboolean
348add_app_generic (UbuntuAppLaunchAppObserver observer, gpointer user_data, const gchar * signal, GList ** list)
349{
350 GDBusConnection * conn = gdbus_upstart_ref();
351
352 if (conn == NULL) {
353 return FALSE;334 return FALSE;
354 }335 }
355336
356 observer_t * observert = g_new0(observer_t, 1);337 observers.erase(iter);
357
358 observert->conn = conn;
359 observert->func = observer;
360 observert->user_data = user_data;
361
362 *list = g_list_prepend(*list, observert);
363
364 observert->sighandle = g_dbus_connection_signal_subscribe(conn,
365 NULL, /* sender */
366 DBUS_INTERFACE_UPSTART, /* interface */
367 "EventEmitted", /* signal */
368 DBUS_PATH_UPSTART, /* path */
369 signal, /* arg0 */
370 G_DBUS_SIGNAL_FLAGS_NONE,
371 observer_cb,
372 observert,
373 NULL); /* user data destroy */
374
375 return TRUE;338 return TRUE;
376}339}
377340
341/** Map of all the observers listening for app started */
342static std::map<std::pair<UbuntuAppLaunchAppObserver, gpointer>, core::ScopedConnection> appStartedObservers;
343
378gboolean344gboolean
379ubuntu_app_launch_observer_add_app_started (UbuntuAppLaunchAppObserver observer, gpointer user_data)345ubuntu_app_launch_observer_add_app_started (UbuntuAppLaunchAppObserver observer, gpointer user_data)
380{346{
381 return add_app_generic(observer, user_data, "started", &started_array);347 return observer_add<&ubuntu::app_launch::Registry::appStarted>(observer, user_data, appStartedObservers);
382}348}
349
350gboolean
351ubuntu_app_launch_observer_delete_app_started (UbuntuAppLaunchAppObserver observer, gpointer user_data)
352{
353 return observer_delete<UbuntuAppLaunchAppObserver>(observer, user_data, appStartedObservers);
354}
355
356/* Map of all the observers listening for app stopped */
357static std::map<std::pair<UbuntuAppLaunchAppObserver, gpointer>, core::ScopedConnection> appStoppedObservers;
383358
384gboolean359gboolean
385ubuntu_app_launch_observer_add_app_stop (UbuntuAppLaunchAppObserver observer, gpointer user_data)360ubuntu_app_launch_observer_add_app_stop (UbuntuAppLaunchAppObserver observer, gpointer user_data)
386{361{
387 return add_app_generic(observer, user_data, "stopped", &stop_array);362 return observer_add<&ubuntu::app_launch::Registry::appStopped>(observer, user_data, appStoppedObservers);
388}363}
389364
390/* Creates the observer structure and registers for the signal with365gboolean
391 GDBus so that we can get a callback */366ubuntu_app_launch_observer_delete_app_stop (UbuntuAppLaunchAppObserver observer, gpointer user_data)
392static gboolean367{
393add_session_generic (UbuntuAppLaunchAppObserver observer, gpointer user_data, const gchar * signal, GList ** list, GDBusSignalCallback session_cb)368 return observer_delete<UbuntuAppLaunchAppObserver>(observer, user_data, appStoppedObservers);
394{369}
395 GDBusConnection * conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);370
396371/** Class to implement the Registry::Manager interface for the C code
397 if (conn == NULL) {372 using a GLib mainloop. */
398 return FALSE;373class CManager : public ubuntu::app_launch::Registry::Manager
399 }374{
400375public:
401 observer_t * observert = g_new0(observer_t, 1);376 CManager () {
402377 g_debug("Creating the CManager object");
403 observert->conn = conn;378 }
404 observert->func = observer;379 virtual ~CManager() {
405 observert->user_data = user_data;380 g_debug("Removing the shared the CManager object");
406381 }
407 *list = g_list_prepend(*list, observert);382
408383 void startingRequest(const std::shared_ptr<ubuntu::app_launch::Application>& app,
409 observert->sighandle = g_dbus_connection_signal_subscribe(conn,384 const std::shared_ptr<ubuntu::app_launch::Application::Instance>& instance,
410 NULL, /* sender */385 std::function<void(bool)> reply) override {
411 "com.canonical.UbuntuAppLaunch", /* interface */386 requestImpl(app, instance, reply, "starting", startingList);
412 signal, /* signal */387 }
413 "/", /* path */388
414 NULL, /* arg0 */389 void focusRequest(const std::shared_ptr<ubuntu::app_launch::Application>& app,
415 G_DBUS_SIGNAL_FLAGS_NONE,390 const std::shared_ptr<ubuntu::app_launch::Application::Instance>& instance,
416 session_cb,391 std::function<void(bool)> reply) override {
417 observert,392 requestImpl(app, instance, reply, "focus", focusList);
418 NULL); /* user data destroy */393 }
419394
420 return TRUE;395 void resumeRequest(const std::shared_ptr<ubuntu::app_launch::Application> &app,
421}396 const std::shared_ptr<ubuntu::app_launch::Application::Instance> &instance,
422397 std::function<void(bool)> reply) override {
423/* Generic handler for a bunch of our signals */398 requestImpl(app, instance, reply, "resume", resumeList);
424static inline void399 }
425generic_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)400
426{401private:
427 observer_t * observer = (observer_t *)user_data;402 /** The Data that we track on an observer. It is the functions to
428 const gchar * appid = NULL;403 call, the user data and the context to call it on. */
429404 struct ObserverData {
430 if (observer->func != NULL) {405 UbuntuAppLaunchAppObserver observer;
431 g_variant_get(params, "(&s)", &appid);406 gpointer user_data;
432 observer->func(appid, observer->user_data);407 std::shared_ptr<GMainContext> context;
433 }408
434}409 /** Handy constructor to get the context in one place */
435410 ObserverData(UbuntuAppLaunchAppObserver obs, gpointer ud)
436/* Handle the focus signal when it occurs, call the observer */411 : observer(obs)
437static void412 , user_data(ud) {
438focus_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)413 context = std::shared_ptr<GMainContext>(g_main_context_ref_thread_default(), [](GMainContext * context) { g_clear_pointer(&context, g_main_context_unref); });
439{414 }
440 ual_tracepoint(observer_start, "focus");415 };
441416
442 generic_signal_cb(conn, sender, object, interface, signal, params, user_data);417 std::list<ObserverData> focusList; /**< List of observers on the focus signal */
443418 std::list<ObserverData> resumeList; /**< List of observers on the resume signal */
444 ual_tracepoint(observer_finish, "focus");419 std::list<ObserverData> startingList; /**< List of observers on the starting signal */
420
421 /** Removes an observer from a specified list */
422 bool removeObserver (std::list<ObserverData> &list, UbuntuAppLaunchAppObserver observer, gpointer user_data) {
423 auto iter = std::find_if(list.begin(), list.end(), [observer, user_data](const ObserverData &data) {
424 return data.observer == observer && data.user_data == user_data;
425 });
426
427 if (iter == list.end()) {
428 return false;
429 }
430
431 list.erase(iter);
432 return true;
433 }
434
435 /** Implements a request for a specified list by calling each observer and then the reply */
436 inline void requestImpl ( const std::shared_ptr<ubuntu::app_launch::Application> &app,
437 const std::shared_ptr<ubuntu::app_launch::Application::Instance> &instance,
438 std::function<void(bool)> reply,
439 const std::string& name,
440 std::list<ObserverData>& list) {
441 std::string sappid = app->appId();
442 g_debug("CManager %s: %s", name.c_str(), sappid.c_str());
443
444 for (const auto &data : list) {
445 executeOnContext(data.context, [data, sappid]() {
446 data.observer(sappid.c_str(), data.user_data);
447 });
448 }
449
450 reply(true);
451 }
452
453public:
454 void addFocus (UbuntuAppLaunchAppObserver observer, gpointer user_data) {
455 focusList.emplace_back(ObserverData(observer, user_data));
456 }
457 void addResume (UbuntuAppLaunchAppObserver observer, gpointer user_data) {
458 resumeList.emplace_back(ObserverData(observer, user_data));
459 }
460 void addStarting (UbuntuAppLaunchAppObserver observer, gpointer user_data) {
461 startingList.emplace_back(ObserverData(observer, user_data));
462 }
463 bool deleteFocus (UbuntuAppLaunchAppObserver observer, gpointer user_data) {
464 return removeObserver(focusList, observer, user_data);
465 }
466 bool deleteResume (UbuntuAppLaunchAppObserver observer, gpointer user_data) {
467 return removeObserver(resumeList, observer, user_data);
468 }
469 bool deleteStarting (UbuntuAppLaunchAppObserver observer, gpointer user_data) {
470 return removeObserver(startingList, observer, user_data);
471 }
472};
473
474/** Weak pointer to the CManager if it is still in use. If it gets free'd by
475 the registry we're okay with that. */
476static std::weak_ptr<CManager> cmanager;
477
478/** Function to create the CManager if it doesn't currently exist. Otherwise
479 just return a lock to it */
480static std::shared_ptr<CManager>
481ensure_cmanager ()
482{
483 auto retval = cmanager.lock();
484
485 if (!retval) {
486 retval = std::make_shared<CManager>();
487 ubuntu::app_launch::Registry::setManager(retval, ubuntu::app_launch::Registry::getDefault());
488 cmanager = retval;
489 }
490
491 return retval;
445}492}
446493
447gboolean494gboolean
448ubuntu_app_launch_observer_add_app_focus (UbuntuAppLaunchAppObserver observer, gpointer user_data)495ubuntu_app_launch_observer_add_app_focus (UbuntuAppLaunchAppObserver observer, gpointer user_data)
449{496{
450 return add_session_generic(observer, user_data, "UnityFocusRequest", &focus_array, focus_signal_cb);497 auto manager = ensure_cmanager();
498 manager->addFocus(observer, user_data);
499 return TRUE;
451}500}
452501
453/* Handle the resume signal when it occurs, call the observer, then send a signal back when we're done */502gboolean
454static void503ubuntu_app_launch_observer_delete_app_focus (UbuntuAppLaunchAppObserver observer, gpointer user_data)
455resume_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
456{504{
457 ual_tracepoint(observer_start, "resume");505 auto manager = ensure_cmanager();
458506 return manager->deleteFocus(observer, user_data) ? TRUE : FALSE;
459 generic_signal_cb(conn, sender, object, interface, signal, params, user_data);
460
461 GError * error = NULL;
462 g_dbus_connection_emit_signal(conn,
463 sender, /* destination */
464 "/", /* path */
465 "com.canonical.UbuntuAppLaunch", /* interface */
466 "UnityResumeResponse", /* signal */
467 params, /* params, the same */
468 &error);
469
470 if (error != NULL) {
471 g_warning("Unable to emit response signal: %s", error->message);
472 g_error_free(error);
473 }
474
475 ual_tracepoint(observer_finish, "resume");
476}507}
477508
478gboolean509gboolean
479ubuntu_app_launch_observer_add_app_resume (UbuntuAppLaunchAppObserver observer, gpointer user_data)510ubuntu_app_launch_observer_add_app_resume (UbuntuAppLaunchAppObserver observer, gpointer user_data)
480{511{
481 return add_session_generic(observer, user_data, "UnityResumeRequest", &resume_array, resume_signal_cb);512 auto manager = ensure_cmanager();
513 manager->addResume(observer, user_data);
514 return TRUE;
482}515}
483516
484/* Handle the starting signal when it occurs, call the observer, then send a signal back when we're done */517gboolean
485static void518ubuntu_app_launch_observer_delete_app_resume (UbuntuAppLaunchAppObserver observer, gpointer user_data)
486starting_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
487{519{
488 ual_tracepoint(observer_start, "starting");520 auto manager = ensure_cmanager();
489521 return manager->deleteResume(observer, user_data) ? TRUE : FALSE;
490 generic_signal_cb(conn, sender, object, interface, signal, params, user_data);
491
492 GError * error = NULL;
493 g_dbus_connection_emit_signal(conn,
494 sender, /* destination */
495 "/", /* path */
496 "com.canonical.UbuntuAppLaunch", /* interface */
497 "UnityStartingSignal", /* signal */
498 params, /* params, the same */
499 &error);
500
501 if (error != NULL) {
502 g_warning("Unable to emit response signal: %s", error->message);
503 g_error_free(error);
504 }
505
506 ual_tracepoint(observer_finish, "starting");
507}522}
508523
509gboolean524gboolean
510ubuntu_app_launch_observer_add_app_starting (UbuntuAppLaunchAppObserver observer, gpointer user_data)525ubuntu_app_launch_observer_add_app_starting (UbuntuAppLaunchAppObserver observer, gpointer user_data)
511{526{
527 auto manager = ensure_cmanager();
512 ubuntu::app_launch::Registry::Impl::watchingAppStarting(true);528 ubuntu::app_launch::Registry::Impl::watchingAppStarting(true);
513 return add_session_generic(observer, user_data, "UnityStartingBroadcast", &starting_array, starting_signal_cb);529 manager->addStarting(observer, user_data);
514}530 return TRUE;
515
516/* Handle the failed signal when it occurs, call the observer */
517static void
518failed_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
519{
520 failed_observer_t * observer = (failed_observer_t *)user_data;
521 const gchar * appid = NULL;
522 const gchar * typestr = NULL;
523
524 ual_tracepoint(observer_start, "failed");
525
526 if (observer->func != NULL) {
527 UbuntuAppLaunchAppFailed type = UBUNTU_APP_LAUNCH_APP_FAILED_CRASH;
528 g_variant_get(params, "(&s&s)", &appid, &typestr);
529
530 if (g_strcmp0("crash", typestr) == 0) {
531 type = UBUNTU_APP_LAUNCH_APP_FAILED_CRASH;
532 } else if (g_strcmp0("start-failure", typestr) == 0) {
533 type = UBUNTU_APP_LAUNCH_APP_FAILED_START_FAILURE;
534 } else {
535 g_warning("Application failure type '%s' unknown, reporting as a crash", typestr);
536 }
537
538 observer->func(appid, type, observer->user_data);
539 }
540
541 ual_tracepoint(observer_finish, "failed");
542}
543
544gboolean
545ubuntu_app_launch_observer_add_app_failed (UbuntuAppLaunchAppFailedObserver observer, gpointer user_data)
546{
547 GDBusConnection * conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
548
549 if (conn == NULL) {
550 return FALSE;
551 }
552
553 failed_observer_t * observert = g_new0(failed_observer_t, 1);
554
555 observert->conn = conn;
556 observert->func = observer;
557 observert->user_data = user_data;
558
559 failed_array = g_list_prepend(failed_array, observert);
560
561 observert->sighandle = g_dbus_connection_signal_subscribe(conn,
562 NULL, /* sender */
563 "com.canonical.UbuntuAppLaunch", /* interface */
564 "ApplicationFailed", /* signal */
565 "/", /* path */
566 NULL, /* arg0 */
567 G_DBUS_SIGNAL_FLAGS_NONE,
568 failed_signal_cb,
569 observert,
570 NULL); /* user data destroy */
571
572 return TRUE;
573}
574
575/* Handle the paused signal when it occurs, call the observer */
576static void
577paused_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
578{
579 paused_resumed_observer_t * observer = (paused_resumed_observer_t *)user_data;
580
581 ual_tracepoint(observer_start, observer->lttng_signal);
582
583 if (observer->func != NULL) {
584 GArray * pidarray = g_array_new(TRUE, TRUE, sizeof(GPid));
585 GVariant * appid = g_variant_get_child_value(params, 0);
586 GVariant * pids = g_variant_get_child_value(params, 1);
587 guint64 pid;
588 GVariantIter thispid;
589 g_variant_iter_init(&thispid, pids);
590
591 while (g_variant_iter_loop(&thispid, "t", &pid)) {
592 GPid gpid = (GPid)pid; /* Should be a no-op for most architectures, but just in case */
593 g_array_append_val(pidarray, gpid);
594 }
595
596 observer->func(g_variant_get_string(appid, NULL), (GPid *)pidarray->data, observer->user_data);
597
598 g_array_free(pidarray, TRUE);
599 g_variant_unref(appid);
600 g_variant_unref(pids);
601 }
602
603 ual_tracepoint(observer_finish, observer->lttng_signal);
604}
605
606static gboolean
607paused_resumed_generic (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data, GList ** queue, const gchar * signal_name, const gchar * lttng_signal)
608{
609 GDBusConnection * conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
610
611 if (conn == NULL) {
612 return FALSE;
613 }
614
615 paused_resumed_observer_t * observert = g_new0(paused_resumed_observer_t, 1);
616
617 observert->conn = conn;
618 observert->func = observer;
619 observert->user_data = user_data;
620 observert->lttng_signal = lttng_signal;
621
622 *queue = g_list_prepend(*queue, observert);
623
624 observert->sighandle = g_dbus_connection_signal_subscribe(conn,
625 NULL, /* sender */
626 "com.canonical.UbuntuAppLaunch", /* interface */
627 signal_name, /* signal */
628 "/", /* path */
629 NULL, /* arg0 */
630 G_DBUS_SIGNAL_FLAGS_NONE,
631 paused_signal_cb,
632 observert,
633 NULL); /* user data destroy */
634
635 return TRUE;
636}
637
638gboolean
639ubuntu_app_launch_observer_add_app_paused (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data)
640{
641 return paused_resumed_generic(observer, user_data, &paused_array, "ApplicationPaused", "paused");
642}
643
644gboolean
645ubuntu_app_launch_observer_add_app_resumed (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data)
646{
647 return paused_resumed_generic(observer, user_data, &resumed_array, "ApplicationResumed", "resumed");
648}
649
650static gboolean
651delete_app_generic (UbuntuAppLaunchAppObserver observer, gpointer user_data, GList ** list)
652{
653 observer_t * observert = NULL;
654 GList * look;
655
656 for (look = *list; look != NULL; look = g_list_next(look)) {
657 observert = (observer_t *)look->data;
658
659 if (observert->func == observer && observert->user_data == user_data) {
660 break;
661 }
662 }
663
664 if (look == NULL) {
665 return FALSE;
666 }
667
668 g_dbus_connection_signal_unsubscribe(observert->conn, observert->sighandle);
669 g_object_unref(observert->conn);
670
671 g_free(observert);
672 *list = g_list_delete_link(*list, look);
673
674 return TRUE;
675}
676
677gboolean
678ubuntu_app_launch_observer_delete_app_started (UbuntuAppLaunchAppObserver observer, gpointer user_data)
679{
680 return delete_app_generic(observer, user_data, &started_array);
681}
682
683gboolean
684ubuntu_app_launch_observer_delete_app_stop (UbuntuAppLaunchAppObserver observer, gpointer user_data)
685{
686 return delete_app_generic(observer, user_data, &stop_array);
687}
688
689gboolean
690ubuntu_app_launch_observer_delete_app_resume (UbuntuAppLaunchAppObserver observer, gpointer user_data)
691{
692 return delete_app_generic(observer, user_data, &resume_array);
693}
694
695gboolean
696ubuntu_app_launch_observer_delete_app_focus (UbuntuAppLaunchAppObserver observer, gpointer user_data)
697{
698 return delete_app_generic(observer, user_data, &focus_array);
699}531}
700532
701gboolean533gboolean
702ubuntu_app_launch_observer_delete_app_starting (UbuntuAppLaunchAppObserver observer, gpointer user_data)534ubuntu_app_launch_observer_delete_app_starting (UbuntuAppLaunchAppObserver observer, gpointer user_data)
703{535{
536 auto manager = ensure_cmanager();
704 ubuntu::app_launch::Registry::Impl::watchingAppStarting(false);537 ubuntu::app_launch::Registry::Impl::watchingAppStarting(false);
705 return delete_app_generic(observer, user_data, &starting_array);538 return manager->deleteStarting(observer, user_data) ? TRUE : FALSE;
539}
540
541/* Map of all the observers listening for app stopped */
542static std::map<std::pair<UbuntuAppLaunchAppFailedObserver, gpointer>, core::ScopedConnection> appFailedObservers;
543
544gboolean
545ubuntu_app_launch_observer_add_app_failed (UbuntuAppLaunchAppFailedObserver observer, gpointer user_data)
546{
547 auto context = std::shared_ptr<GMainContext>(g_main_context_ref_thread_default(), [](GMainContext * context) { g_clear_pointer(&context, g_main_context_unref); });
548
549 appFailedObservers.emplace(std::make_pair(
550 std::make_pair(observer, user_data),
551 core::ScopedConnection(
552 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) {
553 std::string appid = app->appId();
554 executeOnContext(context, [appid, type, observer, user_data]() {
555 UbuntuAppLaunchAppFailed ctype{UBUNTU_APP_LAUNCH_APP_FAILED_CRASH};
556
557 switch (type) {
558 case ubuntu::app_launch::Registry::FailureType::CRASH:
559 ctype = UBUNTU_APP_LAUNCH_APP_FAILED_CRASH;
560 break;
561 case ubuntu::app_launch::Registry::FailureType::START_FAILURE:
562 ctype = UBUNTU_APP_LAUNCH_APP_FAILED_START_FAILURE;
563 break;
564 }
565
566 observer(appid.c_str(), ctype, user_data);
567 });
568 })
569 )
570 ));
571
572 return TRUE;
706}573}
707574
708gboolean575gboolean
709ubuntu_app_launch_observer_delete_app_failed (UbuntuAppLaunchAppFailedObserver observer, gpointer user_data)576ubuntu_app_launch_observer_delete_app_failed (UbuntuAppLaunchAppFailedObserver observer, gpointer user_data)
710{577{
711 failed_observer_t * observert = NULL;578 return observer_delete<UbuntuAppLaunchAppFailedObserver>(observer, user_data, appFailedObservers);
712 GList * look;
713
714 for (look = failed_array; look != NULL; look = g_list_next(look)) {
715 observert = (failed_observer_t *)look->data;
716
717 if (observert->func == observer && observert->user_data == user_data) {
718 break;
719 }
720 }
721
722 if (look == NULL) {
723 return FALSE;
724 }
725
726 g_dbus_connection_signal_unsubscribe(observert->conn, observert->sighandle);
727 g_object_unref(observert->conn);
728
729 g_free(observert);
730 failed_array = g_list_delete_link(failed_array, look);
731
732 return TRUE;
733}579}
734580
581/** Handy helper for pause and resume here */
582template <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>&)>
735static gboolean583static gboolean
736paused_resumed_delete (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data, GList ** list)584observer_add_pause (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data, std::map<std::pair<UbuntuAppLaunchAppPausedResumedObserver, gpointer>, core::ScopedConnection> &observers)
737{585{
738 paused_resumed_observer_t * observert = NULL;586 auto context = std::shared_ptr<GMainContext>(g_main_context_ref_thread_default(), [](GMainContext * context) { g_clear_pointer(&context, g_main_context_unref); });
739 GList * look;587
740588 observers.emplace(std::make_pair(
741 for (look = *list; look != NULL; look = g_list_next(look)) {589 std::make_pair(observer, user_data),
742 observert = (paused_resumed_observer_t *)look->data;590 core::ScopedConnection(
743591 getSignal(ubuntu::app_launch::Registry::getDefault())
744 if (observert->func == observer && observert->user_data == user_data) {592 .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) {
745 break;593 std::vector<pid_t> lpids = pids;
746 }594 lpids.emplace_back(0);
747 }595
748596 std::string appid = app->appId();
749 if (look == NULL) {597
750 return FALSE;598 executeOnContext(context, [appid, observer, user_data, lpids]() {
751 }599 observer(appid.c_str(), (int *)(lpids.data()), user_data);
752600 });
753 g_dbus_connection_signal_unsubscribe(observert->conn, observert->sighandle);601 })
754 g_object_unref(observert->conn);602 )
755603 ));
756 g_free(observert);
757 *list = g_list_delete_link(*list, look);
758604
759 return TRUE;605 return TRUE;
760}606}
761607
608static std::map<std::pair<UbuntuAppLaunchAppPausedResumedObserver, gpointer>, core::ScopedConnection> appPausedObservers;
609
610gboolean
611ubuntu_app_launch_observer_add_app_paused (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data)
612{
613 return observer_add_pause<&ubuntu::app_launch::Registry::appPaused>(observer, user_data, appPausedObservers);
614}
615
762gboolean616gboolean
763ubuntu_app_launch_observer_delete_app_paused (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data)617ubuntu_app_launch_observer_delete_app_paused (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data)
764{618{
765 return paused_resumed_delete(observer, user_data, &paused_array);619 return observer_delete<UbuntuAppLaunchAppPausedResumedObserver>(observer, user_data, appPausedObservers);
620}
621
622static std::map<std::pair<UbuntuAppLaunchAppPausedResumedObserver, gpointer>, core::ScopedConnection> appResumedObservers;
623
624gboolean
625ubuntu_app_launch_observer_add_app_resumed (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data)
626{
627 return observer_add_pause<&ubuntu::app_launch::Registry::appResumed>(observer, user_data, appResumedObservers);
766}628}
767629
768gboolean630gboolean
769ubuntu_app_launch_observer_delete_app_resumed (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data)631ubuntu_app_launch_observer_delete_app_resumed (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data)
770{632{
771 return paused_resumed_delete(observer, user_data, &resumed_array);633 return observer_delete<UbuntuAppLaunchAppPausedResumedObserver>(observer, user_data, appResumedObservers);
772}634}
773635
774typedef void (*per_instance_func_t) (GDBusConnection * con, GVariant * prop_dict, gpointer user_data);636typedef void (*per_instance_func_t) (GDBusConnection * con, GVariant * prop_dict, gpointer user_data);
775637
=== modified file 'tests/CMakeLists.txt'
--- tests/CMakeLists.txt 2016-09-14 16:43:36 +0000
+++ tests/CMakeLists.txt 2017-01-24 04:21:42 +0000
@@ -157,6 +157,7 @@
157157
158add_custom_target(format-tests158add_custom_target(format-tests
159 COMMAND clang-format -i -style=file159 COMMAND clang-format -i -style=file
160 failure-test.cc
160 application-info-desktop.cpp161 application-info-desktop.cpp
161 libual-cpp-test.cc162 libual-cpp-test.cc
162 list-apps.cpp163 list-apps.cpp
163164
=== modified file 'tests/failure-test.cc'
--- tests/failure-test.cc 2016-08-25 18:13:44 +0000
+++ tests/failure-test.cc 2017-01-24 04:21:42 +0000
@@ -17,120 +17,147 @@
17 * Ted Gould <ted.gould@canonical.com>17 * Ted Gould <ted.gould@canonical.com>
18 */18 */
1919
20#include "eventually-fixture.h"
21#include "registry.h"
22#include <gio/gio.h>
23#include <glib/gstdio.h>
20#include <gtest/gtest.h>24#include <gtest/gtest.h>
21#include <glib/gstdio.h>
22#include <gio/gio.h>
23#include <ubuntu-app-launch.h>
24#include "eventually-fixture.h"
2525
26class FailureTest : public EventuallyFixture26class FailureTest : public EventuallyFixture
27{27{
28 private:28private:
29 GTestDBus * testbus = NULL;29 GTestDBus* testbus = NULL;
3030
31 protected:31protected:
32 virtual void SetUp() {32 std::shared_ptr<ubuntu::app_launch::Registry> registry;
33 testbus = g_test_dbus_new(G_TEST_DBUS_NONE);33
34 g_test_dbus_up(testbus);34 virtual void SetUp() override
35 }35 {
3636 /* Click DB test mode */
37 virtual void TearDown() {37 g_setenv("TEST_CLICK_DB", "click-db-dir", TRUE);
38 g_test_dbus_down(testbus);38 g_setenv("TEST_CLICK_USER", "test-user", TRUE);
39 g_clear_object(&testbus);39
40 }40 gchar* linkfarmpath = g_build_filename(CMAKE_SOURCE_DIR, "link-farm", NULL);
41 g_setenv("UBUNTU_APP_LAUNCH_LINK_FARM", linkfarmpath, TRUE);
42 g_free(linkfarmpath);
43
44 g_setenv("XDG_DATA_DIRS", CMAKE_SOURCE_DIR, TRUE);
45 g_setenv("XDG_CACHE_HOME", CMAKE_SOURCE_DIR "/libertine-data", TRUE);
46 g_setenv("XDG_DATA_HOME", CMAKE_SOURCE_DIR "/libertine-home", TRUE);
47
48 testbus = g_test_dbus_new(G_TEST_DBUS_NONE);
49 g_test_dbus_up(testbus);
50
51 registry = std::make_shared<ubuntu::app_launch::Registry>();
52 }
53
54 virtual void TearDown() override
55 {
56 registry.reset();
57
58 g_test_dbus_down(testbus);
59 g_clear_object(&testbus);
60 }
41};61};
4262
43static void
44failed_observer (const gchar * appid, UbuntuAppLaunchAppFailed reason, gpointer user_data)
45{
46 if (reason == UBUNTU_APP_LAUNCH_APP_FAILED_CRASH) {
47 std::string * last = static_cast<std::string *>(user_data);
48 *last = appid;
49 }
50}
51
52TEST_F(FailureTest, CrashTest)63TEST_F(FailureTest, CrashTest)
53{64{
54 g_setenv("EXIT_STATUS", "-100", TRUE);65 g_setenv("EXIT_STATUS", "-100", TRUE);
55 g_setenv("JOB", "application-click", TRUE);66 g_setenv("JOB", "application-click", TRUE);
56 g_setenv("INSTANCE", "foo", TRUE);67 g_setenv("INSTANCE", "foo", TRUE);
5768
58 std::string last_observer;69 std::string last_observer;
59 ASSERT_TRUE(ubuntu_app_launch_observer_add_app_failed(failed_observer, &last_observer));70 ubuntu::app_launch::Registry::appFailed(registry).connect(
6071 [&last_observer](std::shared_ptr<ubuntu::app_launch::Application> app,
61 /* Status based */72 std::shared_ptr<ubuntu::app_launch::Application::Instance> instance,
62 ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL));73 ubuntu::app_launch::Registry::FailureType type) {
6374 if (type == ubuntu::app_launch::Registry::FailureType::CRASH)
64 EXPECT_EVENTUALLY_EQ("foo", last_observer);75 {
6576 last_observer = app->appId();
66 last_observer.clear();77 }
67 g_unsetenv("EXIT_STATUS");78 });
68 g_setenv("EXIT_SIGNAL", "KILL", TRUE);79
6980 /* Status based */
70 /* Signal based */81 ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL));
71 ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL));82
7283 EXPECT_EVENTUALLY_EQ("foo", last_observer);
73 EXPECT_EVENTUALLY_EQ("foo", last_observer);84
7485 last_observer.clear();
75 ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_failed(failed_observer, &last_observer));86 g_unsetenv("EXIT_STATUS");
87 g_setenv("EXIT_SIGNAL", "KILL", TRUE);
88
89 /* Signal based */
90 ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL));
91
92 EXPECT_EVENTUALLY_EQ("foo", last_observer);
76}93}
7794
78TEST_F(FailureTest, LegacyTest)95TEST_F(FailureTest, LegacyTest)
79{96{
80 g_setenv("EXIT_STATUS", "-100", TRUE);97 g_setenv("EXIT_STATUS", "-100", TRUE);
81 g_setenv("JOB", "application-legacy", TRUE);98 g_setenv("JOB", "application-legacy", TRUE);
82 g_setenv("INSTANCE", "foo-1234", TRUE);99 g_setenv("INSTANCE", "foo-1234", TRUE);
83100
84 std::string last_observer;101 std::string last_observer;
85 ASSERT_TRUE(ubuntu_app_launch_observer_add_app_failed(failed_observer, &last_observer));102 ubuntu::app_launch::Registry::appFailed(registry).connect(
86103 [&last_observer](std::shared_ptr<ubuntu::app_launch::Application> app,
87 /* Status based */104 std::shared_ptr<ubuntu::app_launch::Application::Instance> instance,
88 ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL));105 ubuntu::app_launch::Registry::FailureType type) {
89106 g_debug("Signal handler called");
90 EXPECT_EVENTUALLY_EQ("foo", last_observer);107 if (type == ubuntu::app_launch::Registry::FailureType::CRASH)
91108 {
92 ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_failed(failed_observer, &last_observer));109 last_observer = app->appId();
110 }
111 });
112
113 /* Status based */
114 ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL));
115
116 EXPECT_EVENTUALLY_EQ("foo", last_observer);
93}117}
94118
95TEST_F(FailureTest, SnapTest)119TEST_F(FailureTest, SnapTest)
96{120{
97 g_setenv("EXIT_STATUS", "-100", TRUE);121 g_setenv("EXIT_STATUS", "-100", TRUE);
98 g_setenv("JOB", "application-snap", TRUE);122 g_setenv("JOB", "application-snap", TRUE);
99 g_setenv("INSTANCE", "foo_bar_x123-1234", TRUE);123 g_setenv("INSTANCE", "com.test.good_application_1.2.3-1234", TRUE);
100124
101 std::string last_observer;125 std::string last_observer;
102 ASSERT_TRUE(ubuntu_app_launch_observer_add_app_failed(failed_observer, &last_observer));126 ubuntu::app_launch::Registry::appFailed(registry).connect(
103127 [&last_observer](std::shared_ptr<ubuntu::app_launch::Application> app,
104 /* Status based */128 std::shared_ptr<ubuntu::app_launch::Application::Instance> instance,
105 ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL));129 ubuntu::app_launch::Registry::FailureType type) {
106130 if (type == ubuntu::app_launch::Registry::FailureType::CRASH)
107 EXPECT_EVENTUALLY_EQ("foo_bar_x123", last_observer);131 {
108132 last_observer = app->appId();
109 ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_failed(failed_observer, &last_observer));133 }
110}134 });
111135
112static void136 /* Status based */
113failed_start_observer (const gchar * appid, UbuntuAppLaunchAppFailed reason, gpointer user_data)137 ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL));
114{138
115 if (reason == UBUNTU_APP_LAUNCH_APP_FAILED_START_FAILURE) {139 EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", last_observer);
116 std::string * last = static_cast<std::string *>(user_data);
117 *last = appid;
118 }
119}140}
120141
121TEST_F(FailureTest, StartTest)142TEST_F(FailureTest, StartTest)
122{143{
123 g_setenv("JOB", "application-click", TRUE);144 g_setenv("JOB", "application-click", TRUE);
124 g_setenv("INSTANCE", "foo", TRUE);145 g_setenv("INSTANCE", "foo", TRUE);
125 g_unsetenv("EXIT_STATUS");146 g_unsetenv("EXIT_STATUS");
126 g_unsetenv("EXIT_SIGNAL");147 g_unsetenv("EXIT_SIGNAL");
127148
128 std::string last_observer;149 std::string last_observer;
129 ASSERT_TRUE(ubuntu_app_launch_observer_add_app_failed(failed_start_observer, &last_observer));150 ubuntu::app_launch::Registry::appFailed(registry).connect(
130151 [&last_observer](std::shared_ptr<ubuntu::app_launch::Application> app,
131 ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL));152 std::shared_ptr<ubuntu::app_launch::Application::Instance> instance,
132153 ubuntu::app_launch::Registry::FailureType type) {
133 EXPECT_EVENTUALLY_EQ("foo", last_observer);154 if (type == ubuntu::app_launch::Registry::FailureType::START_FAILURE)
134155 {
135 ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_failed(failed_start_observer, &last_observer));156 last_observer = app->appId();
157 }
158 });
159
160 ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL));
161
162 EXPECT_EVENTUALLY_EQ("foo", last_observer);
136}163}
137164
=== modified file 'tests/libual-cpp-test.cc'
--- tests/libual-cpp-test.cc 2017-01-10 18:41:25 +0000
+++ tests/libual-cpp-test.cc 2017-01-24 04:21:42 +0000
@@ -49,32 +49,73 @@
49 DbusTestDbusMock* mock = NULL;49 DbusTestDbusMock* mock = NULL;
50 DbusTestDbusMock* cgmock = NULL;50 DbusTestDbusMock* cgmock = NULL;
51 GDBusConnection* bus = NULL;51 GDBusConnection* bus = NULL;
52 std::string last_focus_appid;
53 std::string last_resume_appid;
54 guint resume_timeout = 0;52 guint resume_timeout = 0;
55 std::shared_ptr<ubuntu::app_launch::Registry> registry;53 std::shared_ptr<ubuntu::app_launch::Registry> registry;
5654
57private:55 class ManagerMock : public ubuntu::app_launch::Registry::Manager
58 static void focus_cb(const gchar* appid, gpointer user_data)56 {
59 {57 GLib::ContextThread thread;
60 g_debug("Focus Callback: %s", appid);58
61 LibUAL* _this = static_cast<LibUAL*>(user_data);59 public:
62 _this->last_focus_appid = appid;60 ManagerMock()
63 }61 {
6462 g_debug("Building a Manager Mock");
65 static void resume_cb(const gchar* appid, gpointer user_data)63 }
66 {64
67 g_debug("Resume Callback: %s", appid);65 ~ManagerMock()
68 LibUAL* _this = static_cast<LibUAL*>(user_data);66 {
69 _this->last_resume_appid = appid;67 g_debug("Freeing a Manager Mock");
7068 }
71 if (_this->resume_timeout > 0)69
72 {70 void quit()
73 _this->pause(_this->resume_timeout);71 {
74 }72 thread.quit();
75 }73 }
7674
77protected:75 ubuntu::app_launch::AppID lastStartedApp;
76 ubuntu::app_launch::AppID lastFocusedApp;
77 ubuntu::app_launch::AppID lastResumedApp;
78
79 bool startingResponse{true};
80 bool focusResponse{true};
81 bool resumeResponse{true};
82
83 std::chrono::milliseconds startingTimeout{0};
84 std::chrono::milliseconds focusTimeout{0};
85 std::chrono::milliseconds resumeTimeout{0};
86
87 void startingRequest(const std::shared_ptr<ubuntu::app_launch::Application>& app,
88 const std::shared_ptr<ubuntu::app_launch::Application::Instance>& instance,
89 std::function<void(bool)> reply) override
90 {
91 thread.timeout(startingTimeout, [this, app, instance, reply]() {
92 lastStartedApp = app->appId();
93 reply(startingResponse);
94 });
95 }
96
97 void focusRequest(const std::shared_ptr<ubuntu::app_launch::Application>& app,
98 const std::shared_ptr<ubuntu::app_launch::Application::Instance>& instance,
99 std::function<void(bool)> reply) override
100 {
101 thread.timeout(focusTimeout, [this, app, instance, reply]() {
102 lastFocusedApp = app->appId();
103 reply(focusResponse);
104 });
105 }
106
107 void resumeRequest(const std::shared_ptr<ubuntu::app_launch::Application>& app,
108 const std::shared_ptr<ubuntu::app_launch::Application::Instance>& instance,
109 std::function<void(bool)> reply) override
110 {
111 thread.timeout(resumeTimeout, [this, app, instance, reply]() {
112 lastResumedApp = app->appId();
113 reply(resumeResponse);
114 });
115 }
116 };
117 std::shared_ptr<ManagerMock> manager;
118
78 /* Useful debugging stuff, but not on by default. You really want to119 /* Useful debugging stuff, but not on by default. You really want to
79 not get all this noise typically */120 not get all this noise typically */
80 void debugConnection()121 void debugConnection()
@@ -127,13 +168,13 @@
127168
128 dbus_test_dbus_mock_object_add_method(mock, obj, "GetJobByName", G_VARIANT_TYPE("s"), G_VARIANT_TYPE("o"),169 dbus_test_dbus_mock_object_add_method(mock, obj, "GetJobByName", G_VARIANT_TYPE("s"), G_VARIANT_TYPE("o"),
129 "if args[0] == 'application-click':\n"170 "if args[0] == 'application-click':\n"
130 " ret = dbus.ObjectPath('/com/test/application_click')\n"171 " ret = dbus.ObjectPath('/com/test/application_click')\n"
131 "elif args[0] == 'application-snap':\n"172 "elif args[0] == 'application-snap':\n"
132 " ret = dbus.ObjectPath('/com/test/application_snap')\n"173 " ret = dbus.ObjectPath('/com/test/application_snap')\n"
133 "elif args[0] == 'application-legacy':\n"174 "elif args[0] == 'application-legacy':\n"
134 " ret = dbus.ObjectPath('/com/test/application_legacy')\n"175 " ret = dbus.ObjectPath('/com/test/application_legacy')\n"
135 "elif args[0] == 'untrusted-helper':\n"176 "elif args[0] == 'untrusted-helper':\n"
136 " ret = dbus.ObjectPath('/com/test/untrusted/helper')\n",177 " ret = dbus.ObjectPath('/com/test/untrusted/helper')\n",
137 NULL);178 NULL);
138179
139 dbus_test_dbus_mock_object_add_method(mock, obj, "SetEnv", G_VARIANT_TYPE("(assb)"), NULL, "", NULL);180 dbus_test_dbus_mock_object_add_method(mock, obj, "SetEnv", G_VARIANT_TYPE("(assb)"), NULL, "", NULL);
@@ -272,18 +313,23 @@
272 /* Make sure we pretend the CG manager is just on our bus */313 /* Make sure we pretend the CG manager is just on our bus */
273 g_setenv("UBUNTU_APP_LAUNCH_CG_MANAGER_SESSION_BUS", "YES", TRUE);314 g_setenv("UBUNTU_APP_LAUNCH_CG_MANAGER_SESSION_BUS", "YES", TRUE);
274315
275 ASSERT_TRUE(ubuntu_app_launch_observer_add_app_focus(focus_cb, this));
276 ASSERT_TRUE(ubuntu_app_launch_observer_add_app_resume(resume_cb, this));
277
278 registry = std::make_shared<ubuntu::app_launch::Registry>();316 registry = std::make_shared<ubuntu::app_launch::Registry>();
317
318 manager = std::make_shared<ManagerMock>();
319 ubuntu::app_launch::Registry::setManager(manager, registry);
279 }320 }
280321
281 virtual void TearDown()322 virtual void TearDown()
282 {323 {
283 ubuntu_app_launch_observer_delete_app_focus(focus_cb, this);324 manager->quit();
284 ubuntu_app_launch_observer_delete_app_resume(resume_cb, this);
285
286 registry.reset();325 registry.reset();
326 manager.reset();
327
328 // NOTE: This should generally always be commented out, but
329 // it is useful for debugging common errors, so leaving it
330 // as a comment to make debugging those eaiser.
331 //
332 // ubuntu::app_launch::Registry::clearDefault();
287333
288 g_clear_object(&mock);334 g_clear_object(&mock);
289 g_clear_object(&cgmock);335 g_clear_object(&cgmock);
@@ -832,33 +878,30 @@
832#endif878#endif
833}879}
834880
835typedef struct
836{
837 unsigned int count;
838 const gchar* name;
839} observer_data_t;
840
841static void observer_cb(const gchar* appid, gpointer user_data)
842{
843 observer_data_t* data = (observer_data_t*)user_data;
844
845 if (data->name == NULL)
846 {
847 data->count++;
848 }
849 else if (g_strcmp0(data->name, appid) == 0)
850 {
851 data->count++;
852 }
853}
854
855TEST_F(LibUAL, StartStopObserver)881TEST_F(LibUAL, StartStopObserver)
856{882{
857 observer_data_t start_data = {.count = 0, .name = nullptr};883 int start_count = 0;
858 observer_data_t stop_data = {.count = 0, .name = nullptr};884 int stop_count = 0;
859885 ubuntu::app_launch::AppID start_appid;
860 ASSERT_TRUE(ubuntu_app_launch_observer_add_app_started(observer_cb, &start_data));886 ubuntu::app_launch::AppID stop_appid;
861 ASSERT_TRUE(ubuntu_app_launch_observer_add_app_stop(observer_cb, &stop_data));887
888 ubuntu::app_launch::Registry::appStarted(registry).connect(
889 [&start_count, &start_appid](std::shared_ptr<ubuntu::app_launch::Application> app,
890 std::shared_ptr<ubuntu::app_launch::Application::Instance> instance) {
891 if (!start_appid.empty() && !(start_appid == app->appId()))
892 return;
893
894 start_count++;
895 });
896
897 ubuntu::app_launch::Registry::appStopped(registry).connect(
898 [&stop_count, &stop_appid](std::shared_ptr<ubuntu::app_launch::Application> app,
899 std::shared_ptr<ubuntu::app_launch::Application::Instance> instance) {
900 if (!stop_appid.empty() && !(stop_appid == app->appId()))
901 return;
902
903 stop_count++;
904 });
862905
863 DbusTestDbusMockObject* obj =906 DbusTestDbusMockObject* obj =
864 dbus_test_dbus_mock_get_object(mock, "/com/ubuntu/Upstart", "com.ubuntu.Upstart0_6", NULL);907 dbus_test_dbus_mock_get_object(mock, "/com/ubuntu/Upstart", "com.ubuntu.Upstart0_6", NULL);
@@ -869,7 +912,7 @@
869 g_variant_new_parsed("('started', ['JOB=application-click', 'INSTANCE=com.test.good_application_1.2.3'])"),912 g_variant_new_parsed("('started', ['JOB=application-click', 'INSTANCE=com.test.good_application_1.2.3'])"),
870 NULL);913 NULL);
871914
872 EXPECT_EVENTUALLY_EQ(1, start_data.count);915 EXPECT_EVENTUALLY_EQ(1, start_count);
873916
874 /* Basic stop */917 /* Basic stop */
875 dbus_test_dbus_mock_object_emit_signal(918 dbus_test_dbus_mock_object_emit_signal(
@@ -877,33 +920,41 @@
877 g_variant_new_parsed("('stopped', ['JOB=application-click', 'INSTANCE=com.test.good_application_1.2.3'])"),920 g_variant_new_parsed("('stopped', ['JOB=application-click', 'INSTANCE=com.test.good_application_1.2.3'])"),
878 NULL);921 NULL);
879922
880 EXPECT_EVENTUALLY_EQ(1, stop_data.count);923 EXPECT_EVENTUALLY_EQ(1, stop_count);
881924
882 /* Start legacy */925 /* Start legacy */
883 start_data.count = 0;926 start_count = 0;
884 start_data.name = "multiple";927 start_appid = ubuntu::app_launch::AppID{ubuntu::app_launch::AppID::Package::from_raw({}),
928 ubuntu::app_launch::AppID::AppName::from_raw("multiple"),
929 ubuntu::app_launch::AppID::Version::from_raw({})};
885930
886 dbus_test_dbus_mock_object_emit_signal(931 dbus_test_dbus_mock_object_emit_signal(
887 mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"),932 mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"),
888 g_variant_new_parsed("('started', ['JOB=application-legacy', 'INSTANCE=multiple-234235'])"), NULL);933 g_variant_new_parsed("('started', ['JOB=application-legacy', 'INSTANCE=multiple-234235'])"), NULL);
889934
890 EXPECT_EVENTUALLY_EQ(1, start_data.count);935 EXPECT_EVENTUALLY_EQ(1, start_count);
891936
892 /* Legacy stop */937 /* Legacy stop */
893 stop_data.count = 0;938 stop_count = 0;
894 stop_data.name = "bar";939 stop_appid = ubuntu::app_launch::AppID{ubuntu::app_launch::AppID::Package::from_raw({}),
940 ubuntu::app_launch::AppID::AppName::from_raw("foo"),
941 ubuntu::app_launch::AppID::Version::from_raw({})};
895942
896 dbus_test_dbus_mock_object_emit_signal(943 dbus_test_dbus_mock_object_emit_signal(
897 mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"),944 mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"),
898 g_variant_new_parsed("('stopped', ['JOB=application-legacy', 'INSTANCE=bar-9344321'])"), NULL);945 g_variant_new_parsed("('stopped', ['JOB=application-legacy', 'INSTANCE=foo-9344321'])"), NULL);
899946
900 EXPECT_EVENTUALLY_EQ(1, stop_data.count);947 EXPECT_EVENTUALLY_EQ(1, stop_count);
901948
902 /* Test Noise Start */949 /* Test Noise Start */
903 start_data.count = 0;950 start_count = 0;
904 start_data.name = "com.test.good_application_1.2.3";951 start_appid = ubuntu::app_launch::AppID{ubuntu::app_launch::AppID::Package::from_raw("com.test.good"),
905 stop_data.count = 0;952 ubuntu::app_launch::AppID::AppName::from_raw("application"),
906 stop_data.name = "com.test.good_application_1.2.3";953 ubuntu::app_launch::AppID::Version::from_raw("1.2.3")};
954 stop_count = 0;
955 stop_appid = ubuntu::app_launch::AppID{ubuntu::app_launch::AppID::Package::from_raw("com.test.good"),
956 ubuntu::app_launch::AppID::AppName::from_raw("application"),
957 ubuntu::app_launch::AppID::Version::from_raw("1.2.3")};
907958
908 /* A full lifecycle */959 /* A full lifecycle */
909 dbus_test_dbus_mock_object_emit_signal(960 dbus_test_dbus_mock_object_emit_signal(
@@ -924,46 +975,33 @@
924 NULL);975 NULL);
925976
926 /* Ensure we just signaled once for each */977 /* Ensure we just signaled once for each */
927 EXPECT_EVENTUALLY_EQ(1, start_data.count);978 EXPECT_EVENTUALLY_EQ(1, start_count);
928 EXPECT_EVENTUALLY_EQ(1, stop_data.count);979 EXPECT_EVENTUALLY_EQ(1, stop_count);
929
930 /* Remove */
931 ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_started(observer_cb, &start_data));
932 ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_stop(observer_cb, &stop_data));
933}
934
935static GDBusMessage* filter_starting(GDBusConnection* conn,
936 GDBusMessage* message,
937 gboolean incomming,
938 gpointer user_data)
939{
940 if (g_strcmp0(g_dbus_message_get_member(message), "UnityStartingSignal") == 0)
941 {
942 unsigned int* count = static_cast<unsigned int*>(user_data);
943 (*count)++;
944 g_object_unref(message);
945 return NULL;
946 }
947
948 return message;
949}
950
951static void starting_observer(const gchar* appid, gpointer user_data)
952{
953 std::string* last = static_cast<std::string*>(user_data);
954 *last = appid;
955 return;
956}980}
957981
958TEST_F(LibUAL, StartingResponses)982TEST_F(LibUAL, StartingResponses)
959{983{
960 std::string last_observer;984 /* Get Bus */
985 GDBusConnection* session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
986
987 /* Setup filter to count signals out */
961 unsigned int starting_count = 0;988 unsigned int starting_count = 0;
962 GDBusConnection* session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);989 guint filter = g_dbus_connection_add_filter(
963 guint filter = g_dbus_connection_add_filter(session, filter_starting, &starting_count, NULL);990 session,
964991 [](GDBusConnection* conn, GDBusMessage* message, gboolean incomming, gpointer user_data) -> GDBusMessage* {
965 EXPECT_TRUE(ubuntu_app_launch_observer_add_app_starting(starting_observer, &last_observer));992 if (g_strcmp0(g_dbus_message_get_member(message), "UnityStartingSignal") == 0)
966993 {
994 unsigned int* count = static_cast<unsigned int*>(user_data);
995 (*count)++;
996 g_object_unref(message);
997 return NULL;
998 }
999
1000 return message;
1001 },
1002 &starting_count, NULL);
1003
1004 /* Emit a signal */
967 g_dbus_connection_emit_signal(session, NULL, /* destination */1005 g_dbus_connection_emit_signal(session, NULL, /* destination */
968 "/", /* path */1006 "/", /* path */
969 "com.canonical.UbuntuAppLaunch", /* interface */1007 "com.canonical.UbuntuAppLaunch", /* interface */
@@ -971,11 +1009,15 @@
971 g_variant_new("(s)", "com.test.good_application_1.2.3"), /* params, the same */1009 g_variant_new("(s)", "com.test.good_application_1.2.3"), /* params, the same */
972 NULL);1010 NULL);
9731011
974 EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", last_observer);1012 /* Make sure we run our observer */
1013 EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID(ubuntu::app_launch::AppID::Package::from_raw("com.test.good"),
1014 ubuntu::app_launch::AppID::AppName::from_raw("application"),
1015 ubuntu::app_launch::AppID::Version::from_raw("1.2.3")),
1016 manager->lastStartedApp);
1017
1018 /* Make sure we return */
975 EXPECT_EVENTUALLY_EQ(1, starting_count);1019 EXPECT_EVENTUALLY_EQ(1, starting_count);
9761020
977 EXPECT_TRUE(ubuntu_app_launch_observer_delete_app_starting(starting_observer, &last_observer));
978
979 g_dbus_connection_remove_filter(session, filter);1021 g_dbus_connection_remove_filter(session, filter);
980 g_object_unref(session);1022 g_object_unref(session);
981}1023}
@@ -986,8 +1028,10 @@
986 auto app = ubuntu::app_launch::Application::create(appid, registry);1028 auto app = ubuntu::app_launch::Application::create(appid, registry);
987 app->launch();1029 app->launch();
9881030
989 EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_focus_appid);1031 EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
990 EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_resume_appid);1032 this->manager->lastFocusedApp);
1033 EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
1034 this->manager->lastResumedApp);
991}1035}
9921036
993GDBusMessage* filter_func_good(GDBusConnection* conn, GDBusMessage* message, gboolean incomming, gpointer user_data)1037GDBusMessage* filter_func_good(GDBusConnection* conn, GDBusMessage* message, gboolean incomming, gpointer user_data)
@@ -1021,8 +1065,10 @@
10211065
1022 app->launch(uris);1066 app->launch(uris);
10231067
1024 EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_focus_appid);1068 EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
1025 EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_resume_appid);1069 this->manager->lastFocusedApp);
1070 EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
1071 this->manager->lastResumedApp);
10261072
1027 g_dbus_connection_remove_filter(session, filter);1073 g_dbus_connection_remove_filter(session, filter);
10281074
@@ -1054,8 +1100,10 @@
10541100
1055 app->launch(uris);1101 app->launch(uris);
10561102
1057 EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_focus_appid);1103 EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
1058 EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_resume_appid);1104 this->manager->lastFocusedApp);
1105 EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
1106 this->manager->lastResumedApp);
1059}1107}
10601108
1061TEST_F(LibUAL, UnityTimeoutTest)1109TEST_F(LibUAL, UnityTimeoutTest)
@@ -1067,8 +1115,10 @@
10671115
1068 app->launch();1116 app->launch();
10691117
1070 EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_resume_appid);1118 EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
1071 EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_focus_appid);1119 this->manager->lastResumedApp);
1120 EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
1121 this->manager->lastFocusedApp);
1072}1122}
10731123
1074TEST_F(LibUAL, UnityTimeoutUriTest)1124TEST_F(LibUAL, UnityTimeoutUriTest)
@@ -1082,8 +1132,10 @@
10821132
1083 app->launch(uris);1133 app->launch(uris);
10841134
1085 EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_focus_appid);1135 EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
1086 EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_resume_appid);1136 this->manager->lastFocusedApp);
1137 EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
1138 this->manager->lastResumedApp);
1087}1139}
10881140
1089GDBusMessage* filter_respawn(GDBusConnection* conn, GDBusMessage* message, gboolean incomming, gpointer user_data)1141GDBusMessage* filter_respawn(GDBusConnection* conn, GDBusMessage* message, gboolean incomming, gpointer user_data)
@@ -1116,8 +1168,10 @@
1116 g_debug("Start call time: %d ms", (end - start) / 1000);1168 g_debug("Start call time: %d ms", (end - start) / 1000);
1117 EXPECT_LT(end - start, 2000 * 1000);1169 EXPECT_LT(end - start, 2000 * 1000);
11181170
1119 EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_focus_appid);1171 EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
1120 EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", this->last_resume_appid);1172 this->manager->lastFocusedApp);
1173 EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"),
1174 this->manager->lastResumedApp);
11211175
1122 g_dbus_connection_remove_filter(session, filter);1176 g_dbus_connection_remove_filter(session, filter);
1123 g_object_unref(session);1177 g_object_unref(session);
@@ -1177,23 +1231,21 @@
1177 g_variant_unref(env);1231 g_variant_unref(env);
1178}1232}
11791233
1180static void failed_observer(const gchar* appid, UbuntuAppLaunchAppFailed reason, gpointer user_data)
1181{
1182 if (reason == UBUNTU_APP_LAUNCH_APP_FAILED_CRASH)
1183 {
1184 std::string* last = static_cast<std::string*>(user_data);
1185 *last = appid;
1186 }
1187 return;
1188}
1189
1190TEST_F(LibUAL, FailingObserver)1234TEST_F(LibUAL, FailingObserver)
1191{1235{
1192 std::string last_observer;1236 ubuntu::app_launch::AppID lastFailedApp;
1237 ubuntu::app_launch::Registry::FailureType lastFailedType;
1238
1239 ubuntu::app_launch::Registry::appFailed(registry).connect(
1240 [&lastFailedApp, &lastFailedType](std::shared_ptr<ubuntu::app_launch::Application> app,
1241 std::shared_ptr<ubuntu::app_launch::Application::Instance> instance,
1242 ubuntu::app_launch::Registry::FailureType type) {
1243 lastFailedApp = app->appId();
1244 lastFailedType = type;
1245 });
1246
1193 GDBusConnection* session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);1247 GDBusConnection* session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
11941248
1195 EXPECT_TRUE(ubuntu_app_launch_observer_add_app_failed(failed_observer, &last_observer));
1196
1197 g_dbus_connection_emit_signal(1249 g_dbus_connection_emit_signal(
1198 session, NULL, /* destination */1250 session, NULL, /* destination */
1199 "/", /* path */1251 "/", /* path */
@@ -1202,9 +1254,10 @@
1202 g_variant_new("(ss)", "com.test.good_application_1.2.3", "crash"), /* params, the same */1254 g_variant_new("(ss)", "com.test.good_application_1.2.3", "crash"), /* params, the same */
1203 NULL);1255 NULL);
12041256
1205 EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", last_observer);1257 EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"), lastFailedApp);
1258 EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::Registry::FailureType::CRASH, lastFailedType);
12061259
1207 last_observer.clear();1260 lastFailedApp = ubuntu::app_launch::AppID();
12081261
1209 g_dbus_connection_emit_signal(1262 g_dbus_connection_emit_signal(
1210 session, NULL, /* destination */1263 session, NULL, /* destination */
@@ -1214,9 +1267,9 @@
1214 g_variant_new("(ss)", "com.test.good_application_1.2.3", "blahblah"), /* params, the same */1267 g_variant_new("(ss)", "com.test.good_application_1.2.3", "blahblah"), /* params, the same */
1215 NULL);1268 NULL);
12161269
1217 EXPECT_EVENTUALLY_EQ("com.test.good_application_1.2.3", last_observer);1270 EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::AppID::parse("com.test.good_application_1.2.3"), lastFailedApp);
12181271
1219 last_observer.clear();1272 lastFailedApp = ubuntu::app_launch::AppID();
12201273
1221 g_dbus_connection_emit_signal(1274 g_dbus_connection_emit_signal(
1222 session, NULL, /* destination */1275 session, NULL, /* destination */
@@ -1226,9 +1279,7 @@
1226 g_variant_new("(ss)", "com.test.good_application_1.2.3", "start-failure"), /* params, the same */1279 g_variant_new("(ss)", "com.test.good_application_1.2.3", "start-failure"), /* params, the same */
1227 NULL);1280 NULL);
12281281
1229 EXPECT_EVENTUALLY_EQ(true, last_observer.empty());1282 EXPECT_EVENTUALLY_EQ(ubuntu::app_launch::Registry::FailureType::START_FAILURE, lastFailedType);
1230
1231 EXPECT_TRUE(ubuntu_app_launch_observer_delete_app_failed(failed_observer, &last_observer));
12321283
1233 g_object_unref(session);1284 g_object_unref(session);
1234}1285}
12351286
=== modified file 'tools/CMakeLists.txt'
--- tools/CMakeLists.txt 2016-06-18 18:24:27 +0000
+++ tools/CMakeLists.txt 2017-01-24 04:21:42 +0000
@@ -39,7 +39,7 @@
39# ubuntu-app-launch39# ubuntu-app-launch
40########################40########################
4141
42add_executable(ubuntu-app-launch ubuntu-app-launch.c)42add_executable(ubuntu-app-launch ubuntu-app-launch.cpp)
43set_target_properties(ubuntu-app-launch PROPERTIES OUTPUT_NAME "ubuntu-app-launch")43set_target_properties(ubuntu-app-launch PROPERTIES OUTPUT_NAME "ubuntu-app-launch")
44target_link_libraries(ubuntu-app-launch ubuntu-launcher)44target_link_libraries(ubuntu-app-launch ubuntu-launcher)
45install(TARGETS ubuntu-app-launch RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}")45install(TARGETS ubuntu-app-launch RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}")
@@ -48,7 +48,7 @@
48# ubuntu-app-watch48# ubuntu-app-watch
49########################49########################
5050
51add_executable(ubuntu-app-watch ubuntu-app-watch.c)51add_executable(ubuntu-app-watch ubuntu-app-watch.cpp)
52set_target_properties(ubuntu-app-watch PROPERTIES OUTPUT_NAME "ubuntu-app-watch")52set_target_properties(ubuntu-app-watch PROPERTIES OUTPUT_NAME "ubuntu-app-watch")
53target_link_libraries(ubuntu-app-watch ubuntu-launcher)53target_link_libraries(ubuntu-app-watch ubuntu-launcher)
54install(TARGETS ubuntu-app-watch RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}")54install(TARGETS ubuntu-app-watch RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}")
@@ -116,3 +116,22 @@
116target_link_libraries(ubuntu-app-usage ubuntu-launcher)116target_link_libraries(ubuntu-app-usage ubuntu-launcher)
117install(TARGETS ubuntu-app-usage RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}")117install(TARGETS ubuntu-app-usage RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}")
118118
119########################
120# Formatting
121########################
122
123add_custom_target(format-tools
124 COMMAND clang-format -i -style=file
125 ubuntu-app-info.cpp
126 ubuntu-app-launch-appids.cpp
127 ubuntu-app-launch.cpp
128 ubuntu-app-list.cpp
129 ubuntu-app-list-pids.cpp
130 ubuntu-app-pid.cpp
131 ubuntu-app-stop.cpp
132 ubuntu-app-triplet.cpp
133 ubuntu-app-watch.cpp
134 ubuntu-helper-list.cpp
135 ubuntu-helper-start.cpp
136 ubuntu-helper-stop.cpp
137)
119138
=== modified file 'tools/ubuntu-app-info.cpp'
--- tools/ubuntu-app-info.cpp 2016-05-04 14:09:10 +0000
+++ tools/ubuntu-app-info.cpp 2017-01-24 04:21:42 +0000
@@ -17,34 +17,40 @@
17 * Ted Gould <ted.gould@canonical.com>17 * Ted Gould <ted.gould@canonical.com>
18 */18 */
1919
20#include <iostream>
21#include "libubuntu-app-launch/application.h"20#include "libubuntu-app-launch/application.h"
22#include "libubuntu-app-launch/registry.h"21#include "libubuntu-app-launch/registry.h"
22#include <iostream>
2323
24int main(int argc, char* argv[])24int main(int argc, char *argv[])
25{25{
26 if (argc != 2) {26 if (argc != 2)
27 {
27 std::cerr << "Usage: " << argv[0] << " (appid)" << std::endl;28 std::cerr << "Usage: " << argv[0] << " (appid)" << std::endl;
28 exit(1);29 exit(1);
29 }30 }
3031
31 auto appid = ubuntu::app_launch::AppID::find(argv[1]);32 auto appid = ubuntu::app_launch::AppID::find(argv[1]);
32 if (appid.empty()) {33 if (appid.empty())
34 {
33 std::cerr << "Unable to find app for appid: " << argv[1] << std::endl;35 std::cerr << "Unable to find app for appid: " << argv[1] << std::endl;
34 return 1;36 return 1;
35 }37 }
3638
37 std::shared_ptr<ubuntu::app_launch::Application> app;39 std::shared_ptr<ubuntu::app_launch::Application> app;
38 try {40 try
41 {
39 app = ubuntu::app_launch::Application::create(appid, ubuntu::app_launch::Registry::getDefault());42 app = ubuntu::app_launch::Application::create(appid, ubuntu::app_launch::Registry::getDefault());
40 if (!app)43 if (!app)
41 throw std::runtime_error("Application object is nullptr");44 throw std::runtime_error("Application object is nullptr");
42 } catch (std::runtime_error &e) {45 }
46 catch (std::runtime_error &e)
47 {
43 std::cerr << "Unable to find application for AppID: " << argv[1] << std::endl;48 std::cerr << "Unable to find application for AppID: " << argv[1] << std::endl;
44 exit(1);49 exit(1);
45 }50 }
4651
47 try {52 try
53 {
48 auto info = app->info();54 auto info = app->info();
4955
50 std::cout << "Name: " << info->name().value() << std::endl;56 std::cout << "Name: " << info->name().value() << std::endl;
@@ -64,8 +70,11 @@
64 std::cout << " Inv Landscape: " << info->supportedOrientations().invertedLandscape << std::endl;70 std::cout << " Inv Landscape: " << info->supportedOrientations().invertedLandscape << std::endl;
65 std::cout << "Rotates: " << info->rotatesWindowContents().value() << std::endl;71 std::cout << "Rotates: " << info->rotatesWindowContents().value() << std::endl;
66 std::cout << "Ubuntu Lifecycle: " << info->supportsUbuntuLifecycle().value() << std::endl;72 std::cout << "Ubuntu Lifecycle: " << info->supportsUbuntuLifecycle().value() << std::endl;
67 } catch (std::runtime_error &e) {73 }
68 std::cerr << "Unable to parse Application info for application '" << std::string(appid) << "': " << e.what() << std::endl;74 catch (std::runtime_error &e)
75 {
76 std::cerr << "Unable to parse Application info for application '" << std::string(appid) << "': " << e.what()
77 << std::endl;
69 exit(1);78 exit(1);
70 }79 }
7180
7281
=== renamed file 'tools/ubuntu-app-launch.c' => 'tools/ubuntu-app-launch.cpp'
--- tools/ubuntu-app-launch.c 2016-02-08 19:03:31 +0000
+++ tools/ubuntu-app-launch.cpp 2017-01-24 04:21:42 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright 2013 Canonical Ltd.2 * Copyright © 2016 Canonical Ltd.
3 *3 *
4 * This program is free software: you can redistribute it and/or modify it4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published5 * under the terms of the GNU General Public License version 3, as published
@@ -17,71 +17,59 @@
17 * Ted Gould <ted.gould@canonical.com>17 * Ted Gould <ted.gould@canonical.com>
18 */18 */
1919
20#include <glib.h>20#include "libubuntu-app-launch/application.h"
21#include "libubuntu-app-launch/ubuntu-app-launch.h"21#include "libubuntu-app-launch/registry.h"
2222#include <csignal>
23const gchar * global_appid = NULL;23#include <future>
24int retval = 0;24#include <iostream>
2525
26static void26ubuntu::app_launch::AppID global_appid;
27good_observer (const gchar * appid, gpointer user_data)27std::promise<int> retval;
28{28
29 if (g_strcmp0(appid, global_appid) != 0) {29int main(int argc, char* argv[])
30 return;30{
31 }31 if (argc < 2)
3232 {
33 g_debug("Application '%s' running", appid);33 std::cerr << "Usage: " << argv[0] << " <app id> [uris]" << std::endl;
34 g_main_loop_quit((GMainLoop *)user_data);34 return 1;
35}35 }
3636
37static void37 global_appid = ubuntu::app_launch::AppID::find(argv[1]);
38bad_observer (const gchar * appid, UbuntuAppLaunchAppFailed failure_type, gpointer user_data)38
39{39 std::vector<ubuntu::app_launch::Application::URL> urls;
40 if (g_strcmp0(appid, global_appid) != 0) {40 for (int i = 2; i < argc; i++)
41 return;41 {
42 }42 urls.push_back(ubuntu::app_launch::Application::URL::from_raw(argv[i]));
4343 }
44 g_debug("Application '%s' failed: %s", appid, failure_type == UBUNTU_APP_LAUNCH_APP_FAILED_CRASH ? "crash" : "startup failure");44
45 retval = -1;45 ubuntu::app_launch::Registry::appStarted().connect(
46 g_main_loop_quit((GMainLoop *)user_data);46 [](std::shared_ptr<ubuntu::app_launch::Application> app,
47}47 std::shared_ptr<ubuntu::app_launch::Application::Instance> instance) {
4848 if (app->appId() != global_appid)
49int49 {
50main (int argc, gchar * argv[]) {50 return;
51 if (argc < 2) {51 }
52 g_printerr("Usage: %s <app id> [uris]\n", argv[0]);52
53 return 1;53 std::cout << "Started: " << (std::string)app->appId() << std::endl;
54 }54 retval.set_value(EXIT_SUCCESS);
5555 });
56 global_appid = argv[1];56
57 GMainLoop * mainloop = g_main_loop_new(NULL, FALSE);57 ubuntu::app_launch::Registry::appFailed().connect(
5858 [](std::shared_ptr<ubuntu::app_launch::Application> app,
59 gchar ** uris = NULL;59 std::shared_ptr<ubuntu::app_launch::Application::Instance> instance,
60 if (argc > 2) {60 ubuntu::app_launch::Registry::FailureType type) {
61 int i;61 if (app->appId() != global_appid)
6262 {
63 uris = g_new0(gchar *, argc - 1);63 return;
6464 }
65 for (i = 2; i < argc; i++) {65
66 uris[i - 2] = argv[i];66 std::cout << "Failed: " << (std::string)app->appId() << std::endl;
67 }67 retval.set_value(EXIT_FAILURE);
68 }68 });
6969
70 ubuntu_app_launch_observer_add_app_started(good_observer, mainloop);70 auto app = ubuntu::app_launch::Application::create(global_appid, ubuntu::app_launch::Registry::getDefault());
71 ubuntu_app_launch_observer_add_app_focus(good_observer, mainloop);71 app->launch(urls);
7272
73 ubuntu_app_launch_observer_add_app_failed(bad_observer, mainloop);73 std::signal(SIGTERM, [](int signal) -> void { retval.set_value(EXIT_SUCCESS); });
7474 return retval.get_future().get();
75 ubuntu_app_launch_start_application(global_appid, (const gchar * const *)uris);
76
77 g_main_loop_run(mainloop);
78
79 ubuntu_app_launch_observer_delete_app_started(good_observer, mainloop);
80 ubuntu_app_launch_observer_delete_app_focus(good_observer, mainloop);
81 ubuntu_app_launch_observer_delete_app_failed(bad_observer, mainloop);
82
83 g_main_loop_unref(mainloop);
84 g_free(uris);
85
86 return retval;
87}75}
8876
=== modified file 'tools/ubuntu-app-list-pids.cpp'
--- tools/ubuntu-app-list-pids.cpp 2016-05-03 01:46:27 +0000
+++ tools/ubuntu-app-list-pids.cpp 2017-01-24 04:21:42 +0000
@@ -30,12 +30,14 @@
30 }30 }
3131
32 auto appid = ubuntu::app_launch::AppID::find(argv[1]);32 auto appid = ubuntu::app_launch::AppID::find(argv[1]);
33 if (appid.empty()) {33 if (appid.empty())
34 {
34 std::cerr << "Unable to find app for appid: " << argv[1] << std::endl;35 std::cerr << "Unable to find app for appid: " << argv[1] << std::endl;
35 return 1;36 return 1;
36 }37 }
3738
38 try {39 try
40 {
39 auto app = ubuntu::app_launch::Application::create(appid, ubuntu::app_launch::Registry::getDefault());41 auto app = ubuntu::app_launch::Application::create(appid, ubuntu::app_launch::Registry::getDefault());
4042
41 for (auto instance : app->instances())43 for (auto instance : app->instances())
@@ -45,7 +47,9 @@
45 std::cout << pid << std::endl;47 std::cout << pid << std::endl;
46 }48 }
47 }49 }
48 } catch (std::runtime_error &e) {50 }
51 catch (std::runtime_error& e)
52 {
49 std::cerr << "Unable to find application for '" << std::string(appid) << "': " << e.what() << std::endl;53 std::cerr << "Unable to find application for '" << std::string(appid) << "': " << e.what() << std::endl;
50 return 1;54 return 1;
51 }55 }
5256
=== modified file 'tools/ubuntu-app-list.cpp'
--- tools/ubuntu-app-list.cpp 2016-02-09 21:12:54 +0000
+++ tools/ubuntu-app-list.cpp 2017-01-24 04:21:42 +0000
@@ -17,8 +17,8 @@
17 * Ted Gould <ted.gould@canonical.com>17 * Ted Gould <ted.gould@canonical.com>
18 */18 */
1919
20#include "libubuntu-app-launch/registry.h"
20#include <iostream>21#include <iostream>
21#include "libubuntu-app-launch/registry.h"
2222
23int main(int argc, char* argv[])23int main(int argc, char* argv[])
24{24{
2525
=== modified file 'tools/ubuntu-app-pid.cpp'
--- tools/ubuntu-app-pid.cpp 2016-05-03 01:46:51 +0000
+++ tools/ubuntu-app-pid.cpp 2017-01-24 04:21:42 +0000
@@ -18,8 +18,8 @@
18 */18 */
1919
20#include <iostream>20#include <iostream>
21#include <libubuntu-app-launch/application.h>
21#include <libubuntu-app-launch/registry.h>22#include <libubuntu-app-launch/registry.h>
22#include <libubuntu-app-launch/application.h>
2323
24int main(int argc, char* argv[])24int main(int argc, char* argv[])
25{25{
@@ -31,12 +31,14 @@
31 }31 }
3232
33 auto appid = ubuntu::app_launch::AppID::find(argv[1]);33 auto appid = ubuntu::app_launch::AppID::find(argv[1]);
34 if (appid.empty()) {34 if (appid.empty())
35 {
35 std::cerr << "Unable to find app for appid: " << argv[1] << std::endl;36 std::cerr << "Unable to find app for appid: " << argv[1] << std::endl;
36 return 1;37 return 1;
37 }38 }
3839
39 try {40 try
41 {
40 auto app = ubuntu::app_launch::Application::create(appid, ubuntu::app_launch::Registry::getDefault());42 auto app = ubuntu::app_launch::Application::create(appid, ubuntu::app_launch::Registry::getDefault());
41 auto pid = app->instances()[0]->primaryPid();43 auto pid = app->instances()[0]->primaryPid();
4244
@@ -47,7 +49,9 @@
4749
48 std::cout << pid << std::endl;50 std::cout << pid << std::endl;
49 return 0;51 return 0;
50 } catch (std::runtime_error &e) {52 }
53 catch (std::runtime_error& e)
54 {
51 std::cerr << "Unable to find application for '" << std::string(appid) << "': " << e.what() << std::endl;55 std::cerr << "Unable to find application for '" << std::string(appid) << "': " << e.what() << std::endl;
52 return 1;56 return 1;
53 }57 }
5458
=== modified file 'tools/ubuntu-app-stop.cpp'
--- tools/ubuntu-app-stop.cpp 2016-05-03 01:46:27 +0000
+++ tools/ubuntu-app-stop.cpp 2017-01-24 04:21:42 +0000
@@ -30,19 +30,23 @@
30 }30 }
3131
32 auto appid = ubuntu::app_launch::AppID::find(argv[1]);32 auto appid = ubuntu::app_launch::AppID::find(argv[1]);
33 if (appid.empty()) {33 if (appid.empty())
34 {
34 std::cerr << "Unable to find app for appid: " << argv[1] << std::endl;35 std::cerr << "Unable to find app for appid: " << argv[1] << std::endl;
35 return 1;36 return 1;
36 }37 }
3738
38 try {39 try
40 {
39 auto app = ubuntu::app_launch::Application::create(appid, ubuntu::app_launch::Registry::getDefault());41 auto app = ubuntu::app_launch::Application::create(appid, ubuntu::app_launch::Registry::getDefault());
4042
41 for (auto instance : app->instances())43 for (auto instance : app->instances())
42 {44 {
43 instance->stop();45 instance->stop();
44 }46 }
45 } catch (std::runtime_error &e) {47 }
48 catch (std::runtime_error& e)
49 {
46 std::cerr << "Unable to find application for '" << std::string(appid) << "': " << e.what() << std::endl;50 std::cerr << "Unable to find application for '" << std::string(appid) << "': " << e.what() << std::endl;
47 return 1;51 return 1;
48 }52 }
4953
=== modified file 'tools/ubuntu-app-triplet.cpp'
--- tools/ubuntu-app-triplet.cpp 2016-02-09 21:12:54 +0000
+++ tools/ubuntu-app-triplet.cpp 2017-01-24 04:21:42 +0000
@@ -17,8 +17,8 @@
17 * Ted Gould <ted.gould@canonical.com>17 * Ted Gould <ted.gould@canonical.com>
18 */18 */
1919
20#include "libubuntu-app-launch/application.h"
20#include <iostream>21#include <iostream>
21#include "libubuntu-app-launch/application.h"
2222
23int main(int argc, char* argv[])23int main(int argc, char* argv[])
24{24{
2525
=== renamed file 'tools/ubuntu-app-watch.c' => 'tools/ubuntu-app-watch.cpp'
--- tools/ubuntu-app-watch.c 2016-02-08 19:03:31 +0000
+++ tools/ubuntu-app-watch.cpp 2017-01-24 04:21:42 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright 2013 Canonical Ltd.2 * Copyright © 2015 Canonical Ltd.
3 *3 *
4 * This program is free software: you can redistribute it and/or modify it4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published5 * under the terms of the GNU General Public License version 3, as published
@@ -17,91 +17,64 @@
17 * Ted Gould <ted.gould@canonical.com>17 * Ted Gould <ted.gould@canonical.com>
18 */18 */
1919
20#include "libubuntu-app-launch/ubuntu-app-launch.h"20#include "libubuntu-app-launch/registry.h"
2121#include <csignal>
22void22#include <future>
23starting (const gchar * appid, gpointer user_data)23
24{24std::promise<int> retval;
25 g_print("Starting %s\n", appid);25
26 return;26int main(int argc, char* argv[])
27}27{
2828 ubuntu::app_launch::Registry registry;
29void29
30started (const gchar * appid, gpointer user_data)30 registry.appStarted().connect([](const std::shared_ptr<ubuntu::app_launch::Application>& app,
31{31 const std::shared_ptr<ubuntu::app_launch::Application::Instance>& instance) {
32 g_print("Started %s\n", appid);32 std::cout << "Started: " << (std::string)app->appId() << std::endl;
33 return;33 });
34}34 registry.appStopped().connect([](const std::shared_ptr<ubuntu::app_launch::Application>& app,
3535 const std::shared_ptr<ubuntu::app_launch::Application::Instance>& instance) {
36void36 std::cout << "Stopped: " << (std::string)app->appId() << std::endl;
37stopped (const gchar * appid, gpointer user_data)37 });
38{38 registry.appPaused().connect([](const std::shared_ptr<ubuntu::app_launch::Application>& app,
39 g_print("Stop %s\n", appid);39 const std::shared_ptr<ubuntu::app_launch::Application::Instance>& instance,
40 return;40 const std::vector<pid_t>& pids) {
41}41 std::cout << "Paused: " << (std::string)app->appId() << " (";
4242
43void43 for (auto pid : pids)
44resumed (const gchar * appid, GPid * pids, gpointer user_data)44 {
45{45 std::cout << std::to_string(pid) << " ";
46 g_print("Resumed %s\n", appid);46 }
47 return;47
48}48 std::cout << ")" << std::endl;
4949 });
50void50 registry.appResumed().connect([](const std::shared_ptr<ubuntu::app_launch::Application>& app,
51paused (const gchar * appid, GPid * pids, gpointer user_data)51 const std::shared_ptr<ubuntu::app_launch::Application::Instance>& instance,
52{52 const std::vector<pid_t>& pids) {
53 g_print("Paused %s\n", appid);53 std::cout << "Resumed: " << (std::string)app->appId() << " (";
54 return;54
55}55 for (auto pid : pids)
5656 {
57void57 std::cout << std::to_string(pid) << " ";
58focus (const gchar * appid, gpointer user_data)58 }
59{59
60 g_print("Focus %s\n", appid);60 std::cout << ")" << std::endl;
61 return;61 });
62}62 registry.appFailed().connect([](const std::shared_ptr<ubuntu::app_launch::Application>& app,
6363 const std::shared_ptr<ubuntu::app_launch::Application::Instance>& instance,
64void64 ubuntu::app_launch::Registry::FailureType type) {
65fail (const gchar * appid, UbuntuAppLaunchAppFailed failhow, gpointer user_data)65 std::cout << "Failed: " << (std::string)app->appId();
66{66 switch (type)
67 const gchar * failstr = "unknown";67 {
68 switch (failhow) {68 case ubuntu::app_launch::Registry::FailureType::CRASH:
69 case UBUNTU_APP_LAUNCH_APP_FAILED_CRASH:69 std::cout << " (crash)";
70 failstr = "crashed";70 break;
71 break;71 case ubuntu::app_launch::Registry::FailureType::START_FAILURE:
72 case UBUNTU_APP_LAUNCH_APP_FAILED_START_FAILURE:72 std::cout << " (start failure)";
73 failstr = "startup";73 break;
74 break;74 }
75 }75 std::cout << std::endl;
7676 });
77 g_print("Fail %s (%s)\n", appid, failstr);77
78 return;78 std::signal(SIGTERM, [](int signal) -> void { retval.set_value(EXIT_SUCCESS); });
79}79 return retval.get_future().get();
80
81
82int
83main (int argc, gchar * argv[])
84{
85 ubuntu_app_launch_observer_add_app_starting(starting, NULL);
86 ubuntu_app_launch_observer_add_app_started(started, NULL);
87 ubuntu_app_launch_observer_add_app_stop(stopped, NULL);
88 ubuntu_app_launch_observer_add_app_focus(focus, NULL);
89 ubuntu_app_launch_observer_add_app_resumed(resumed, NULL);
90 ubuntu_app_launch_observer_add_app_paused(paused, NULL);
91 ubuntu_app_launch_observer_add_app_failed(fail, NULL);
92
93 GMainLoop * mainloop = g_main_loop_new(NULL, FALSE);
94 g_main_loop_run(mainloop);
95
96 ubuntu_app_launch_observer_delete_app_starting(starting, NULL);
97 ubuntu_app_launch_observer_delete_app_started(started, NULL);
98 ubuntu_app_launch_observer_delete_app_stop(stopped, NULL);
99 ubuntu_app_launch_observer_delete_app_focus(focus, NULL);
100 ubuntu_app_launch_observer_delete_app_resumed(resumed, NULL);
101 ubuntu_app_launch_observer_delete_app_paused(paused, NULL);
102 ubuntu_app_launch_observer_delete_app_failed(fail, NULL);
103
104 g_main_loop_unref(mainloop);
105
106 return 0;
107}80}
10881
=== modified file 'tools/ubuntu-helper-list.cpp'
--- tools/ubuntu-helper-list.cpp 2016-02-09 21:12:54 +0000
+++ tools/ubuntu-helper-list.cpp 2017-01-24 04:21:42 +0000
@@ -17,8 +17,8 @@
17 * Ted Gould <ted.gould@canonical.com>17 * Ted Gould <ted.gould@canonical.com>
18 */18 */
1919
20#include "libubuntu-app-launch/registry.h"
20#include <iostream>21#include <iostream>
21#include "libubuntu-app-launch/registry.h"
2222
23int main(int argc, char* argv[])23int main(int argc, char* argv[])
24{24{
2525
=== modified file 'tools/ubuntu-helper-start.cpp'
--- tools/ubuntu-helper-start.cpp 2016-05-03 01:46:27 +0000
+++ tools/ubuntu-helper-start.cpp 2017-01-24 04:21:42 +0000
@@ -17,9 +17,9 @@
17 * Ted Gould <ted.gould@canonical.com>17 * Ted Gould <ted.gould@canonical.com>
18 */18 */
1919
20#include <iostream>
21#include "libubuntu-app-launch/helper.h"20#include "libubuntu-app-launch/helper.h"
22#include "libubuntu-app-launch/registry.h"21#include "libubuntu-app-launch/registry.h"
22#include <iostream>
2323
24int main(int argc, char* argv[])24int main(int argc, char* argv[])
25{25{
@@ -31,20 +31,25 @@
3131
32 auto type = ubuntu::app_launch::Helper::Type::from_raw(argv[1]);32 auto type = ubuntu::app_launch::Helper::Type::from_raw(argv[1]);
33 auto appid = ubuntu::app_launch::AppID::find(argv[2]);33 auto appid = ubuntu::app_launch::AppID::find(argv[2]);
34 if (appid.empty()) {34 if (appid.empty())
35 {
35 std::cerr << "Unable to find helper for appid: " << argv[1] << std::endl;36 std::cerr << "Unable to find helper for appid: " << argv[1] << std::endl;
36 return 1;37 return 1;
37 }38 }
3839
39 auto registry = std::make_shared<ubuntu::app_launch::Registry>();40 auto registry = std::make_shared<ubuntu::app_launch::Registry>();
4041
41 try {42 try
43 {
42 auto helper = ubuntu::app_launch::Helper::create(type, appid, registry);44 auto helper = ubuntu::app_launch::Helper::create(type, appid, registry);
4345
44 helper->launch();46 helper->launch();
45 return 0;47 return 0;
46 } catch (std::runtime_error &e) {48 }
47 std::cerr << "Unable to find helper for '" << std::string(appid) << "' type '" << type.value() << "': " << e.what() << std::endl;49 catch (std::runtime_error& e)
50 {
51 std::cerr << "Unable to find helper for '" << std::string(appid) << "' type '" << type.value()
52 << "': " << e.what() << std::endl;
48 return 1;53 return 1;
49 }54 }
50}55}
5156
=== modified file 'tools/ubuntu-helper-stop.cpp'
--- tools/ubuntu-helper-stop.cpp 2016-05-03 01:46:27 +0000
+++ tools/ubuntu-helper-stop.cpp 2017-01-24 04:21:42 +0000
@@ -17,9 +17,9 @@
17 * Ted Gould <ted.gould@canonical.com>17 * Ted Gould <ted.gould@canonical.com>
18 */18 */
1919
20#include <iostream>
21#include "libubuntu-app-launch/helper.h"20#include "libubuntu-app-launch/helper.h"
22#include "libubuntu-app-launch/registry.h"21#include "libubuntu-app-launch/registry.h"
22#include <iostream>
2323
24int main(int argc, char* argv[])24int main(int argc, char* argv[])
25{25{
@@ -31,14 +31,16 @@
3131
32 auto type = ubuntu::app_launch::Helper::Type::from_raw(argv[1]);32 auto type = ubuntu::app_launch::Helper::Type::from_raw(argv[1]);
33 auto appid = ubuntu::app_launch::AppID::find(argv[2]);33 auto appid = ubuntu::app_launch::AppID::find(argv[2]);
34 if (appid.empty()) {34 if (appid.empty())
35 {
35 std::cerr << "Unable to find helper for appid: " << argv[1] << std::endl;36 std::cerr << "Unable to find helper for appid: " << argv[1] << std::endl;
36 return 1;37 return 1;
37 }38 }
3839
39 auto registry = std::make_shared<ubuntu::app_launch::Registry>();40 auto registry = std::make_shared<ubuntu::app_launch::Registry>();
4041
41 try {42 try
43 {
42 auto helper = ubuntu::app_launch::Helper::create(type, appid, registry);44 auto helper = ubuntu::app_launch::Helper::create(type, appid, registry);
4345
44 for (auto instance : helper->instances())46 for (auto instance : helper->instances())
@@ -47,8 +49,11 @@
47 }49 }
4850
49 return 0;51 return 0;
50 } catch (std::runtime_error &e) {52 }
51 std::cerr << "Unable to find helper for '" << std::string(appid) << "' type '" << type.value() << "': " << e.what() << std::endl;53 catch (std::runtime_error& e)
54 {
55 std::cerr << "Unable to find helper for '" << std::string(appid) << "' type '" << type.value()
56 << "': " << e.what() << std::endl;
52 return 1;57 return 1;
53 }58 }
54}59}

Subscribers

People subscribed via source and target branches