File indexing completed on 2024-11-10 04:57:28
0001 /* 0002 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org> 0003 SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 #include "output.h" 0008 #include "display.h" 0009 #include "display_p.h" 0010 #include "utils/resource.h" 0011 0012 #include "core/output.h" 0013 0014 #include "qwayland-server-wayland.h" 0015 0016 #include <QList> 0017 #include <QPointer> 0018 #include <QTimer> 0019 0020 #include <cmath> 0021 0022 namespace KWin 0023 { 0024 static const int s_version = 4; 0025 0026 class OutputInterfacePrivate : public QtWaylandServer::wl_output 0027 { 0028 public: 0029 explicit OutputInterfacePrivate(Display *display, OutputInterface *q, Output *handle); 0030 0031 void sendScale(Resource *resource); 0032 void sendGeometry(Resource *resource); 0033 void sendMode(Resource *resource); 0034 void sendDone(Resource *resource); 0035 0036 OutputInterface *q; 0037 QPointer<Display> display; 0038 QPointer<Output> handle; 0039 QSize physicalSize; 0040 QPoint globalPosition; 0041 QString manufacturer; 0042 QString model; 0043 int scale = 1; 0044 Output::SubPixel subPixel = Output::SubPixel::Unknown; 0045 OutputTransform transform = OutputTransform::Normal; 0046 QSize modeSize; 0047 int refreshRate = 0; 0048 QString name; 0049 QString description; 0050 QTimer doneTimer; 0051 0052 private: 0053 void output_destroy_global() override; 0054 void output_bind_resource(Resource *resource) override; 0055 void output_release(Resource *resource) override; 0056 }; 0057 0058 OutputInterfacePrivate::OutputInterfacePrivate(Display *display, OutputInterface *q, Output *handle) 0059 : QtWaylandServer::wl_output(*display, s_version) 0060 , q(q) 0061 , display(display) 0062 , handle(handle) 0063 { 0064 } 0065 0066 void OutputInterfacePrivate::sendMode(Resource *resource) 0067 { 0068 send_mode(resource->handle, mode_current, modeSize.width(), modeSize.height(), refreshRate); 0069 } 0070 0071 void OutputInterfacePrivate::sendScale(Resource *resource) 0072 { 0073 if (resource->version() >= WL_OUTPUT_SCALE_SINCE_VERSION) { 0074 send_scale(resource->handle, scale); 0075 } 0076 } 0077 0078 static quint32 kwaylandServerTransformToWaylandTransform(OutputTransform transform) 0079 { 0080 switch (transform.kind()) { 0081 case OutputTransform::Normal: 0082 return OutputInterfacePrivate::transform_normal; 0083 case OutputTransform::Rotate90: 0084 return OutputInterfacePrivate::transform_90; 0085 case OutputTransform::Rotate180: 0086 return OutputInterfacePrivate::transform_180; 0087 case OutputTransform::Rotate270: 0088 return OutputInterfacePrivate::transform_270; 0089 case OutputTransform::FlipX: 0090 return OutputInterfacePrivate::transform_flipped; 0091 case OutputTransform::FlipX90: 0092 return OutputInterfacePrivate::transform_flipped_90; 0093 case OutputTransform::FlipX180: 0094 return OutputInterfacePrivate::transform_flipped_180; 0095 case OutputTransform::FlipX270: 0096 return OutputInterfacePrivate::transform_flipped_270; 0097 default: 0098 Q_UNREACHABLE(); 0099 } 0100 } 0101 0102 static quint32 kwaylandServerSubPixelToWaylandSubPixel(Output::SubPixel subPixel) 0103 { 0104 switch (subPixel) { 0105 case Output::SubPixel::Unknown: 0106 return OutputInterfacePrivate::subpixel_unknown; 0107 case Output::SubPixel::None: 0108 return OutputInterfacePrivate::subpixel_none; 0109 case Output::SubPixel::Horizontal_RGB: 0110 return OutputInterfacePrivate::subpixel_horizontal_rgb; 0111 case Output::SubPixel::Horizontal_BGR: 0112 return OutputInterfacePrivate::subpixel_horizontal_bgr; 0113 case Output::SubPixel::Vertical_RGB: 0114 return OutputInterfacePrivate::subpixel_vertical_rgb; 0115 case Output::SubPixel::Vertical_BGR: 0116 return OutputInterfacePrivate::subpixel_vertical_bgr; 0117 default: 0118 Q_UNREACHABLE(); 0119 } 0120 } 0121 0122 void OutputInterfacePrivate::sendGeometry(Resource *resource) 0123 { 0124 send_geometry(resource->handle, 0125 globalPosition.x(), 0126 globalPosition.y(), 0127 physicalSize.width(), 0128 physicalSize.height(), 0129 kwaylandServerSubPixelToWaylandSubPixel(subPixel), 0130 manufacturer, 0131 model, 0132 kwaylandServerTransformToWaylandTransform(transform)); 0133 } 0134 0135 void OutputInterfacePrivate::sendDone(Resource *resource) 0136 { 0137 if (resource->version() >= WL_OUTPUT_DONE_SINCE_VERSION) { 0138 send_done(resource->handle); 0139 } 0140 } 0141 0142 void OutputInterfacePrivate::output_destroy_global() 0143 { 0144 delete q; 0145 } 0146 0147 void OutputInterfacePrivate::output_release(Resource *resource) 0148 { 0149 wl_resource_destroy(resource->handle); 0150 } 0151 0152 void OutputInterfacePrivate::output_bind_resource(Resource *resource) 0153 { 0154 if (isGlobalRemoved()) { 0155 return; // We are waiting for the wl_output global to be destroyed. 0156 } 0157 0158 if (resource->version() >= WL_OUTPUT_NAME_SINCE_VERSION) { 0159 send_name(resource->handle, name); 0160 } 0161 if (resource->version() >= WL_OUTPUT_DESCRIPTION_SINCE_VERSION) { 0162 send_description(resource->handle, description); 0163 } 0164 0165 sendMode(resource); 0166 sendScale(resource); 0167 sendGeometry(resource); 0168 sendDone(resource); 0169 0170 Q_EMIT q->bound(display->getConnection(resource->client()), resource->handle); 0171 } 0172 0173 OutputInterface::OutputInterface(Display *display, Output *handle, QObject *parent) 0174 : QObject(parent) 0175 , d(new OutputInterfacePrivate(display, this, handle)) 0176 { 0177 DisplayPrivate *displayPrivate = DisplayPrivate::get(display); 0178 displayPrivate->outputs.append(this); 0179 0180 // Delay the done event to batch property updates. 0181 d->doneTimer.setSingleShot(true); 0182 d->doneTimer.setInterval(0); 0183 connect(&d->doneTimer, &QTimer::timeout, this, [this]() { 0184 const auto resources = d->resourceMap(); 0185 for (const auto &resource : resources) { 0186 d->sendDone(resource); 0187 } 0188 }); 0189 0190 d->name = handle->name(); 0191 d->description = handle->description(); 0192 d->transform = handle->transform(); 0193 d->manufacturer = handle->manufacturer(); 0194 d->model = handle->model(); 0195 d->physicalSize = handle->physicalSize(); 0196 d->globalPosition = handle->geometry().topLeft(); 0197 d->scale = std::ceil(handle->scale()); 0198 d->modeSize = handle->modeSize(); 0199 d->refreshRate = handle->refreshRate(); 0200 d->subPixel = handle->subPixel(); 0201 0202 connect(handle, &Output::geometryChanged, this, [this]() { 0203 const QPoint position = d->handle->geometry().topLeft(); 0204 if (d->globalPosition != position) { 0205 d->globalPosition = position; 0206 const auto resources = d->resourceMap(); 0207 for (const auto &resource : resources) { 0208 d->sendGeometry(resource); 0209 } 0210 scheduleDone(); 0211 } 0212 }); 0213 0214 connect(handle, &Output::scaleChanged, this, [this]() { 0215 const int scale = std::ceil(d->handle->scale()); 0216 if (d->scale != scale) { 0217 d->scale = scale; 0218 const auto resources = d->resourceMap(); 0219 for (const auto &resource : resources) { 0220 d->sendScale(resource); 0221 } 0222 scheduleDone(); 0223 } 0224 }); 0225 0226 connect(handle, &Output::transformChanged, this, [this]() { 0227 const OutputTransform transform = d->handle->transform(); 0228 if (d->transform != transform) { 0229 d->transform = transform; 0230 const auto resources = d->resourceMap(); 0231 for (const auto &resource : resources) { 0232 d->sendGeometry(resource); 0233 } 0234 scheduleDone(); 0235 } 0236 }); 0237 0238 connect(handle, &Output::currentModeChanged, this, [this]() { 0239 const QSize size = d->handle->modeSize(); 0240 const int refreshRate = d->handle->refreshRate(); 0241 if (d->modeSize != size || d->refreshRate != refreshRate) { 0242 d->modeSize = size; 0243 d->refreshRate = refreshRate; 0244 const auto resources = d->resourceMap(); 0245 for (const auto &resource : resources) { 0246 d->sendMode(resource); 0247 } 0248 scheduleDone(); 0249 } 0250 }); 0251 } 0252 0253 OutputInterface::~OutputInterface() 0254 { 0255 remove(); 0256 } 0257 0258 Display *OutputInterface::display() const 0259 { 0260 return d->display; 0261 } 0262 0263 Output *OutputInterface::handle() const 0264 { 0265 return d->handle; 0266 } 0267 0268 bool OutputInterface::isRemoved() const 0269 { 0270 return d->isGlobalRemoved(); 0271 } 0272 0273 void OutputInterface::remove() 0274 { 0275 if (d->isGlobalRemoved()) { 0276 return; 0277 } 0278 0279 d->doneTimer.stop(); 0280 if (d->handle) { 0281 disconnect(d->handle, nullptr, this, nullptr); 0282 } 0283 0284 if (d->display) { 0285 DisplayPrivate *displayPrivate = DisplayPrivate::get(d->display); 0286 displayPrivate->outputs.removeOne(this); 0287 } 0288 0289 Q_EMIT removed(); 0290 d->globalRemove(); 0291 } 0292 0293 QList<wl_resource *> OutputInterface::clientResources(wl_client *client) const 0294 { 0295 const auto outputResources = d->resourceMap().values(client); 0296 QList<wl_resource *> ret; 0297 ret.reserve(outputResources.count()); 0298 0299 for (OutputInterfacePrivate::Resource *resource : outputResources) { 0300 ret.append(resource->handle); 0301 } 0302 0303 return ret; 0304 } 0305 0306 void OutputInterface::scheduleDone() 0307 { 0308 if (!d->isGlobalRemoved()) { 0309 d->doneTimer.start(); 0310 } 0311 } 0312 0313 void OutputInterface::done(wl_client *client) 0314 { 0315 if (!d->isGlobalRemoved()) { 0316 d->sendDone(d->resourceMap().value(client)); 0317 } 0318 } 0319 0320 OutputInterface *OutputInterface::get(wl_resource *native) 0321 { 0322 if (auto outputPrivate = resource_cast<OutputInterfacePrivate *>(native)) { 0323 return outputPrivate->q; 0324 } 0325 return nullptr; 0326 } 0327 0328 } // namespace KWin 0329 0330 #include "wayland/moc_output.cpp"