| | | 1 | | // SPDX-FileCopyrightText: 2026 Alper Çelik |
| | | 2 | | // SPDX-License-Identifier: MIT OR Apache-2.0 |
| | | 3 | | |
| | | 4 | | //! Window operations (owned by the window worker). |
| | | 5 | | //! |
| | | 6 | | //! Everything here talks to `bridge::window::ffi` (`qt_window.cpp`). Functions |
| | | 7 | | //! return `Err` when Qt cannot answer truthfully on this platform: an id that |
| | | 8 | | //! is not in the C++ registry is `Error::WindowNotFound`, and an operation the |
| | | 9 | | //! platform cannot honour (an unreportable colour scheme, a refused drag) is a |
| | | 10 | | //! message-carrying error instead of a fabricated value. |
| | | 11 | | //! |
| | | 12 | | //! Window *positions* are deliberately not in that set. A Wayland compositor |
| | | 13 | | //! owns placement and honours neither `move()` nor a stacking hint, but Qt keeps |
| | | 14 | | //! the position an app asked for and reports it back, and callers treat the |
| | | 15 | | //! position as always available: `tauri-plugin-window-state`, for one, aborts |
| | | 16 | | //! its startup restore — including the `show()` that maps a hidden window — |
| | | 17 | | //! on the first `Err` it gets from `outer_position`. So `inner_position`, |
| | | 18 | | //! `outer_position` and `set_position` answer with Qt's own bookkeeping on every |
| | | 19 | | //! platform, exactly like tao does, and report the app's *requested* position on |
| | | 20 | | //! Wayland rather than failing. See `docs/wayland-ci.md`. |
| | | 21 | | //! |
| | | 22 | | //! Qt reports geometry in device-independent ("logical") pixels; tauri asks |
| | | 23 | | //! for physical ones, so every geometry value crosses the boundary through the |
| | | 24 | | //! window's own device pixel ratio (`qt_window_scale_factor`). The conversions |
| | | 25 | | //! themselves (and Qt's 24-bit hint sentinels) live in |
| | | 26 | | //! `tauri-runtime-qt-core::window`. |
| | | 27 | | |
| | | 28 | | use tauri_runtime::dpi::{PhysicalPosition, PhysicalSize, Position, Size}; |
| | | 29 | | use tauri_runtime::window::WindowSizeConstraints; |
| | | 30 | | use tauri_runtime::{Error, Icon, Result}; |
| | | 31 | | use tauri_runtime_qt_core as qtcore; |
| | | 32 | | use tauri_utils::config::Color; |
| | | 33 | | use tauri_utils::Theme; |
| | | 34 | | |
| | | 35 | | use crate::bridge::window::ffi; |
| | | 36 | | |
| | | 37 | | // ---- helpers ---- |
| | | 38 | | |
| | | 39 | | /// Fails with `Error::WindowNotFound` for an id that is not in the C++ window |
| | | 40 | | /// registry, instead of asking Qt about a window that is not there. |
| | 520 | 41 | | fn require_window(native: u64) -> Result<()> { |
| | 520 | 42 | | if ffi::qt_window_alive(native) { |
| | 520 | 43 | | Ok(()) |
| | | 44 | | } else { |
| | 0 | 45 | | Err(Error::WindowNotFound) |
| | | 46 | | } |
| | 520 | 47 | | } |
| | | 48 | | |
| | | 49 | | /// A truthful error for an operation Qt cannot honour here. |
| | | 50 | | /// |
| | | 51 | | /// `tauri_runtime::Error` has no dedicated "unsupported" variant, so the |
| | | 52 | | /// message-carrying variant is used (as `ops::unimplemented` does for the |
| | | 53 | | /// operations that do not exist at all). |
| | 0 | 54 | | fn unavailable(what: &str, why: &str) -> Error { |
| | 0 | 55 | | Error::CreateWebview(format!("tauri-runtime-qt: {what} is unavailable: {why}").into()) |
| | 0 | 56 | | } |
| | | 57 | | |
| | | 58 | | /// Name of the Qt platform plugin in use (e.g. `"xcb"`, `"wayland"`). |
| | 0 | 59 | | fn platform() -> String { |
| | 0 | 60 | | ffi::qt_window_platform() |
| | 0 | 61 | | } |
| | | 62 | | |
| | | 63 | | /// The window's device pixel ratio (physical pixels per logical pixel), with |
| | | 64 | | /// Qt's "not on a screen yet" reading normalised to 1. |
| | 160 | 65 | | fn device_pixel_ratio(native: u64) -> f64 { |
| | 160 | 66 | | qtcore::window::device_pixel_ratio(ffi::qt_window_scale_factor(native)) |
| | 160 | 67 | | } |
| | | 68 | | |
| | | 69 | | /// Qt logical pixels → tauri physical pixels. |
| | 80 | 70 | | fn physical_size(native: u64, size: ffi::QtWindowSize) -> PhysicalSize<u32> { |
| | 80 | 71 | | qtcore::window::to_physical_size(device_pixel_ratio(native), size.width, size.height) |
| | 80 | 72 | | } |
| | | 73 | | |
| | | 74 | | /// Qt logical pixels → tauri physical pixels. |
| | 80 | 75 | | fn physical_position(native: u64, point: ffi::QtWindowPoint) -> PhysicalPosition<i32> { |
| | 80 | 76 | | qtcore::window::to_physical_position(device_pixel_ratio(native), point.x, point.y) |
| | 80 | 77 | | } |
| | | 78 | | |
| | | 79 | | // ---- lifecycle ---- |
| | | 80 | | |
| | 0 | 81 | | pub(crate) fn show(native: u64) -> Result<()> { |
| | 0 | 82 | | ffi::qt_window_show(native); |
| | 0 | 83 | | Ok(()) |
| | 0 | 84 | | } |
| | | 85 | | |
| | 0 | 86 | | pub(crate) fn hide(native: u64) -> Result<()> { |
| | 0 | 87 | | ffi::qt_window_hide(native); |
| | 0 | 88 | | Ok(()) |
| | 0 | 89 | | } |
| | | 90 | | |
| | 0 | 91 | | pub(crate) fn close(native: u64) -> Result<()> { |
| | 0 | 92 | | ffi::qt_window_close(native); |
| | 0 | 93 | | Ok(()) |
| | 0 | 94 | | } |
| | | 95 | | |
| | 0 | 96 | | pub(crate) fn destroy(native: u64) -> Result<()> { |
| | 0 | 97 | | ffi::qt_window_close(native); |
| | 0 | 98 | | Ok(()) |
| | 0 | 99 | | } |
| | | 100 | | |
| | | 101 | | // ---- getters ---- |
| | | 102 | | |
| | 40 | 103 | | pub(crate) fn scale_factor(native: u64) -> Result<f64> { |
| | 40 | 104 | | Ok(ffi::qt_window_scale_factor(native)) |
| | 40 | 105 | | } |
| | | 106 | | |
| | 40 | 107 | | pub(crate) fn inner_size(native: u64) -> Result<PhysicalSize<u32>> { |
| | 40 | 108 | | require_window(native)?; |
| | 40 | 109 | | Ok(physical_size(native, ffi::qt_window_inner_size(native))) |
| | 40 | 110 | | } |
| | | 111 | | |
| | 40 | 112 | | pub(crate) fn outer_size(native: u64) -> Result<PhysicalSize<u32>> { |
| | 40 | 113 | | require_window(native)?; |
| | 40 | 114 | | let size = ffi::qt_window_outer_size(native); |
| | 40 | 115 | | if size.width <= 0.0 || size.height <= 0.0 { |
| | | 116 | | // An empty frame rectangle means the platform does not report the |
| | | 117 | | // frame; a `0x0` outer size would be a fabricated value. |
| | 0 | 118 | | return Err(unavailable( |
| | 0 | 119 | | "the window frame size", |
| | 0 | 120 | | "the platform does not report the window frame geometry", |
| | 0 | 121 | | )); |
| | 40 | 122 | | } |
| | 40 | 123 | | Ok(physical_size(native, size)) |
| | 40 | 124 | | } |
| | | 125 | | |
| | | 126 | | /// Position of the window's client area. |
| | | 127 | | /// |
| | | 128 | | /// On Wayland this is Qt's own record of where the window was asked to be (the |
| | | 129 | | /// compositor decides where it really is and does not report it), which is what |
| | | 130 | | /// tao answers there too — see the module docs. |
| | 40 | 131 | | pub(crate) fn inner_position(native: u64) -> Result<PhysicalPosition<i32>> { |
| | 40 | 132 | | require_window(native)?; |
| | 40 | 133 | | Ok(physical_position( |
| | 40 | 134 | | native, |
| | 40 | 135 | | ffi::qt_window_inner_position(native), |
| | 40 | 136 | | )) |
| | 40 | 137 | | } |
| | | 138 | | |
| | | 139 | | /// Position of the window's frame (its top-left corner on the desktop). |
| | | 140 | | /// |
| | | 141 | | /// Same Wayland caveat as [`inner_position`]: the value is the requested |
| | | 142 | | /// position, not a compositor-reported one. |
| | 40 | 143 | | pub(crate) fn outer_position(native: u64) -> Result<PhysicalPosition<i32>> { |
| | 40 | 144 | | require_window(native)?; |
| | 40 | 145 | | Ok(physical_position( |
| | 40 | 146 | | native, |
| | 40 | 147 | | ffi::qt_window_outer_position(native), |
| | 40 | 148 | | )) |
| | 40 | 149 | | } |
| | | 150 | | |
| | 40 | 151 | | pub(crate) fn title(native: u64) -> Result<String> { |
| | 40 | 152 | | require_window(native)?; |
| | 40 | 153 | | Ok(ffi::qt_window_title(native)) |
| | 40 | 154 | | } |
| | | 155 | | |
| | 0 | 156 | | pub(crate) fn theme(native: u64) -> Result<Theme> { |
| | 0 | 157 | | require_window(native)?; |
| | 0 | 158 | | let code = ffi::qt_window_theme(native); |
| | 0 | 159 | | qtcore::window::theme_from_code(code).ok_or_else(|| { |
| | 0 | 160 | | unavailable( |
| | 0 | 161 | | "the window theme", |
| | 0 | 162 | | &format!("Qt reports the colour scheme as unknown (code {code})"), |
| | | 163 | | ) |
| | 0 | 164 | | }) |
| | 0 | 165 | | } |
| | | 166 | | |
| | 40 | 167 | | pub(crate) fn is_fullscreen(native: u64) -> Result<bool> { |
| | 40 | 168 | | require_window(native)?; |
| | 40 | 169 | | Ok(ffi::qt_window_is_fullscreen(native)) |
| | 40 | 170 | | } |
| | | 171 | | |
| | 40 | 172 | | pub(crate) fn is_minimized(native: u64) -> Result<bool> { |
| | 40 | 173 | | require_window(native)?; |
| | 40 | 174 | | Ok(ffi::qt_window_is_minimized(native)) |
| | 40 | 175 | | } |
| | | 176 | | |
| | 40 | 177 | | pub(crate) fn is_maximized(native: u64) -> Result<bool> { |
| | 40 | 178 | | require_window(native)?; |
| | 40 | 179 | | Ok(ffi::qt_window_is_maximized(native)) |
| | 40 | 180 | | } |
| | | 181 | | |
| | 40 | 182 | | pub(crate) fn is_focused(native: u64) -> Result<bool> { |
| | 40 | 183 | | require_window(native)?; |
| | 40 | 184 | | Ok(ffi::qt_window_is_focused(native)) |
| | 40 | 185 | | } |
| | | 186 | | |
| | 40 | 187 | | pub(crate) fn is_decorated(native: u64) -> Result<bool> { |
| | 40 | 188 | | require_window(native)?; |
| | 40 | 189 | | Ok(ffi::qt_window_is_decorated(native)) |
| | 40 | 190 | | } |
| | | 191 | | |
| | 40 | 192 | | pub(crate) fn is_resizable(native: u64) -> Result<bool> { |
| | 40 | 193 | | require_window(native)?; |
| | 40 | 194 | | Ok(ffi::qt_window_is_resizable(native)) |
| | 40 | 195 | | } |
| | | 196 | | |
| | 40 | 197 | | pub(crate) fn is_visible(native: u64) -> Result<bool> { |
| | 40 | 198 | | Ok(ffi::qt_window_is_visible(native)) |
| | 40 | 199 | | } |
| | | 200 | | |
| | 0 | 201 | | pub(crate) fn is_enabled(native: u64) -> Result<bool> { |
| | 0 | 202 | | require_window(native)?; |
| | 0 | 203 | | Ok(ffi::qt_window_is_enabled(native)) |
| | 0 | 204 | | } |
| | | 205 | | |
| | 40 | 206 | | pub(crate) fn is_always_on_top(native: u64) -> Result<bool> { |
| | 40 | 207 | | require_window(native)?; |
| | 40 | 208 | | Ok(ffi::qt_window_is_always_on_top(native)) |
| | 40 | 209 | | } |
| | | 210 | | |
| | 0 | 211 | | pub(crate) fn is_maximizable(native: u64) -> Result<bool> { |
| | 0 | 212 | | require_window(native)?; |
| | 0 | 213 | | Ok(ffi::qt_window_is_maximizable(native)) |
| | 0 | 214 | | } |
| | | 215 | | |
| | 0 | 216 | | pub(crate) fn is_minimizable(native: u64) -> Result<bool> { |
| | 0 | 217 | | require_window(native)?; |
| | 0 | 218 | | Ok(ffi::qt_window_is_minimizable(native)) |
| | 0 | 219 | | } |
| | | 220 | | |
| | 0 | 221 | | pub(crate) fn is_closable(native: u64) -> Result<bool> { |
| | 0 | 222 | | require_window(native)?; |
| | 0 | 223 | | Ok(ffi::qt_window_is_closable(native)) |
| | 0 | 224 | | } |
| | | 225 | | |
| | | 226 | | // ---- setters ---- |
| | | 227 | | |
| | 20 | 228 | | pub(crate) fn set_title(native: u64, title: &str) -> Result<()> { |
| | 20 | 229 | | ffi::qt_window_set_title(native, title); |
| | 20 | 230 | | Ok(()) |
| | 20 | 231 | | } |
| | | 232 | | |
| | 0 | 233 | | pub(crate) fn set_size(native: u64, width: i32, height: i32) -> Result<()> { |
| | 0 | 234 | | ffi::qt_window_set_size(native, width, height); |
| | 0 | 235 | | Ok(()) |
| | 0 | 236 | | } |
| | | 237 | | |
| | 0 | 238 | | pub(crate) fn center(native: u64) -> Result<()> { |
| | 0 | 239 | | ffi::qt_window_center(native); |
| | 0 | 240 | | Ok(()) |
| | 0 | 241 | | } |
| | | 242 | | |
| | 0 | 243 | | pub(crate) fn set_decorations(native: u64, decorations: bool) -> Result<()> { |
| | 0 | 244 | | ffi::qt_window_set_decorations(native, decorations); |
| | 0 | 245 | | Ok(()) |
| | 0 | 246 | | } |
| | | 247 | | |
| | 0 | 248 | | pub(crate) fn set_maximized(native: u64, maximized: bool) -> Result<()> { |
| | 0 | 249 | | ffi::qt_window_set_maximized(native, maximized); |
| | 0 | 250 | | Ok(()) |
| | 0 | 251 | | } |
| | | 252 | | |
| | 0 | 253 | | pub(crate) fn minimize(native: u64) -> Result<()> { |
| | 0 | 254 | | require_window(native)?; |
| | 0 | 255 | | ffi::qt_window_minimize(native); |
| | 0 | 256 | | Ok(()) |
| | 0 | 257 | | } |
| | | 258 | | |
| | 0 | 259 | | pub(crate) fn unminimize(native: u64) -> Result<()> { |
| | 0 | 260 | | require_window(native)?; |
| | 0 | 261 | | ffi::qt_window_unminimize(native); |
| | 0 | 262 | | Ok(()) |
| | 0 | 263 | | } |
| | | 264 | | |
| | 40 | 265 | | pub(crate) fn set_fullscreen(native: u64, fullscreen: bool) -> Result<()> { |
| | 40 | 266 | | require_window(native)?; |
| | 40 | 267 | | ffi::qt_window_set_fullscreen(native, fullscreen); |
| | 40 | 268 | | Ok(()) |
| | 40 | 269 | | } |
| | | 270 | | |
| | 0 | 271 | | pub(crate) fn set_min_size(native: u64, size: Option<Size>) -> Result<()> { |
| | 0 | 272 | | require_window(native)?; |
| | 0 | 273 | | let (width, height) = match size { |
| | 0 | 274 | | Some(size) => qtcore::window::qt_size(device_pixel_ratio(native), size), |
| | | 275 | | // Qt's "no minimum size": QSize(0, 0). |
| | 0 | 276 | | None => qtcore::window::NO_MINIMUM, |
| | | 277 | | }; |
| | 0 | 278 | | ffi::qt_window_set_min_size(native, width, height); |
| | 0 | 279 | | Ok(()) |
| | 0 | 280 | | } |
| | | 281 | | |
| | 0 | 282 | | pub(crate) fn set_max_size(native: u64, size: Option<Size>) -> Result<()> { |
| | 0 | 283 | | require_window(native)?; |
| | 0 | 284 | | let (width, height) = match size { |
| | 0 | 285 | | Some(size) => qtcore::window::qt_size(device_pixel_ratio(native), size), |
| | | 286 | | // Qt's "no maximum size": QWIDGETSIZE_MAX. |
| | 0 | 287 | | None => qtcore::window::NO_MAXIMUM, |
| | | 288 | | }; |
| | 0 | 289 | | ffi::qt_window_set_max_size(native, width, height); |
| | 0 | 290 | | Ok(()) |
| | 0 | 291 | | } |
| | | 292 | | |
| | 0 | 293 | | pub(crate) fn set_size_constraints(native: u64, constraints: WindowSizeConstraints) -> Result<()> { |
| | 0 | 294 | | require_window(native)?; |
| | 0 | 295 | | let hints = qtcore::window::size_hints(device_pixel_ratio(native), constraints); |
| | | 296 | | // Both hints are written in one GUI-thread turn, so no resize can observe |
| | | 297 | | // half of the new constraints. |
| | 0 | 298 | | ffi::qt_window_set_min_size(native, hints.min_width, hints.min_height); |
| | 0 | 299 | | ffi::qt_window_set_max_size(native, hints.max_width, hints.max_height); |
| | 0 | 300 | | Ok(()) |
| | 0 | 301 | | } |
| | | 302 | | |
| | 0 | 303 | | pub(crate) fn set_icon(native: u64, icon: Icon) -> Result<()> { |
| | 0 | 304 | | require_window(native)?; |
| | 0 | 305 | | if let Some(message) = |
| | 0 | 306 | | qtcore::window::icon_buffer_error(icon.width, icon.height, icon.rgba.len()) |
| | | 307 | | { |
| | 0 | 308 | | return Err(Error::InvalidIcon(message.into())); |
| | 0 | 309 | | } |
| | 0 | 310 | | if !ffi::qt_window_set_icon(native, icon.rgba.as_ref(), icon.width, icon.height) { |
| | 0 | 311 | | return Err(Error::InvalidIcon("Qt rejected the icon pixels".into())); |
| | 0 | 312 | | } |
| | 0 | 313 | | Ok(()) |
| | 0 | 314 | | } |
| | | 315 | | |
| | 0 | 316 | | pub(crate) fn set_focus(native: u64) -> Result<()> { |
| | 0 | 317 | | require_window(native)?; |
| | 0 | 318 | | ffi::qt_window_set_focus(native); |
| | 0 | 319 | | Ok(()) |
| | 0 | 320 | | } |
| | | 321 | | |
| | 0 | 322 | | pub(crate) fn set_resizable(native: u64, resizable: bool) -> Result<()> { |
| | 0 | 323 | | require_window(native)?; |
| | 0 | 324 | | ffi::qt_window_set_resizable(native, resizable); |
| | 0 | 325 | | Ok(()) |
| | 0 | 326 | | } |
| | | 327 | | |
| | 0 | 328 | | pub(crate) fn set_enabled(native: u64, enabled: bool) -> Result<()> { |
| | 0 | 329 | | require_window(native)?; |
| | 0 | 330 | | ffi::qt_window_set_enabled(native, enabled); |
| | 0 | 331 | | Ok(()) |
| | 0 | 332 | | } |
| | | 333 | | |
| | 0 | 334 | | pub(crate) fn set_always_on_bottom(native: u64, always_on_bottom: bool) -> Result<()> { |
| | 0 | 335 | | require_window(native)?; |
| | 0 | 336 | | let platform = platform(); |
| | 0 | 337 | | if !qtcore::window::placement_supported(&platform) { |
| | 0 | 338 | | return Err(unavailable( |
| | 0 | 339 | | "always-on-bottom", |
| | 0 | 340 | | &format!( |
| | 0 | 341 | | "the '{platform}' Qt platform has no on-bottom state (a Wayland compositor does not expose one)" |
| | 0 | 342 | | ), |
| | 0 | 343 | | )); |
| | 0 | 344 | | } |
| | 0 | 345 | | ffi::qt_window_set_always_on_bottom(native, always_on_bottom); |
| | 0 | 346 | | Ok(()) |
| | 0 | 347 | | } |
| | | 348 | | |
| | 0 | 349 | | pub(crate) fn set_always_on_top(native: u64, always_on_top: bool) -> Result<()> { |
| | 0 | 350 | | require_window(native)?; |
| | 0 | 351 | | let platform = platform(); |
| | 0 | 352 | | if !qtcore::window::placement_supported(&platform) { |
| | 0 | 353 | | return Err(unavailable( |
| | 0 | 354 | | "always-on-top", |
| | 0 | 355 | | &format!( |
| | 0 | 356 | | "the '{platform}' Qt platform has no on-top state (Wayland compositors ignore Qt::WindowStaysOnTopHint, |
| | 0 | 357 | | ), |
| | 0 | 358 | | )); |
| | 0 | 359 | | } |
| | 0 | 360 | | ffi::qt_window_set_always_on_top(native, always_on_top); |
| | 0 | 361 | | Ok(()) |
| | 0 | 362 | | } |
| | | 363 | | |
| | 0 | 364 | | pub(crate) fn set_background_color(native: u64, color: Option<Color>) -> Result<()> { |
| | 0 | 365 | | require_window(native)?; |
| | 0 | 366 | | match color { |
| | 0 | 367 | | Some(Color(r, g, b, a)) => ffi::qt_window_set_background_color(native, r, g, b, a), |
| | 0 | 368 | | None => ffi::qt_window_reset_background_color(native), |
| | | 369 | | } |
| | 0 | 370 | | Ok(()) |
| | 0 | 371 | | } |
| | | 372 | | |
| | | 373 | | /// Moves the window to `position`, in the window's own pixel unit. |
| | | 374 | | /// |
| | | 375 | | /// On X11 the window manager is asked to place the frame there. On Wayland the |
| | | 376 | | /// compositor owns placement, so `move()` only records the position Qt reports |
| | | 377 | | /// back through [`outer_position`]; the call still succeeds, as it does in tao. |
| | 0 | 378 | | pub(crate) fn set_position(native: u64, position: Position) -> Result<()> { |
| | 0 | 379 | | require_window(native)?; |
| | 0 | 380 | | let (x, y) = qtcore::window::window_position(device_pixel_ratio(native), position); |
| | 0 | 381 | | ffi::qt_window_set_position(native, x, y); |
| | 0 | 382 | | Ok(()) |
| | 0 | 383 | | } |
| | | 384 | | |
| | 0 | 385 | | pub(crate) fn start_dragging(native: u64) -> Result<()> { |
| | 0 | 386 | | require_window(native)?; |
| | 0 | 387 | | if ffi::qt_window_start_dragging(native) { |
| | 0 | 388 | | return Ok(()); |
| | 0 | 389 | | } |
| | 0 | 390 | | Err(unavailable( |
| | 0 | 391 | | "window dragging", |
| | 0 | 392 | | "QWindow::startSystemMove() returned false (no native window handle, or the call did not come from a mouse press |
| | 0 | 393 | | )) |
| | 0 | 394 | | } |
| | | 395 | | |
| | 0 | 396 | | pub(crate) fn start_resize_dragging(native: u64, direction: i32) -> Result<()> { |
| | 0 | 397 | | require_window(native)?; |
| | 0 | 398 | | if !(1..=8).contains(&direction) { |
| | 0 | 399 | | return Err(unavailable( |
| | 0 | 400 | | "window resize dragging", |
| | 0 | 401 | | &format!("unknown resize direction {direction}"), |
| | 0 | 402 | | )); |
| | 0 | 403 | | } |
| | 0 | 404 | | if ffi::qt_window_start_resize_dragging(native, direction) { |
| | 0 | 405 | | return Ok(()); |
| | 0 | 406 | | } |
| | 0 | 407 | | Err(unavailable( |
| | 0 | 408 | | "window resize dragging", |
| | 0 | 409 | | "QWindow::startSystemResize() returned false (no native window handle, or the call did not come from a mouse pre |
| | 0 | 410 | | )) |
| | 0 | 411 | | } |