Changeset View
Changeset View
Standalone View
Standalone View
main_wayland.cpp
Show All 21 Lines | |||||
22 | #include "virtualkeyboard.h" | 22 | #include "virtualkeyboard.h" | ||
23 | #include "workspace.h" | 23 | #include "workspace.h" | ||
24 | #include <config-kwin.h> | 24 | #include <config-kwin.h> | ||
25 | // kwin | 25 | // kwin | ||
26 | #include "platform.h" | 26 | #include "platform.h" | ||
27 | #include "effects.h" | 27 | #include "effects.h" | ||
28 | #include "tabletmodemanager.h" | 28 | #include "tabletmodemanager.h" | ||
29 | #include "wayland_server.h" | 29 | #include "wayland_server.h" | ||
30 | #include "xcbutils.h" | 30 | #include "xwl/xwayland.h" | ||
31 | 31 | | |||
32 | // KWayland | 32 | // KWayland | ||
33 | #include <KWayland/Server/display.h> | 33 | #include <KWayland/Server/display.h> | ||
34 | #include <KWayland/Server/seat_interface.h> | 34 | #include <KWayland/Server/seat_interface.h> | ||
35 | // KDE | 35 | // KDE | ||
36 | #include <KLocalizedString> | 36 | #include <KLocalizedString> | ||
37 | #include <KPluginLoader> | 37 | #include <KPluginLoader> | ||
38 | #include <KPluginMetaData> | 38 | #include <KPluginMetaData> | ||
39 | #include <KQuickAddons/QtQuickSettings> | 39 | #include <KQuickAddons/QtQuickSettings> | ||
40 | 40 | | |||
41 | // Qt | 41 | // Qt | ||
42 | #include <qplatformdefs.h> | 42 | #include <qplatformdefs.h> | ||
43 | #include <QAbstractEventDispatcher> | | |||
44 | #include <QCommandLineParser> | 43 | #include <QCommandLineParser> | ||
45 | #include <QtConcurrentRun> | | |||
46 | #include <QFile> | | |||
47 | #include <QFileInfo> | 44 | #include <QFileInfo> | ||
48 | #include <QFutureWatcher> | | |||
49 | #include <QProcess> | 45 | #include <QProcess> | ||
50 | #include <QSocketNotifier> | | |||
51 | #include <QStyle> | 46 | #include <QStyle> | ||
52 | #include <QThread> | | |||
53 | #include <QDebug> | 47 | #include <QDebug> | ||
54 | #include <QWindow> | 48 | #include <QWindow> | ||
55 | 49 | | |||
56 | // system | 50 | // system | ||
57 | #ifdef HAVE_UNISTD_H | | |||
58 | #include <unistd.h> | | |||
59 | #endif // HAVE_UNISTD_H | | |||
60 | | ||||
61 | #if HAVE_SYS_PRCTL_H | 51 | #if HAVE_SYS_PRCTL_H | ||
62 | #include <sys/prctl.h> | 52 | #include <sys/prctl.h> | ||
63 | #endif | 53 | #endif | ||
64 | #if HAVE_SYS_PROCCTL_H | 54 | #if HAVE_SYS_PROCCTL_H | ||
65 | #include <unistd.h> | | |||
66 | #include <sys/procctl.h> | 55 | #include <sys/procctl.h> | ||
67 | #endif | 56 | #endif | ||
68 | 57 | | |||
69 | #if HAVE_LIBCAP | 58 | #if HAVE_LIBCAP | ||
70 | #include <sys/capability.h> | 59 | #include <sys/capability.h> | ||
71 | #endif | 60 | #endif | ||
72 | 61 | | |||
73 | #include <sched.h> | 62 | #include <sched.h> | ||
74 | 63 | | |||
75 | #include <iostream> | 64 | #include <iostream> | ||
76 | #include <iomanip> | 65 | #include <iomanip> | ||
77 | 66 | | |||
78 | namespace KWin | 67 | namespace KWin | ||
79 | { | 68 | { | ||
80 | 69 | | |||
81 | static void sighandler(int) | 70 | static void sighandler(int) | ||
82 | { | 71 | { | ||
83 | QApplication::exit(); | 72 | QApplication::exit(); | ||
84 | } | 73 | } | ||
85 | 74 | | |||
86 | static void readDisplay(int pipe); | | |||
87 | | ||||
88 | enum class RealTimeFlags | 75 | enum class RealTimeFlags | ||
89 | { | 76 | { | ||
90 | DontReset, | 77 | DontReset, | ||
91 | ResetOnFork | 78 | ResetOnFork | ||
92 | }; | 79 | }; | ||
93 | 80 | | |||
94 | namespace { | 81 | namespace { | ||
95 | void gainRealTime(RealTimeFlags flags = RealTimeFlags::DontReset) | 82 | void gainRealTime(RealTimeFlags flags = RealTimeFlags::DontReset) | ||
Show All 32 Lines | 114 | if (kwinApp()->platform()) { | |||
128 | kwinApp()->platform()->setOutputsEnabled(false); | 115 | kwinApp()->platform()->setOutputsEnabled(false); | ||
129 | } | 116 | } | ||
130 | // need to unload all effects prior to destroying X connection as they might do X calls | 117 | // need to unload all effects prior to destroying X connection as they might do X calls | ||
131 | if (effects) { | 118 | if (effects) { | ||
132 | static_cast<EffectsHandlerImpl*>(effects)->unloadAllEffects(); | 119 | static_cast<EffectsHandlerImpl*>(effects)->unloadAllEffects(); | ||
133 | } | 120 | } | ||
134 | destroyWorkspace(); | 121 | destroyWorkspace(); | ||
135 | waylandServer()->dispatch(); | 122 | waylandServer()->dispatch(); | ||
136 | disconnect(m_xwaylandFailConnection); | 123 | | ||
137 | if (x11Connection()) { | | |||
138 | Xcb::setInputFocus(XCB_INPUT_FOCUS_POINTER_ROOT); | | |||
139 | destroyAtoms(); | | |||
140 | emit x11ConnectionAboutToBeDestroyed(); | | |||
141 | xcb_disconnect(x11Connection()); | | |||
142 | setX11Connection(nullptr); | | |||
143 | } | | |||
144 | if (m_xwaylandProcess) { | | |||
145 | m_xwaylandProcess->terminate(); | | |||
146 | while (m_xwaylandProcess->state() != QProcess::NotRunning) { | | |||
147 | processEvents(QEventLoop::WaitForMoreEvents); | | |||
148 | } | | |||
149 | waylandServer()->destroyXWaylandConnection(); | | |||
150 | } | | |||
151 | if (QStyle *s = style()) { | 124 | if (QStyle *s = style()) { | ||
152 | s->unpolish(this); | 125 | s->unpolish(this); | ||
153 | } | 126 | } | ||
127 | // kill Xwayland before terminating its connection | ||||
128 | delete m_xwayland; | ||||
129 | m_xwayland = nullptr; | ||||
154 | waylandServer()->terminateClientConnections(); | 130 | waylandServer()->terminateClientConnections(); | ||
155 | destroyCompositor(); | 131 | destroyCompositor(); | ||
156 | } | 132 | } | ||
157 | 133 | | |||
158 | void ApplicationWayland::performStartup() | 134 | void ApplicationWayland::performStartup() | ||
159 | { | 135 | { | ||
160 | if (m_startXWayland) { | 136 | if (m_startXWayland) { | ||
161 | setOperationMode(OperationModeXwayland); | 137 | setOperationMode(OperationModeXwayland); | ||
Show All 30 Lines | 166 | { | |||
192 | createScreens(); | 168 | createScreens(); | ||
193 | 169 | | |||
194 | if (operationMode() == OperationModeWaylandOnly) { | 170 | if (operationMode() == OperationModeWaylandOnly) { | ||
195 | createCompositor(); | 171 | createCompositor(); | ||
196 | connect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::continueStartupWithSceen); | 172 | connect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::continueStartupWithSceen); | ||
197 | return; | 173 | return; | ||
198 | } | 174 | } | ||
199 | createCompositor(); | 175 | createCompositor(); | ||
200 | connect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::startXwaylandServer); | 176 | connect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::continueStartupWithXwayland); | ||
201 | } | 177 | } | ||
202 | 178 | | |||
203 | void ApplicationWayland::continueStartupWithSceen() | 179 | void ApplicationWayland::continueStartupWithSceen() | ||
204 | { | 180 | { | ||
205 | disconnect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::continueStartupWithSceen); | 181 | disconnect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::continueStartupWithSceen); | ||
206 | startSession(); | 182 | startSession(); | ||
207 | createWorkspace(); | 183 | createWorkspace(); | ||
208 | notifyKSplash(); | 184 | notifyKSplash(); | ||
209 | } | 185 | } | ||
210 | 186 | | |||
211 | void ApplicationWayland::continueStartupWithX() | 187 | void ApplicationWayland::continueStartupWithXwayland() | ||
212 | { | 188 | { | ||
213 | createX11Connection(); | 189 | disconnect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::continueStartupWithXwayland); | ||
214 | xcb_connection_t *c = x11Connection(); | | |||
215 | if (!c) { | | |||
216 | // about to quit | | |||
217 | return; | | |||
218 | } | | |||
219 | QSocketNotifier *notifier = new QSocketNotifier(xcb_get_file_descriptor(c), QSocketNotifier::Read, this); | | |||
220 | auto processXcbEvents = [this, c] { | | |||
221 | while (auto event = xcb_poll_for_event(c)) { | | |||
222 | long result = 0; | | |||
223 | QThread::currentThread()->eventDispatcher()->filterNativeEvent(QByteArrayLiteral("xcb_generic_event_t"), event, &result); | | |||
224 | free(event); | | |||
225 | } | | |||
226 | xcb_flush(c); | | |||
227 | }; | | |||
228 | connect(notifier, &QSocketNotifier::activated, this, processXcbEvents); | | |||
229 | connect(QThread::currentThread()->eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, processXcbEvents); | | |||
230 | connect(QThread::currentThread()->eventDispatcher(), &QAbstractEventDispatcher::awake, this, processXcbEvents); | | |||
231 | | ||||
232 | // create selection owner for WM_S0 - magic X display number expected by XWayland | | |||
233 | KSelectionOwner owner("WM_S0", c, x11RootWindow()); | | |||
234 | owner.claim(true); | | |||
235 | | ||||
236 | createAtoms(); | | |||
237 | | ||||
238 | setupEventFilters(); | | |||
239 | | ||||
240 | // Check whether another windowmanager is running | | |||
241 | const uint32_t maskValues[] = {XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT}; | | |||
242 | ScopedCPointer<xcb_generic_error_t> redirectCheck(xcb_request_check(connection(), | | |||
243 | xcb_change_window_attributes_checked(connection(), | | |||
244 | rootWindow(), | | |||
245 | XCB_CW_EVENT_MASK, | | |||
246 | maskValues))); | | |||
247 | if (!redirectCheck.isNull()) { | | |||
248 | fputs(i18n("kwin_wayland: an X11 window manager is running on the X11 Display.\n").toLocal8Bit().constData(), stderr); | | |||
249 | ::exit(1); | | |||
250 | } | | |||
251 | | ||||
252 | m_environment.insert(QStringLiteral("DISPLAY"), QString::fromUtf8(qgetenv("DISPLAY"))); | | |||
253 | | ||||
254 | startSession(); | | |||
255 | createWorkspace(); | | |||
256 | | ||||
257 | Xcb::sync(); // Trigger possible errors, there's still a chance to abort | | |||
258 | 190 | | |||
259 | notifyKSplash(); | 191 | m_xwayland = new Xwl::Xwayland(this); | ||
192 | connect(m_xwayland, &Xwl::Xwayland::criticalError, this, [](int code) { | ||||
193 | // we currently exit on Xwayland errors always directly | ||||
194 | // TODO: restart Xwayland | ||||
195 | std::cerr << "Xwayland had a critical error. Going to exit now." << std::endl; | ||||
196 | exit(code); | ||||
197 | }); | ||||
198 | m_xwayland->init(); | ||||
260 | } | 199 | } | ||
261 | 200 | | |||
262 | void ApplicationWayland::startSession() | 201 | void ApplicationWayland::startSession() | ||
263 | { | 202 | { | ||
264 | if (!m_inputMethodServerToStart.isEmpty()) { | 203 | if (!m_inputMethodServerToStart.isEmpty()) { | ||
265 | int socket = dup(waylandServer()->createInputMethodConnection()); | 204 | int socket = dup(waylandServer()->createInputMethodConnection()); | ||
266 | if (socket >= 0) { | 205 | if (socket >= 0) { | ||
267 | QProcessEnvironment environment = m_environment; | 206 | QProcessEnvironment environment = m_environment; | ||
Show All 35 Lines | 239 | for (const QString &application: m_applicationsToStart) { | |||
303 | QProcess *p = new Process(this); | 242 | QProcess *p = new Process(this); | ||
304 | p->setProcessChannelMode(QProcess::ForwardedErrorChannel); | 243 | p->setProcessChannelMode(QProcess::ForwardedErrorChannel); | ||
305 | p->setProcessEnvironment(m_environment); | 244 | p->setProcessEnvironment(m_environment); | ||
306 | p->start(application); | 245 | p->start(application); | ||
307 | } | 246 | } | ||
308 | } | 247 | } | ||
309 | } | 248 | } | ||
310 | 249 | | |||
311 | void ApplicationWayland::createX11Connection() | | |||
312 | { | | |||
313 | int screenNumber = 0; | | |||
314 | xcb_connection_t *c = nullptr; | | |||
315 | if (m_xcbConnectionFd == -1) { | | |||
316 | c = xcb_connect(nullptr, &screenNumber); | | |||
317 | } else { | | |||
318 | c = xcb_connect_to_fd(m_xcbConnectionFd, nullptr); | | |||
319 | } | | |||
320 | if (int error = xcb_connection_has_error(c)) { | | |||
321 | std::cerr << "FATAL ERROR: Creating connection to XServer failed: " << error << std::endl; | | |||
322 | exit(1); | | |||
323 | return; | | |||
324 | } | | |||
325 | setX11Connection(c); | | |||
326 | // we don't support X11 multi-head in Wayland | | |||
327 | setX11ScreenNumber(screenNumber); | | |||
328 | setX11RootWindow(defaultScreen()->root); | | |||
329 | } | | |||
330 | | ||||
331 | void ApplicationWayland::startXwaylandServer() | | |||
332 | { | | |||
333 | disconnect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::startXwaylandServer); | | |||
334 | int pipeFds[2]; | | |||
335 | if (pipe(pipeFds) != 0) { | | |||
336 | std::cerr << "FATAL ERROR failed to create pipe to start Xwayland " << std::endl; | | |||
337 | exit(1); | | |||
338 | return; | | |||
339 | } | | |||
340 | int sx[2]; | | |||
341 | if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sx) < 0) { | | |||
342 | std::cerr << "FATAL ERROR: failed to open socket to open XCB connection" << std::endl; | | |||
343 | exit(1); | | |||
344 | return; | | |||
345 | } | | |||
346 | int fd = dup(sx[1]); | | |||
347 | if (fd < 0) { | | |||
348 | std::cerr << "FATAL ERROR: failed to open socket to open XCB connection" << std::endl; | | |||
349 | exit(20); | | |||
350 | return; | | |||
351 | } | | |||
352 | | ||||
353 | const int waylandSocket = waylandServer()->createXWaylandConnection(); | | |||
354 | if (waylandSocket == -1) { | | |||
355 | std::cerr << "FATAL ERROR: failed to open socket for Xwayland" << std::endl; | | |||
356 | exit(1); | | |||
357 | return; | | |||
358 | } | | |||
359 | const int wlfd = dup(waylandSocket); | | |||
360 | if (wlfd < 0) { | | |||
361 | std::cerr << "FATAL ERROR: failed to open socket for Xwayland" << std::endl; | | |||
362 | exit(20); | | |||
363 | return; | | |||
364 | } | | |||
365 | | ||||
366 | m_xcbConnectionFd = sx[0]; | | |||
367 | | ||||
368 | m_xwaylandProcess = new Process(kwinApp()); | | |||
369 | m_xwaylandProcess->setProcessChannelMode(QProcess::ForwardedErrorChannel); | | |||
370 | m_xwaylandProcess->setProgram(QStringLiteral("Xwayland")); | | |||
371 | QProcessEnvironment env = m_environment; | | |||
372 | env.insert("WAYLAND_SOCKET", QByteArray::number(wlfd)); | | |||
373 | env.insert("EGL_PLATFORM", QByteArrayLiteral("DRM")); | | |||
374 | m_xwaylandProcess->setProcessEnvironment(env); | | |||
375 | m_xwaylandProcess->setArguments({QStringLiteral("-displayfd"), | | |||
376 | QString::number(pipeFds[1]), | | |||
377 | QStringLiteral("-rootless"), | | |||
378 | QStringLiteral("-wm"), | | |||
379 | QString::number(fd)}); | | |||
380 | m_xwaylandFailConnection = connect(m_xwaylandProcess, static_cast<void (QProcess::*)(QProcess::ProcessError)>(&QProcess::error), this, | | |||
381 | [] (QProcess::ProcessError error) { | | |||
382 | if (error == QProcess::FailedToStart) { | | |||
383 | std::cerr << "FATAL ERROR: failed to start Xwayland" << std::endl; | | |||
384 | } else { | | |||
385 | std::cerr << "FATAL ERROR: Xwayland failed, going to exit now" << std::endl; | | |||
386 | } | | |||
387 | exit(1); | | |||
388 | } | | |||
389 | ); | | |||
390 | const int xDisplayPipe = pipeFds[0]; | | |||
391 | connect(m_xwaylandProcess, &QProcess::started, this, | | |||
392 | [this, xDisplayPipe] { | | |||
393 | QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this); | | |||
394 | QObject::connect(watcher, &QFutureWatcher<void>::finished, this, &ApplicationWayland::continueStartupWithX, Qt::QueuedConnection); | | |||
395 | QObject::connect(watcher, &QFutureWatcher<void>::finished, watcher, &QFutureWatcher<void>::deleteLater, Qt::QueuedConnection); | | |||
396 | watcher->setFuture(QtConcurrent::run(readDisplay, xDisplayPipe)); | | |||
397 | } | | |||
398 | ); | | |||
399 | m_xwaylandProcess->start(); | | |||
400 | close(pipeFds[1]); | | |||
401 | } | | |||
402 | | ||||
403 | static void readDisplay(int pipe) | | |||
404 | { | | |||
405 | QFile readPipe; | | |||
406 | if (!readPipe.open(pipe, QIODevice::ReadOnly)) { | | |||
407 | std::cerr << "FATAL ERROR failed to open pipe to start X Server" << std::endl; | | |||
408 | exit(1); | | |||
409 | } | | |||
410 | QByteArray displayNumber = readPipe.readLine(); | | |||
411 | | ||||
412 | displayNumber.prepend(QByteArray(":")); | | |||
413 | displayNumber.remove(displayNumber.size() -1, 1); | | |||
414 | std::cout << "X-Server started on display " << displayNumber.constData() << std::endl; | | |||
415 | | ||||
416 | setenv("DISPLAY", displayNumber.constData(), true); | | |||
417 | | ||||
418 | // close our pipe | | |||
419 | close(pipe); | | |||
420 | } | | |||
421 | | ||||
422 | static const QString s_waylandPlugin = QStringLiteral("KWinWaylandWaylandBackend"); | 250 | static const QString s_waylandPlugin = QStringLiteral("KWinWaylandWaylandBackend"); | ||
423 | static const QString s_x11Plugin = QStringLiteral("KWinWaylandX11Backend"); | 251 | static const QString s_x11Plugin = QStringLiteral("KWinWaylandX11Backend"); | ||
424 | static const QString s_fbdevPlugin = QStringLiteral("KWinWaylandFbdevBackend"); | 252 | static const QString s_fbdevPlugin = QStringLiteral("KWinWaylandFbdevBackend"); | ||
425 | #if HAVE_DRM | 253 | #if HAVE_DRM | ||
426 | static const QString s_drmPlugin = QStringLiteral("KWinWaylandDrmBackend"); | 254 | static const QString s_drmPlugin = QStringLiteral("KWinWaylandDrmBackend"); | ||
427 | #endif | 255 | #endif | ||
428 | #if HAVE_LIBHYBRIS | 256 | #if HAVE_LIBHYBRIS | ||
429 | static const QString s_hwcomposerPlugin = QStringLiteral("KWinWaylandHwcomposerBackend"); | 257 | static const QString s_hwcomposerPlugin = QStringLiteral("KWinWaylandHwcomposerBackend"); | ||
▲ Show 20 Lines • Show All 396 Lines • Show Last 20 Lines |