Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qohosplatformscreen.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <QtCore/private/qcore_ohos_p.h>
5#include <QtCore/qdebug.h>
6#include <QtCore/qdatetime.h>
7
8#include <qpa/qwindowsysteminterface.h>
9
10#include <qohosdisplayinfo.h>
15#include "qohosjsmain.h"
17#include "render/qohosview.h"
18#include <algorithm>
19#include <multimedia/image_framework/image/pixelmap_native.h>
20#include <optional>
21#include <qarkui/qarkuiutils.h>
22#include <qohosapppermissions_p.h>
23#include <qohosjsutils.h>
24#include <QtGui/private/qohosimageconversions_p.h>
25#include <qohospixelmapconversions.h>
26#include <qohosutils.h>
27#include <render/qwindowproxyregistry.h>
28#include <window_manager/oh_display_capture.h>
29#include <window_manager/oh_display_manager.h>
30
31#include <QtGui/qguiapplication.h>
32#include <QtGui/qwindow.h>
33#include <QtCore/private/qnapi_p.h>
34#include <QtCore/private/qohoslogger_p.h>
35#include <QtGui/private/qhighdpiscaling_p.h>
36#include <QtGui/private/qwindow_p.h>
37
38#include <vector>
39
41
42namespace {
43
44static const int ohosLogicalDpi = 72;
45
47 QOhosJsState &, QOhosDisplayInfo::JsDisplayId displayId)
48{
49 ::OH_PixelmapNative *pixelMapNativePtr;
50
51 QArkUi::callArkUiOrFailOnErrorResult(
52 Q_OHOS_NAMED_FUNC(::OH_NativeDisplayManager_CaptureScreenPixelmap),
53 static_cast<std::uint32_t>(displayId.value()), &pixelMapNativePtr);
54
55 return wrapOhosNativePixelMapPtr(pixelMapNativePtr);
56}
57
59 QOhosJsState &jsState, QOhosDisplayInfo::JsDisplayId displayId,
60 QOhosConsumer<std::shared_ptr<::OH_PixelmapNative>> pixelMapOrNullConsumer)
61{
62 static constexpr const char *ohosCustomScreenCapturePermission =
63 "ohos.permission.CUSTOM_SCREEN_CAPTURE";
64
66 jsState, ohosCustomScreenCapturePermission,
67 [pixelMapOrNullConsumer = std::move(pixelMapOrNullConsumer), displayId](
68 auto &jsState, bool permissionGranted) {
69 if (permissionGranted) {
70 pixelMapOrNullConsumer(captureScreenPixelmap(jsState, displayId));
71 } else {
72 qOhosPrintfError(
73 "%s: %s hasn't been granted by user. Cannot grab window.", Q_FUNC_INFO,
74 ohosCustomScreenCapturePermission);
75 pixelMapOrNullConsumer(nullptr);
76 }
77 });
78}
79
81{
82 auto allWindows = qApp->allWindows();
83 auto windowItByWId = std::find_if(
84 std::begin(allWindows), std::end(allWindows),
85 [&](QWindow *w) {
86 return w->winId() == wId;
87 });
88
89 if (windowItByWId == allWindows.end())
90 return nullptr;
91
92 return *windowItByWId;
93}
94
96 const QSize &windowSnapshotSize, const QRect &windowSpaceCaptureRect)
97{
98 return {
99 windowSpaceCaptureRect.width() < 0
100 ? qMax(0, windowSnapshotSize.width() - windowSpaceCaptureRect.x())
101 : windowSpaceCaptureRect.width(),
102 windowSpaceCaptureRect.height() < 0
103 ? qMax(0, windowSnapshotSize.height() - windowSpaceCaptureRect.y())
104 : windowSpaceCaptureRect.height()
105 };
106}
107
109 const QRect &windowSpaceCaptureRect, const QRect &windowFragmentRect)
110{
111 return {
112 qAbs(windowSpaceCaptureRect.x() - windowFragmentRect.x()),
113 qAbs(windowSpaceCaptureRect.y() - windowFragmentRect.y()),
114 windowFragmentRect.width(),
115 windowFragmentRect.height()
116 };
117}
118
120 const QOhosPlatformScreen *platformScreen, const QPixmap &capturedWindowPixmap,
121 const QRect &windowSpaceCaptureRect)
122{
123 auto resultSize = calculateResultWindowCapturePixmapSize(
124 QHighDpi::fromNativePixels(capturedWindowPixmap.size(), platformScreen),
125 windowSpaceCaptureRect);
126
127 if (resultSize.isEmpty())
128 return {};
129
130 auto windowFragmentRect = capturedWindowPixmap.rect().intersected(
131 QRect(windowSpaceCaptureRect.topLeft(), resultSize));
132
133 if (windowFragmentRect.isEmpty())
134 return QPixmap(resultSize);
135
136 auto resultSpaceWindowFragmentRect =
137 calculateResultPixmapWindowFragmentRect(windowSpaceCaptureRect, windowFragmentRect);
138
139 QPixmap result(resultSize);
140 QPainter p(&result);
141 p.drawPixmap(
142 QHighDpi::toNativePixels(resultSpaceWindowFragmentRect, platformScreen),
143 capturedWindowPixmap,
144 QHighDpi::toNativePixels(windowFragmentRect, platformScreen));
145
146 return result;
147}
148
150{
151 using JsDisplayOrientation = QOhosDisplayInfo::JsDisplayOrientation;
152
153 switch (jsDisplayOrientation) {
154 case JsDisplayOrientation::PORTRAIT:
155 return Qt::ScreenOrientation::PortraitOrientation;
156 case JsDisplayOrientation::LANDSCAPE:
157 return Qt::ScreenOrientation::LandscapeOrientation;
158 case JsDisplayOrientation::PORTRAIT_INVERTED:
159 return Qt::ScreenOrientation::InvertedPortraitOrientation;
160 case JsDisplayOrientation::LANDSCAPE_INVERTED:
161 return Qt::ScreenOrientation::InvertedLandscapeOrientation;
162 }
163
164 return std::nullopt;
165}
166
167// log2 index, one step per 90 degrees, as in QPlatformScreen::angleBetween().
168int orientationToIndex(Qt::ScreenOrientation orientation)
169{
170 switch (orientation) {
171 case Qt::ScreenOrientation::PortraitOrientation:
172 return 0;
173 case Qt::ScreenOrientation::LandscapeOrientation:
174 return 1;
175 case Qt::ScreenOrientation::InvertedPortraitOrientation:
176 return 2;
177 case Qt::ScreenOrientation::InvertedLandscapeOrientation:
178 return 3;
179 default:
180 return -1;
181 }
182}
183
185tryMapNativeDisplayOrientationToQt(NativeDisplayManager_Orientation nativeOrientation)
186{
187 switch (nativeOrientation) {
188 case DISPLAY_MANAGER_PORTRAIT:
189 return Qt::ScreenOrientation::PortraitOrientation;
190 case DISPLAY_MANAGER_LANDSCAPE:
191 return Qt::ScreenOrientation::LandscapeOrientation;
192 case DISPLAY_MANAGER_PORTRAIT_INVERTED:
193 return Qt::ScreenOrientation::InvertedPortraitOrientation;
194 case DISPLAY_MANAGER_LANDSCAPE_INVERTED:
195 return Qt::ScreenOrientation::InvertedLandscapeOrientation;
196 case DISPLAY_MANAGER_UNKNOWN:
197 break;
198 }
199
200 return std::nullopt;
201}
202
204{
205 switch (((index % 4) + 4) % 4) {
206 case 0:
207 return Qt::ScreenOrientation::PortraitOrientation;
208 case 1:
209 return Qt::ScreenOrientation::LandscapeOrientation;
210 case 2:
211 return Qt::ScreenOrientation::InvertedPortraitOrientation;
212 default:
213 return Qt::ScreenOrientation::InvertedLandscapeOrientation;
214 }
215}
216
217}
218
219QOhosPlatformScreen::QOhosPlatformScreen(const QOhosDisplayInfo &displayInfo, QOhosSupplier<std::vector<QOhosPlatformScreen *>> platformScreenListSupplier)
220 : QObject()
221 , QPlatformScreen()
222 , m_format(QImage::Format_ARGB32_Premultiplied)
223 , m_depth(32)
224 , m_platformCursor(new QOhosPlatformCursor())
225 , m_displayInfo(displayInfo)
226 , m_availableGeometry(getAvailableArea())
227 , m_platformScreenListSupplier(std::move(platformScreenListSupplier))
228{
229 auto __dbg = make_QCScopedDebug("QOhosPlatformScreen::QOhosPlatformScreen");
230}
231
233
234QOhosPlatformScreen *QOhosPlatformScreen::fromQScreen(QScreen *screen)
235{
236 auto *platformScreen = screen->handle();
237 if (Q_UNLIKELY(platformScreen == nullptr))
238 qOhosReportFatalErrorAndAbort("QScreen::handle() returned null");
239 return static_cast<QOhosPlatformScreen *>(platformScreen);
240}
241
242QWindow *QOhosPlatformScreen::topLevelAt(const QPoint &p) const
243{
244 for (auto *platformScreen : virtualSiblings()) {
245 auto *ohosPlatformScreen = static_cast<QOhosPlatformScreen *>(platformScreen);
246
247 auto windowIds = QOhosWindowProxy::queryWindowIdsByCoordinate(ohosPlatformScreen->m_displayInfo.id, p);
248 if (!windowIds.empty())
249 return QWindowProxyRegistry::instance().findQWindowByJsWindowIdOrNull(windowIds.front());
250 }
251
252 return nullptr;
253}
254
256{
257 return m_platformCursor.data();
258}
259
261{
262 auto geometryChanged = displayInfo.displayGeometryPixels() != m_displayInfo.displayGeometryPixels();
263 auto logicalDpiChanged = displayInfo.densityDPI != m_displayInfo.densityDPI;
264 const Qt::ScreenOrientation previousOrientation = orientation();
265
266 QVector<QPair<QWindow *, std::optional<QRect>>> windowRectPairs;
267
268 if (logicalDpiChanged) {
269 for (auto *window : qGuiApp->allWindows()) {
270 auto *platformWindow = QOhosPlatformWindow::fromQWindowOrNull(window);
271 if (platformWindow == nullptr || platformWindow->screen() != this || !window->isVisible())
272 continue;
273
274 auto *ownedView = platformWindow->ownedViewOrNull();
275 if (ownedView == nullptr)
276 continue;
277
278 windowRectPairs.push_back({
279 window,
280 ownedView->viewType() == QOhosView::ViewType::EmbeddedWindow
281 ? std::optional(window->geometry())
282 : std::nullopt});
283 }
284 }
285
286 m_displayInfo = displayInfo;
287
288 if (geometryChanged)
289 QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(), availableGeometry());
290
291 if (orientation() != previousOrientation)
292 QWindowSystemInterface::handleScreenOrientationChange(QPlatformScreen::screen(), orientation());
293
294 if (logicalDpiChanged) {
295 auto ldpi = logicalDpi();
296 QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(
297 screen(), ldpi.first, ldpi.second);
298
299 QWindowSystemInterface::flushWindowSystemEvents();
300
301 for (const auto &windowRectPair : windowRectPairs) {
302 auto *qWindow = windowRectPair.first;
303 auto *platformWindow = QOhosPlatformWindow::fromQWindow(qWindow);
304 if (windowRectPair.second.has_value())
305 qWindow->setGeometry(windowRectPair.second.value());
306 platformWindow->handleDpiChange();
307 }
308 }
309}
310
312{
313 qreal lDpi = m_displayInfo.densityPixels * ohosLogicalDpi;
314 return QDpi(lDpi, lDpi);
315}
316
318{
319 // densityPixels is the scaling coefficient between the virtual pixel
320 // and the physical pixels.
321 return m_displayInfo.densityPixels;
322}
323
325{
326 return m_displayInfo.refreshRate;
327}
328
333
335{
336 return
337 qAndThen(m_displayInfo.orientation, tryMapJsDisplayOrientationToQt)
338 .value_or(Qt::ScreenOrientation::PrimaryOrientation);
339}
340
342{
343 // No direct query exists, so derive it from this display's orientation
344 // stepped back by its rotation. A hardware constant, so cache it.
345 if (m_nativeOrientation)
346 return *m_nativeOrientation;
347
348 NativeDisplayManager_DisplayInfo *displayInfo = nullptr;
349 const auto displayId = static_cast<uint32_t>(m_displayInfo.id.value());
350 if (OH_NativeDisplayManager_CreateDisplayById(displayId, &displayInfo) != DISPLAY_MANAGER_OK
351 || displayInfo == nullptr) {
352 return Qt::ScreenOrientation::PrimaryOrientation;
353 }
354
355 const NativeDisplayManager_Orientation nativeOrientation = displayInfo->orientation;
356 const NativeDisplayManager_Rotation rotation = displayInfo->rotation;
357 OH_NativeDisplayManager_DestroyDisplay(displayInfo);
358
359 const auto current = tryMapNativeDisplayOrientationToQt(nativeOrientation);
360 if (!current)
361 return Qt::ScreenOrientation::PrimaryOrientation;
362
363 const int currentIndex = orientationToIndex(*current);
364 if (currentIndex < 0)
365 return Qt::ScreenOrientation::PrimaryOrientation;
366
367 m_nativeOrientation = orientationFromIndex(currentIndex - static_cast<int>(rotation));
368 return *m_nativeOrientation;
369}
370
371QRect QOhosPlatformScreen::getAvailableArea() const
372{
373 return QOhosJsThreadGateway::evalWithPromise<QRect>(
374 [&](QOhosJsState &jsState, QOhosTaskPromise<QRect> evalPromise) {
375
376 QNapi::Object display;
377 try {
378 display = jsState.eval<QNapi::Object>(
379 "@ohos.display.getDisplayByIdSync(*)", {m_displayInfo.id.value()});
380 } catch (const Napi::Error &error) {
381 qOhosPrintfError(
382 "%s: getDisplayByIdSync(%f) failed with error: %s",
383 Q_FUNC_INFO, m_displayInfo.id.value(), error.Message().c_str());
384 evalPromise(QRect());
385 return;
386 }
387
388 auto thenCatchPromises = std::move(evalPromise).makeThenCatchBranches(Q_FUNC_INFO);
389 display.evalToPromiseOrRejectOnThrow("getAvailableArea()")
390 .onThen(
391 [thenPromise = std::move(thenCatchPromises.first)](const QOhosCallbackInfo &cbInfo) {
392 auto availableArea = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
393 thenPromise(
394 QRect(
395 availableArea.get<QNapi::Number>("left"),
396 availableArea.get<QNapi::Number>("top"),
397 availableArea.get<QNapi::Number>("width"),
398 availableArea.get<QNapi::Number>("height")));
399 })
400 .onCatch(
401 [catchPromise = std::move(thenCatchPromises.second)](const QOhosCallbackInfo &cbInfo) {
402 QtOhos::logJsCallbackError(cbInfo, "Error occurred in JS getAvailableArea()");
403 catchPromise(QRect());
404 });
405 },
406 Q_FUNC_INFO);
407}
408
409void QOhosPlatformScreen::releaseSurface()
410{
411}
412
414{
415 return m_displayInfo;
416}
417
419{
420 return m_displayInfo.id.value();
421}
422
424{
425 return m_displayInfo.displayGeometryPixels();
426}
427
429{
430 auto screenGeometry = geometry();
431 return !m_availableGeometry.isEmpty()
432 ? m_availableGeometry.translated(screenGeometry.topLeft())
433 : screenGeometry;
434}
435
437{
438 return m_depth;
439}
440
442{
443 return m_format;
444}
445
447{
448 return m_displayInfo.physicalSize();
449}
450
452{
453 if (rect != m_availableGeometry) {
454 m_availableGeometry = rect;
455 QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(), availableGeometry());
456 }
457}
458
460{
461 if (!m_displayInfo.isDisplayMainOrExtended())
462 return QPlatformScreen::virtualSiblings();
463
464 QList<QPlatformScreen *> result;
465 auto allPlatformScreens = m_platformScreenListSupplier();
466 std::copy_if(
467 allPlatformScreens.begin(), allPlatformScreens.end(),
468 std::back_inserter(result),
469 [](QOhosPlatformScreen *ohosPlatformScreen) {
470 return ohosPlatformScreen->displayInfo().isDisplayMainOrExtended();
471 });
472
473 return result;
474}
475
476QPixmap QOhosPlatformScreen::grabWindow(WId wId, int x, int y, int width, int height) const
477{
478 auto captureRect = QRect(x, y, width, height);
479
480 if (wId != 0) {
481 auto *window = tryFindWindowByWIdOrNull(wId);
482 if (window == nullptr) {
483 qOhosPrintfError(
484 "%s: Cannot find window with the given WId: %lld.", Q_FUNC_INFO, wId);
485 return {};
486 }
487 return grabWindowFromCapturedScreenPixmap(
488 this, QOhosPlatformWindow::fromQWindow(window)->makeSnapshot(), captureRect);
489 }
490
491 auto capturedScreenPixmap = QOhosJsThreadGateway::evalWithPromise<QPixmap>(
492 [displayId = m_displayInfo.id](
493 QOhosJsState &jsState, QOhosTaskPromise<QPixmap> evalPromise) {
494 auto sharedEvalPromise = QtOhos::moveToSharedPtr(std::move(evalPromise).makeChained(Q_FUNC_INFO));
495 tryCaptureScreenPixelmapWithPermissionCheck(
496 jsState, displayId,
497 [sharedEvalPromise](
498 std::shared_ptr<::OH_PixelmapNative> optPixelMap) {
499 (*sharedEvalPromise)(
500 optPixelMap
501 ? QPixmap::fromImage(createQImageFromNativePixelMap(optPixelMap.get()))
502 : QPixmap());
503 });
504 },
505 Q_FUNC_INFO);
506
507 return capturedScreenPixmap.copy(captureRect);
508}
509
511{
512 return m_displayInfo.name;
513}
514
515QT_END_NAMESPACE
QPixmap grabWindow(WId wId, int x, int y, int width, int height) const override
This function is called when Qt needs to be able to grab the content of a window.
double displayId() const override
QWindow * topLevelAt(const QPoint &p) const override
Return the given top level window for a given position.
Qt::ScreenOrientation orientation() const override
Reimplement this function in subclass to return the current orientation of the screen,...
QRect availableGeometry() const override
Reimplement in subclass to return the pixel geometry of the available space This normally is the desk...
QSizeF physicalSize() const override
Reimplement this function in subclass to return the physical size of the screen, in millimeters.
qreal refreshRate() const override
Reimplement this function in subclass to return the vertical refresh rate of the screen,...
QImage::Format format() const override
Reimplement in subclass to return the image format which corresponds to the screen format.
Qt::ScreenOrientation nativeOrientation() const override
Reimplement this function in subclass to return the native orientation of the screen,...
const QOhosDisplayInfo & displayInfo() const
QList< QPlatformScreen * > virtualSiblings() const override
Returns a list of all the platform screens that are part of the same virtual desktop.
QString name() const override
QPlatformCursor * cursor() const override
Reimplement this function in subclass to return the cursor of the screen.
qreal pixelScalingCoefficient() const
void setAvailableGeometry(const QRect &rect)
void setDisplayInfo(const QOhosDisplayInfo &displayInfo)
QRect geometry() const override
Reimplement in subclass to return the pixel geometry of the screen.
QDpi logicalDpi() const override
Reimplement this function in subclass to return the logical horizontal and vertical dots per inch met...
int depth() const override
Reimplement in subclass to return current depth of the screen.
QDpi logicalBaseDpi() const override
Reimplement to return the base logical DPI for the platform.
void checkAppPermissionGrantedWithConsumer(QOhosJsState &jsState, const std::string &permissionName, QOhosConsumer< QOhosJsState &, bool > resultConsumer)
Combined button and popup list for selecting options.
Qt::ScreenOrientation orientationFromIndex(int index)
QWindow * tryFindWindowByWIdOrNull(WId wId)
QRect calculateResultPixmapWindowFragmentRect(const QRect &windowSpaceCaptureRect, const QRect &windowFragmentRect)
std::optional< Qt::ScreenOrientation > tryMapJsDisplayOrientationToQt(QOhosDisplayInfo::JsDisplayOrientation jsDisplayOrientation)
std::shared_ptr<::OH_PixelmapNative > captureScreenPixelmap(QOhosJsState &, QOhosDisplayInfo::JsDisplayId displayId)
static const int ohosLogicalDpi
void tryCaptureScreenPixelmapWithPermissionCheck(QOhosJsState &jsState, QOhosDisplayInfo::JsDisplayId displayId, QOhosConsumer< std::shared_ptr<::OH_PixelmapNative > > pixelMapOrNullConsumer)
std::optional< Qt::ScreenOrientation > tryMapNativeDisplayOrientationToQt(NativeDisplayManager_Orientation nativeOrientation)
int orientationToIndex(Qt::ScreenOrientation orientation)
QSize calculateResultWindowCapturePixmapSize(const QSize &windowSnapshotSize, const QRect &windowSpaceCaptureRect)
QPixmap grabWindowFromCapturedScreenPixmap(const QOhosPlatformScreen *platformScreen, const QPixmap &capturedWindowPixmap, const QRect &windowSpaceCaptureRect)
bool isDisplayMainOrExtended() const
QtOhosQpa::enums::ohos::display::Orientation JsDisplayOrientation