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
qohosscreenmanager.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 <qohosscreenmanager.h>
5#include <cstdint>
6#include <memory>
7#include <utility>
8#include <qohosdisplayinfo.h>
9#include <qohosjsutils.h>
10#include <qohosplatformscreen.h>
11#include <qpa/qwindowsysteminterface.h>
12
14
15namespace
16{
17
19{
20 auto primaryDisplay = jsState.eval<QNapi::Object>("@ohos.display.getPrimaryDisplaySync()");
21 return QOhosDisplayInfo::makeFromOhosDisplayObject(jsState, primaryDisplay);
22}
23
25{
26 QOhosOptional<QNapi::Object> result;
27 try {
28 result = jsState.eval<QNapi::Object>(
29 "@ohos.display.getDisplayByIdSync(*)", {displayId.value()});
30 } catch (const Napi::Error &error) {
31 qOhosPrintfError("%s: Failed to retrieve display with id: %f", Q_FUNC_INFO, displayId.value());
32 }
33 return result;
34}
35
36const std::string displayCallbackNameChangeEvent = "change";
37const std::string displayCallbackNameAddEvent = "add";
38const std::string displayCallbackNameRemoveEvent = "remove";
39
40QDebug &operator<<(QDebug &outputStream, const QOhosDisplayInfo &displayInfo)
41{
42 outputStream << "id:" << displayInfo.id.value()
43 << "name:" << displayInfo.name
44 << "sizePixels:" << displayInfo.sizePixels
45 << "densityDPI:" << displayInfo.densityDPI
46 << "densityPixels:" << displayInfo.densityPixels
47 << "densityScaled:" << displayInfo.densityScaled
48 << "dpi:" << displayInfo.dpi;
49 return outputStream;
50}
51
52bool shouldIgnoreDisplay(const QOhosDisplayInfo &displayInfo)
53{
54 constexpr int virtualDisplayBaseId = 1000;
55 static const QOhosDisplayInfo::DisplaySourceMode sourceModesToIgnore[] = {
59 };
60
61 const auto *sourceModeToIgnoreIter = std::find(
62 std::begin(sourceModesToIgnore), std::end(sourceModesToIgnore),
63 displayInfo.sourceMode);
64 bool ignoreBySoureMode = sourceModeToIgnoreIter != std::end(sourceModesToIgnore);
65
66 return displayInfo.sourceMode.hasValue()
67 ? ignoreBySoureMode
68 : displayInfo.id.value() >= virtualDisplayBaseId;
69}
70
71}
72
73QOhosScreenManager::QOhosScreenManager()
74{
75 QOhosDisplayInfo primaryDisplayInfo;
76 auto selfRef = QtOhos::QThreadSafeRef<QOhosScreenManager>(this);
77
78 auto displayInfos = QtOhos::evalInJsThreadWithConsumer<std::vector<QOhosDisplayInfo>>(
79 [](QtOhos::JsState &jsState, auto resultConsumer) {
80 jsState.eval<QNapi::Promise>("@ohos.display.getAllDisplay()")
81 .withContext(std::move(resultConsumer))
82 .onThenWithContext(
83 [](const QtOhos::CallbackInfo &cbInfo, auto &resultConsumer) {
84 auto displayObjectsArray = cbInfo.getFirstArg<QNapi::Array>(Q_FUNC_INFO);
85 resultConsumer(
86 QNapi::getArrayElements<std::vector<QOhosDisplayInfo>, QNapi::Object>(
87 displayObjectsArray,
88 [&](QNapi::Object jsDisplay) {
89 return QOhosDisplayInfo::makeFromOhosDisplayObject(cbInfo.jsState(), jsDisplay);
90 }));
91 })
92 .onCatchWithContext(
93 [](auto &resultConsumer) {
94 qOhosPrintfError("%s: Failed to enumerate displays", Q_FUNC_INFO);
95 resultConsumer({});
96 });
97 });
98
100 primaryDisplayInfo = getDisplayInfoForPrimaryDisplay(jsState);
101
102 m_jsScopeData = QtOhos::makeProxyWithJsThreadDeleter(
103 JsScopeData::create(
104 jsState, JsScopeData::CreateInfo{
105 .displayInfos = displayInfos,
106 .displayChangedCb = [selfRef](QtOhos::JsState &jsState, JsDisplayId changedDisplayId) {
107 auto displayObject = tryGetDisplayById(jsState, changedDisplayId);
108 if (!displayObject.hasValue()) {
109 qOhosPrintfError(
110 "%s: Failed to retrieve display with id: %f during display changed callback. Ignoring the event...",
111 Q_FUNC_INFO, changedDisplayId.value());
112 return;
113 }
114
115 auto displayInfo = QOhosDisplayInfo::makeFromOhosDisplayObject(jsState, displayObject.value());
116 selfRef.visitInQtThreadIfAlive([displayInfo](QOhosScreenManager &self) {
117 self.handleDisplayChangedCallbackInQtThread(displayInfo);
118 });
119 },
120 .displayAddedCb = [selfRef](QtOhos::JsState &jsState, JsDisplayId displayId) {
121 auto displayObject = tryGetDisplayById(jsState, displayId);
122 if (!displayObject.hasValue()) {
123 qOhosPrintfError(
124 "%s: Failed to retrieve display with id: %f during display added callback. Ignoring the event ...",
125 Q_FUNC_INFO, displayId.value());
126 return;
127 }
128
129 auto displayInfo = QOhosDisplayInfo::makeFromOhosDisplayObject(jsState, displayObject.value());
130 selfRef.visitInQtThreadIfAlive([displayInfo](QOhosScreenManager &self) {
131 self.handleDisplayAdded(displayInfo);
132 });
133 },
134 .displayRemovedCb = [selfRef](QtOhos::JsState &, JsDisplayId displayId) {
135 selfRef.visitInQtThreadIfAlive([displayId](QOhosScreenManager &self) {
136 self.handleDisplayRemoved(displayId);
137 });
138 },
139 .displayAvailableAreaChangedCb = [selfRef](QtOhos::JsState &, JsDisplayId displayId, QRectF availableArea) {
140 selfRef.visitInQtThreadIfAlive([displayId, availableArea](QOhosScreenManager &self) {
141 self.handleDisplayAvailableAreaChanged(displayId, availableArea);
142 });
143 },
144 }));
145 });
146
147 m_primaryDisplayId = primaryDisplayInfo.id;
148 for (const auto &displayInfo : m_jsScopeData->getRegisteredDisplayInfos())
149 addScreen(displayInfo);
150}
151
152void QOhosScreenManager::handleDisplayChangedCallbackInQtThread(const QOhosDisplayInfo &displayInfo)
153{
154 if (shouldIgnoreDisplay(displayInfo))
155 return;
156 auto *platformScreen = platformScreenForDisplayIdOrNull(displayInfo.id);
157 if (platformScreen == nullptr) {
158 qCWarning(QtForOhos) << "Received display 'change' event for unknown display with id:"
159 << displayInfo.id.value();
160 return;
161 }
162
163 qCDebug(QtForOhos) << "DisplayChanged:" << displayInfo;
164
165 platformScreen->setDisplayInfo(displayInfo);
166 updatePrimaryPlatformScreenIfNeeded();
167}
168
169void QOhosScreenManager::JsScopeData::registerDisplayCallbackListener(
170 QNapi::Object displayModule, const std::string &eventName,
171 QOhosConsumer<QtOhos::JsState &, JsDisplayId> handleFunction)
172{
173 m_destroyNotifiers.push_back(
174 QtOhos::registerOnOffMethodsBasedEventHandler(
175 displayModule, eventName,
176 [handleFunction = std::move(handleFunction)](const QtOhos::CallbackInfo &cbInfo) {
177 auto changedDisplayIdValue = cbInfo.getFirstArg<QNapi::Number>(Q_FUNC_INFO);
178 auto changedDisplayId = JsDisplayId{changedDisplayIdValue};
179 handleFunction(cbInfo.jsState(), changedDisplayId);
180 }));
181}
182
183bool QOhosScreenManager::JsScopeData::tryRegisterDisplay(
184 QtOhos::JsState &jsState, JsDisplayId displayId)
185{
186 auto optDisplay = tryGetDisplayById(jsState, displayId);
187 if (!optDisplay.hasValue()) {
188 qOhosPrintfError(
189 "%s: Display with id: %f went missing during its registration.",
190 Q_FUNC_INFO, displayId.value());
191 return false;
192 }
193
194 auto availableAreaChangeHandle = QtOhos::registerOnOffMethodsBasedEventHandler(
195 optDisplay.value(),
196 "availableAreaChange",
197 [this, displayId](const QtOhos::CallbackInfo &cbInfo) {
198 auto availableArea = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
199 auto availableAreaQRectF = QRectF(
200 availableArea.get<QNapi::Number>("left"),
201 availableArea.get<QNapi::Number>("top"),
202 availableArea.get<QNapi::Number>("width"),
203 availableArea.get<QNapi::Number>("height"));
204 m_availableAreaChangedCb(cbInfo.jsState(), displayId, availableAreaQRectF);
205 });
206
207 bool added = false;
208 std::tie(std::ignore, added) = m_perDisplayDestroyNotifiers.insert(
209 std::make_pair(displayId, std::move(availableAreaChangeHandle)));
210 if (!added)
211 qOhosPrintfError("Duplicate display added event for display id: %f", displayId.value());
212
213 return added;
214}
215
216void QOhosScreenManager::JsScopeData::unregisterDisplay(JsDisplayId displayId)
217{
218 if (m_perDisplayDestroyNotifiers.erase(displayId) == 0)
219 qOhosPrintfError("Attempted to erase unknown display with id: %f", displayId.value());
220}
221
222
223QOhosScreenManager::QOhosPlatformScreenHolder::QOhosPlatformScreenHolder(
224 const QOhosDisplayInfo &displayInfo)
225{
226 auto screen = std::make_unique<QOhosPlatformScreen>(displayInfo);
227 m_platformScreen = screen.get();
228 QWindowSystemInterface::handleScreenAdded(screen.release());
229}
230
231QOhosPlatformScreen *QOhosScreenManager::QOhosPlatformScreenHolder::platformScreenOrNull() const
232{
233 return m_platformScreen;
234}
235
236QOhosScreenManager::QOhosPlatformScreenHolder::~QOhosPlatformScreenHolder()
237{
238 if (m_platformScreen != nullptr)
239 QWindowSystemInterface::handleScreenRemoved(m_platformScreen);
240}
241
242QOhosScreenManager::QOhosPlatformScreenHolder *
243QOhosScreenManager::platformScreenHolderForDisplayIdOrNull(JsDisplayId displayId) const
244{
245 auto it = m_displays.find(displayId);
246 return it != m_displays.end()
247 ? it->second.get()
248 : nullptr;
249}
250
252{
253 auto *platformScreenHolder = platformScreenHolderForDisplayIdOrNull(displayId);
254 return platformScreenHolder != nullptr
255 ? platformScreenHolder->platformScreenOrNull()
256 : nullptr;
257}
258
260{
261 auto *platformScreen = platformScreenForDisplayIdOrNull(displayId);
262 if (platformScreen == nullptr)
263 qOhosReportFatalErrorAndAbort("Failed to find platform screen for display id: %f", displayId.value());
264 return platformScreen;
265}
266
267void QOhosScreenManager::addScreen(QOhosDisplayInfo displayInfo)
268{
269 auto it = m_displays.find(displayInfo.id);
270 if (it != m_displays.end()) {
271 qCWarning(QtForOhos) << "Attemting to add display with non unique id:" << displayInfo.id.value()
272 << "previous display with that id will be removed";
273 }
274 m_displays[displayInfo.id] = std::make_unique<QOhosPlatformScreenHolder>(displayInfo);
275}
276
277void QOhosScreenManager::removeScreenIfExists(JsDisplayId displayId)
278{
279 std::ignore = m_displays.erase(displayId);
280}
281
282std::shared_ptr<QOhosScreenManager::JsScopeData> QOhosScreenManager::JsScopeData::create(
283 QtOhos::JsState &jsState, CreateInfo createInfo)
284{
285 auto jsScopeData = std::shared_ptr<JsScopeData>(new JsScopeData(jsState));
286 jsScopeData->initialize(jsState, std::move(createInfo));
287 return jsScopeData;
288}
289
290std::vector<QOhosDisplayInfo> QOhosScreenManager::JsScopeData::getRegisteredDisplayInfos()
291{
292 return m_registeredDisplayInfos;
293}
294
295QOhosScreenManager::JsScopeData::JsScopeData(QtOhos::JsState &)
296{
297}
298
299void QOhosScreenManager::JsScopeData::initialize(QtOhos::JsState &jsState, CreateInfo createInfo)
300{
301 for (const auto &displayInfo : createInfo.displayInfos) {
302 if (!shouldIgnoreDisplay(displayInfo) && tryRegisterDisplay(jsState, displayInfo.id)) {
303 m_registeredDisplayInfos.push_back(displayInfo);
304 } else {
305 qOhosPrintfError(
306 "%s: Failed to register display (%f) during display initialization.",
307 Q_FUNC_INFO,
308 displayInfo.id.value());
309 }
310 }
311
312 auto displayModule = jsState.eval<QNapi::Object>("@ohos.display");
313 m_availableAreaChangedCb = std::move(createInfo.displayAvailableAreaChangedCb);
314 registerDisplayCallbackListener(
315 displayModule,
316 displayCallbackNameChangeEvent,
317 std::move(createInfo.displayChangedCb));
318 registerDisplayCallbackListener(
319 displayModule,
320 displayCallbackNameAddEvent,
321 [this, displayAddedCb = std::move(createInfo.displayAddedCb)](QtOhos::JsState &jsState, JsDisplayId displayId) {
322 if (tryRegisterDisplay(jsState, displayId)) {
323 displayAddedCb(jsState, displayId);
324 } else {
325 qOhosPrintfError(
326 "%s: Failed to register display (%f) during display added callback.",
327 Q_FUNC_INFO,
328 displayId.value());
329 }
330 });
331 registerDisplayCallbackListener(
332 displayModule,
333 displayCallbackNameRemoveEvent,
334 [this, displayRemovedCb = std::move(createInfo.displayRemovedCb)](QtOhos::JsState &jsState, JsDisplayId displayId) {
335 unregisterDisplay(displayId);
336 displayRemovedCb(jsState, displayId);
337 });
338}
339
340void QOhosScreenManager::handleDisplayAdded(const QOhosDisplayInfo &displayInfo)
341{
342 if (shouldIgnoreDisplay(displayInfo)) {
343 qCWarning(QtForOhos) << "Display add ignored (based on display id):" << displayInfo.id.value();
344 return;
345 }
346 qCWarning(QtForOhos) << "Display added:" << displayInfo;
347 addScreen(displayInfo);
348 updatePrimaryPlatformScreenIfNeeded();
349}
350
351void QOhosScreenManager::handleDisplayRemoved(JsDisplayId displayId)
352{
353 qCWarning(QtForOhos) << "Display removed: id:" << displayId.value();
354 updatePrimaryPlatformScreenIfNeeded();
355
356 if (m_primaryDisplayId == displayId)
357 qOhosReportFatalErrorAndAbort("Primary display removed - this is not supported");
358
359 removeScreenIfExists(displayId);
360}
361
362void QOhosScreenManager::handleDisplayAvailableAreaChanged(
363 JsDisplayId jsDisplayId, QRectF availableArea)
364{
365 auto displaysIt = m_displays.find(jsDisplayId);
366 if (displaysIt != m_displays.end()) {
367 auto *platformScreen = displaysIt->second->platformScreenOrNull();
368 if (platformScreen != nullptr)
369 platformScreen->setAvailableGeometry(availableArea.toRect());
370 }
371}
372
373void QOhosScreenManager::updatePrimaryPlatformScreenIfNeeded()
374{
375 auto primaryDisplayInfo = QtOhos::evalInJsThread(&getDisplayInfoForPrimaryDisplay);
376
377 if (m_primaryDisplayId == primaryDisplayInfo.id)
378 return;
379
380 qCDebug(QtForOhos)
381 << "Primary display changed from:" << m_primaryDisplayId.value()
382 << "to:" << primaryDisplayInfo.id.value();
383
384 auto *primaryPlatformScreen = platformScreenForDisplayIdOrNull(primaryDisplayInfo.id);
385 if (primaryPlatformScreen == nullptr) {
386 qCWarning(QtForOhos)
387 << Q_FUNC_INFO
388 << "Primary screen changed, but it was not registered previously"
389 << "adding the screen with display id:" << primaryDisplayInfo.id.value();
390 addScreen(primaryDisplayInfo);
391 primaryPlatformScreen = platformScreenForDisplayIdOrFail(primaryDisplayInfo.id);
392 }
393
394 m_primaryDisplayId = primaryDisplayInfo.id;
395 QWindowSystemInterface::handlePrimaryScreenChanged(primaryPlatformScreen);
396}
397
398QT_END_NAMESPACE
QOhosPlatformScreen * platformScreenForDisplayIdOrNull(QOhosDisplayInfo::JsDisplayId displayId) const
QOhosPlatformScreen * platformScreenForDisplayIdOrFail(QOhosDisplayInfo::JsDisplayId displayId) const
Combined button and popup list for selecting options.
bool shouldIgnoreDisplay(const QOhosDisplayInfo &displayInfo)
QOhosDisplayInfo getDisplayInfoForPrimaryDisplay(QtOhos::JsState &jsState)
const std::string displayCallbackNameAddEvent
const std::string displayCallbackNameRemoveEvent
QOhosOptional< QNapi::Object > tryGetDisplayById(QtOhos::JsState &jsState, QOhosDisplayInfo::JsDisplayId displayId)
QDebug & operator<<(QDebug &outputStream, const QOhosDisplayInfo &displayInfo)
const std::string displayCallbackNameChangeEvent
void runInJsThreadAndWait(const std::function< void(JsState &)> &task)