~dandrader/qtmir/keyState

« back to all changes in this revision

Viewing changes to src/modules/Unity/Application/upstart/applicationcontroller.cpp

  • Committer: Gerry Boland
  • Date: 2014-07-01 13:38:06 UTC
  • mto: This revision was merged to the branch mainline in revision 158.
  • Revision ID: gerry.boland@canonical.com-20140701133806-lwfnplkijthydzj4
Update QML plugin: rename to Unity.Application, merge latest unity-mir changes, add tests (not passing yet) and use category logging

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2014 Canonical, Ltd.
 
3
 *
 
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.
 
7
 *
 
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.
 
12
 *
 
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/>.
 
15
 *
 
16
 */
 
17
 
 
18
#include "applicationcontroller.h"
 
19
 
 
20
// qtmir
 
21
#include <logging.h>
 
22
 
 
23
// Qt
 
24
#include <QStandardPaths>
 
25
 
 
26
// upstart
 
27
extern "C" {
 
28
    #include "ubuntu-app-launch.h"
 
29
}
 
30
 
 
31
namespace qtmir
 
32
{
 
33
namespace upstart
 
34
{
 
35
 
 
36
struct ApplicationController::Private
 
37
{
 
38
    UbuntuAppLaunchAppObserver preStartCallback = nullptr;
 
39
    UbuntuAppLaunchAppObserver startedCallback = nullptr;
 
40
    UbuntuAppLaunchAppObserver stopCallback = nullptr;
 
41
    UbuntuAppLaunchAppObserver focusCallback = nullptr;
 
42
    UbuntuAppLaunchAppObserver resumeCallback = nullptr;
 
43
    UbuntuAppLaunchAppFailedObserver failureCallback = nullptr;
 
44
};
 
45
 
 
46
namespace {
 
47
/**
 
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
 
51
 */
 
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);
 
57
        g_free(package);
 
58
        g_free(application);
 
59
        return shortAppId;
 
60
    } else {
 
61
        return appId;
 
62
    }
 
63
}
 
64
 
 
65
/**
 
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.
 
70
 */
 
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
 
74
        return shortAppId;
 
75
    } else {
 
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
 
80
            return shortAppId;
 
81
        }
 
82
 
 
83
        // ask upstart for the long appId corresponding to this short appId
 
84
        QStringList parts = shortAppId.split("_");
 
85
        gchar *longAppId;
 
86
        longAppId = ubuntu_app_launch_triplet_to_app_id(parts.first().toLatin1().constData(),
 
87
                                                         parts.last().toLatin1().constData(),
 
88
                                                         nullptr);
 
89
        if (longAppId == nullptr) {
 
90
            // was unable to construct a long appId from the short appId, return input unchanged
 
91
            return shortAppId;
 
92
        } else {
 
93
            QString appId(longAppId);
 
94
            g_free(longAppId);
 
95
            return appId;
 
96
        }
 
97
    }
 
98
}
 
99
 
 
100
} // namespace
 
101
 
 
102
ApplicationController::ApplicationController()
 
103
    : qtmir::ApplicationController(),
 
104
      impl(new Private())
 
105
{
 
106
    impl->preStartCallback = [](const gchar * appId, gpointer userData) {
 
107
        auto thiz = static_cast<ApplicationController*>(userData);
 
108
        Q_EMIT(thiz->applicationAboutToBeStarted(toShortAppIdIfPossible(appId)));
 
109
    };
 
110
 
 
111
    impl->startedCallback = [](const gchar * appId, gpointer userData) {
 
112
        auto thiz = static_cast<ApplicationController*>(userData);
 
113
        Q_EMIT(thiz->applicationStarted(toShortAppIdIfPossible(appId)));
 
114
    };
 
115
 
 
116
    impl->stopCallback = [](const gchar * appId, gpointer userData) {
 
117
        auto thiz = static_cast<ApplicationController*>(userData);
 
118
        Q_EMIT(thiz->applicationStopped(toShortAppIdIfPossible(appId)));
 
119
    };
 
120
 
 
121
    impl->focusCallback = [](const gchar * appId, gpointer userData) {
 
122
        auto thiz = static_cast<ApplicationController*>(userData);
 
123
        Q_EMIT(thiz->applicationFocusRequest(toShortAppIdIfPossible(appId)));
 
124
    };
 
125
 
 
126
    impl->resumeCallback = [](const gchar * appId, gpointer userData) {
 
127
        auto thiz = static_cast<ApplicationController*>(userData);
 
128
        Q_EMIT(thiz->applicationResumeRequest(toShortAppIdIfPossible(appId)));
 
129
    };
 
130
 
 
131
    impl->failureCallback = [](const gchar * appId, UbuntuAppLaunchAppFailed failureType, gpointer userData) {
 
132
        ApplicationController::Error error;
 
133
        switch(failureType)
 
134
        {
 
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;
 
137
        }
 
138
 
 
139
        auto thiz = static_cast<ApplicationController*>(userData);
 
140
        Q_EMIT(thiz->applicationError(toShortAppIdIfPossible(appId), error));
 
141
    };
 
142
 
 
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);
 
