1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qeglfsx11integration.h"
5#include <QThread>
6
7#include <X11/Xlib.h>
8#include <X11/Xlib-xcb.h>
9
10/* Make no mistake: This is not a replacement for the xcb platform plugin.
11 This here is barely an extremely useful tool for developing eglfs itself because
12 it allows to do so without any requirements for devices or drivers. */
13
14QT_BEGIN_NAMESPACE
15
16class EventReader : public QThread
17{
18public:
19 EventReader(QEglFSX11Integration *integration)
20 : m_integration(integration) { }
21
22 void run() override;
23
24private:
25 QEglFSX11Integration *m_integration;
26};
27
28Q_CONSTINIT static QBasicAtomicInt running = Q_BASIC_ATOMIC_INITIALIZER(0);
29
30void EventReader::run()
31{
32 xcb_generic_event_t *event = nullptr;
33 while (running.loadRelaxed() && (event = xcb_wait_for_event(c: m_integration->connection()))) {
34 uint response_type = event->response_type & ~0x80;
35 switch (response_type) {
36 case XCB_CLIENT_MESSAGE: {
37 xcb_client_message_event_t *client = (xcb_client_message_event_t *) event;
38 const xcb_atom_t *atoms = m_integration->atoms();
39 if (client->format == 32
40 && client->type == atoms[Atoms::WM_PROTOCOLS]
41 && client->data.data32[0] == atoms[Atoms::WM_DELETE_WINDOW]) {
42 QWindow *window = m_integration->platformWindow() ? m_integration->platformWindow()->window() : nullptr;
43 if (window)
44 QWindowSystemInterface::handleCloseEvent(window);
45 }
46 break;
47 }
48 default:
49 break;
50 }
51 }
52}
53
54void QEglFSX11Integration::sendConnectionEvent(xcb_atom_t a)
55{
56 xcb_client_message_event_t event;
57 memset(s: &event, c: 0, n: sizeof(event));
58
59 event.response_type = XCB_CLIENT_MESSAGE;
60 event.format = 32;
61 event.sequence = 0;
62 event.window = m_connectionEventListener;
63 event.type = a;
64
65 xcb_send_event(c: m_connection, propagate: false, destination: m_connectionEventListener, event_mask: XCB_EVENT_MASK_NO_EVENT, event: (const char *)&event);
66 xcb_flush(c: m_connection);
67}
68
69#define DISPLAY ((Display *) m_display)
70
71void QEglFSX11Integration::platformInit()
72{
73 m_display = XOpenDisplay(nullptr);
74 if (Q_UNLIKELY(!m_display))
75 qFatal(msg: "Could not open display");
76
77 XSetEventQueueOwner(DISPLAY, owner: XCBOwnsEventQueue);
78 m_connection = XGetXCBConnection(DISPLAY);
79
80 running.ref();
81
82 xcb_screen_iterator_t it = xcb_setup_roots_iterator(R: xcb_get_setup(c: m_connection));
83
84 m_connectionEventListener = xcb_generate_id(c: m_connection);
85 xcb_create_window(c: m_connection, XCB_COPY_FROM_PARENT,
86 wid: m_connectionEventListener, parent: it.data->root,
87 x: 0, y: 0, width: 1, height: 1, border_width: 0, class: XCB_WINDOW_CLASS_INPUT_ONLY,
88 visual: it.data->root_visual, value_mask: 0, value_list: nullptr);
89
90 m_eventReader = new EventReader(this);
91 m_eventReader->start();
92}
93
94void QEglFSX11Integration::platformDestroy()
95{
96 running.deref();
97
98 sendConnectionEvent(a: XCB_ATOM_NONE);
99
100 m_eventReader->wait();
101 delete m_eventReader;
102 m_eventReader = nullptr;
103
104 XCloseDisplay(DISPLAY);
105 m_display = nullptr;
106 m_connection = nullptr;
107}
108
109EGLNativeDisplayType QEglFSX11Integration::platformDisplay() const
110{
111 return DISPLAY;
112}
113
114QSize QEglFSX11Integration::screenSize() const
115{
116 if (m_screenSize.isEmpty()) {
117 QList<QByteArray> env = qgetenv(varName: "EGLFS_X11_SIZE").split(sep: 'x');
118 if (env.size() == 2) {
119 m_screenSize = QSize(env.at(i: 0).toInt(), env.at(i: 1).toInt());
120 } else {
121 XWindowAttributes a;
122 if (XGetWindowAttributes(DISPLAY, DefaultRootWindow(DISPLAY), &a))
123 m_screenSize = QSize(a.width, a.height);
124 }
125 }
126 return m_screenSize;
127}
128
129EGLNativeWindowType QEglFSX11Integration::createNativeWindow(QPlatformWindow *platformWindow,
130 const QSize &size,
131 const QSurfaceFormat &format)
132{
133 Q_UNUSED(format);
134
135 m_platformWindow = platformWindow;
136
137 xcb_screen_iterator_t it = xcb_setup_roots_iterator(R: xcb_get_setup(c: m_connection));
138 m_window = xcb_generate_id(c: m_connection);
139 xcb_create_window(c: m_connection, XCB_COPY_FROM_PARENT, wid: m_window, parent: it.data->root,
140 x: 0, y: 0, width: size.width(), height: size.height(), border_width: 0,
141 class: XCB_WINDOW_CLASS_INPUT_OUTPUT, visual: it.data->root_visual,
142 value_mask: 0, value_list: nullptr);
143
144 xcb_intern_atom_cookie_t cookies[Atoms::N_ATOMS];
145 static const char *atomNames[Atoms::N_ATOMS] = {
146 "_NET_WM_NAME",
147 "UTF8_STRING",
148 "WM_PROTOCOLS",
149 "WM_DELETE_WINDOW",
150 "_NET_WM_STATE",
151 "_NET_WM_STATE_FULLSCREEN"
152 };
153
154 for (int i = 0; i < Atoms::N_ATOMS; ++i) {
155 cookies[i] = xcb_intern_atom(c: m_connection, only_if_exists: false, name_len: strlen(s: atomNames[i]), name: atomNames[i]);
156 xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(c: m_connection, cookie: cookies[i], e: nullptr);
157 m_atoms[i] = reply->atom;
158 free(ptr: reply);
159 }
160
161 // Set window title
162 xcb_change_property(c: m_connection, mode: XCB_PROP_MODE_REPLACE, window: m_window,
163 property: m_atoms[Atoms::_NET_WM_NAME], type: m_atoms[Atoms::UTF8_STRING], format: 8, data_len: 5, data: "EGLFS");
164
165 // Enable WM_DELETE_WINDOW
166 xcb_change_property(c: m_connection, mode: XCB_PROP_MODE_REPLACE, window: m_window,
167 property: m_atoms[Atoms::WM_PROTOCOLS], type: XCB_ATOM_ATOM, format: 32, data_len: 1, data: &m_atoms[Atoms::WM_DELETE_WINDOW]);
168
169 // Go fullscreen.
170 xcb_change_property(c: m_connection, mode: XCB_PROP_MODE_REPLACE, window: m_window,
171 property: m_atoms[Atoms::_NET_WM_STATE], type: XCB_ATOM_ATOM, format: 32, data_len: 1, data: &m_atoms[Atoms::_NET_WM_STATE_FULLSCREEN]);
172
173 xcb_map_window(c: m_connection, window: m_window);
174
175 xcb_flush(c: m_connection);
176
177 return qt_egl_cast<EGLNativeWindowType>(from: m_window);
178}
179
180void QEglFSX11Integration::destroyNativeWindow(EGLNativeWindowType window)
181{
182 xcb_destroy_window(c: m_connection, window: qt_egl_cast<xcb_window_t>(from: window));
183}
184
185bool QEglFSX11Integration::hasCapability(QPlatformIntegration::Capability cap) const
186{
187 Q_UNUSED(cap);
188 return false;
189}
190
191QT_END_NAMESPACE
192

source code of qtbase/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp