< Summary

Information
Class: /home/runner/work/TauriQt/TauriQt/tauri-runtime-qt/src/ops/screen.rs
Assembly: Default
File(s): /home/runner/work/TauriQt/TauriQt/tauri-runtime-qt/src/ops/screen.rs
Line coverage
69%
Covered lines: 44
Uncovered lines: 19
Coverable lines: 63
Total lines: 120
Line coverage: 69.8%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

File(s)

/home/runner/work/TauriQt/TauriQt/tauri-runtime-qt/src/ops/screen.rs

#LineLine coverage
 1// SPDX-FileCopyrightText: 2026 Alper Çelik
 2// SPDX-License-Identifier: MIT OR Apache-2.0
 3
 4//! Screen / monitor / cursor operations (owned by the screen worker).
 5//!
 6//! This is the provider module other workers consume for monitors: the window
 7//! and runtime workers must call these functions instead of querying Qt
 8//! themselves, so the mapping lives in exactly one place.
 9//!
 10//! Units: Qt answers in device-independent (logical) global pixels — the space
 11//! `QScreen::geometry()` and `QGuiApplication::screenAt()` use, and the one
 12//! tao accepts for `monitor_from_point` on Linux
 13//! (`gdk_display_get_monitor_at_point`). The values returned here are physical
 14//! pixels instead: logical lengths are multiplied by the screen's
 15//! `devicePixelRatio` with the same rounding as `dpi`'s `to_physical`, which is
 16//! what tao reports for `Monitor` on X11. So the `monitor_from_point` argument is
 17//! logical while `Monitor::position`/`size` are physical, exactly as in tao.
 18//! `cursor_position` is physical too (Qt's logical position times the scale
 19//! factor of the screen under the cursor).
 20
 21use tauri_runtime::dpi::{PhysicalPosition, PhysicalRect, PhysicalSize};
 22use tauri_runtime::monitor::Monitor;
 23use tauri_runtime::{Error, Result};
 24use tauri_runtime_qt_core as qtcore;
 25
 26use crate::bridge::screen::ffi::{self, QtScreenInfo};
 27
 28/// Turns one Qt screen into a tauri [`Monitor`] (physical pixels).
 6029fn to_monitor(info: &QtScreenInfo) -> Monitor {
 30    // A missing/zero ratio would silently collapse every value; Qt always
 31    // reports >= 1, so only an impossible value falls back to 1.
 6032    let scale_factor = qtcore::screen::scale_factor_or_one(info.scale_factor);
 33
 34    Monitor {
 6035        name: (!info.name.is_empty()).then(|| info.name.clone()),
 6036        size: PhysicalSize::new(
 6037            qtcore::screen::to_physical_u32(info.width, scale_factor),
 6038            qtcore::screen::to_physical_u32(info.height, scale_factor),
 39        ),
 6040        position: PhysicalPosition::new(
 6041            qtcore::screen::to_physical(info.x, scale_factor),
 6042            qtcore::screen::to_physical(info.y, scale_factor),
 43        ),
 6044        work_area: PhysicalRect {
 6045            position: PhysicalPosition::new(
 6046                qtcore::screen::to_physical(info.available_x, scale_factor),
 6047                qtcore::screen::to_physical(info.available_y, scale_factor),
 6048            ),
 6049            size: PhysicalSize::new(
 6050                qtcore::screen::to_physical_u32(info.available_width, scale_factor),
 6051                qtcore::screen::to_physical_u32(info.available_height, scale_factor),
 6052            ),
 6053        },
 6054        scale_factor,
 55    }
 6056}
 57
 58/// Monitors can only be answered once Qt has a live `QGuiApplication`; before
 59/// that, reporting an empty list would claim there are no screens.
 6060fn ensure_ready() -> Result<()> {
 6061    if ffi::qt_screen_application_ready() {
 6062        Ok(())
 63    } else {
 064        Err(Error::FailedToGetMonitor)
 65    }
 6066}
 67
 68/// All monitors, in Qt's screen order.
 2069pub(crate) fn available_monitors() -> Result<Vec<Monitor>> {
 2070    ensure_ready()?;
 2071    let monitors: Vec<Monitor> = ffi::qt_screen_monitors().iter().map(to_monitor).collect();
 72
 2073    if std::env::var_os("TAURIQT_SCREEN_DEBUG").is_some() {
 074        eprintln!("[tauri-runtime-qt] available_monitors: {monitors:#?}");
 2075    }
 76
 2077    Ok(monitors)
 2078}
 79
 80/// The primary monitor, if the platform identifies one.
 2081pub(crate) fn primary_monitor() -> Result<Option<Monitor>> {
 2082    ensure_ready()?;
 2083    let info = ffi::qt_screen_primary();
 2084    Ok(info.valid.then(|| to_monitor(&info)))
 2085}
 86
 87/// The monitor containing the given desktop point (device-independent global
 88/// pixels, the space `QGuiApplication::screenAt` uses).
 089pub(crate) fn monitor_from_point(x: f64, y: f64) -> Result<Option<Monitor>> {
 090    ensure_ready()?;
 091    let info = ffi::qt_screen_at_point(x, y);
 092    Ok(info.valid.then(|| to_monitor(&info)))
 093}
 94
 95/// The monitor the given window currently sits on.
 2096pub(crate) fn monitor_for_window(native: u64) -> Result<Option<Monitor>> {
 2097    ensure_ready()?;
 2098    let lookup = ffi::qt_screen_for_window(native);
 2099    if !lookup.window_known {
 0100        return Err(Error::WindowNotFound);
 20101    }
 20102    Ok(lookup.screen.valid.then(|| to_monitor(&lookup.screen)))
 20103}
 104
 105/// Cursor position relative to the top-left corner of the virtual desktop
 106/// (physical pixels; Qt reports device-independent ones).
 0107pub(crate) fn cursor_position() -> Result<PhysicalPosition<f64>> {
 0108    let cursor = ffi::qt_screen_cursor_position();
 0109    if !cursor.available {
 110        // Wayland does not expose a global pointer position (and without a
 111        // QGuiApplication there is nothing to ask) — say so instead of
 112        // reporting a stale or invented point.
 0113        return Err(Error::FailedToGetCursorPosition);
 0114    }
 0115    let scale_factor = qtcore::screen::scale_factor_or_one(cursor.scale_factor);
 0116    Ok(PhysicalPosition::new(
 0117        cursor.x * scale_factor,
 0118        cursor.y * scale_factor,
 0119    ))
 0120}