149
}
 
150
 
 
151
ApplicationController::~ApplicationController()
 
152
{
 
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);
 
159
}
 
160
 
 
161
pid_t ApplicationController::primaryPidForAppId(const QString& appId)
 
162
{
 
163
    GPid pid = ubuntu_app_launch_get_primary_pid(toLongAppIdIfPossible(appId).toLatin1().constData());
 
164
    if (!pid)
 
165
        qDebug() << "ApplicationController::primaryPidForAppId FAILED to get PID for appId=" << appId;
 
166
 
 
167
    return pid;
 
168
}
 
169
 
 
170
bool ApplicationController::appIdHasProcessId(pid_t pid, const QString& appId)
 
171
{
 
172
    return ubuntu_app_launch_pid_in_app_id(pid, toLongAppIdIfPossible(appId).toLatin1().constData());
 
173
}
 
174
 
 
175
bool ApplicationController::stopApplicationWithAppId(const QString& appId)
 
176
{
 
177
    auto result = ubuntu_app_launch_stop_application(toLongAppIdIfPossible(appId).toLatin1().constData());
 
178
    if (!result)
 
179
        qDebug() << "ApplicationController::stopApplication FAILED to stop appId=" << appId;
 
180
 
 
181
    return result;
 
182
}
 
183
 
 
184
bool ApplicationController::startApplicationWithAppIdAndArgs(const QString& appId, const QStringList& arguments)
 
185
{
 
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);
 
189
 
 
190
    for (int i=0; i<arguments.length(); i++) {
 
191
        upstartArgs[i] = g_strdup(arguments.at(i).toUtf8().data());
 
192
    }
 
193
 
 
194
    auto result = ubuntu_app_launch_start_application(
 
195
                toLongAppIdIfPossible(appId).toLatin1().constData(),
 
196
                static_cast<const gchar * const *>(upstartArgs));
 
197
 
 
198
    g_strfreev(upstartArgs);
 
199
 
 
200
    if (!result)
 
201
        qDebug() << "Application::Controller::startApplicationWithAppIdAndArgs FAILED to start appId" << appId;
 
202
 
 
203
    return result;
 
204
}
 
205
 
 
206
QFileInfo ApplicationController::findDesktopFileForAppId(const QString &appId) const
 
207
{
 
208
    qCDebug(QTMIR_APPLICATIONS) << "ApplicationController::desktopFilePathForAppId - appId=" << appId;
 
209
 
 
210
    // Search for the correct desktop file using a simple heuristic
 
211
    int dashPos = -1;
 
212
    QString helper = toLongAppIdIfPossible(appId);
 
213
    QString desktopFile;
 
214
 
 
215
    do {
 
216
        if (dashPos != -1) {
 
217
            helper = helper.replace(dashPos, 1, '/');
 
218
        }
 
219
 
 
220
        desktopFile = QStandardPaths::locate(QStandardPaths::ApplicationsLocation, QString("%1.desktop").arg(helper));
 
221
        if (!desktopFile.isEmpty()) return desktopFile;
 
222
 
 
223
        dashPos = helper.indexOf("-");
 
224
    } while (dashPos != -1);
 
225
 
 
226
    return QFileInfo();
 
227
}
 
228
 
 
229
} // namespace upstart
 
230
} // namespace qtmir