2
* Copyright (C) 2014 Canonical, Ltd.
4
* This program is free software: you can redistribute it and/or modify it under
5
* the terms of the GNU Lesser General Public License version 3, as published by
6
* the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful, but WITHOUT
9
* ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10
* SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
* Lesser General Public License for more details.
13
* You should have received a copy of the GNU Lesser General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18
#include "applicationcontroller.h"
24
#include <QStandardPaths>
28
#include "ubuntu-app-launch.h"
36
struct ApplicationController::Private
38
UbuntuAppLaunchAppObserver preStartCallback = nullptr;
39
UbuntuAppLaunchAppObserver startedCallback = nullptr;
40
UbuntuAppLaunchAppObserver stopCallback = nullptr;
41
UbuntuAppLaunchAppObserver focusCallback = nullptr;
42
UbuntuAppLaunchAppObserver resumeCallback = nullptr;
43
UbuntuAppLaunchAppFailedObserver failureCallback = nullptr;
48
* @brief toShortAppIdIfPossible
49
* @param appId - any string that you think is an appId
50
* @return if a valid appId was input, a shortened appId is returned, else returns the input string unaltered
52
QString toShortAppIdIfPossible(const QString &appId) {
53
gchar *package, *application;
54
if (ubuntu_app_launch_app_id_parse(appId.toLatin1().constData(), &package, &application, nullptr)) {
55
// is long appId, so assemble its short appId
56
QString shortAppId = QString("%1_%2").arg(package).arg(application);
66
* @brief toLongAppIdIfPossible
67
* @param shortAppId - any string that you think is a short appId
68
* @return if valid short appId was input, the corresponding long appId is returned. If a long appId was
69
* entered, it is returned unchanged. Anything else is also returned unchanged.
71
QString toLongAppIdIfPossible(const QString &shortAppId) {
72
if (ubuntu_app_launch_app_id_parse(shortAppId.toLatin1().constData(), nullptr, nullptr, nullptr)) {
73
// then we got a long appId after all, just return it
76
// try to parse the string in the form "$package_$application"
77
QRegExp shortAppIdMask("[a-z0-9][a-z0-9+.-]+_[a-zA-Z0-9+.-]+");
78
if (!shortAppIdMask.exactMatch(shortAppId)) {
79
// input string not a short appId, so just return it unchanged
83
// ask upstart for the long appId corresponding to this short appId
84
QStringList parts = shortAppId.split("_");
86
longAppId = ubuntu_app_launch_triplet_to_app_id(parts.first().toLatin1().constData(),
87
parts.last().toLatin1().constData(),
89
if (longAppId == nullptr) {
90
// was unable to construct a long appId from the short appId, return input unchanged
93
QString appId(longAppId);
102
ApplicationController::ApplicationController()
103
: qtmir::ApplicationController(),
106
impl->preStartCallback = [](const gchar * appId, gpointer userData) {
107
auto thiz = static_cast<ApplicationController*>(userData);
108
Q_EMIT(thiz->applicationAboutToBeStarted(toShortAppIdIfPossible(appId)));
111
impl->startedCallback = [](const gchar * appId, gpointer userData) {
112
auto thiz = static_cast<ApplicationController*>(userData);
113
Q_EMIT(thiz->applicationStarted(toShortAppIdIfPossible(appId)));
116
impl->stopCallback = [](const gchar * appId, gpointer userData) {
117
auto thiz = static_cast<ApplicationController*>(userData);
118
Q_EMIT(thiz->applicationStopped(toShortAppIdIfPossible(appId)));
121
impl->focusCallback = [](const gchar * appId, gpointer userData) {
122
auto thiz = static_cast<ApplicationController*>(userData);
123
Q_EMIT(thiz->applicationFocusRequest(toShortAppIdIfPossible(appId)));
126
impl->resumeCallback = [](const gchar * appId, gpointer userData) {
127
auto thiz = static_cast<ApplicationController*>(userData);
128
Q_EMIT(thiz->applicationResumeRequest(toShortAppIdIfPossible(appId)));
131
impl->failureCallback = [](const gchar * appId, UbuntuAppLaunchAppFailed failureType, gpointer userData) {
132
ApplicationController::Error error;
135
case UBUNTU_APP_LAUNCH_APP_FAILED_CRASH: error = ApplicationController::Error::APPLICATION_CRASHED;
136
case UBUNTU_APP_LAUNCH_APP_FAILED_START_FAILURE: error = ApplicationController::Error::APPLICATION_FAILED_TO_START;
139
auto thiz = static_cast<ApplicationController*>(userData);
140
Q_EMIT(thiz->applicationError(toShortAppIdIfPossible(appId), error));
143
ubuntu_app_launch_observer_add_app_starting(impl->preStartCallback, this);
144
ubuntu_app_launch_observer_add_app_started(impl->startedCallback, this);
145
ubuntu_app_launch_observer_add_app_stop(impl->stopCallback, this);
146
ubuntu_app_launch_observer_add_app_focus(impl->focusCallback, this);
147
ubuntu_app_launch_observer_add_app_resume(impl->resumeCallback, this);
148
ubuntu_app_launch_observer_add_app_failed(impl->failureCallback, this);
151
ApplicationController::~ApplicationController()
153
ubuntu_app_launch_observer_delete_app_starting(impl->preStartCallback, this);
154
ubuntu_app_launch_observer_delete_app_started(impl->startedCallback, this);
155
ubuntu_app_launch_observer_delete_app_stop(impl->stopCallback, this);
156
ubuntu_app_launch_observer_delete_app_focus(impl->focusCallback, this);
157
ubuntu_app_launch_observer_delete_app_resume(impl->resumeCallback, this);
158
ubuntu_app_launch_observer_delete_app_failed(impl->failureCallback, this);
161
pid_t ApplicationController::primaryPidForAppId(const QString& appId)
163
GPid pid = ubuntu_app_launch_get_primary_pid(toLongAppIdIfPossible(appId).toLatin1().constData());
165
qDebug() << "ApplicationController::primaryPidForAppId FAILED to get PID for appId=" << appId;
170
bool ApplicationController::appIdHasProcessId(pid_t pid, const QString& appId)
172
return ubuntu_app_launch_pid_in_app_id(pid, toLongAppIdIfPossible(appId).toLatin1().constData());
175
bool ApplicationController::stopApplicationWithAppId(const QString& appId)
177
auto result = ubuntu_app_launch_stop_application(toLongAppIdIfPossible(appId).toLatin1().constData());
179
qDebug() << "ApplicationController::stopApplication FAILED to stop appId=" << appId;
184
bool ApplicationController::startApplicationWithAppIdAndArgs(const QString& appId, const QStringList& arguments)
186
// Convert arguments QStringList into format suitable for ubuntu-app-launch
187
// The last item should be null, which is done by g_new0, we just don't fill it.
188
auto upstartArgs = g_new0(gchar *, arguments.length() + 1);
190
for (int i=0; i<arguments.length(); i++) {
191
upstartArgs[i] = g_strdup(arguments.at(i).toUtf8().data());
194
auto result = ubuntu_app_launch_start_application(
195
toLongAppIdIfPossible(appId).toLatin1().constData(),
196
static_cast<const gchar * const *>(upstartArgs));
198
g_strfreev(upstartArgs);
201
qDebug() << "Application::Controller::startApplicationWithAppIdAndArgs FAILED to start appId" << appId;
206
QFileInfo ApplicationController::findDesktopFileForAppId(const QString &appId) const
208
qCDebug(QTMIR_APPLICATIONS) << "ApplicationController::desktopFilePathForAppId - appId=" << appId;
210
// Search for the correct desktop file using a simple heuristic
212
QString helper = toLongAppIdIfPossible(appId);
217
helper = helper.replace(dashPos, 1, '/');
220
desktopFile = QStandardPaths::locate(QStandardPaths::ApplicationsLocation, QString("%1.desktop").arg(helper));
221
if (!desktopFile.isEmpty()) return desktopFile;
223
dashPos = helper.indexOf("-");
224
} while (dashPos != -1);
229
} // namespace upstart