< Summary

Information
Class: /home/runner/work/TauriQt/TauriQt/tauri-runtime-qt/src/qt_screen.cpp
Assembly: Default
File(s): /home/runner/work/TauriQt/TauriQt/tauri-runtime-qt/src/qt_screen.cpp
Line coverage
62%
Covered lines: 72
Uncovered lines: 44
Coverable lines: 116
Total lines: 203
Line coverage: 62%
Branch coverage
40%
Covered branches: 17
Total branches: 42
Branch coverage: 40.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

File(s)

/home/runner/work/TauriQt/TauriQt/tauri-runtime-qt/src/qt_screen.cpp

#LineLine coverage
 1// SPDX-FileCopyrightText: 2026 Alper Çelik
 2// SPDX-License-Identifier: MIT OR Apache-2.0
 3
 4//! Screen/monitor/cursor provider for the Qt runtime.
 5//!
 6//! Reads Qt's own screen model (`QGuiApplication`, `QScreen`) and the cursor
 7//! position, so the other modules never touch them. Qt reports geometry in
 8//! device-independent (logical) global pixels; converting to physical pixels
 9//! with `scale_factor` is the Rust side's job.
 10//!
 11//! Every entry point may be called from any thread: work that has to run on the
 12//! GUI thread is marshalled there (see `on_gui_thread`).
 13
 14#include "qt_screen.hpp"
 15
 16// cxx-generated header of this bridge: declares the entry points and defines
 17// the shared structs they return.
 18#include "tauri-runtime-qt/src/bridge/screen.rs.h"
 19// `qt_window_winid()` (registry handle -> native window id), used by
 20// `qt_screen_for_window()` to find the window behind a handle. Included instead
 21// of hand-declaring it, so the linkage cxx expects is the one cxx generates.
 22#include "tauri-runtime-qt/src/bridge/window.rs.h"
 23
 24#include <QtCore/QCoreApplication>
 25#include <QtCore/QList>
 26#include <QtCore/QMetaObject>
 27#include <QtCore/QPoint>
 28#include <QtCore/QString>
 29#include <QtCore/QThread>
 30#include <QtCore/QtGlobal>
 31#include <QtGui/QCursor>
 32#include <QtGui/QGuiApplication>
 33#include <QtGui/QScreen>
 34#include <QtGui/QWindow>
 35#include <QtWidgets/QApplication>
 36#include <QtWidgets/QWidget>
 37
 38#include <cstdint>
 39#include <string>
 40#include <type_traits>
 41#include <utility>
 42
 43namespace {
 44
 45/// The `QGuiApplication` of this process, or `nullptr` when there is none (or
 46/// only a plain `QCoreApplication`, which has no screens).
 10547QGuiApplication* gui_application() {
 10548    return qobject_cast<QGuiApplication*>(QCoreApplication::instance());
 10549}
 50
 51/// Runs `body` on the Qt GUI thread and returns its value.
 52///
 53/// On the GUI thread (the common case) `body` runs inline. From another thread
 54/// the call is queued to the application object — which lives on the GUI thread
 55/// — as a blocking queued invocation, so the caller gets the real value and the
 56/// Qt platform classes are only ever touched from their own thread.
 57///
 58/// A blocking invocation waits for the GUI thread to process events; it must
 59/// therefore not be used from inside a GUI-thread callback that blocks the
 60/// event loop on the calling thread.
 61template <typename F>
 12662auto on_gui_thread(F&& body) -> std::invoke_result_t<F> {
 12663    QCoreApplication* app = QCoreApplication::instance();
 12664    if (app == nullptr || QThread::currentThread() == app->thread()) {
 12665        return body();
 12666    }
 67
 068    using Result = std::invoke_result_t<F>;
 69    if constexpr (std::is_void_v<Result>) {
 70        QMetaObject::invokeMethod(app, std::forward<F>(body), Qt::BlockingQueuedConnection);
 071    } else {
 072        Result result{};
 073        QMetaObject::invokeMethod(
 074            app, [&body, &result]() { result = body(); }, Qt::BlockingQueuedConnection);
 075        return result;
 076    }
 077}
 78
 79/// Copies one `QScreen` into the bridge representation (logical pixels).
 6380QtScreenInfo screen_info(const QScreen* screen) {
 6381    const QRect geometry = screen->geometry();
 6382    const QRect available = screen->availableGeometry();
 83
 6384    QtScreenInfo info;
 6385    info.valid = true;
 6386    info.name = rust::String(screen->name().toStdString());
 6387    info.x = geometry.x();
 6388    info.y = geometry.y();
 6389    info.width = geometry.width();
 6390    info.height = geometry.height();
 6391    info.available_x = available.x();
 6392    info.available_y = available.y();
 6393    info.available_width = available.width();
 6394    info.available_height = available.height();
 6395    info.scale_factor = screen->devicePixelRatio();
 6396    return info;
 6397}
 98
 99}  // namespace
 100
 63101bool qt_screen_application_ready() {
 63102    return on_gui_thread([] { return gui_application() != nullptr; });
 63103}
 104
 21105rust::Vec<QtScreenInfo> qt_screen_monitors() {
 21106    return on_gui_thread([]() -> rust::Vec<QtScreenInfo> {
 21107        rust::Vec<QtScreenInfo> monitors;
 21108        if (gui_application() == nullptr) {
 0109            return monitors;  // Rust turns this into an error (see ready check)
 0110        }
 21111        for (QScreen* screen : QGuiApplication::screens()) {
 21112            if (screen != nullptr) {
 21113                monitors.push_back(screen_info(screen));
 21114            }
 21115        }
 21116        return monitors;
 21117    });
 21118}
 119
 21120QtScreenInfo qt_screen_primary() {
 21121    return on_gui_thread([]() -> QtScreenInfo {
 21122        if (gui_application() == nullptr) {
 0123            return QtScreenInfo{};
 0124        }
 21125        const QScreen* primary = QGuiApplication::primaryScreen();
 21126        return primary != nullptr ? screen_info(primary) : QtScreenInfo{};
 21127    });
 21128}
 129
 0130QtScreenInfo qt_screen_at_point(double x, double y) {
 0131    return on_gui_thread([x, y]() -> QtScreenInfo {
 0132        if (gui_application() == nullptr) {
 0133            return QtScreenInfo{};
 0134        }
 0135        const QPoint point(qRound(x), qRound(y));
 0136        const QScreen* screen = QGuiApplication::screenAt(point);
 0137        return screen != nullptr ? screen_info(screen) : QtScreenInfo{};
 0138    });
 0139}
 140
 21141QtWindowScreen qt_screen_for_window(std::uint64_t native) {
 21142    return on_gui_thread([native]() -> QtWindowScreen {
 21143        QtWindowScreen result;
 144
 145        // 0 is the no-such-handle sentinel of `qt_window_winid`; the handle is
 146        // also what identifies the widget below.
 21147        const std::uint64_t wid = qt_window_winid(native);
 21148        if (wid == 0) {
 0149            return result;  // window_known stays false
 0150        }
 21151        result.window_known = true;
 152
 21153        QWidget* window = nullptr;
 21154        if (qobject_cast<QApplication*>(QCoreApplication::instance()) != nullptr) {
 155            // Only widgets that are already native can be the window behind
 156            // `wid`; widgets without a QWindow are skipped rather than forced.
 21157            for (QWidget* candidate : QApplication::topLevelWidgets()) {
 21158                QWindow* handle = candidate != nullptr ? candidate->windowHandle() : nullptr;
 21159                if (handle != nullptr && static_cast<std::uint64_t>(handle->winId()) == wid) {
 21160                    window = candidate;
 21161                    break;
 21162                }
 21163            }
 21164        }
 21165        if (window == nullptr) {
 0166            return result;  // the window has no native screen yet
 0167        }
 168
 169        // The screen of the window's platform handle, exactly as Qt exposes it
 170        // (`window->screen()` would fall back to the primary screen, which is a
 171        // guess rather than an answer).
 21172        QWindow* window_handle = window->windowHandle();
 21173        const QScreen* screen = window_handle != nullptr ? window_handle->screen() : nullptr;
 21174        if (screen != nullptr) {
 21175            result.screen = screen_info(screen);
 21176        }
 21177        return result;
 21178    });
 21179}
 180
 0181QtCursorInfo qt_screen_cursor_position() {
 0182    return on_gui_thread([]() -> QtCursorInfo {
 0183        QtCursorInfo info;
 184
 0185        if (gui_application() == nullptr) {
 0186            return info;  // available stays false
 0187        }
 0188        if (QGuiApplication::platformName() == QStringLiteral("wayland")) {
 189            // Wayland has no global pointer position: clients only learn where
 190            // the cursor is from events on their own surfaces, so QCursor::pos()
 191            // cannot answer truthfully here.
 0192            return info;
 0193        }
 194
 0195        const QPoint position = QCursor::pos();
 0196        const QScreen* screen = QGuiApplication::screenAt(position);
 0197        info.available = true;
 0198        info.x = position.x();
 0199        info.y = position.y();
 0200        info.scale_factor = screen != nullptr ? screen->devicePixelRatio() : 1.0;
 0201        return info;
 0202    });
 0203}

