~ubuntu-branches/ubuntu/wily/kwin/wily-proposed

« back to all changes in this revision

Viewing changes to x11windowed_backend.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2015-08-10 23:16:37 UTC
  • mfrom: (1.1.10)
  • Revision ID: package-import@ubuntu.com-20150810231637-5zb2tstjkez93hml
Tags: 4:5.3.95-0ubuntu1
new upstream beta release

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/********************************************************************
2
 
 KWin - the KDE window manager
3
 
 This file is part of the KDE project.
4
 
 
5
 
Copyright (C) 2015 Martin Gräßlin <mgraesslin@kde.org>
6
 
 
7
 
This program is free software; you can redistribute it and/or modify
8
 
it under the terms of the GNU General Public License as published by
9
 
the Free Software Foundation; either version 2 of the License, or
10
 
(at your option) any later version.
11
 
 
12
 
This program is distributed in the hope that it will be useful,
13
 
but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 
GNU General Public License for more details.
16
 
 
17
 
You should have received a copy of the GNU General Public License
18
 
along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
 
*********************************************************************/
20
 
#include "x11windowed_backend.h"
21
 
#include "composite.h"
22
 
#include "input.h"
23
 
#include "scene_qpainter.h"
24
 
#include "screens_x11windowed.h"
25
 
#include "utils.h"
26
 
#include "wayland_server.h"
27
 
#include "xcbutils.h"
28
 
#ifdef KWIN_HAVE_EGL
29
 
#if HAVE_X11_XCB
30
 
#include "eglonxbackend.h"
31
 
#endif
32
 
#endif
33
 
#include <kwinxrenderutils.h>
34
 
#include <QAbstractEventDispatcher>
35
 
#include <QCoreApplication>
36
 
#include <QDebug>
37
 
#include <QSocketNotifier>
38
 
// kwayland
39
 
#include <KWayland/Server/buffer_interface.h>
40
 
#include <KWayland/Server/seat_interface.h>
41
 
#include <KWayland/Server/surface_interface.h>
42
 
// system
43
 
#include <linux/input.h>
44
 
#if HAVE_X11_XCB
45
 
#include <X11/Xlib-xcb.h>
46
 
#endif
47
 
 
48
 
namespace KWin
49
 
{
50
 
 
51
 
X11WindowedBackend::X11WindowedBackend(const QByteArray &display, const QSize &size, QObject *parent)
52
 
    : AbstractBackend(parent)
53
 
    , m_size(size)
54
 
{
55
 
    int screen = 0;
56
 
    xcb_connection_t *c = nullptr;
57
 
#if HAVE_X11_XCB
58
 
    Display *xDisplay = XOpenDisplay(display.constData());
59
 
    if (xDisplay) {
60
 
        c = XGetXCBConnection(xDisplay);
61
 
        XSetEventQueueOwner(xDisplay, XCBOwnsEventQueue);
62
 
        screen = XDefaultScreen(xDisplay);
63
 
    }
64
 
#else
65
 
    c = xcb_connect(display.constData(), &screen);
66
 
#endif
67
 
    if (c && !xcb_connection_has_error(c)) {
68
 
        m_connection = c;
69
 
        m_screenNumber = screen;
70
 
#if HAVE_X11_XCB
71
 
        m_display = xDisplay;
72
 
#endif
73
 
        for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(m_connection));
74
 
            it.rem;
75
 
            --screen, xcb_screen_next(&it)) {
76
 
            if (screen == m_screenNumber) {
77
 
                m_screen = it.data;
78
 
            }
79
 
        }
80
 
        XRenderUtils::init(m_connection, m_screen->root);
81
 
        createWindow();
82
 
        startEventReading();
83
 
    }
84
 
}
85
 
 
86
 
X11WindowedBackend::~X11WindowedBackend()
87
 
{
88
 
    if (m_connection) {
89
 
        if (m_window) {
90
 
            xcb_unmap_window(m_connection, m_window);
91
 
            xcb_destroy_window(m_connection, m_window);
92
 
        }
93
 
        if (m_cursor) {
94
 
            xcb_free_cursor(m_connection, m_cursor);
95
 
        }
96
 
        xcb_disconnect(m_connection);
97
 
    }
98
 
}
99
 
 
100
 
void X11WindowedBackend::createWindow()
101
 
{
102
 
    Q_ASSERT(m_window == XCB_WINDOW_NONE);
103
 
    Xcb::Atom protocolsAtom(QByteArrayLiteral("WM_PROTOCOLS"), false, m_connection);
104
 
    Xcb::Atom deleteWindowAtom(QByteArrayLiteral("WM_DELETE_WINDOW"), false, m_connection);
105
 
    m_window = xcb_generate_id(m_connection);
106
 
    uint32_t mask = XCB_CW_EVENT_MASK;
107
 
    const uint32_t values[] = {
108
 
        XCB_EVENT_MASK_KEY_PRESS |
109
 
        XCB_EVENT_MASK_KEY_RELEASE |
110
 
        XCB_EVENT_MASK_BUTTON_PRESS |
111
 
        XCB_EVENT_MASK_BUTTON_RELEASE |
112
 
        XCB_EVENT_MASK_POINTER_MOTION |
113
 
        XCB_EVENT_MASK_ENTER_WINDOW |
114
 
        XCB_EVENT_MASK_LEAVE_WINDOW |
115
 
        XCB_EVENT_MASK_STRUCTURE_NOTIFY |
116
 
        XCB_EVENT_MASK_EXPOSURE
117
 
    };
118
 
    xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, m_window, m_screen->root,
119
 
                      0, 0, m_size.width(), m_size.height(),
120
 
                      0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, mask, values);
