1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the plugins of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qeglfsx11integration.h"
41#include <QThread>
42
43#include <X11/Xlib.h>
44#include <X11/Xlib-xcb.h>
45
46/* Make no mistake: This is not a replacement for the xcb platform plugin.
47 This here is barely an extremely useful tool for developing eglfs itself because
48 it allows to do so without any requirements for devices or drivers. */
49
50QT_BEGIN_NAMESPACE
51
52class EventReader : public QThread
53{
54public:
55 EventReader(QEglFSX11Integration *integration)
56 : m_integration(integration) { }
57
58 void run() override;
59
60private:
61 QEglFSX11Integration *m_integration;
62};
63
64QAtomicInt running;
65
66void EventReader::run()
67{
68 xcb_generic_event_t *event = nullptr;
69 while (running.loadRelaxed() && (event = xcb_wait_for_event(m_integration->connection()))) {
70 uint response_type = event->response_type & ~0x80;
71 switch (response_type) {
72 case XCB_CLIENT_MESSAGE: {
73 xcb_client_message_event_t *client = (xcb_client_message_event_t *) event;
74 const xcb_atom_t *atoms = m_integration->atoms();
75 if (client->format == 32
76 && client->type == atoms[Atoms::WM_PROTOCOLS]
77 && client->data.data32[0] == atoms[Atoms::WM_DELETE_WINDOW]) {
78 QWindow *window = m_integration->platformWindow() ? m_integration->platformWindow()->window() : nullptr;
79 if (window)
80 QWindowSystemInterface::handleCloseEvent(window);
81 }
82 break;
83 }
84 default:
85 break;
86 }
87 }
88}
89
90void QEglFSX11Integration::sendConnectionEvent(xcb_atom_t a)
91{
92 xcb_client_message_event_t event;
93 memset(&event, 0, sizeof(event));
94
95 event.response_type = XCB_CLIENT_MESSAGE;
96 event.format = 32;
97 event.sequence = 0;
98 event.window = m_connectionEventListener;
99 event.type = a;
100
101 xcb_send_event(m_connection, false, m_connectionEventListener, XCB_EVENT_MASK_NO_EVENT, (const char *)&event);
102 xcb_flush(m_connection);
103}
104
105#define DISPLAY ((Display *) m_display)
106
107void QEglFSX11Integration::platformInit()
108{
109 m_display = XOpenDisplay(nullptr);
110 if (Q_UNLIKELY(!m_display))
111 qFatal("Could not open display");
112
113 XSetEventQueueOwner(DISPLAY, XCBOwnsEventQueue);
114 m_connection = XGetXCBConnection(DISPLAY);
115
116 running.ref();
117
118 xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(m_connection));
119
120 m_connectionEventListener = xcb_generate_id(m_connection);
121 xcb_create_window(m_connection, XCB_COPY_FROM_PARENT,
122 m_connectionEventListener, it.data->root,
123 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY,
124 it.data->root_visual, 0, nullptr);
125
126 m_eventReader = new EventReader(this);
127 m_eventReader->start();
128}
129
130void QEglFSX11Integration::platformDestroy()
131{
132 running.deref();
133
134 sendConnectionEvent(XCB_ATOM_NONE);
135
136 m_eventReader->wait();
137 delete m_eventReader;
138 m_eventReader = nullptr;
139
140 XCloseDisplay(DISPLAY);
141 m_display = nullptr;
142 m_connection = nullptr;
143}
144
145EGLNativeDisplayType QEglFSX11Integration::platformDisplay() const
146{
147 return DISPLAY;
148}
149
150QSize QEglFSX11Integration::screenSize() const
151{
152 if (m_screenSize.isEmpty()) {
153 QList<QByteArray> env = qgetenv("EGLFS_X11_SIZE").split('x');
154 if (env.length() == 2) {
155 m_screenSize = QSize(env.at(0).toInt(), env.at(1).toInt());
156 } else {
157 XWindowAttributes a;
158 if (XGetWindowAttributes(DISPLAY, DefaultRootWindow(DISPLAY), &a))
159 m_screenSize = QSize(a.width, a.height);
160 }
161 }
162 return m_screenSize;
163}
164
165EGLNativeWindowType QEglFSX11Integration::createNativeWindow(QPlatformWindow *platformWindow,
166 const QSize &size,
167 const QSurfaceFormat &format)
168{
169 Q_UNUSED(format);
170
171 m_platformWindow = platformWindow;
172
173 xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(m_connection));
174 m_window = xcb_generate_id(m_connection);
175 xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, m_window, it.data->root,
176 0, 0, size.width(), size.height(), 0,
177 XCB_WINDOW_CLASS_INPUT_OUTPUT, it.data->root_visual,
178 0, nullptr);
179
180 xcb_intern_atom_cookie_t cookies[Atoms::N_ATOMS];
181 static const char *atomNames[Atoms::N_ATOMS] = {
182 "_NET_WM_NAME",
183 "UTF8_STRING",
184 "WM_PROTOCOLS",
185 "WM_DELETE_WINDOW",
186 "_NET_WM_STATE",
187 "_NET_WM_STATE_FULLSCREEN"
188 };
189
190 for (int i = 0; i < Atoms::N_ATOMS; ++i) {
191 cookies[i] = xcb_intern_atom(m_connection, false, strlen(atomNames[i]), atomNames[i]);
192 xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(m_connection, cookies[i], nullptr);
193 m_atoms[i] = reply->atom;
194 free(reply);
195 }
196
197 // Set window title
198 xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window,
199 m_atoms[Atoms::_NET_WM_NAME], m_atoms[Atoms::UTF8_STRING], 8, 5, "EGLFS");
200
201 // Enable WM_DELETE_WINDOW
202 xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window,
203 m_atoms[Atoms::WM_PROTOCOLS], XCB_ATOM_ATOM, 32, 1, &m_atoms[Atoms::WM_DELETE_WINDOW]);
204
205 // Go fullscreen.
206 xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window,
207 m_atoms[Atoms::_NET_WM_STATE], XCB_ATOM_ATOM, 32, 1, &m_atoms[Atoms::_NET_WM_STATE_FULLSCREEN]);
208
209 xcb_map_window(m_connection, m_window);
210
211 xcb_flush(m_connection);
212
213 return qt_egl_cast<EGLNativeWindowType>(m_window);
214}
215
216void QEglFSX11Integration::destroyNativeWindow(EGLNativeWindowType window)
217{
218 xcb_destroy_window(m_connection, qt_egl_cast<xcb_window_t>(window));
219}
220
221bool QEglFSX11Integration::hasCapability(QPlatformIntegration::Capability cap) const
222{
223 Q_UNUSED(cap);
224 return false;
225}
226
227QT_END_NAMESPACE
228