Methods/Properties

qt_screen.cpp:_ZN12_GLOBAL__N_115gui_applicationEv
qt_screen.cpp:_ZN12_GLOBAL__N_113on_gui_threadIZ27qt_screen_application_readyvE3$_0EENSt13invoke_resultIT_JEE4typeEOS3_
qt_screen.cpp:_ZN12_GLOBAL__N_113on_gui_threadIZ18qt_screen_monitorsvE3$_0EENSt13invoke_resultIT_JEE4typeEOS3_
qt_screen.cpp:_ZN12_GLOBAL__N_113on_gui_threadIZ17qt_screen_primaryvE3$_0EENSt13invoke_resultIT_JEE4typeEOS3_
qt_screen.cpp:_ZN12_GLOBAL__N_113on_gui_threadIZ18qt_screen_at_pointddE3$_0EENSt13invoke_resultIT_JEE4typeEOS3_
qt_screen.cpp:_ZN12_GLOBAL__N_113on_gui_threadIZ20qt_screen_for_windowmE3$_0EENSt13invoke_resultIT_JEE4typeEOS3_
qt_screen.cpp:_ZN12_GLOBAL__N_113on_gui_threadIZ25qt_screen_cursor_positionvE3$_0EENSt13invoke_resultIT_JEE4typeEOS3_
qt_screen.cpp:_ZZN12_GLOBAL__N_113on_gui_threadIZ27qt_screen_application_readyvE3$_0EENSt13invoke_resultIT_JEE4typeEOS3_ENKUlvE_clEv
qt_screen.cpp:_ZZN12_GLOBAL__N_113on_gui_threadIZ18qt_screen_monitorsvE3$_0EENSt13invoke_resultIT_JEE4typeEOS3_ENKUlvE_clEv
qt_screen.cpp:_ZZN12_GLOBAL__N_113on_gui_threadIZ17qt_screen_primaryvE3$_0EENSt13invoke_resultIT_JEE4typeEOS3_ENKUlvE_clEv
qt_screen.cpp:_ZZN12_GLOBAL__N_113on_gui_threadIZ18qt_screen_at_pointddE3$_0EENSt13invoke_resultIT_JEE4typeEOS3_ENKUlvE_clEv
qt_screen.cpp:_ZZN12_GLOBAL__N_113on_gui_threadIZ20qt_screen_for_windowmE3$_0EENSt13invoke_resultIT_JEE4typeEOS3_ENKUlvE_clEv
qt_screen.cpp:_ZZN12_GLOBAL__N_113on_gui_threadIZ25qt_screen_cursor_positionvE3$_0EENSt13invoke_resultIT_JEE4typeEOS3_ENKUlvE_clEv
qt_screen.cpp:_ZN12_GLOBAL__N_111screen_infoEPK7QScreen
_Z27qt_screen_application_readyv
qt_screen.cpp:_ZZ27qt_screen_application_readyvENK3$_0clEv
_Z18qt_screen_monitorsv
qt_screen.cpp:_ZZ18qt_screen_monitorsvENK3$_0clEv
_Z17qt_screen_primaryv
qt_screen.cpp:_ZZ17qt_screen_primaryvENK3$_0clEv
_Z18qt_screen_at_pointdd
qt_screen.cpp:_ZZ18qt_screen_at_pointddENK3$_0clEv
_Z20qt_screen_for_windowm
qt_screen.cpp:_ZZ20qt_screen_for_windowmENK3$_0clEv
_Z25qt_screen_cursor_positionv
qt_screen.cpp:_ZZ25qt_screen_cursor_positionvENK3$_0clEv