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

« back to all changes in this revision

Viewing changes to screens_wayland.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) 2013 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 "screens_wayland.h"
21
 
 
22
 
#include "wayland_backend.h"
23
 
#include <KWayland/Client/output.h>
24
 
#include "main.h"
25
 
#include "utils.h"
26
 
#include "xcbutils.h"
27
 
 
28
 
#include <QDebug>
29
 
 
30
 
#include <xcb/randr.h>
31
 
 
32
 
namespace KWin
33
 
{
34
 
 
35
 
WaylandScreens::WaylandScreens(Wayland::WaylandBackend *backend, QObject* parent)
36
 
    : Screens(parent)
37
 
    , m_backend(backend)
38
 
{
39
 
}
40
 
 
41
 
WaylandScreens::~WaylandScreens()
42
 
{
43
 
}
44
 
 
45
 
void WaylandScreens::init()
46
 
{
47
 
    Screens::init();
48
 
    connect(m_backend, &Wayland::WaylandBackend::outputsChanged,
49
 
            this, &WaylandScreens::startChangedTimer);
50
 
    updateCount();
51
 
}
52
 
 
53
 
QRect WaylandScreens::geometry(int screen) const
54
 
{
55
 
    if (screen >= m_geometries.size()) {
56
 
        return QRect();
57
 
    }
58
 
    return m_geometries.at(screen);
59
 
}
60
 
 
61
 
QSize WaylandScreens::size(int screen) const
62
 
{
63
 
    return geometry(screen).size();
64
 
}
65
 
 
66
 
int WaylandScreens::number(const QPoint &pos) const
67
 
{
68
 
    int bestScreen = 0;
69
 
    int minDistance = INT_MAX;
70
 
    for (int i = 0; i < m_geometries.size(); ++i) {
71
 
        const QRect &geo = m_geometries.at(i);
72
 
        if (geo.contains(pos)) {
73
 
            return i;
74
 
        }
75
 
        int distance = QPoint(geo.topLeft() - pos).manhattanLength();
76
 
        distance = qMin(distance, QPoint(geo.topRight() - pos).manhattanLength());
77
 
        distance = qMin(distance, QPoint(geo.bottomRight() - pos).manhattanLength());
78
 
        distance = qMin(distance, QPoint(geo.bottomLeft() - pos).manhattanLength());
79
 
        if (distance < minDistance) {
80
 
            minDistance = distance;
81
 
            bestScreen = i;
82
 
        }
83
 
    }
84
 
    return bestScreen;
85
 
}
86
 
 
87
 
void WaylandScreens::updateCount()
88
 
{
89
 
    m_geometries.clear();
90
 
    int count = 0;
91
 
    const QList<KWayland::Client::Output*> &outputs = m_backend->outputs();
92
 
    for (auto it = outputs.begin(); it != outputs.end(); ++it) {
93
 
        if ((*it)->pixelSize().isEmpty()) {
94
 
            continue;
95
 
        }
96
 
        count++;
97
 
        m_geometries.append(QRect((*it)->globalPosition(), (*it)->pixelSize()));
98
 
    }
99
 
    if (m_geometries.isEmpty()) {
100
 
        // we need a fake screen
101
 
        m_geometries.append(QRect(0, 0, displayWidth(), displayHeight()));
102
 
        setCount(1);
103
 
        return;
104
 
    }
105
 
    setCount(m_geometries.count());
106
 
    updateXRandr();
107
 
    emit changed();
108
 
}
109
 
 
110
 
namespace RandR
111
 
{
112
 
using namespace Xcb;
113
 
XCB_WRAPPER(CurrentResources, xcb_randr_get_screen_resources_current, xcb_window_t)
114
 
}
115
 
 
116
 
static bool setNewScreenSize(const QSize &size)
117
 
{
118
 
    auto c = xcb_randr_set_screen_size_checked(connection(), rootWindow(), size.width(), size.height(), 1, 1);
119
 
    ScopedCPointer<xcb_generic_error_t> error(xcb_request_check(connection(), c));
120
 
    if (!error.isNull()) {
121
 
        qCDebug(KWIN_CORE) << "Error setting screen size: " << error->error_code;
122
 
        return false;
123
 
    }
124
 
    return true;
125
 
}
126
 
 
127
 
static xcb_randr_crtc_t getCrtc(const xcb_randr_get_screen_resources_current_reply_t* r)
128
 
{
129
 
    if (xcb_randr_get_screen_resources_current_crtcs_length(r) == 0) {
130
 
        qCDebug(KWIN_CORE) << "No CRTCs";
131
 
        return XCB_NONE;
132
 
    }
133
 
    return xcb_randr_get_screen_resources_current_crtcs(r)[0];
134
 
}
135
 
 
136
 
static xcb_randr_output_t getOutputForCrtc(xcb_randr_crtc_t crtc)
137
 
{
138
 
    ScopedCPointer<xcb_randr_get_crtc_info_reply_t> info(xcb_randr_get_crtc_info_reply(
139
 
        connection(), xcb_randr_get_crtc_info(connection(), crtc, XCB_TIME_CURRENT_TIME), nullptr));
140
 
    if (info->num_outputs == 0) {
141
 
        return XCB_NONE;
142
 
    }
143
 
    return xcb_randr_get_crtc_info_outputs(info.data())[0];
144
 
}
145
 
 
146
 
static xcb_randr_mode_t createNewMode(const QSize &size)
147
 
{
148
 
    // need to create the new mode
149
 
    qCDebug(KWIN_CORE) << "Creating a new mode";
150
 
    QString name(QString::number(size.width()));
151
 
    name.append('x');
152
 
    name.append(QString::number(size.height()));
153
 
    xcb_randr_mode_info_t newInfo;
154
 
    newInfo.dot_clock = 0;
155
 
    newInfo.height = size.height();
156
 
    newInfo.hskew = 0;
157
 
    newInfo.hsync_end = 0;
158
 
    newInfo.hsync_start = 0;
159
 
    newInfo.htotal = size.width();
160
 
    newInfo.id = 0;
161
 
    newInfo.mode_flags = 0;
162
 
    newInfo.vsync_end = 0;
163
 
    newInfo.vsync_start = 0;
164
 
    newInfo.vtotal = size.height();
165
 
    newInfo.width = size.width();
166
 
    newInfo.name_len = name.length();
167
 
    auto cookie = xcb_randr_create_mode_unchecked(connection(), rootWindow(), newInfo, name.length(), name.toUtf8().constData());
168
 
    ScopedCPointer<xcb_randr_create_mode_reply_t> reply(xcb_randr_create_mode_reply(connection(), cookie, nullptr));
169
 
    if (!reply.isNull()) {
170
 
        return reply->mode;
171
 
    }
172
 
    return XCB_NONE;
173
 
}
174
 
 
175
 
static xcb_randr_mode_t getModeForSize(const QSize &size, const xcb_randr_get_screen_resources_current_reply_t* r)
176
 
{
177
 
    xcb_randr_mode_info_t *infos = xcb_randr_get_screen_resources_current_modes(r);
178
 
    const int modeInfoLength = xcb_randr_get_screen_resources_current_modes_length(r);
179
 
    // check available modes
180
 
    for (int i = 0; i < modeInfoLength; ++i) {
181
 
        xcb_randr_mode_info_t modeInfo = infos[i];
182
 
        if (modeInfo.width == size.width() && modeInfo.height == size.height()) {
183
 
            qCDebug(KWIN_CORE) << "Found our required mode";
184
 
            return modeInfo.id;
185
 
        }
186
 
    }
187
 
    // did not find our mode, so create it
188
 
    return createNewMode(size);
189
 
}
190
 
 
191
 
static bool addModeToOutput(xcb_randr_output_t output, xcb_randr_mode_t mode)
192
 
{
193
 
    ScopedCPointer<xcb_randr_get_output_info_reply_t> info(xcb_randr_get_output_info_reply(connection(),
194
 
        xcb_randr_get_output_info(connection(), output, XCB_TIME_CURRENT_TIME), nullptr));
195
 
    xcb_randr_mode_t *modes = xcb_randr_get_output_info_modes(info.data());
196
 
    for (int i = 0; i < info->num_modes; ++i) {
197
 
        if (modes[i] == mode) {
198
 
            return true;
199
 
        }
200
 
    }
201
 
    qCDebug(KWIN_CORE) << "Need to add the mode to output";
202
 
    auto c = xcb_randr_add_output_mode_checked(connection(), output, mode);
203
 
    ScopedCPointer<xcb_generic_error_t> error(xcb_request_check(connection(), c));
204
 
    if (!error.isNull()) {
205
 
        qCDebug(KWIN_CORE) << "Error while adding mode to output: " << error->error_code;
206
 
        return false;
207
 
    }
208
 
    return true;
209
 
}
210
 
 
211
 
void WaylandScreens::updateXRandr()
212
 
{
213
 
    if (kwinApp()->operationMode() == Application::OperationModeXwayland) {
214
 
        // no need to update, will be done automagically by Xwayland
215
 
        return;
216
 
    }
217
 
    if (!Xcb::Extensions::self()->isRandrAvailable()) {
218
 
        qCDebug(KWIN_CORE) << "No RandR extension available, cannot sync with X";
219
 
        return;
220
 
    }
221
 
    QRegion screens;
222
 
    foreach (const QRect &rect, m_geometries) {
223
 
        screens = screens.united(rect);
224
 
    }
225
 
    const QSize &size = screens.boundingRect().size();
226
 
    if (size.isEmpty()) {
227
 
        return;
228
 
    }
229
 
 
230
 
    RandR::CurrentResources currentResources(rootWindow());
231
 
    xcb_randr_crtc_t crtc = getCrtc(currentResources.data());
232
 
    if (crtc == XCB_NONE) {
233
 
        return;
234
 
    }
235
 
    xcb_randr_output_t output = getOutputForCrtc(crtc);
236
 
    if (output == XCB_NONE) {
237
 
        return;
238
 
    }
239
 
    // first disable the first CRTC
240
 
    xcb_randr_set_crtc_config(connection(), crtc, XCB_TIME_CURRENT_TIME, XCB_TIME_CURRENT_TIME,
241
 
                              0, 0, XCB_NONE, XCB_RANDR_ROTATION_ROTATE_0, 0, nullptr);
242
 
 
243
 
    // then set new screen size
244
 
    if (!setNewScreenSize(size)) {
245
 
        return;
246
 
    }
247
 
 
248
 
    xcb_randr_mode_t mode = getModeForSize(size, currentResources.data());
249
 
    if (mode == XCB_NONE) {
250
 
        return;
251
 
    }
252
 
 
253
 
    if (!addModeToOutput(output, mode)) {
254
 
        return;
255
 
    }
256
 
    // enable CRTC again
257
 
    xcb_randr_set_crtc_config(connection(), crtc, XCB_TIME_CURRENT_TIME, XCB_TIME_CURRENT_TIME,
258
 
                              0, 0, mode, XCB_RANDR_ROTATION_ROTATE_0, 1, &output);
259
 
}
260
 
 
261
 
}