[backends/x11] Prepare for multi-output rendering

The backend is prepared for managing multiple x11 windows. Currently
it's still only one window, but it's all internally working through
the architecture for multiple windows.
This commit is contained in:
Martin Gräßlin 2015-11-24 10:47:52 +01:00
parent f5905e5e48
commit 1e3013b58c
2 changed files with 92 additions and 43 deletions

View file

@ -57,9 +57,10 @@ X11WindowedBackend::~X11WindowedBackend()
if (m_keySymbols) { if (m_keySymbols) {
xcb_key_symbols_free(m_keySymbols); xcb_key_symbols_free(m_keySymbols);
} }
if (m_window) { for (auto it = m_windows.begin(); it != m_windows.end(); ++it) {
xcb_unmap_window(m_connection, m_window); xcb_unmap_window(m_connection, (*it).window);
xcb_destroy_window(m_connection, m_window); xcb_destroy_window(m_connection, (*it).window);
delete (*it).winInfo;
} }
if (m_cursor) { if (m_cursor) {
xcb_free_cursor(m_connection, m_cursor); xcb_free_cursor(m_connection, m_cursor);
@ -103,10 +104,10 @@ void X11WindowedBackend::init()
void X11WindowedBackend::createWindow() void X11WindowedBackend::createWindow()
{ {
Q_ASSERT(m_window == XCB_WINDOW_NONE);
Xcb::Atom protocolsAtom(QByteArrayLiteral("WM_PROTOCOLS"), false, m_connection); Xcb::Atom protocolsAtom(QByteArrayLiteral("WM_PROTOCOLS"), false, m_connection);
Xcb::Atom deleteWindowAtom(QByteArrayLiteral("WM_DELETE_WINDOW"), false, m_connection); Xcb::Atom deleteWindowAtom(QByteArrayLiteral("WM_DELETE_WINDOW"), false, m_connection);
m_window = xcb_generate_id(m_connection); Output o;
o.window = xcb_generate_id(m_connection);
uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
const uint32_t values[] = { const uint32_t values[] = {
m_screen->black_pixel, m_screen->black_pixel,
@ -120,17 +121,20 @@ void X11WindowedBackend::createWindow()
XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_STRUCTURE_NOTIFY |
XCB_EVENT_MASK_EXPOSURE XCB_EVENT_MASK_EXPOSURE
}; };
m_size = initialWindowSize(); o.size = initialWindowSize();
xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, m_window, m_screen->root, if (!m_windows.isEmpty()) {
0, 0, m_size.width(), m_size.height(), const auto &p = m_windows.last();
o.internalPosition = QPoint(p.internalPosition.x() + p.size.width(), 0);
}
xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, o.window, m_screen->root,
0, 0, o.size.width(), o.size.height(),
0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, mask, values); 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, mask, values);
m_winInfo = new NETWinInfo(m_connection, m_window, m_screen->root, NET::WMWindowType, NET::Properties2()); o.winInfo = new NETWinInfo(m_connection, o.window, m_screen->root, NET::WMWindowType, NET::Properties2());
m_winInfo->setWindowType(NET::Normal); o.winInfo->setWindowType(NET::Normal);
updateWindowTitle(); o.winInfo->setPid(QCoreApplication::applicationPid());
m_winInfo->setPid(QCoreApplication::applicationPid());
QIcon windowIcon = QIcon::fromTheme(QStringLiteral("kwin")); QIcon windowIcon = QIcon::fromTheme(QStringLiteral("kwin"));
auto addIcon = [this, &windowIcon] (const QSize &size) { auto addIcon = [&o, &windowIcon] (const QSize &size) {
if (windowIcon.actualSize(size) != size) { if (windowIcon.actualSize(size) != size) {
return; return;
} }
@ -138,17 +142,21 @@ void X11WindowedBackend::createWindow()
icon.data = windowIcon.pixmap(size).toImage().bits(); icon.data = windowIcon.pixmap(size).toImage().bits();
icon.size.width = size.width(); icon.size.width = size.width();
icon.size.height = size.height(); icon.size.height = size.height();
m_winInfo->setIcon(icon, false); o.winInfo->setIcon(icon, false);
}; };
addIcon(QSize(16, 16)); addIcon(QSize(16, 16));
addIcon(QSize(32, 32)); addIcon(QSize(32, 32));
addIcon(QSize(48, 48)); addIcon(QSize(48, 48));
xcb_map_window(m_connection, m_window); xcb_map_window(m_connection, o.window);
m_protocols = protocolsAtom; m_protocols = protocolsAtom;
m_deleteWindowProtocol = deleteWindowAtom; m_deleteWindowProtocol = deleteWindowAtom;
xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window, m_protocols, XCB_ATOM_ATOM, 32, 1, &m_deleteWindowProtocol); xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, o.window, m_protocols, XCB_ATOM_ATOM, 32, 1, &m_deleteWindowProtocol);
m_windows << o;
updateWindowTitle();
xcb_flush(m_connection); xcb_flush(m_connection);
} }
@ -178,7 +186,13 @@ void X11WindowedBackend::handleEvent(xcb_generic_event_t *e)
break; break;
case XCB_MOTION_NOTIFY: { case XCB_MOTION_NOTIFY: {
auto event = reinterpret_cast<xcb_motion_notify_event_t*>(e); auto event = reinterpret_cast<xcb_motion_notify_event_t*>(e);
pointerMotion(QPointF(event->event_x, event->event_y), event->time); auto it = std::find_if(m_windows.constBegin(), m_windows.constEnd(), [event] (const Output &o) { return o.window == event->event; });
if (it == m_windows.constEnd()) {
break;
}
pointerMotion(QPointF(event->root_x - (*it).xPosition.x() + (*it).internalPosition.x(),
event->root_y - (*it).xPosition.y() + (*it).internalPosition.y()),
event->time);
} }
break; break;
case XCB_KEY_PRESS: case XCB_KEY_PRESS:
@ -203,7 +217,13 @@ void X11WindowedBackend::handleEvent(xcb_generic_event_t *e)
break; break;
case XCB_ENTER_NOTIFY: { case XCB_ENTER_NOTIFY: {
auto event = reinterpret_cast<xcb_enter_notify_event_t*>(e); auto event = reinterpret_cast<xcb_enter_notify_event_t*>(e);
pointerMotion(QPointF(event->event_x, event->event_y), event->time); auto it = std::find_if(m_windows.constBegin(), m_windows.constEnd(), [event] (const Output &o) { return o.window == event->event; });
if (it == m_windows.constEnd()) {
break;
}
pointerMotion(QPointF(event->root_x - (*it).xPosition.x() + (*it).internalPosition.x(),
event->root_y - (*it).xPosition.y() + (*it).internalPosition.y()),
event->time);
} }
break; break;
case XCB_CLIENT_MESSAGE: case XCB_CLIENT_MESSAGE:
@ -230,19 +250,19 @@ void X11WindowedBackend::grabKeyboard(xcb_timestamp_t time)
xcb_ungrab_pointer(m_connection, time); xcb_ungrab_pointer(m_connection, time);
m_keyboardGrabbed = false; m_keyboardGrabbed = false;
} else { } else {
const auto c = xcb_grab_keyboard_unchecked(m_connection, false, m_window, time, const auto c = xcb_grab_keyboard_unchecked(m_connection, false, window(), time,
XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
ScopedCPointer<xcb_grab_keyboard_reply_t> grab(xcb_grab_keyboard_reply(m_connection, c, nullptr)); ScopedCPointer<xcb_grab_keyboard_reply_t> grab(xcb_grab_keyboard_reply(m_connection, c, nullptr));
if (grab.isNull()) { if (grab.isNull()) {
return; return;
} }
if (grab->status == XCB_GRAB_STATUS_SUCCESS) { if (grab->status == XCB_GRAB_STATUS_SUCCESS) {
const auto c = xcb_grab_pointer_unchecked(m_connection, false, m_window, const auto c = xcb_grab_pointer_unchecked(m_connection, false, window(),
XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_POINTER_MOTION |
XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW, XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW,
XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,
m_window, XCB_CURSOR_NONE, time); window(), XCB_CURSOR_NONE, time);
ScopedCPointer<xcb_grab_pointer_reply_t> grab(xcb_grab_pointer_reply(m_connection, c, nullptr)); ScopedCPointer<xcb_grab_pointer_reply_t> grab(xcb_grab_pointer_reply(m_connection, c, nullptr));
if (grab.isNull() || grab->status != XCB_GRAB_STATUS_SUCCESS) { if (grab.isNull() || grab->status != XCB_GRAB_STATUS_SUCCESS) {
xcb_ungrab_keyboard(m_connection, time); xcb_ungrab_keyboard(m_connection, time);
@ -263,12 +283,15 @@ void X11WindowedBackend::updateWindowTitle()
const QString title = QStringLiteral("%1 (%2) - %3").arg(i18n("KDE Wayland Compositor")) const QString title = QStringLiteral("%1 (%2) - %3").arg(i18n("KDE Wayland Compositor"))
.arg(waylandServer()->display()->socketName()) .arg(waylandServer()->display()->socketName())
.arg(grab); .arg(grab);
m_winInfo->setName(title.toUtf8().constData()); for (auto it = m_windows.constBegin(); it != m_windows.constEnd(); ++it) {
(*it).winInfo->setName(title.toUtf8().constData());
}
} }
void X11WindowedBackend::handleClientMessage(xcb_client_message_event_t *event) void X11WindowedBackend::handleClientMessage(xcb_client_message_event_t *event)
{ {
if (event->window != m_window) { auto it = std::find_if(m_windows.begin(), m_windows.end(), [event] (const Output &o) { return o.window == event->window; });
if (it == m_windows.end()) {
return; return;
} }
if (event->type == m_protocols && m_protocols != XCB_ATOM_NONE) { if (event->type == m_protocols && m_protocols != XCB_ATOM_NONE) {
@ -281,6 +304,10 @@ void X11WindowedBackend::handleClientMessage(xcb_client_message_event_t *event)
void X11WindowedBackend::handleButtonPress(xcb_button_press_event_t *event) void X11WindowedBackend::handleButtonPress(xcb_button_press_event_t *event)
{ {
auto it = std::find_if(m_windows.constBegin(), m_windows.constEnd(), [event] (const Output &o) { return o.window == event->event; });
if (it == m_windows.constEnd()) {
return;
}
bool const pressed = (event->response_type & ~0x80) == XCB_BUTTON_PRESS; bool const pressed = (event->response_type & ~0x80) == XCB_BUTTON_PRESS;
if (event->detail >= XCB_BUTTON_INDEX_4 && event->detail <= 7) { if (event->detail >= XCB_BUTTON_INDEX_4 && event->detail <= 7) {
// wheel // wheel
@ -311,7 +338,9 @@ void X11WindowedBackend::handleButtonPress(xcb_button_press_event_t *event)
button = event->detail + BTN_LEFT - 1; button = event->detail + BTN_LEFT - 1;
return; return;
} }
pointerMotion(QPointF(event->event_x, event->event_y), event->time); pointerMotion(QPointF(event->root_x - (*it).xPosition.x() + (*it).internalPosition.x(),
event->root_y - (*it).xPosition.y() + (*it).internalPosition.y()),
event->time);
if (pressed) { if (pressed) {
pointerButtonPressed(button, event->time); pointerButtonPressed(button, event->time);
} else { } else {
@ -326,12 +355,19 @@ void X11WindowedBackend::handleExpose(xcb_expose_event_t *event)
void X11WindowedBackend::updateSize(xcb_configure_notify_event_t *event) void X11WindowedBackend::updateSize(xcb_configure_notify_event_t *event)
{ {
if (event->window != m_window) { auto it = std::find_if(m_windows.begin(), m_windows.end(), [event] (const Output &o) { return o.window == event->window; });
if (it == m_windows.end()) {
return; return;
} }
(*it).xPosition = QPoint(event->x, event->y);
QSize s = QSize(event->width, event->height); QSize s = QSize(event->width, event->height);
if (s != m_size) { if (s != (*it).size) {
m_size = s; (*it).size = s;
int x = (*it).internalPosition.x() + s.width();
for (; it != m_windows.end(); ++it) {
(*it).internalPosition.setX(x);
x += (*it).size.width();
}
emit sizeChanged(); emit sizeChanged();
} }
} }
@ -368,7 +404,9 @@ void X11WindowedBackend::createCursor(const QImage &img, const QPoint &hotspot)
XRenderPicture pic(pix, 32); XRenderPicture pic(pix, 32);
xcb_render_create_cursor(m_connection, cid, pic, hotspot.x(), hotspot.y()); xcb_render_create_cursor(m_connection, cid, pic, hotspot.x(), hotspot.y());
xcb_change_window_attributes(m_connection, m_window, XCB_CW_CURSOR, &cid); for (auto it = m_windows.constBegin(); it != m_windows.constEnd(); ++it) {
xcb_change_window_attributes(m_connection, (*it).window, XCB_CW_CURSOR, &cid);
}
xcb_free_pixmap(m_connection, pix); xcb_free_pixmap(m_connection, pix);
xcb_free_gc(m_connection, gc); xcb_free_gc(m_connection, gc);
@ -411,14 +449,26 @@ QPainterBackend *X11WindowedBackend::createQPainterBackend()
void X11WindowedBackend::warpPointer(const QPointF &globalPos) void X11WindowedBackend::warpPointer(const QPointF &globalPos)
{ {
xcb_warp_pointer(m_connection, m_window, m_window, 0, 0, 0, 0, globalPos.x(), globalPos.y()); const xcb_window_t w = m_windows.at(0).window;
xcb_warp_pointer(m_connection, w, w, 0, 0, 0, 0, globalPos.x(), globalPos.y());
xcb_flush(m_connection); xcb_flush(m_connection);
} }
xcb_window_t X11WindowedBackend::windowForScreen(int screen) const xcb_window_t X11WindowedBackend::windowForScreen(int screen) const
{ {
Q_UNUSED(screen) if (screen > m_windows.count()) {
return window(); return XCB_WINDOW_NONE;
}
return m_windows.at(screen).window;
}
QVector<QRect> X11WindowedBackend::screenGeometries() const
{
QVector<QRect> ret;
for (auto it = m_windows.constBegin(); it != m_windows.constEnd(); ++it) {
ret << QRect((*it).internalPosition, (*it).size);
}
return ret;
} }
} }

View file

@ -41,14 +41,12 @@ class KWIN_EXPORT X11WindowedBackend : public AbstractBackend
Q_OBJECT Q_OBJECT
Q_INTERFACES(KWin::AbstractBackend) Q_INTERFACES(KWin::AbstractBackend)
Q_PLUGIN_METADATA(IID "org.kde.kwin.AbstractBackend" FILE "x11.json") Q_PLUGIN_METADATA(IID "org.kde.kwin.AbstractBackend" FILE "x11.json")
Q_PROPERTY(QSize size READ size NOTIFY sizeChanged) Q_PROPERTY(QSize size READ screenSize NOTIFY sizeChanged)
public: public:
X11WindowedBackend(QObject *parent = nullptr); X11WindowedBackend(QObject *parent = nullptr);
virtual ~X11WindowedBackend(); virtual ~X11WindowedBackend();
void init() override; void init() override;
QSize screenSize() const override { QVector<QRect> screenGeometries() const override;
return m_size;
}
xcb_connection_t *connection() const { xcb_connection_t *connection() const {
return m_connection; return m_connection;
@ -57,7 +55,7 @@ public:
return m_screenNumber; return m_screenNumber;
} }
xcb_window_t window() const { xcb_window_t window() const {
return m_window; return windowForScreen(0);
} }
xcb_window_t windowForScreen(int screen) const; xcb_window_t windowForScreen(int screen) const;
Display *display() const { Display *display() const {
@ -65,10 +63,6 @@ public:
} }
xcb_window_t rootWindow() const; xcb_window_t rootWindow() const;
QSize size() const {
return m_size;
}
void installCursorFromServer() override; void installCursorFromServer() override;
void installCursorImage(Qt::CursorShape shape) override; void installCursorImage(Qt::CursorShape shape) override;
@ -96,13 +90,18 @@ private:
xcb_screen_t *m_screen = nullptr; xcb_screen_t *m_screen = nullptr;
xcb_key_symbols_t *m_keySymbols = nullptr; xcb_key_symbols_t *m_keySymbols = nullptr;
int m_screenNumber = 0; int m_screenNumber = 0;
xcb_window_t m_window = XCB_WINDOW_NONE; struct Output {
QSize m_size; xcb_window_t window = XCB_WINDOW_NONE;
QSize size;
QPoint xPosition;
QPoint internalPosition;
NETWinInfo *winInfo = nullptr;
};
QVector<Output> m_windows;
xcb_atom_t m_protocols = XCB_ATOM_NONE; xcb_atom_t m_protocols = XCB_ATOM_NONE;
xcb_atom_t m_deleteWindowProtocol = XCB_ATOM_NONE; xcb_atom_t m_deleteWindowProtocol = XCB_ATOM_NONE;
xcb_cursor_t m_cursor = XCB_CURSOR_NONE; xcb_cursor_t m_cursor = XCB_CURSOR_NONE;
Display *m_display = nullptr; Display *m_display = nullptr;
NETWinInfo *m_winInfo = nullptr;
bool m_keyboardGrabbed = false; bool m_keyboardGrabbed = false;
}; };