1 /*
2   clientconnectionmanager.cpp
3 
4   This file is part of GammaRay, the Qt application inspection and
5   manipulation tool.
6 
7   Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
8   Author: Volker Krause <volker.krause@kdab.com>
9 
10   Licensees holding valid commercial KDAB GammaRay licenses may use this file in
11   accordance with GammaRay Commercial License Agreement provided with the Software.
12 
13   Contact info@kdab.com if any conditions of this licensing are not clear to you.
14 
15   This program is free software; you can redistribute it and/or modify
16   it under the terms of the GNU General Public License as published by
17   the Free Software Foundation, either version 2 of the License, or
18   (at your option) any later version.
19 
20   This program is distributed in the hope that it will be useful,
21   but WITHOUT ANY WARRANTY; without even the implied warranty of
22   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23   GNU General Public License for more details.
24 
25   You should have received a copy of the GNU General Public License
26   along with this program.  If not, see <http://www.gnu.org/licenses/>.
27 */
28 
29 #include "clientconnectionmanager.h"
30 
31 #include "client.h"
32 #include "enumrepositoryclient.h"
33 #include "classesiconsrepositoryclient.h"
34 #include "remotemodel.h"
35 #include "selectionmodelclient.h"
36 #include "propertycontrollerclient.h"
37 #include "probecontrollerclient.h"
38 #include "processtracker.h"
39 #include "paintanalyzerclient.h"
40 #include "remoteviewclient.h"
41 #include <toolmanagerclient.h>
42 
43 #include <common/objectbroker.h>
44 #include <common/streamoperators.h>
45 
46 #include <ui/mainwindow.h>
47 #include <ui/splashscreen.h>
48 #include <ui/clienttoolmanager.h>
49 
50 #include <QApplication>
51 #include <QMessageBox>
52 #include <QTimer>
53 
54 using namespace GammaRay;
55 
modelFactory(const QString & name)56 static QAbstractItemModel *modelFactory(const QString &name)
57 {
58     return new RemoteModel(name, qApp);
59 }
60 
selectionModelFactory(QAbstractItemModel * model)61 static QItemSelectionModel *selectionModelFactory(QAbstractItemModel *model)
62 {
63     return new SelectionModelClient(model->objectName() + ".selection", model, qApp);
64 }
65 
createPropertyController(const QString & name,QObject * parent)66 static QObject *createPropertyController(const QString &name, QObject *parent)
67 {
68     return new PropertyControllerClient(name, parent);
69 }
70 
createProbeController(const QString & name,QObject * parent)71 static QObject *createProbeController(const QString &name, QObject *parent)
72 {
73     QObject *o = new ProbeControllerClient(parent);
74     ObjectBroker::registerObject(name, o);
75     return o;
76 }
77 
createToolManager(const QString & name,QObject * parent)78 static QObject *createToolManager(const QString &name, QObject *parent)
79 {
80     QObject *o = new ToolManagerClient(parent);
81     ObjectBroker::registerObject(name, o);
82     return o;
83 }
84 
createPaintAnalyzerClient(const QString & name,QObject * parent)85 static QObject *createPaintAnalyzerClient(const QString &name, QObject *parent)
86 {
87     return new PaintAnalyzerClient(name, parent);
88 }
89 
createRemoteViewClient(const QString & name,QObject * parent)90 static QObject *createRemoteViewClient(const QString &name, QObject *parent)
91 {
92     return new RemoteViewClient(name, parent);
93 }
94 
createEnumRepositoryClient(const QString &,QObject * parent)95 static QObject *createEnumRepositoryClient(const QString &, QObject *parent)
96 {
97     return new EnumRepositoryClient(parent);
98 }
99 
createClassesIconsRepositoryClient(const QString &,QObject * parent)100 static QObject *createClassesIconsRepositoryClient(const QString &, QObject *parent)
101 {
102     return new ClassesIconsRepositoryClient(parent);
103 }
104 
init()105 void ClientConnectionManager::init()
106 {
107     StreamOperators::registerOperators();
108 
109     ObjectBroker::registerClientObjectFactoryCallback<PropertyControllerInterface *>(
110         createPropertyController);
111     ObjectBroker::registerClientObjectFactoryCallback<ProbeControllerInterface *>(
112         createProbeController);
113     ObjectBroker::registerClientObjectFactoryCallback<ToolManagerInterface *>(createToolManager);
114     ObjectBroker::registerClientObjectFactoryCallback<PaintAnalyzerInterface *>(
115         createPaintAnalyzerClient);
116     ObjectBroker::registerClientObjectFactoryCallback<RemoteViewInterface *>(createRemoteViewClient);
117     ObjectBroker::registerClientObjectFactoryCallback<EnumRepository*>(createEnumRepositoryClient);
118     ObjectBroker::registerClientObjectFactoryCallback<ClassesIconsRepository*>(createClassesIconsRepositoryClient);
119 
120     ObjectBroker::setModelFactoryCallback(modelFactory);
121     ObjectBroker::setSelectionModelFactoryCallback(selectionModelFactory);
122 }
123 
ClientConnectionManager(QObject * parent,bool showSplashScreenOnStartUp)124 ClientConnectionManager::ClientConnectionManager(QObject *parent, bool showSplashScreenOnStartUp)
125     : QObject(parent)
126     , m_client(new Client(this))
127     , m_processTracker(new GammaRay::ProcessTracker(this))
128     , m_toolManager(new ClientToolManager(this))
129     , m_mainWindow(nullptr)
130     , m_ignorePersistentError(false)
131     , m_tries(0)
132 {
133     if (showSplashScreenOnStartUp)
134         showSplashScreen();
135     connect(m_processTracker, &ProcessTracker::backendChanged, this,
136             &ClientConnectionManager::processTrackerBackendChanged);
137     connect(m_processTracker, &ProcessTracker::infoChanged, this,
138             &ClientConnectionManager::processTrackerInfoChanged);
139     connect(this, &ClientConnectionManager::ready, this, &ClientConnectionManager::clientConnected);
140     connect(this, &ClientConnectionManager::disconnected, this, &ClientConnectionManager::clientDisconnected);
141     connect(m_client, &Endpoint::disconnected, this, &ClientConnectionManager::disconnected);
142     connect(m_client, &Client::transientConnectionError, this, &ClientConnectionManager::transientConnectionError);
143     connect(m_client, &Client::persisitentConnectionError,
144             this, &ClientConnectionManager::persistentConnectionError);
145     connect(this, &ClientConnectionManager::persistentConnectionError, this, &ClientConnectionManager::delayedHideSplashScreen);
146     connect(this, &ClientConnectionManager::ready, this, &ClientConnectionManager::delayedHideSplashScreen);
147     connect(m_toolManager, &ClientToolManager::toolListAvailable, this, &ClientConnectionManager::ready);
148 }
149 
~ClientConnectionManager()150 ClientConnectionManager::~ClientConnectionManager()
151 {
152     delete m_mainWindow;
153 }
154 
toolManager() const155 ClientToolManager *ClientConnectionManager::toolManager() const
156 {
157     return m_toolManager;
158 }
159 
mainWindow() const160 QMainWindow *ClientConnectionManager::mainWindow() const
161 {
162     return m_mainWindow;
163 }
164 
connectToHost(const QUrl & url,int tryAgain)165 void ClientConnectionManager::connectToHost(const QUrl &url, int tryAgain)
166 {
167     m_serverUrl = url;
168     m_connectionTimeout.start();
169     m_tries = tryAgain;
170     doConnectToHost();
171 }
172 
showSplashScreen()173 void ClientConnectionManager::showSplashScreen()
174 {
175     ::showSplashScreen();
176 }
177 
processTrackerBackend() const178 GammaRay::ProcessTrackerBackend *ClientConnectionManager::processTrackerBackend() const
179 {
180     return m_processTracker->backend();
181 }
182 
setProcessTrackerBackend(GammaRay::ProcessTrackerBackend * backend)183 void ClientConnectionManager::setProcessTrackerBackend(GammaRay::ProcessTrackerBackend *backend)
184 {
185     m_processTracker->setBackend(backend);
186     updateProcessTrackerState();
187 }
188 
processTrackerPid() const189 qint64 ClientConnectionManager::processTrackerPid() const
190 {
191     return m_processTracker->pid();
192 }
193 
setProcessTrackerPid(qint64 pid)194 void ClientConnectionManager::setProcessTrackerPid(qint64 pid)
195 {
196     m_processTracker->setPid(pid);
197     updateProcessTrackerState();
198 }
199 
endPointLabel() const200 QString ClientConnectionManager::endPointLabel() const
201 {
202     return m_client->label();
203 }
204 
endPointKey() const205 QString ClientConnectionManager::endPointKey() const
206 {
207     return m_client->key();
208 }
209 
endPointPid() const210 qint64 ClientConnectionManager::endPointPid() const
211 {
212     return m_client->pid();
213 }
214 
disconnectFromHost()215 void ClientConnectionManager::disconnectFromHost()
216 {
217     targetQuitRequested();
218     m_client->disconnectFromHost();
219 }
220 
doConnectToHost()221 void ClientConnectionManager::doConnectToHost()
222 {
223     m_client->connectToHost(m_serverUrl, m_tries ? m_tries-- : 0);
224 }
225 
createMainWindow()226 QMainWindow *ClientConnectionManager::createMainWindow()
227 {
228     delete m_mainWindow;
229     m_mainWindow = new MainWindow;
230     m_mainWindow->setupFeedbackProvider();
231     connect(m_mainWindow.data(), &MainWindow::targetQuitRequested, this, &ClientConnectionManager::targetQuitRequested);
232     m_ignorePersistentError = false;
233     m_mainWindow->show();
234     return m_mainWindow;
235 }
236 
transientConnectionError()237 void ClientConnectionManager::transientConnectionError()
238 {
239     if (m_connectionTimeout.elapsed() < 60 * 1000) {
240         // client wasn't up yet, keep trying
241         QTimer::singleShot(1000, this, &ClientConnectionManager::doConnectToHost);
242     } else {
243         emit persistentConnectionError(tr("Connection refused."));
244     }
245 }
246 
handlePersistentConnectionError(const QString & msg)247 void ClientConnectionManager::handlePersistentConnectionError(const QString &msg)
248 {
249     if (m_ignorePersistentError)
250         return;
251 
252     QString errorMsg;
253     if (m_mainWindow)
254         errorMsg = tr("Lost connection to remote host: %1").arg(msg);
255     else
256         errorMsg = tr("Could not establish connection to remote host: %1").arg(msg);
257 
258     QMessageBox::critical(m_mainWindow, tr("GammaRay - Connection Error"), errorMsg);
259     QApplication::exit(1);
260 }
261 
delayedHideSplashScreen()262 void ClientConnectionManager::delayedHideSplashScreen()
263 {
264     QTimer::singleShot(0, this, &ClientConnectionManager::hideSplashScreen);
265 }
266 
hideSplashScreen()267 void ClientConnectionManager::hideSplashScreen()
268 {
269     ::hideSplashScreen();
270 }
271 
targetQuitRequested()272 void ClientConnectionManager::targetQuitRequested()
273 {
274     m_ignorePersistentError = true;
275 }
276 
updateProcessTrackerState()277 void ClientConnectionManager::updateProcessTrackerState()
278 {
279     if (!m_client->isConnected()) {
280         m_processTracker->stop();
281     }
282     else if (m_processTracker->isActive()) {
283         if (!m_processTracker->backend() || m_processTracker->pid() < 0) {
284             m_processTracker->stop();
285         }
286     }
287     else {
288         if (m_processTracker->backend() && m_processTracker->pid() >= 0) {
289             m_processTracker->start();
290         }
291     }
292 }
293 
clientConnected()294 void ClientConnectionManager::clientConnected()
295 {
296     setProcessTrackerPid(m_client->pid());
297 }
298 
clientDisconnected()299 void ClientConnectionManager::clientDisconnected()
300 {
301     setProcessTrackerPid(-1);
302     emit processTrackerInfoChanged(ProcessTrackerInfo());
303 }
304