121
 
    xcb_map_window(m_connection, m_window);
122
 
 
123
 
    m_protocols = protocolsAtom;
124
 
    m_deleteWindowProtocol = deleteWindowAtom;
125
 
    xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window, m_protocols, XCB_ATOM_ATOM, 32, 1, &m_deleteWindowProtocol);
126
 
 
127
 
    xcb_flush(m_connection);
128
 
}
129
 
 
130
 
void X11WindowedBackend::startEventReading()
131
 
{
132
 
    QSocketNotifier *notifier = new QSocketNotifier(xcb_get_file_descriptor(m_connection), QSocketNotifier::Read, this);
133
 
    auto processXcbEvents = [this] {
134
 
        while (auto event = xcb_poll_for_event(m_connection)) {
135
 
            handleEvent(event);
136
 
            free(event);
137
 
        }
138
 
        xcb_flush(m_connection);
139
 
    };
140
 
    connect(notifier, &QSocketNotifier::activated, this, processXcbEvents);
141
 
    connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, processXcbEvents);
142
 
    connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::awake, this, processXcbEvents);
143
 
}
144
 
 
145
 
void X11WindowedBackend::handleEvent(xcb_generic_event_t *e)
146
 
{
147
 
    const uint8_t eventType = e->response_type & ~0x80;
148
 
    switch (eventType) {
149
 
    case XCB_BUTTON_PRESS:
150
 
    case XCB_BUTTON_RELEASE:
151
 
        handleButtonPress(reinterpret_cast<xcb_button_press_event_t*>(e));
152
 
        break;
153
 
    case XCB_MOTION_NOTIFY:
154
 
        if (input()) {
155
 
            auto event = reinterpret_cast<xcb_motion_notify_event_t*>(e);
156
 
            input()->processPointerMotion(QPointF(event->event_x, event->event_y), event->time);
157
 
        }
158
 
        break;
159
 
    case XCB_KEY_PRESS:
160
 
    case XCB_KEY_RELEASE:
161
 
        if (input()) {
162
 
            auto event = reinterpret_cast<xcb_key_press_event_t*>(e);
163
 
            input()->processKeyboardKey(event->detail - 8, eventType == XCB_KEY_PRESS ? InputRedirection::KeyboardKeyPressed : InputRedirection::KeyboardKeyReleased, event->time);
164
 
        }
165
 
        break;
166
 
    case XCB_CONFIGURE_NOTIFY:
167
 
        updateSize(reinterpret_cast<xcb_configure_notify_event_t*>(e));
168
 
        break;
169
 
    case XCB_ENTER_NOTIFY:
170
 
        if (input()) {
171
 
            auto event = reinterpret_cast<xcb_enter_notify_event_t*>(e);
172
 
            input()->processPointerMotion(QPointF(event->event_x, event->event_y), event->time);
173
 
        }
174
 
        break;
175
 
    case XCB_CLIENT_MESSAGE:
176
 
        handleClientMessage(reinterpret_cast<xcb_client_message_event_t*>(e));
177
 
        break;
178
 
    case XCB_EXPOSE:
179
 
        handleExpose(reinterpret_cast<xcb_expose_event_t*>(e));
180
 
        break;
181
 
    default:
182
 
        break;
183
 
    }
184
 
}
185
 
 
186
 
void X11WindowedBackend::handleClientMessage(xcb_client_message_event_t *event)
187
 
{
188
 
    if (event->window != m_window) {
189
 
        return;
190
 
    }
191
 
    if (event->type == m_protocols && m_protocols != XCB_ATOM_NONE) {
192
 
        if (event->data.data32[0] == m_deleteWindowProtocol && m_deleteWindowProtocol != XCB_ATOM_NONE) {
193
 
            qCDebug(KWIN_CORE) << "Backend window is going to be closed, shutting down.";
194
 
            QCoreApplication::quit();
195
 
        }
196
 
    }
197
 
}
198
 
 
199
 
void X11WindowedBackend::handleButtonPress(xcb_button_press_event_t *event)
200
 
