From 73098cbfdc53dc1c6782deb4be5909fa190c3d23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Wed, 17 Sep 2014 09:05:11 +0200 Subject: [PATCH] Split out WaylandScreens into own header and source file This reduces the needed ifdefs in screens and makes it easier to unit test Screens. --- CMakeLists.txt | 1 + screens.cpp | 229 +-------------------------------------- screens.h | 21 ---- screens_wayland.cpp | 254 ++++++++++++++++++++++++++++++++++++++++++++ screens_wayland.h | 48 +++++++++ 5 files changed, 305 insertions(+), 248 deletions(-) create mode 100644 screens_wayland.cpp create mode 100644 screens_wayland.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f0df8b26c..956a99cb49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -426,6 +426,7 @@ endif() if(Wayland_Client_FOUND AND XKB_FOUND) set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} + screens_wayland.cpp wayland_backend.cpp wayland_client/buffer.cpp wayland_client/compositor.cpp diff --git a/screens.cpp b/screens.cpp index 01dbbc20c5..ee76051faa 100644 --- a/screens.cpp +++ b/screens.cpp @@ -22,10 +22,9 @@ along with this program. If not, see . #include "cursor.h" #include "settings.h" #include "workspace.h" +#include #if HAVE_WAYLAND -#include "wayland_backend.h" -#include "wayland_client/output.h" -#include +#include "screens_wayland.h" #endif #include @@ -209,228 +208,4 @@ void DesktopWidgetScreens::updateCount() setCount(m_desktop->screenCount()); } -#if HAVE_WAYLAND -WaylandScreens::WaylandScreens(QObject* parent) - : Screens(parent) -{ -} - -WaylandScreens::~WaylandScreens() -{ -} - -void WaylandScreens::init() -{ - Screens::init(); - connect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::outputsChanged, - this, &WaylandScreens::startChangedTimer); - updateCount(); -} - -QRect WaylandScreens::geometry(int screen) const -{ - if (screen >= m_geometries.size()) { - return QRect(); - } - return m_geometries.at(screen); -} - -QSize WaylandScreens::size(int screen) const -{ - return geometry(screen).size(); -} - -int WaylandScreens::number(const QPoint &pos) const -{ - int bestScreen = 0; - int minDistance = INT_MAX; - for (int i = 0; i < m_geometries.size(); ++i) { - const QRect &geo = m_geometries.at(i); - if (geo.contains(pos)) { - return i; - } - int distance = QPoint(geo.topLeft() - pos).manhattanLength(); - distance = qMin(distance, QPoint(geo.topRight() - pos).manhattanLength()); - distance = qMin(distance, QPoint(geo.bottomRight() - pos).manhattanLength()); - distance = qMin(distance, QPoint(geo.bottomLeft() - pos).manhattanLength()); - if (distance < minDistance) { - minDistance = distance; - bestScreen = i; - } - } - return bestScreen; -} - -void WaylandScreens::updateCount() -{ - m_geometries.clear(); - int count = 0; - const QList &outputs = Wayland::WaylandBackend::self()->outputs(); - for (auto it = outputs.begin(); it != outputs.end(); ++it) { - if ((*it)->pixelSize().isEmpty()) { - continue; - } - count++; - m_geometries.append(QRect((*it)->globalPosition(), (*it)->pixelSize())); - } - if (m_geometries.isEmpty()) { - // we need a fake screen - m_geometries.append(QRect(0, 0, displayWidth(), displayHeight())); - setCount(1); - return; - } - setCount(m_geometries.count()); - updateXRandr(); - emit changed(); -} - -namespace RandR -{ -using namespace Xcb; -XCB_WRAPPER(CurrentResources, xcb_randr_get_screen_resources_current, xcb_window_t) -} - -static bool setNewScreenSize(const QSize &size) -{ - auto c = xcb_randr_set_screen_size_checked(connection(), rootWindow(), size.width(), size.height(), 1, 1); - ScopedCPointer error(xcb_request_check(connection(), c)); - if (!error.isNull()) { - qDebug() << "Error setting screen size: " << error->error_code; - return false; - } - return true; -} - -static xcb_randr_crtc_t getCrtc(const xcb_randr_get_screen_resources_current_reply_t* r) -{ - if (xcb_randr_get_screen_resources_current_crtcs_length(r) == 0) { - qDebug() << "No CRTCs"; - return XCB_NONE; - } - return xcb_randr_get_screen_resources_current_crtcs(r)[0]; -} - -static xcb_randr_output_t getOutputForCrtc(xcb_randr_crtc_t crtc) -{ - ScopedCPointer info(xcb_randr_get_crtc_info_reply( - connection(), xcb_randr_get_crtc_info(connection(), crtc, XCB_TIME_CURRENT_TIME), nullptr)); - if (info->num_outputs == 0) { - return XCB_NONE; - } - return xcb_randr_get_crtc_info_outputs(info.data())[0]; -} - -static xcb_randr_mode_t createNewMode(const QSize &size) -{ - // need to create the new mode - qDebug() << "Creating a new mode"; - QString name(QString::number(size.width())); - name.append('x'); - name.append(QString::number(size.height())); - xcb_randr_mode_info_t newInfo; - newInfo.dot_clock = 0; - newInfo.height = size.height(); - newInfo.hskew = 0; - newInfo.hsync_end = 0; - newInfo.hsync_start = 0; - newInfo.htotal = size.width(); - newInfo.id = 0; - newInfo.mode_flags = 0; - newInfo.vsync_end = 0; - newInfo.vsync_start = 0; - newInfo.vtotal = size.height(); - newInfo.width = size.width(); - newInfo.name_len = name.length(); - auto cookie = xcb_randr_create_mode_unchecked(connection(), rootWindow(), newInfo, name.length(), name.toUtf8().constData()); - ScopedCPointer reply(xcb_randr_create_mode_reply(connection(), cookie, nullptr)); - if (!reply.isNull()) { - return reply->mode; - } - return XCB_NONE; -} - -static xcb_randr_mode_t getModeForSize(const QSize &size, const xcb_randr_get_screen_resources_current_reply_t* r) -{ - xcb_randr_mode_info_t *infos = xcb_randr_get_screen_resources_current_modes(r); - const int modeInfoLength = xcb_randr_get_screen_resources_current_modes_length(r); - // check available modes - for (int i = 0; i < modeInfoLength; ++i) { - xcb_randr_mode_info_t modeInfo = infos[i]; - if (modeInfo.width == size.width() && modeInfo.height == size.height()) { - qDebug() << "Found our required mode"; - return modeInfo.id; - } - } - // did not find our mode, so create it - return createNewMode(size); -} - -static bool addModeToOutput(xcb_randr_output_t output, xcb_randr_mode_t mode) -{ - ScopedCPointer info(xcb_randr_get_output_info_reply(connection(), - xcb_randr_get_output_info(connection(), output, XCB_TIME_CURRENT_TIME), nullptr)); - xcb_randr_mode_t *modes = xcb_randr_get_output_info_modes(info.data()); - for (int i = 0; i < info->num_modes; ++i) { - if (modes[i] == mode) { - return true; - } - } - qDebug() << "Need to add the mode to output"; - auto c = xcb_randr_add_output_mode_checked(connection(), output, mode); - ScopedCPointer error(xcb_request_check(connection(), c)); - if (!error.isNull()) { - qDebug() << "Error while adding mode to output: " << error->error_code; - return false; - } - return true; -} - -void WaylandScreens::updateXRandr() -{ - if (!Xcb::Extensions::self()->isRandrAvailable()) { - qDebug() << "No RandR extension available, cannot sync with X"; - return; - } - QRegion screens; - foreach (const QRect &rect, m_geometries) { - screens = screens.united(rect); - } - const QSize &size = screens.boundingRect().size(); - if (size.isEmpty()) { - return; - } - - RandR::CurrentResources currentResources(rootWindow()); - xcb_randr_crtc_t crtc = getCrtc(currentResources.data()); - if (crtc == XCB_NONE) { - return; - } - xcb_randr_output_t output = getOutputForCrtc(crtc); - if (output == XCB_NONE) { - return; - } - // first disable the first CRTC - xcb_randr_set_crtc_config(connection(), crtc, XCB_TIME_CURRENT_TIME, XCB_TIME_CURRENT_TIME, - 0, 0, XCB_NONE, XCB_RANDR_ROTATION_ROTATE_0, 0, nullptr); - - // then set new screen size - if (!setNewScreenSize(size)) { - return; - } - - xcb_randr_mode_t mode = getModeForSize(size, currentResources.data()); - if (mode == XCB_NONE) { - return; - } - - if (!addModeToOutput(output, mode)) { - return; - } - // enable CRTC again - xcb_randr_set_crtc_config(connection(), crtc, XCB_TIME_CURRENT_TIME, XCB_TIME_CURRENT_TIME, - 0, 0, mode, XCB_RANDR_ROTATION_ROTATE_0, 1, &output); -} - -#endif - } // namespace diff --git a/screens.h b/screens.h index 0d7e56476e..25fed8052f 100644 --- a/screens.h +++ b/screens.h @@ -22,7 +22,6 @@ along with this program. If not, see . // KWin includes #include -#include // KDE includes #include #include @@ -161,26 +160,6 @@ private: QDesktopWidget *m_desktop; }; -#if HAVE_WAYLAND -class WaylandScreens : public Screens -{ - Q_OBJECT -public: - explicit WaylandScreens(QObject *parent); - virtual ~WaylandScreens(); - void init() override; - QRect geometry(int screen) const override; - QSize size(int screen) const override; - int number(const QPoint& pos) const override; - -protected Q_SLOTS: - void updateCount() override; -private: - void updateXRandr(); - QList m_geometries; -}; -#endif - inline void Screens::setConfig(KSharedConfig::Ptr config) { diff --git a/screens_wayland.cpp b/screens_wayland.cpp new file mode 100644 index 0000000000..398d92bdd8 --- /dev/null +++ b/screens_wayland.cpp @@ -0,0 +1,254 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2013 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#include "screens_wayland.h" + +#include "wayland_backend.h" +#include "wayland_client/output.h" +#include "xcbutils.h" + +#include + +#include + +namespace KWin +{ + +WaylandScreens::WaylandScreens(QObject* parent) + : Screens(parent) +{ +} + +WaylandScreens::~WaylandScreens() +{ +} + +void WaylandScreens::init() +{ + Screens::init(); + connect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::outputsChanged, + this, &WaylandScreens::startChangedTimer); + updateCount(); +} + +QRect WaylandScreens::geometry(int screen) const +{ + if (screen >= m_geometries.size()) { + return QRect(); + } + return m_geometries.at(screen); +} + +QSize WaylandScreens::size(int screen) const +{ + return geometry(screen).size(); +} + +int WaylandScreens::number(const QPoint &pos) const +{ + int bestScreen = 0; + int minDistance = INT_MAX; + for (int i = 0; i < m_geometries.size(); ++i) { + const QRect &geo = m_geometries.at(i); + if (geo.contains(pos)) { + return i; + } + int distance = QPoint(geo.topLeft() - pos).manhattanLength(); + distance = qMin(distance, QPoint(geo.topRight() - pos).manhattanLength()); + distance = qMin(distance, QPoint(geo.bottomRight() - pos).manhattanLength()); + distance = qMin(distance, QPoint(geo.bottomLeft() - pos).manhattanLength()); + if (distance < minDistance) { + minDistance = distance; + bestScreen = i; + } + } + return bestScreen; +} + +void WaylandScreens::updateCount() +{ + m_geometries.clear(); + int count = 0; + const QList &outputs = Wayland::WaylandBackend::self()->outputs(); + for (auto it = outputs.begin(); it != outputs.end(); ++it) { + if ((*it)->pixelSize().isEmpty()) { + continue; + } + count++; + m_geometries.append(QRect((*it)->globalPosition(), (*it)->pixelSize())); + } + if (m_geometries.isEmpty()) { + // we need a fake screen + m_geometries.append(QRect(0, 0, displayWidth(), displayHeight())); + setCount(1); + return; + } + setCount(m_geometries.count()); + updateXRandr(); + emit changed(); +} + +namespace RandR +{ +using namespace Xcb; +XCB_WRAPPER(CurrentResources, xcb_randr_get_screen_resources_current, xcb_window_t) +} + +static bool setNewScreenSize(const QSize &size) +{ + auto c = xcb_randr_set_screen_size_checked(connection(), rootWindow(), size.width(), size.height(), 1, 1); + ScopedCPointer error(xcb_request_check(connection(), c)); + if (!error.isNull()) { + qDebug() << "Error setting screen size: " << error->error_code; + return false; + } + return true; +} + +static xcb_randr_crtc_t getCrtc(const xcb_randr_get_screen_resources_current_reply_t* r) +{ + if (xcb_randr_get_screen_resources_current_crtcs_length(r) == 0) { + qDebug() << "No CRTCs"; + return XCB_NONE; + } + return xcb_randr_get_screen_resources_current_crtcs(r)[0]; +} + +static xcb_randr_output_t getOutputForCrtc(xcb_randr_crtc_t crtc) +{ + ScopedCPointer info(xcb_randr_get_crtc_info_reply( + connection(), xcb_randr_get_crtc_info(connection(), crtc, XCB_TIME_CURRENT_TIME), nullptr)); + if (info->num_outputs == 0) { + return XCB_NONE; + } + return xcb_randr_get_crtc_info_outputs(info.data())[0]; +} + +static xcb_randr_mode_t createNewMode(const QSize &size) +{ + // need to create the new mode + qDebug() << "Creating a new mode"; + QString name(QString::number(size.width())); + name.append('x'); + name.append(QString::number(size.height())); + xcb_randr_mode_info_t newInfo; + newInfo.dot_clock = 0; + newInfo.height = size.height(); + newInfo.hskew = 0; + newInfo.hsync_end = 0; + newInfo.hsync_start = 0; + newInfo.htotal = size.width(); + newInfo.id = 0; + newInfo.mode_flags = 0; + newInfo.vsync_end = 0; + newInfo.vsync_start = 0; + newInfo.vtotal = size.height(); + newInfo.width = size.width(); + newInfo.name_len = name.length(); + auto cookie = xcb_randr_create_mode_unchecked(connection(), rootWindow(), newInfo, name.length(), name.toUtf8().constData()); + ScopedCPointer reply(xcb_randr_create_mode_reply(connection(), cookie, nullptr)); + if (!reply.isNull()) { + return reply->mode; + } + return XCB_NONE; +} + +static xcb_randr_mode_t getModeForSize(const QSize &size, const xcb_randr_get_screen_resources_current_reply_t* r) +{ + xcb_randr_mode_info_t *infos = xcb_randr_get_screen_resources_current_modes(r); + const int modeInfoLength = xcb_randr_get_screen_resources_current_modes_length(r); + // check available modes + for (int i = 0; i < modeInfoLength; ++i) { + xcb_randr_mode_info_t modeInfo = infos[i]; + if (modeInfo.width == size.width() && modeInfo.height == size.height()) { + qDebug() << "Found our required mode"; + return modeInfo.id; + } + } + // did not find our mode, so create it + return createNewMode(size); +} + +static bool addModeToOutput(xcb_randr_output_t output, xcb_randr_mode_t mode) +{ + ScopedCPointer info(xcb_randr_get_output_info_reply(connection(), + xcb_randr_get_output_info(connection(), output, XCB_TIME_CURRENT_TIME), nullptr)); + xcb_randr_mode_t *modes = xcb_randr_get_output_info_modes(info.data()); + for (int i = 0; i < info->num_modes; ++i) { + if (modes[i] == mode) { + return true; + } + } + qDebug() << "Need to add the mode to output"; + auto c = xcb_randr_add_output_mode_checked(connection(), output, mode); + ScopedCPointer error(xcb_request_check(connection(), c)); + if (!error.isNull()) { + qDebug() << "Error while adding mode to output: " << error->error_code; + return false; + } + return true; +} + +void WaylandScreens::updateXRandr() +{ + if (!Xcb::Extensions::self()->isRandrAvailable()) { + qDebug() << "No RandR extension available, cannot sync with X"; + return; + } + QRegion screens; + foreach (const QRect &rect, m_geometries) { + screens = screens.united(rect); + } + const QSize &size = screens.boundingRect().size(); + if (size.isEmpty()) { + return; + } + + RandR::CurrentResources currentResources(rootWindow()); + xcb_randr_crtc_t crtc = getCrtc(currentResources.data()); + if (crtc == XCB_NONE) { + return; + } + xcb_randr_output_t output = getOutputForCrtc(crtc); + if (output == XCB_NONE) { + return; + } + // first disable the first CRTC + xcb_randr_set_crtc_config(connection(), crtc, XCB_TIME_CURRENT_TIME, XCB_TIME_CURRENT_TIME, + 0, 0, XCB_NONE, XCB_RANDR_ROTATION_ROTATE_0, 0, nullptr); + + // then set new screen size + if (!setNewScreenSize(size)) { + return; + } + + xcb_randr_mode_t mode = getModeForSize(size, currentResources.data()); + if (mode == XCB_NONE) { + return; + } + + if (!addModeToOutput(output, mode)) { + return; + } + // enable CRTC again + xcb_randr_set_crtc_config(connection(), crtc, XCB_TIME_CURRENT_TIME, XCB_TIME_CURRENT_TIME, + 0, 0, mode, XCB_RANDR_ROTATION_ROTATE_0, 1, &output); +} + +} diff --git a/screens_wayland.h b/screens_wayland.h new file mode 100644 index 0000000000..0063df1b0b --- /dev/null +++ b/screens_wayland.h @@ -0,0 +1,48 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2013 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#ifndef KWIN_SCREENS_WAYLAND_H +#define KWIN_SCREENS_WAYLAND_H + +#include "screens.h" + +namespace KWin +{ + +class WaylandScreens : public Screens +{ + Q_OBJECT +public: + explicit WaylandScreens(QObject *parent); + virtual ~WaylandScreens(); + void init() override; + QRect geometry(int screen) const override; + QSize size(int screen) const override; + int number(const QPoint& pos) const override; + +protected Q_SLOTS: + void updateCount() override; +private: + void updateXRandr(); + QList m_geometries; +}; + +} + +#endif