< 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).
 10047QGuiApplication* gui_application() {
 10048    return qobject_cast<QGuiApplication*>(QCoreApplication::instance());
 10049}
 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>
 12062auto on_gui_thread(F&& body) -> std::invoke_result_t<F> {
 12063    QCoreApplication* app = QCoreApplication::instance();
 12064    if (app == nullptr || QThread::currentThread() == app->thread()) {
 12065        return body();
 12066    }
 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).
 6080QtScreenInfo screen_info(const QScreen* screen) {
 6081    const QRect geometry = screen->geometry();
 6082    const QRect available = screen->availableGeometry();
 83
 6084    QtScreenInfo info;
 6085    info.valid = true;
 6086    info.name = rust::String(screen->name().toStdString());
 6087    info.x = geometry.x();
 6088    info.y = geometry.y();
 6089    info.width = geometry.width();
 6090    info.height = geometry.height();
 6091    info.available_x = available.x();
 6092    info.available_y = available.y();
 6093    info.available_width = available.width();
 6094    info.available_height = available.height();
 6095    info.scale_factor = screen->devicePixelRatio();
 6096    return info;
 6097}
 98
 99}  // namespace
 100
 60101bool qt_screen_application_ready() {
 60102    return on_gui_thread([] { return gui_application() != nullptr; });
 60103}
 104
 20105rust::Vec<QtScreenInfo> qt_screen_monitors() {
 20106    return on_gui_thread([]() -> rust::Vec<QtScreenInfo> {
 20107        rust::Vec<QtScreenInfo> monitors;
 20108        if (gui_application() == nullptr) {
 0109            return monitors;  // Rust turns this into an error (see ready check)
 0110        }
 20111        for (QScreen* screen : QGuiApplication::screens()) {
 20112            if (screen != nullptr) {
 20113                monitors.push_back(screen_info(screen));
 20114            }
 20115        }
 20116        return monitors;
 20117    });
 20118}
 119
 20120QtScreenInfo qt_screen_primary() {
 20121    return on_gui_thread([]() -> QtScreenInfo {
 20122        if (gui_application() == nullptr) {
 0123            return QtScreenInfo{};
 0124        }
 20125        const QScreen* primary = QGuiApplication::primaryScreen();
 20126        return primary != nullptr ? screen_info(primary) : QtScreenInfo{};
 20127    });
 20128}
 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
 20141QtWindowScreen qt_screen_for_window(std::uint64_t native) {
 20142    return on_gui_thread([native]() -> QtWindowScreen {
 20143        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.
 20147        const std::uint64_t wid = qt_window_winid(native);
 20148        if (wid == 0) {
 0149            return result;  // window_known stays false
 0150        }
 20151        result.window_known = true;
 152
 20153        QWidget* window = nullptr;
 20154        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.
 20157            for (QWidget* candidate : QApplication::topLevelWidgets()) {
 20158                QWindow* handle = candidate != nullptr ? candidate->windowHandle() : nullptr;
 20159                if (handle != nullptr && static_cast<std::uint64_t>(handle->winId()) == wid) {
 20160                    window = candidate;
 20161                    break;
 20162                }
 20163            }
 20164        }
 20165        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).
 20172        QWindow* window_handle = window->windowHandle();
 20173        const QScreen* screen = window_handle != nullptr ? window_handle->screen() : nullptr;
 20174        if (screen != nullptr) {
 20175            result.screen = screen_info(screen);
 20176        }
 20177        return result;
 20178    });
 20179}
 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