KWayland

output_interface.cpp
1 /*
2  SPDX-FileCopyrightText: 2014 Martin Gräßlin <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5 */
6 #include "output_interface.h"
7 #include "display.h"
8 #include "global_p.h"
9 
10 #include <QVector>
11 
12 #include <wayland-server.h>
13 
14 namespace KWayland
15 {
16 namespace Server
17 {
18 class OutputInterface::Private : public Global::Private
19 {
20 public:
21  struct ResourceData {
22  wl_resource *resource;
23  uint32_t version;
24  };
25  Private(OutputInterface *q, Display *d);
26  ~Private() override;
27  void sendMode(wl_resource *resource, const Mode &mode);
28  void sendDone(const ResourceData &data);
29  void updateGeometry();
30  void updateScale();
31 
32  QSize physicalSize;
33  QPoint globalPosition;
34  QString manufacturer = QStringLiteral("org.kde.kwin");
35  QString model = QStringLiteral("none");
36  int scale = 1;
37  SubPixel subPixel = SubPixel::Unknown;
38  Transform transform = Transform::Normal;
39  QList<Mode> modes;
40  QList<ResourceData> resources;
41  struct {
42  DpmsMode mode = DpmsMode::On;
43  bool supported = false;
44  } dpms;
45 
46  static OutputInterface *get(wl_resource *native);
47 
48 private:
49  static Private *cast(wl_resource *native);
50  static void releaseCallback(wl_client *client, wl_resource *resource);
51  static void unbind(wl_resource *resource);
52  void bind(wl_client *client, uint32_t version, uint32_t id) override;
53  int32_t toTransform() const;
54  int32_t toSubPixel() const;
55  void sendGeometry(wl_resource *resource);
56  void sendScale(const ResourceData &data);
57 
58  OutputInterface *q;
59  static QVector<Private *> s_privates;
60  static const struct wl_output_interface s_interface;
61  static const quint32 s_version;
62 };
63 
64 QVector<OutputInterface::Private *> OutputInterface::Private::s_privates;
65 const quint32 OutputInterface::Private::s_version = 3;
66 
67 OutputInterface::Private::Private(OutputInterface *q, Display *d)
68  : Global::Private(d, &wl_output_interface, s_version)
69  , q(q)
70 {
71  s_privates << this;
72 }
73 
74 OutputInterface::Private::~Private()
75 {
76  s_privates.removeAll(this);
77 }
78 
79 #ifndef K_DOXYGEN
80 const struct wl_output_interface OutputInterface::Private::s_interface = {releaseCallback};
81 #endif
82 
83 void OutputInterface::Private::releaseCallback(wl_client *client, wl_resource *resource)
84 {
85  Q_UNUSED(client);
86  unbind(resource);
87 }
88 
89 OutputInterface *OutputInterface::Private::get(wl_resource *native)
90 {
91  if (Private *p = cast(native)) {
92  return p->q;
93  }
94  return nullptr;
95 }
96 
97 OutputInterface::Private *OutputInterface::Private::cast(wl_resource *native)
98 {
99  for (auto it = s_privates.constBegin(); it != s_privates.constEnd(); ++it) {
100  const auto &resources = (*it)->resources;
101  auto rit = std::find_if(resources.constBegin(), resources.constEnd(), [native](const ResourceData &data) {
102  return data.resource == native;
103  });
104  if (rit != resources.constEnd()) {
105  return (*it);
106  }
107  }
108  return nullptr;
109 }
110 
111 OutputInterface::OutputInterface(Display *display, QObject *parent)
112  : Global(new Private(this, display), parent)
113 {
114  Q_D();
115  connect(this, &OutputInterface::currentModeChanged, this, [this] {
116  Q_D();
117  auto currentModeIt = std::find_if(d->modes.constBegin(), d->modes.constEnd(), [](const Mode &mode) {
118  return mode.flags.testFlag(ModeFlag::Current);
119  });
120  if (currentModeIt == d->modes.constEnd()) {
121  return;
122  }
123  for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) {
124  d->sendMode((*it).resource, *currentModeIt);
125  d->sendDone(*it);
126  }
127  wl_display_flush_clients(*(d->display));
128  });
129  connect(this, &OutputInterface::subPixelChanged, this, [d] {
130  d->updateGeometry();
131  });
132  connect(this, &OutputInterface::transformChanged, this, [d] {
133  d->updateGeometry();
134  });
135  connect(this, &OutputInterface::globalPositionChanged, this, [d] {
136  d->updateGeometry();
137  });
138  connect(this, &OutputInterface::modelChanged, this, [d] {
139  d->updateGeometry();
140  });
141  connect(this, &OutputInterface::manufacturerChanged, this, [d] {
142  d->updateGeometry();
143  });
144  connect(this, &OutputInterface::scaleChanged, this, [d] {
145  d->updateScale();
146  });
147 }
148 
149 OutputInterface::~OutputInterface() = default;
150 
151 QSize OutputInterface::pixelSize() const
152 {
153  Q_D();
154  auto it = std::find_if(d->modes.constBegin(), d->modes.constEnd(), [](const Mode &mode) {
155  return mode.flags.testFlag(ModeFlag::Current);
156  });
157  if (it == d->modes.constEnd()) {
158  return QSize();
159  }
160  return (*it).size;
161 }
162 
163 int OutputInterface::refreshRate() const
164 {
165  Q_D();
166  auto it = std::find_if(d->modes.constBegin(), d->modes.constEnd(), [](const Mode &mode) {
167  return mode.flags.testFlag(ModeFlag::Current);
168  });
169  if (it == d->modes.constEnd()) {
170  return 60000;
171  }
172  return (*it).refreshRate;
173 }
174 
175 void OutputInterface::addMode(const QSize &size, OutputInterface::ModeFlags flags, int refreshRate)
176 {
177  Q_ASSERT(!isValid());
178  Q_D();
179 
180  auto currentModeIt = std::find_if(d->modes.begin(), d->modes.end(), [](const Mode &mode) {
181  return mode.flags.testFlag(ModeFlag::Current);
182  });
183  if (currentModeIt == d->modes.end() && !flags.testFlag(ModeFlag::Current)) {
184  // no mode with current flag - enforce
185  flags |= ModeFlag::Current;
186  }
187  if (currentModeIt != d->modes.end() && flags.testFlag(ModeFlag::Current)) {
188  // another mode has the current flag - remove
189  (*currentModeIt).flags &= ~uint(ModeFlag::Current);
190  }
191 
192  if (flags.testFlag(ModeFlag::Preferred)) {
193  // remove from existing Preferred mode
194  auto preferredIt = std::find_if(d->modes.begin(), d->modes.end(), [](const Mode &mode) {
195  return mode.flags.testFlag(ModeFlag::Preferred);
196  });
197  if (preferredIt != d->modes.end()) {
198  (*preferredIt).flags &= ~uint(ModeFlag::Preferred);
199  }
200  }
201 
202  auto existingModeIt = std::find_if(d->modes.begin(), d->modes.end(), [size, refreshRate](const Mode &mode) {
203  return mode.size == size && mode.refreshRate == refreshRate;
204  });
205  auto emitChanges = [this, flags, size, refreshRate] {
206  Q_EMIT modesChanged();
207  if (flags.testFlag(ModeFlag::Current)) {
208  Q_EMIT refreshRateChanged(refreshRate);
209  Q_EMIT pixelSizeChanged(size);
210  Q_EMIT currentModeChanged();
211  }
212  };
213  if (existingModeIt != d->modes.end()) {
214  if ((*existingModeIt).flags == flags) {
215  // nothing to do
216  return;
217  }
218  (*existingModeIt).flags = flags;
219  emitChanges();
220  return;
221  }
222  Mode mode;
223  mode.size = size;
224  mode.refreshRate = refreshRate;
225  mode.flags = flags;
226  d->modes << mode;
227  emitChanges();
228 }
229 
230 void OutputInterface::setCurrentMode(const QSize &size, int refreshRate)
231 {
232  Q_D();
233  auto currentModeIt = std::find_if(d->modes.begin(), d->modes.end(), [](const Mode &mode) {
234  return mode.flags.testFlag(ModeFlag::Current);
235  });
236  if (currentModeIt != d->modes.end()) {
237  // another mode has the current flag - remove
238  (*currentModeIt).flags &= ~uint(ModeFlag::Current);
239  }
240 
241  auto existingModeIt = std::find_if(d->modes.begin(), d->modes.end(), [size, refreshRate](const Mode &mode) {
242  return mode.size == size && mode.refreshRate == refreshRate;
243  });
244 
245  Q_ASSERT(existingModeIt != d->modes.end());
246  (*existingModeIt).flags |= ModeFlag::Current;
247  Q_EMIT modesChanged();
248  Q_EMIT refreshRateChanged((*existingModeIt).refreshRate);
249  Q_EMIT pixelSizeChanged((*existingModeIt).size);
250  Q_EMIT currentModeChanged();
251 }
252 
253 int32_t OutputInterface::Private::toTransform() const
254 {
255  switch (transform) {
256  case Transform::Normal:
257  return WL_OUTPUT_TRANSFORM_NORMAL;
258  case Transform::Rotated90:
259  return WL_OUTPUT_TRANSFORM_90;
260  case Transform::Rotated180:
261  return WL_OUTPUT_TRANSFORM_180;
262  case Transform::Rotated270:
263  return WL_OUTPUT_TRANSFORM_270;
264  case Transform::Flipped:
265  return WL_OUTPUT_TRANSFORM_FLIPPED;
266  case Transform::Flipped90:
267  return WL_OUTPUT_TRANSFORM_FLIPPED_90;
268  case Transform::Flipped180:
269  return WL_OUTPUT_TRANSFORM_FLIPPED_180;
270  case Transform::Flipped270:
271  return WL_OUTPUT_TRANSFORM_FLIPPED_270;
272  }
273  abort();
274 }
275 
276 int32_t OutputInterface::Private::toSubPixel() const
277 {
278  switch (subPixel) {
279  case SubPixel::Unknown:
280  return WL_OUTPUT_SUBPIXEL_UNKNOWN;
281  case SubPixel::None:
282  return WL_OUTPUT_SUBPIXEL_NONE;
283  case SubPixel::HorizontalRGB:
284  return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB;
285  case SubPixel::HorizontalBGR:
286  return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR;
287  case SubPixel::VerticalRGB:
288  return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB;
289  case SubPixel::VerticalBGR:
290  return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR;
291  }
292  abort();
293 }
294 
295 void OutputInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id)
296 {
297  auto c = display->getConnection(client);
298  wl_resource *resource = c->createResource(&wl_output_interface, qMin(version, s_version), id);
299  if (!resource) {
300  wl_client_post_no_memory(client);
301  return;
302  }
303  wl_resource_set_user_data(resource, this);
304  wl_resource_set_implementation(resource, &s_interface, this, unbind);
305  ResourceData r;
306  r.resource = resource;
307  r.version = version;
308  resources << r;
309 
310  sendGeometry(resource);
311  sendScale(r);
312 
313  auto currentModeIt = modes.constEnd();
314  for (auto it = modes.constBegin(); it != modes.constEnd(); ++it) {
315  const Mode &mode = *it;
316  if (mode.flags.testFlag(ModeFlag::Current)) {
317  // needs to be sent as last mode
318  currentModeIt = it;
319  continue;
320  }
321  sendMode(resource, mode);
322  }
323 
324  if (currentModeIt != modes.constEnd()) {
325  sendMode(resource, *currentModeIt);
326  }
327 
328  sendDone(r);
329  c->flush();
330 }
331 
332 void OutputInterface::Private::unbind(wl_resource *resource)
333 {
334  Private *o = cast(resource);
335  if (!o) {
336  return;
337  }
338  auto it = std::find_if(o->resources.begin(), o->resources.end(), [resource](const ResourceData &r) {
339  return r.resource == resource;
340  });
341  if (it != o->resources.end()) {
342  o->resources.erase(it);
343  }
344 }
345 
346 void OutputInterface::Private::sendMode(wl_resource *resource, const Mode &mode)
347 {
348  int32_t flags = 0;
349  if (mode.flags.testFlag(ModeFlag::Current)) {
350  flags |= WL_OUTPUT_MODE_CURRENT;
351  }
352  if (mode.flags.testFlag(ModeFlag::Preferred)) {
353  flags |= WL_OUTPUT_MODE_PREFERRED;
354  }
355  wl_output_send_mode(resource, flags, mode.size.width(), mode.size.height(), mode.refreshRate);
356 }
357 
358 void OutputInterface::Private::sendGeometry(wl_resource *resource)
359 {
360  wl_output_send_geometry(resource,
361  globalPosition.x(),
362  globalPosition.y(),
363  physicalSize.width(),
364  physicalSize.height(),
365  toSubPixel(),
366  qPrintable(manufacturer),
367  qPrintable(model),
368  toTransform());
369 }
370 
371 void OutputInterface::Private::sendScale(const ResourceData &data)
372 {
373  if (data.version < 2) {
374  return;
375  }
376  wl_output_send_scale(data.resource, scale);
377 }
378 
379 void OutputInterface::Private::sendDone(const ResourceData &data)
380 {
381  if (data.version < 2) {
382  return;
383  }
384  wl_output_send_done(data.resource);
385 }
386 
387 void OutputInterface::Private::updateGeometry()
388 {
389  for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) {
390  sendGeometry((*it).resource);
391  sendDone(*it);
392  }
393 }
394 
395 void OutputInterface::Private::updateScale()
396 {
397  for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) {
398  sendScale(*it);
399  sendDone(*it);
400  }
401 }
402 
403 // clang-format off
404 #define SETTER(setterName, type, argumentName) \
405  void OutputInterface::setterName(type arg) \
406  { \
407  Q_D(); \
408  if (d->argumentName == arg) { \
409  return; \
410  } \
411  d->argumentName = arg; \
412  Q_EMIT argumentName##Changed(d->argumentName); \
413  }
414 // clang-format on
415 
416 SETTER(setPhysicalSize, const QSize &, physicalSize)
417 SETTER(setGlobalPosition, const QPoint &, globalPosition)
418 SETTER(setManufacturer, const QString &, manufacturer)
419 SETTER(setModel, const QString &, model)
420 SETTER(setScale, int, scale)
421 SETTER(setSubPixel, SubPixel, subPixel)
422 SETTER(setTransform, Transform, transform)
423 
424 #undef SETTER
425 
426 QSize OutputInterface::physicalSize() const
427 {
428  Q_D();
429  return d->physicalSize;
430 }
431 
432 QPoint OutputInterface::globalPosition() const
433 {
434  Q_D();
435  return d->globalPosition;
436 }
437 
438 QString OutputInterface::manufacturer() const
439 {
440  Q_D();
441  return d->manufacturer;
442 }
443 
444 QString OutputInterface::model() const
445 {
446  Q_D();
447  return d->model;
448 }
449 
450 int OutputInterface::scale() const
451 {
452  Q_D();
453  return d->scale;
454 }
455 
456 OutputInterface::SubPixel OutputInterface::subPixel() const
457 {
458  Q_D();
459  return d->subPixel;
460 }
461 
462 OutputInterface::Transform OutputInterface::transform() const
463 {
464  Q_D();
465  return d->transform;
466 }
467 
468 QList<OutputInterface::Mode> OutputInterface::modes() const
469 {
470  Q_D();
471  return d->modes;
472 }
473 
474 void OutputInterface::setDpmsMode(OutputInterface::DpmsMode mode)
475 {
476  Q_D();
477  if (d->dpms.mode == mode) {
478  return;
479  }
480  d->dpms.mode = mode;
481  Q_EMIT dpmsModeChanged();
482 }
483 
484 void OutputInterface::setDpmsSupported(bool supported)
485 {
486  Q_D();
487  if (d->dpms.supported == supported) {
488  return;
489  }
490  d->dpms.supported = supported;
491  Q_EMIT dpmsSupportedChanged();
492 }
493 
494 OutputInterface::DpmsMode OutputInterface::dpmsMode() const
495 {
496  Q_D();
497  return d->dpms.mode;
498 }
499 
500 bool OutputInterface::isDpmsSupported() const
501 {
502  Q_D();
503  return d->dpms.supported;
504 }
505 
506 QVector<wl_resource *> OutputInterface::clientResources(ClientConnection *client) const
507 {
508  Q_D();
510  for (auto it = d->resources.constBegin(), end = d->resources.constEnd(); it != end; ++it) {
511  if (wl_resource_get_client((*it).resource) == client->client()) {
512  ret << (*it).resource;
513  }
514  }
515  return ret;
516 }
517 
518 OutputInterface *OutputInterface::get(wl_resource *native)
519 {
520  return Private::get(native);
521 }
522 
523 OutputInterface::Private *OutputInterface::d_func() const
524 {
525  return reinterpret_cast<Private *>(d.data());
526 }
527 
528 }
529 }
KDOCTOOLS_EXPORT QString transform(const QString &file, const QString &stylesheet, const QVector< const char * > &params=QVector< const char * >())
KIOCORE_EXPORT TransferJob * get(const QUrl &url, LoadType reload=NoReload, JobFlags flags=DefaultFlags)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
Global for the wl_output interface.
Convenient Class which represents a wl_client.
unsigned int version()
bool isValid(QStringView ifopt)
Q_SCRIPTABLE Q_NOREPLY void abort()
Q_D(Todo)
virtual QVariant get(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Feb 7 2023 03:56:21 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.