11#include "config-kwin.h"
44#include <KConfigGroup>
45#include <KLocalizedString>
47#include <QOpenGLContext>
49#include <private/qtx11extras_p.h>
61 bool event(xcb_generic_event_t *
event)
override;
80 auto *xrrEvent =
reinterpret_cast<xcb_randr_screen_change_notify_event_t *
>(
event);
81 xcb_screen_t *screen = Xcb::defaultScreen();
82 if (xrrEvent->rotation & (XCB_RANDR_ROTATION_ROTATE_90 | XCB_RANDR_ROTATION_ROTATE_270)) {
83 screen->width_in_pixels = xrrEvent->height;
84 screen->height_in_pixels = xrrEvent->width;
85 screen->width_in_millimeters = xrrEvent->mheight;
86 screen->height_in_millimeters = xrrEvent->mwidth;
88 screen->width_in_pixels = xrrEvent->width;
89 screen->height_in_pixels = xrrEvent->height;
90 screen->width_in_millimeters = xrrEvent->mwidth;
91 screen->height_in_millimeters = xrrEvent->mheight;
99 , m_updateOutputsTimer(std::make_unique<QTimer>())
100 , m_x11Display(QX11Info::display())
101 , m_renderLoop(std::make_unique<
RenderLoop>(nullptr))
104 if (!qEnvironmentVariableIsSet(
"KWIN_NO_XI2")) {
105 m_xinputIntegration = std::make_unique<XInputIntegration>(m_x11Display,
this);
106 m_xinputIntegration->init();
107 if (!m_xinputIntegration->hasXinput()) {
108 m_xinputIntegration.reset();
115 m_updateOutputsTimer->setSingleShot(
true);
118 m_keyboard = std::make_unique<X11Keyboard>();
123 m_eglDisplay.reset();
134 return kwinApp()->x11Connection();
139 return kwinApp()->x11RootWindow();
144 if (!QX11Info::isPlatformX11()) {
151 m_randrEventFilter = std::make_unique<XrandrEventFilter>(
this);
163 return std::make_unique<GlxBackend>(m_x11Display,
this);
165 qCWarning(KWIN_X11STANDALONE) <<
"Glx not available, trying EGL instead.";
171 return std::make_unique<EglBackend>(m_x11Display,
this);
180 if (!m_screenEdgesFilter) {
181 m_screenEdgesFilter = std::make_unique<ScreenEdgesFilter>();
183 return std::make_unique<WindowBasedEdge>(edges);
189 auto c = std::make_unique<X11Cursor>(m_xinputIntegration !=
nullptr);
190 if (m_xinputIntegration) {
191 m_xinputIntegration->setCursor(c.get());
199 return std::make_unique<X11Cursor>(
false);
203bool X11StandaloneBackend::hasGlx()
210 auto c = kwinApp()->x11Connection();
212 xcb_xfixes_get_cursor_image_reply(c,
213 xcb_xfixes_get_cursor_image_unchecked(c),
219 QImage qcursorimg((uchar *)xcb_xfixes_get_cursor_image_cursor_image(cursor.get()), cursor->width, cursor->height,
220 QImage::Format_ARGB32_Premultiplied);
225void X11StandaloneBackend::updateCursor()
228 xcb_xfixes_hide_cursor(kwinApp()->x11Connection(), kwinApp()->x11RootWindow());
230 xcb_xfixes_show_cursor(kwinApp()->x11Connection(), kwinApp()->x11RootWindow());
236 if (!m_windowSelector) {
237 m_windowSelector = std::make_unique<WindowSelector>();
239 m_windowSelector->start(callback, cursorName);
244 if (!m_windowSelector) {
245 m_windowSelector = std::make_unique<WindowSelector>();
247 m_windowSelector->start(callback);
252 return std::make_unique<NonCompositedOutlineVisual>(outline);
262 QList<CompositingType> compositors;
272 doUpdateOutputs<Xcb::RandR::ScreenResources>();
278 m_updateOutputsTimer->start();
283 doUpdateOutputs<Xcb::RandR::CurrentResources>();
288void X11StandaloneBackend::doUpdateOutputs()
290 QList<Output *> changed;
291 QList<Output *> added;
292 QList<Output *> removed = m_outputs;
296 if (!resources.isNull()) {
298 std::span crtcs(resources.crtcs(), resources->num_crtcs);
299 for (
auto crtc : crtcs) {
302 const QRect geometry = info.rect();
303 if (!geometry.isValid()) {
307 float refreshRate = -1.0f;
309 for (
auto mode : std::span(resources.modes(), resources->num_modes)) {
310 if (info->mode == mode.id) {
311 if (mode.htotal != 0 && mode.vtotal != 0) {
313 int dotclock = mode.dot_clock,
314 vtotal = mode.vtotal;
315 if (mode.mode_flags & XCB_RANDR_MODE_FLAG_INTERLACE) {
318 if (mode.mode_flags & XCB_RANDR_MODE_FLAG_DOUBLE_SCAN) {
321 refreshRate = dotclock / float(mode.htotal * vtotal);
327 for (
auto xcbOutput : std::span(info.
outputs(), info->num_outputs)) {
328 Xcb::RandR::OutputInfo outputInfo(xcbOutput, resources->config_timestamp);
329 if (outputInfo->crtc != crtc) {
333 X11Output *output = findX11Output(outputInfo.name());
335 changed.append(output);
336 removed.removeOne(output);
338 output =
new X11Output(
this);
339 added.append(output);
345 Xcb::RandR::CrtcGamma gamma(crtc);
347 output->setRenderLoop(m_renderLoop.get());
348 output->setCrtc(crtc);
349 output->setGammaRampSize(gamma.isNull() ? 0 : gamma->size);
350 auto it = std::find(crtcs.begin(), crtcs.end(), crtc);
351 int crtcIndex = std::distance(crtcs.begin(), it);
352 output->setXineramaNumber(crtcIndex);
354 QSize physicalSize(outputInfo->mm_width, outputInfo->mm_height);
355 switch (info->rotation) {
356 case XCB_RANDR_ROTATION_ROTATE_0:
357 case XCB_RANDR_ROTATION_ROTATE_180:
359 case XCB_RANDR_ROTATION_ROTATE_90:
360 case XCB_RANDR_ROTATION_ROTATE_270:
361 physicalSize.transpose();
363 case XCB_RANDR_ROTATION_REFLECT_X:
364 case XCB_RANDR_ROTATION_REFLECT_Y:
368 X11Output::Information information{
369 .name = outputInfo.name(),
370 .physicalSize = physicalSize,
373 auto edidProperty = Xcb::RandR::OutputProperty(xcbOutput,
atoms->
edid, XCB_ATOM_INTEGER, 0, 100,
false,
false);
375 if (
auto data = edidProperty.toByteArray(&ok); ok && !data.isEmpty()) {
376 if (
auto edid = Edid(data, edidProperty.data()->num_items); edid.isValid()) {
377 information.manufacturer = edid.manufacturerString();
378 information.model = edid.monitorName();
379 information.serialNumber = edid.serialNumber();
380 information.edid = edid;
384 auto mode = std::make_shared<OutputMode>(geometry.size(), refreshRate * 1000);
386 X11Output::State state = output->m_state;
387 state.modes = {mode};
388 state.currentMode = mode;
389 state.position = geometry.topLeft();
391 output->setInformation(information);
392 output->setState(state);
401 if (changed.isEmpty() && added.isEmpty()) {
402 auto dummyOutput =
new X11PlaceholderOutput(
this);
403 m_outputs << dummyOutput;
405 dummyOutput->updateEnabled(
true);
409 for (Output *output : std::as_const(added)) {
410 m_outputs.append(output);
412 if (
auto placeholderOutput = qobject_cast<X11PlaceholderOutput *>(output)) {
413 placeholderOutput->updateEnabled(
true);
414 }
else if (
auto nativeOutput = qobject_cast<X11Output *>(output)) {
415 nativeOutput->updateEnabled(
true);
420 for (Output *output : std::as_const(removed)) {
421 m_outputs.removeOne(output);
422 if (
auto placeholderOutput = qobject_cast<X11PlaceholderOutput *>(output)) {
423 placeholderOutput->updateEnabled(
false);
424 }
else if (
auto nativeOutput = qobject_cast<X11Output *>(output)) {
425 nativeOutput->updateEnabled(
false);
433 std::sort(m_outputs.begin(), m_outputs.end(), [](
const Output *a,
const Output *b) {
434 const auto xa = qobject_cast<const X11Output *>(a);
438 const auto xb = qobject_cast<const X11Output *>(b);
442 return xa->xineramaNumber() < xb->xineramaNumber();
445 Q_EMIT outputsQueried();
448X11Output *X11StandaloneBackend::findX11Output(
const QString &name)
const
450 for (Output *output : m_outputs) {
451 if (output->name() == name) {
452 return qobject_cast<X11Output *>(output);
465 return m_keyboard.get();
470 return m_renderLoop.get();
473static bool refreshRate_compare(
const Output *first,
const Output *smallest)
478static int currentRefreshRate()
480 static const int refreshRate = qEnvironmentVariableIntValue(
"KWIN_X11_REFRESH_RATE");
485 const QList<Output *>
outputs = kwinApp()->outputBackend()->outputs();
490 static const QString syncDisplayDevice = qEnvironmentVariable(
"__GL_SYNC_DISPLAY_DEVICE");
491 if (!syncDisplayDevice.isEmpty()) {
492 for (
const Output *output :
outputs) {
493 if (output->name() == syncDisplayDevice) {
494 return output->refreshRate();
499 auto syncIt = std::min_element(
outputs.begin(),
outputs.end(), refreshRate_compare);
500 return (*syncIt)->refreshRate();
503void X11StandaloneBackend::updateRefreshRate()
505 int refreshRate = currentRefreshRate();
506 if (refreshRate <= 0) {
507 qCWarning(KWIN_X11STANDALONE) <<
"Bogus refresh rate" << refreshRate;
511 m_renderLoop->setRefreshRate(refreshRate);
514void X11StandaloneBackend::setEglDisplay(std::unique_ptr<EglDisplay> &&display)
516 m_eglDisplay = std::move(display);
519EglDisplay *X11StandaloneBackend::sceneEglDisplayObject()
const
521 return m_eglDisplay.get();
525#include "moc_x11_standalone_backend.cpp"
KWin::OpenGLPlatformInterface glPlatformInterface
This class is used to show the outline of a given geometry.
void outputAdded(Output *output)
void outputRemoved(Output *output)
uint32_t refreshRate() const
Class for controlling screen edges.
std::unique_ptr< OutlineVisual > createOutline(Outline *outline)
::Display * display() const
bool initialize() override
xcb_connection_t * connection() const
std::unique_ptr< Edge > createScreenEdge(ScreenEdges *parent)
std::unique_ptr< OpenGLBackend > createOpenGLBackend() override
X11StandaloneBackend(QObject *parent=nullptr)
~X11StandaloneBackend() override
void createEffectsHandler(Compositor *compositor, WorkspaceScene *scene)
void scheduleUpdateOutputs()
PlatformCursorImage cursorImage() const
void startInteractiveWindowSelection(std::function< void(KWin::Window *)> callback, const QByteArray &cursorName=QByteArray())
std::unique_ptr< Cursor > createPlatformCursor()
xcb_window_t rootWindow() const
void startInteractivePositionSelection(std::function< void(const QPointF &)> callback)
QList< CompositingType > supportedCompositors() const override
Outputs outputs() const override
static Extensions * self()
int randrNotifyEvent() const
void setConfig(const KSharedConfigPtr &config)
XrandrEventFilter(X11StandaloneBackend *backend)
bool event(xcb_generic_event_t *event) override
QList< KWayland::Client::Output * > outputs
void init(xcb_connection_t *connection, xcb_window_t rootWindow)
InputRedirection * input()
KWIN_EXPORT Atoms * atoms
std::unique_ptr< T, CDeleter > UniqueCPtr