{
201
 
    if (!input()) {
202
 
        return;
203
 
    }
204
 
    bool const pressed = (event->response_type & ~0x80) == XCB_BUTTON_PRESS;
205
 
    if (event->detail >= XCB_BUTTON_INDEX_4 && event->detail <= 7) {
206
 
        // wheel
207
 
        if (!pressed) {
208
 
            return;
209
 
        }
210
 
        const int delta = (event->detail == XCB_BUTTON_INDEX_4 || event->detail == 6) ? -1 : 1;
211
 
        InputRedirection::PointerAxis axis = (event->detail > 5) ? InputRedirection::PointerAxisHorizontal : InputRedirection::PointerAxisVertical;
212
 
        static const qreal s_defaultAxisStepDistance = 10.0;
213
 
        input()->processPointerAxis(axis, delta * s_defaultAxisStepDistance, event->time);
214
 
        return;
215
 
    }
216
 
    uint32_t button = 0;
217
 
    switch (event->detail) {
218
 
    case XCB_BUTTON_INDEX_1:
219
 
        button = BTN_LEFT;
220
 
        break;
221
 
    case XCB_BUTTON_INDEX_2:
222
 
        button = BTN_MIDDLE;
223
 
        break;
224
 
    case XCB_BUTTON_INDEX_3:
225
 
        button = BTN_RIGHT;
226
 
        break;
227
 
    default:
228
 
        button = event->detail + BTN_LEFT - 1;
229
 
        return;
230
 
    }
231
 
    input()->processPointerMotion(QPointF(event->event_x, event->event_y), event->time);
232
 
    input()->processPointerButton(button, pressed ? InputRedirection::PointerButtonPressed : InputRedirection::PointerButtonReleased, event->time);
233
 
}
234
 
 
235
 
void X11WindowedBackend::handleExpose(xcb_expose_event_t *event)
236
 
{
237
 
    if (!Compositor::self()) {
238
 
        return;
239
 
    }
240
 
    Compositor::self()->addRepaint(QRect(event->x, event->y, event->width, event->height));
241
 
}
242
 
 
243
 
void X11WindowedBackend::updateSize(xcb_configure_notify_event_t *event)
244
 
{
245
 
    if (event->window != m_window) {
246
 
        return;
247
 
    }
248
 
    QSize s = QSize(event->width, event->height);
249
 
    if (s != m_size) {
250
 
        m_size = s;
251
 
        emit sizeChanged();
252
 
    }
253
 
}
254
 
 
255
 
void X11WindowedBackend::installCursorFromServer()
256
 
{
257
 
    if (!waylandServer() || !waylandServer()->seat()->focusedPointer()) {
258
 
        return;
259
 
    }
260
 
    auto c = waylandServer()->seat()->focusedPointer()->cursor();
261
 
    if (c) {
262
 
        auto cursorSurface = c->surface();
263
 
        if (!cursorSurface.isNull()) {
264
 
            auto buffer = cursorSurface.data()->buffer();
265
 
            if (buffer) {
266
 
                // TODO: cache generated cursors?
267
 
                const xcb_pixmap_t pix = xcb_generate_id(m_connection);
268
 
                const xcb_gcontext_t gc = xcb_generate_id(m_connection);
269
 
                const xcb_cursor_t cid = xcb_generate_id(m_connection);
270
 
 
271
 
                xcb_create_pixmap(m_connection, 32, pix, m_screen->root, buffer->size().width(), buffer->size().height());
272
 
                xcb_create_gc(m_connection, gc, pix, 0, nullptr);
273
 
 
274
 
                const QImage img = buffer->data();
275
 
                xcb_put_image(m_connection, XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc, img.width(), img.height(), 0, 0, 0, 32, img.byteCount(), img.constBits());
276
 
 
277
 
                XRenderPicture pic(pix, 32);
278
 
                xcb_render_create_cursor(m_connection, cid, pic, c->hotspot().x(), c->hotspot().y());
279
 
                xcb_change_window_attributes(m_connection, m_window, XCB_CW_CURSOR, &cid);
280
 
 
281
 
                xcb_free_pixmap(m_connection, pix);
282
 
                xcb_free_gc(m_connection, gc);
283
 
                if (m_cursor) {
284
 
                    xcb_free_cursor(m_connection, m_cursor);
285
 
                }
286
 
                m_cursor = cid;
287
 
                xcb_flush(m_connection);
288
 
                return;
289
 
            }
290
 
        }
291
 
    }
292
 
    // TODO: unset cursor
293
 
}
294
 
 
295
 
xcb_window_t X11WindowedBackend::rootWindow() const
296
 
{
297
 
    if (!m_screen) {
298
 
        return XCB_WINDOW_NONE;
299
 
    }
300
 
    return m_screen->root;
301
 
}
302
 
 
303
 
Screens *X11WindowedBackend::createScreens(QObject *parent)
304
 
{
305
 
    return new X11WindowedScreens(this, parent);
306
 
}
307
 
 
308
 
OpenGLBackend *X11WindowedBackend::createOpenGLBackend()
309
 
{
310
 
#ifdef KWIN_HAVE_EGL
311
 
#if HAVE_X11_XCB
312
 
    return  new EglOnXBackend(this);
313
 
#endif
314
 
#endif
315
 
    return nullptr;
316
 
}
317
 
 
318
 
QPainterBackend *X11WindowedBackend::createQPainterBackend()
319
 
{
320
 
    return new X11WindowedQPainterBackend(this);
321
 
}
322
 
 
323
 
}