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

KDE's Doxygen guidelines are available online.