summaryrefslogtreecommitdiffstats
path: root/Telegram/SourceFiles/core/sandbox.h
blob: 4c15c2828bfe163baceee764026df6ac2acc36d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.

For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once

#include "mtproto/mtproto_proxy_data.h"
#include "base/qt/qt_common_adapters.h"

#include <QtWidgets/QApplication>
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QLocalSocket>
#include <QtCore/QAbstractNativeEventFilter>

class QLockFile;

namespace Core {

class UpdateChecker;
class Application;

class Sandbox final
	: public QApplication
	, private QAbstractNativeEventFilter {
private:
	auto createEventNestingLevel() {
		incrementEventNestingLevel();
		return gsl::finally([=] { decrementEventNestingLevel(); });
	}

public:
	Sandbox(int &argc, char **argv);

	Sandbox(const Sandbox &other) = delete;
	Sandbox &operator=(const Sandbox &other) = delete;

	int start();

	void refreshGlobalProxy();

	void postponeCall(FnMut<void()> &&callable);
	bool notify(QObject *receiver, QEvent *e) override;

	template <typename Callable>
	auto customEnterFromEventLoop(Callable &&callable) {
		registerEnterFromEventLoop();
		const auto wrap = createEventNestingLevel();
		return callable();
	}

	rpl::producer<> widgetUpdateRequests() const;

	MTP::ProxyData sandboxProxy() const;

	static Sandbox &Instance() {
		Expects(QCoreApplication::instance() != nullptr);

		return *static_cast<Sandbox*>(QCoreApplication::instance());
	}
	static void QuitWhenStarted();

	~Sandbox();

protected:
	bool event(QEvent *e) override;

private:
	typedef QPair<QLocalSocket*, QByteArray> LocalClient;
	typedef QList<LocalClient> LocalClients;

	struct PostponedCall {
		int loopNestingLevel = 0;
		FnMut<void()> callable;
	};

	bool notifyOrInvoke(QObject *receiver, QEvent *e);

	void closeApplication(); // will be done in aboutToQuit()
	void checkForQuit(); // will be done in exec()
	void checkForEmptyLoopNestingLevel();
	void registerEnterFromEventLoop();
	void incrementEventNestingLevel();
	void decrementEventNestingLevel();
	bool nativeEventFilter(
		const QByteArray &eventType,
		void *message,
		base::NativeEventResult *result) override;
	void processPostponedCalls(int level);
	void singleInstanceChecked();
	void launchApplication();
	void setupScreenScale();

	// Return window id for activation.
	uint64 execExternal(const QString &cmd);

	// Single instance application
	void socketConnected();
	void socketError(QLocalSocket::LocalSocketError e);
	void socketDisconnected();
	void socketWritten(qint64 bytes);
	void socketReading();
	void newInstanceConnected();

	void readClients();
	void removeClients();

	const Qt::HANDLE _mainThreadId = nullptr;
	int _eventNestingLevel = 0;
	int _loopNestingLevel = 0;
	std::vector<int> _previousLoopNestingLevels;
	std::vector<PostponedCall> _postponedCalls;

	std::unique_ptr<Application> _application;

	QString _localServerName, _localSocketReadData;
	QLocalServer _localServer;
	QLocalSocket _localSocket;
	LocalClients _localClients;
	std::unique_ptr<QLockFile> _lockFile;
	bool _secondInstance = false;
	bool _started = false;
	static bool QuitOnStartRequested;

	std::unique_ptr<UpdateChecker> _updateChecker;

	QByteArray _lastCrashDump;
	MTP::ProxyData _sandboxProxy;

	rpl::event_stream<> _widgetUpdateRequests;

	std::unique_ptr<QThread> _deadlockDetector;

};

} // namespace Core