diff --git a/.changes/15.md b/.changes/15.md new file mode 100644 index 0000000000..924cd34765 --- /dev/null +++ b/.changes/15.md @@ -0,0 +1,5 @@ +--- +"tao": minor +--- + +Update to gtk 0.15 diff --git a/.changes/accelerator-error.md b/.changes/accelerator-error.md new file mode 100644 index 0000000000..87168d8af7 --- /dev/null +++ b/.changes/accelerator-error.md @@ -0,0 +1,5 @@ +--- +"tao": patch +--- + +Emit errors when parsing an invalid accelerator from a string. \ No newline at end of file diff --git a/.changes/accelerator-strings.md b/.changes/accelerator-strings.md new file mode 100644 index 0000000000..a817fbe4f5 --- /dev/null +++ b/.changes/accelerator-strings.md @@ -0,0 +1,5 @@ +--- +tao: minor +--- + +Add support for more accelerator keys: `,` `-` `.` `=` `;` `/` `\` `'` `` ` `` `[` `]` `Space` `Tab` and `F13`-`F24` diff --git a/.changes/borderless-resizing-inset.md b/.changes/borderless-resizing-inset.md new file mode 100644 index 0000000000..b140273a03 --- /dev/null +++ b/.changes/borderless-resizing-inset.md @@ -0,0 +1,4 @@ +--- +"tao": patch +--- +Increased Borderless window resizing inset. \ No newline at end of file diff --git a/.changes/edition.md b/.changes/edition.md new file mode 100644 index 0000000000..81697a76fe --- /dev/null +++ b/.changes/edition.md @@ -0,0 +1,6 @@ +--- +"tao": patch +--- + +Update to 2021 edition and msrv to 1.56 + diff --git a/.changes/exit_with_code.md b/.changes/exit_with_code.md new file mode 100644 index 0000000000..1b4aa78469 --- /dev/null +++ b/.changes/exit_with_code.md @@ -0,0 +1,5 @@ +--- +"tao": minor +--- + +**Breaking:** Rename the `Exit` variant of `ControlFlow` to `ExitWithCode`, which holds a value to control the exit code after running. Add an `Exit` constant which aliases to `ExitWithCode(0)` instead to avoid major breakage. This shouldn't affect most existing programs. diff --git a/.changes/fix-quit-menuitem-windows.md b/.changes/fix-quit-menuitem-windows.md new file mode 100644 index 0000000000..a00065cb46 --- /dev/null +++ b/.changes/fix-quit-menuitem-windows.md @@ -0,0 +1,5 @@ +--- +"tao": patch +--- + +Fixes the `MenuItem::Quit` behavior on Windows. diff --git a/.changes/keyboard-windows-space.md b/.changes/keyboard-windows-space.md new file mode 100644 index 0000000000..39bf0483dc --- /dev/null +++ b/.changes/keyboard-windows-space.md @@ -0,0 +1,5 @@ +--- +"tao": patch +--- + +Add support for `SPACE` shortcut key on Windows. diff --git a/.changes/linux-fix-loop.md b/.changes/linux-fix-loop.md new file mode 100644 index 0000000000..ecaabb620e --- /dev/null +++ b/.changes/linux-fix-loop.md @@ -0,0 +1,5 @@ +--- +"tao": patch +--- + +* Fix redrawn event that causing infinite lopp on Linux diff --git a/.changes/linux-fix-native-menu-items.md b/.changes/linux-fix-native-menu-items.md new file mode 100644 index 0000000000..b673bd3801 --- /dev/null +++ b/.changes/linux-fix-native-menu-items.md @@ -0,0 +1,5 @@ +--- +"tao": patch +--- + +Fix linux native menu items not working. \ No newline at end of file diff --git a/.changes/linux-fix-undecorated-resize.md b/.changes/linux-fix-undecorated-resize.md new file mode 100644 index 0000000000..1a2eb3995d --- /dev/null +++ b/.changes/linux-fix-undecorated-resize.md @@ -0,0 +1,6 @@ +--- +"tao": patch +--- + +* Fix resizing undecorated window on Linux. +* Undecorated window can be resized using touch on Linux. \ No newline at end of file diff --git a/.changes/linux-focus-events.md b/.changes/linux-focus-events.md new file mode 100644 index 0000000000..70731cc5ea --- /dev/null +++ b/.changes/linux-focus-events.md @@ -0,0 +1,5 @@ +--- +"tao": patch +--- + +Fix focus events not firing on Linux diff --git a/.changes/linux-monitor.md b/.changes/linux-monitor.md new file mode 100644 index 0000000000..a261b13660 --- /dev/null +++ b/.changes/linux-monitor.md @@ -0,0 +1,6 @@ +--- +"tao": patch +--- + +Add monitor selection when fullscreen on Linux and close possible way to create VideoMode on Linux since gtk doesn't acutally have such feature. + diff --git a/.changes/linux-redraw.md b/.changes/linux-redraw.md new file mode 100644 index 0000000000..bb880c2d8f --- /dev/null +++ b/.changes/linux-redraw.md @@ -0,0 +1,5 @@ +--- +"tao": patch +--- + +* Add `RedrawEventsCleared` and `RedrawRequested` on Linux diff --git a/.changes/linux-run-return.md b/.changes/linux-run-return.md new file mode 100644 index 0000000000..89cefed613 --- /dev/null +++ b/.changes/linux-run-return.md @@ -0,0 +1,6 @@ +--- +"tao": patch +--- + +Add run_return trait on Linux + diff --git a/.changes/linux-skip-taskbar-skips-pages.md b/.changes/linux-skip-taskbar-skips-pages.md new file mode 100644 index 0000000000..ad98469792 --- /dev/null +++ b/.changes/linux-skip-taskbar-skips-pages.md @@ -0,0 +1,5 @@ +--- +"tao": patch +--- + +`window.set_skip_taskbar()` on Linux will now also skip the pager (Alt+Tab), this matches the behavior on Windows. \ No newline at end of file diff --git a/.changes/linux-tray.md b/.changes/linux-tray.md new file mode 100644 index 0000000000..4db26df275 --- /dev/null +++ b/.changes/linux-tray.md @@ -0,0 +1,6 @@ +--- +"tao": patch +--- + +Update tray dependency version. + diff --git a/.changes/linux-unreg.md b/.changes/linux-unreg.md new file mode 100644 index 0000000000..ffe9eb19af --- /dev/null +++ b/.changes/linux-unreg.md @@ -0,0 +1,6 @@ +--- +"tao": patch +--- + +Fix deadlock when unregistering shortcut on Linux. + diff --git a/.changes/linx-moved-resized-on-min-maximized.md b/.changes/linx-moved-resized-on-min-maximized.md new file mode 100644 index 0000000000..54a0e0c1cd --- /dev/null +++ b/.changes/linx-moved-resized-on-min-maximized.md @@ -0,0 +1,5 @@ +--- +"tao": patch +--- + +Fire `WindowEvent::Resized` and `WindowEvent::Moved` when window is min/maximized on Linux to align with Windows behavior. \ No newline at end of file diff --git a/.changes/mac-borderless-menu.md b/.changes/mac-borderless-menu.md new file mode 100644 index 0000000000..dc66002066 --- /dev/null +++ b/.changes/mac-borderless-menu.md @@ -0,0 +1,6 @@ +--- +"tao": patch +--- + +Fix menubar missing on borderless window. + diff --git a/.changes/mac-core-video-sys.md b/.changes/mac-core-video-sys.md new file mode 100644 index 0000000000..619f2f4d3d --- /dev/null +++ b/.changes/mac-core-video-sys.md @@ -0,0 +1,5 @@ +--- +"tao": patch +--- + +Fix core-video-sys dependency. diff --git a/.changes/mac-link.md b/.changes/mac-link.md new file mode 100644 index 0000000000..35c6031656 --- /dev/null +++ b/.changes/mac-link.md @@ -0,0 +1,6 @@ +--- +"tao": patch +--- + +Fix linking to the `ColorSync` framework on macOS 10.7, and in newer Rust versions. + diff --git a/.changes/parse-more-keycode-strings.md b/.changes/parse-more-keycode-strings.md new file mode 100644 index 0000000000..4db87c7aa7 --- /dev/null +++ b/.changes/parse-more-keycode-strings.md @@ -0,0 +1,5 @@ +--- +tao: minor +--- + +Allow more strings to parse to keycode, for example `,` is now parsed as a comma. diff --git a/.changes/raw-window-handle.md b/.changes/raw-window-handle.md new file mode 100644 index 0000000000..6cd9bded88 --- /dev/null +++ b/.changes/raw-window-handle.md @@ -0,0 +1,6 @@ +--- +"tao": patch +--- + +* Update `raw-window-handle` to `0.4` +* Add `raw_window_handle()` implementation on linux. \ No newline at end of file diff --git a/.changes/tray-mac.md b/.changes/tray-mac.md new file mode 100644 index 0000000000..e4e1094275 --- /dev/null +++ b/.changes/tray-mac.md @@ -0,0 +1,5 @@ +--- +"tao": patch +--- + +Fix click events missing whe tray has menu. diff --git a/.changes/unhide-applications.md b/.changes/unhide-applications.md new file mode 100644 index 0000000000..bf94a704a0 --- /dev/null +++ b/.changes/unhide-applications.md @@ -0,0 +1,5 @@ +--- +tao: minor +--- + +Add macOS `show_application()` method diff --git a/.changes/unix_new_any_thread.md b/.changes/unix_new_any_thread.md new file mode 100644 index 0000000000..4c39c023b6 --- /dev/null +++ b/.changes/unix_new_any_thread.md @@ -0,0 +1,5 @@ +--- +"tao": patch +--- + +Add new_any_thread to Unix event loop. diff --git a/.changes/webview2-com-sys.md b/.changes/webview2-com-sys.md new file mode 100644 index 0000000000..dd9ba58873 --- /dev/null +++ b/.changes/webview2-com-sys.md @@ -0,0 +1,5 @@ +--- +"tao": patch +--- + +Replace all of the `winapi` crate references with the `windows` crate. The generated bindings are in the `webview2-com-sys` crate to share types with WRY later. \ No newline at end of file diff --git a/.changes/window-target-clone.md b/.changes/window-target-clone.md new file mode 100644 index 0000000000..14bc2bae89 --- /dev/null +++ b/.changes/window-target-clone.md @@ -0,0 +1,5 @@ +--- +"tao": patch +--- + +Implement `Clone` for `EventLoopWindowTarget`. diff --git a/.changes/windows-0.25.0.md b/.changes/windows-0.25.0.md new file mode 100644 index 0000000000..14ee56d6de --- /dev/null +++ b/.changes/windows-0.25.0.md @@ -0,0 +1,5 @@ +--- +"tao": patch +--- + +Update the `windows` crate to 0.25.0, which comes with pre-built libraries. Tao no longer depends on `webview2-com-sys` to generate bindings shared with WRY. \ No newline at end of file diff --git a/.changes/windows-0.29.0.md b/.changes/windows-0.29.0.md new file mode 100644 index 0000000000..3f5d499cc8 --- /dev/null +++ b/.changes/windows-0.29.0.md @@ -0,0 +1,5 @@ +--- +"tao": patch +--- + +Update the `windows` crate to 0.29.0. \ No newline at end of file diff --git a/.changes/windows-0.30.0.md b/.changes/windows-0.30.0.md new file mode 100644 index 0000000000..4174c6c6ee --- /dev/null +++ b/.changes/windows-0.30.0.md @@ -0,0 +1,5 @@ +--- +"tao": patch +--- + +Update the `windows` crate to 0.30.0. This version re-introduced a lot of new-types for things like HWND, LRESULT, WPARAM, LPARAM, etc. \ No newline at end of file diff --git a/.changes/windows-fix-maximized-state.md b/.changes/windows-fix-maximized-state.md new file mode 100644 index 0000000000..7b795c51f2 --- /dev/null +++ b/.changes/windows-fix-maximized-state.md @@ -0,0 +1,5 @@ +--- +"tao": "patch" +--- + +Fix using `WindowBuilder::with_visible` and `WindowBuilder::with_maximized` not behaving correctly. diff --git a/.changes/windows-system-tray-event-position.md b/.changes/windows-system-tray-event-position.md new file mode 100644 index 0000000000..b70b50b48e --- /dev/null +++ b/.changes/windows-system-tray-event-position.md @@ -0,0 +1,5 @@ +--- +"tao": "patch" +--- + +On Windows, send correct position on system tray events. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 3a2675caf7..7cb3c1f282 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,36 +1,30 @@ +Update "[ ]" to "[x]" to check a box - - +Please make sure to read the Pull Request Guidelines: https://github.com/tauri-apps/tauri/blob/dev/.github/CONTRIBUTING.md#pull-request-guidelines +--> -**What kind of change does this PR introduce?** (check at least one) +### What kind of change does this PR introduce? + - [ ] Bugfix - [ ] Feature +- [ ] Docs +- [ ] New Binding issue #___ - [ ] Code style update - [ ] Refactor -- [ ] Documentation - [ ] Build-related changes - [ ] Other, please describe: -**Does this PR introduce a breaking change?** (check one) - +### Does this PR introduce a breaking change? + -- [ ] Yes. Issue #___ +- [ ] Yes, and the changes were approved in issue #___ - [ ] No - -**The PR fulfills these requirements:** - -- [ ] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix: #xxx[,#xxx]`, where "xxx" is the issue number) +### Checklist +- [ ] When resolving issues, they are referenced in the PR's title (e.g `fix: remove a typo, closes #___, #___`) - [ ] A change file is added if any packages will require a version bump due to this PR per [the instructions in the readme](https://github.com/tauri-apps/tauri/blob/dev/.changes/readme.md). +- [ ] I have added a convincing reason for adding this feature, if necessary -If adding a **new feature**, the PR's description includes: -- [ ] A convincing reason for adding this feature (to avoid wasting your time, it's best to open a suggestion issue first and wait for approval before working on it) - -**Other information:** +### Other information diff --git a/.github/workflows/fmt.yml b/.github/workflows/fmt.yml index 25b0126389..71857361a9 100644 --- a/.github/workflows/fmt.yml +++ b/.github/workflows/fmt.yml @@ -15,14 +15,9 @@ jobs: - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly + toolchain: stable override: true - components: rustfmt, clippy - - name: clippy check - uses: actions-rs/clippy-check@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - args: --all-targets -- -D warnings + components: rustfmt - name: fmt uses: actions-rs/cargo@v1 with: diff --git a/Cargo.toml b/Cargo.toml index 096f341656..c3f7dab9a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,8 @@ authors = [ "Tauri Programme within The Commons Conservancy", "The winit contributors" ] -edition = "2018" +edition = "2021" +rust-version = "1.56" keywords = [ "windowing" ] license = "Apache-2.0" readme = "README.md" @@ -27,7 +28,7 @@ targets = [ [features] default = [ "tray" ] -tray = [ "tauri-libappindicator" ] +tray = [ "libappindicator" ] ayatana = [ "libayatana-appindicator" ] dox = [ "gtk/dox" ] @@ -37,13 +38,13 @@ lazy_static = "1" libc = "0.2" log = "0.4" serde = { version = "1", optional = true, features = [ "serde_derive" ] } -raw-window-handle = "0.3" +raw-window-handle = "0.4" bitflags = "1" crossbeam-channel = "0.5" [dev-dependencies] image = "0.23" -simple_logger = "1.13" +env_logger = "0.9" [target."cfg(target_os = \"android\")".dependencies] ndk = "0.4" @@ -60,8 +61,8 @@ core-graphics = "0.22" dispatch = "0.2" scopeguard = "1.1" - [target."cfg(target_os = \"macos\")".dependencies.core-video-sys] - version = "0.1" + [target."cfg(target_os = \"macos\")".dependencies.tao-core-video-sys] + version = "0.2" default_features = false features = [ "display_link" ] @@ -71,43 +72,49 @@ cc = "1" [target."cfg(target_os = \"windows\")".dependencies] parking_lot = "0.11" unicode-segmentation = "1.8.0" +windows_macros = "0.30.0" - [target."cfg(target_os = \"windows\")".dependencies.winapi] - version = "0.3" - features = [ - "combaseapi", - "commctrl", - "dwmapi", - "errhandlingapi", - "imm", - "hidusage", - "libloaderapi", - "objbase", - "ole2", - "processthreadsapi", - "shellapi", - "shellscalingapi", - "shobjidl_core", - "unknwnbase", - "winbase", - "windowsx", - "winerror", - "wingdi", - "winnt", - "winnls", - "winuser", - "impl-default" +[target."cfg(target_os = \"windows\")".dependencies.windows] +version = "0.30.0" +features = [ + "alloc", + "Win32_Devices_HumanInterfaceDevice", + "Win32_Foundation", + "Win32_Globalization", + "Win32_Graphics_Dwm", + "Win32_Graphics_Gdi", + "Win32_System_Com", + "Win32_System_Com_StructuredStorage", + "Win32_System_DataExchange", + "Win32_System_Diagnostics_Debug", + "Win32_System_LibraryLoader", + "Win32_System_Memory", + "Win32_System_Ole", + "Win32_System_SystemServices", + "Win32_System_Threading", + "Win32_System_WindowsProgramming", + "Win32_UI_Accessibility", + "Win32_UI_Controls", + "Win32_UI_HiDpi", + "Win32_UI_Input_Ime", + "Win32_UI_Input_KeyboardAndMouse", + "Win32_UI_Input_Pointer", + "Win32_UI_Input_Touch", + "Win32_UI_Shell", + "Win32_UI_TextServices", + "Win32_UI_WindowsAndMessaging", ] [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] -cairo-rs = "0.14" -gio = "0.14" -glib = "0.14" -glib-sys = "0.14" -gtk = { version = "0.14", features = [ "v3_22" ] } -gdk = { version = "0.14", features = [ "v3_22" ] } -gdk-sys = "0.14" -gdk-pixbuf = { version = "0.14", features = [ "v2_36_8" ] } -libayatana-appindicator = { version = "0.1.6", optional = true } -tauri-libappindicator = { version = "0.1.2", optional = true } +cairo-rs = "0.15" +gio = "0.15" +glib = "0.15" +glib-sys = "0.15" +gtk = { version = "0.15", features = [ "v3_22" ] } +gdk = { version = "0.15", features = [ "v3_22" ] } +gdk-sys = "0.15" +gdkx11-sys = "0.15" +gdk-pixbuf = { version = "0.15", features = [ "v2_36_8" ] } +libayatana-appindicator = { version = "0.2", optional = true } +libappindicator = { version = "0.7", optional = true } x11-dl = "2.18" diff --git a/audits/Radically_Open_Security-v1-report.pdf b/audits/Radically_Open_Security-v1-report.pdf new file mode 100644 index 0000000000..743d8be9bf Binary files /dev/null and b/audits/Radically_Open_Security-v1-report.pdf differ diff --git a/examples/accelerator.rs b/examples/accelerator.rs index feab62cad5..f277a0d091 100644 --- a/examples/accelerator.rs +++ b/examples/accelerator.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 fn main() { - use simple_logger::SimpleLogger; use tao::{ accelerator::{Accelerator, RawMods}, dpi::LogicalSize, @@ -12,7 +11,7 @@ fn main() { window::WindowBuilder, }; - SimpleLogger::new().init().unwrap(); + env_logger::init(); // create a sample hotkey let hotkey = Accelerator::new(RawMods::Shift, KeyCode::Digit1); diff --git a/examples/control_flow.rs b/examples/control_flow.rs index 147fde41a9..61d68a9950 100644 --- a/examples/control_flow.rs +++ b/examples/control_flow.rs @@ -3,7 +3,6 @@ use std::{thread, time}; -use simple_logger::SimpleLogger; use tao::{ event::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -23,7 +22,7 @@ const POLL_SLEEP_TIME: time::Duration = time::Duration::from_millis(100); #[allow(clippy::single_match)] fn main() { - SimpleLogger::new().init().unwrap(); + env_logger::init(); println!("Press '1' to switch to Wait mode."); println!("Press '2' to switch to WaitUntil mode."); diff --git a/examples/cursor.rs b/examples/cursor.rs index 4f606e223d..cdc035bc37 100644 --- a/examples/cursor.rs +++ b/examples/cursor.rs @@ -1,7 +1,6 @@ // Copyright 2019-2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -use simple_logger::SimpleLogger; use tao::{ event::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -9,7 +8,7 @@ use tao::{ }; fn main() { - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); diff --git a/examples/cursor_grab.rs b/examples/cursor_grab.rs index a20fb641ac..d4c00a9431 100644 --- a/examples/cursor_grab.rs +++ b/examples/cursor_grab.rs @@ -1,7 +1,6 @@ // Copyright 2019-2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -use simple_logger::SimpleLogger; use tao::{ event::{DeviceEvent, ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -11,7 +10,7 @@ use tao::{ #[allow(clippy::single_match)] fn main() { - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); let window = WindowBuilder::new() diff --git a/examples/custom_events.rs b/examples/custom_events.rs index be3e09fc3b..5d7f037f59 100644 --- a/examples/custom_events.rs +++ b/examples/custom_events.rs @@ -3,7 +3,7 @@ #[allow(clippy::single_match)] fn main() { - use simple_logger::SimpleLogger; + env_logger::init(); use tao::{ event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -15,7 +15,6 @@ fn main() { Timer, } - SimpleLogger::new().init().unwrap(); let event_loop = EventLoop::::with_user_event(); let _window = WindowBuilder::new() diff --git a/examples/custom_menu.rs b/examples/custom_menu.rs index b99b10789a..5ccf8cb017 100644 --- a/examples/custom_menu.rs +++ b/examples/custom_menu.rs @@ -1,7 +1,6 @@ // Copyright 2019-2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -use simple_logger::SimpleLogger; #[cfg(target_os = "macos")] use tao::platform::macos::{CustomMenuItemExtMacOS, NativeImage}; use tao::{ @@ -15,7 +14,7 @@ use tao::{ }; fn main() { - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); // create clipboard instance @@ -48,6 +47,7 @@ fn main() { // add `my_sub_menu` children of `first_menu` with `Sub menu` title first_menu.add_submenu("Sub menu", true, my_sub_menu); + first_menu.add_native_item(MenuItem::CloseWindow); first_menu.add_native_item(MenuItem::Quit); // create custom item `Selected and disabled` children of `second_menu` @@ -80,9 +80,6 @@ fn main() { window_id, .. } if window_id == window.id() => *control_flow = ControlFlow::Exit, - Event::MainEventsCleared => { - window.request_redraw(); - } Event::MenuEvent { window_id, menu_id, diff --git a/examples/drag_window.rs b/examples/drag_window.rs index 470ddef562..977a3da071 100644 --- a/examples/drag_window.rs +++ b/examples/drag_window.rs @@ -1,7 +1,6 @@ // Copyright 2019-2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -use simple_logger::SimpleLogger; use tao::{ event::{ElementState, Event, KeyEvent, MouseButton, StartCause, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -11,7 +10,7 @@ use tao::{ #[allow(clippy::single_match)] fn main() { - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); let window_1 = WindowBuilder::new().build(&event_loop).unwrap(); diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index ce317d4e5d..ec02c0930f 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -3,7 +3,6 @@ use std::io::{stdin, stdout, Write}; -use simple_logger::SimpleLogger; use tao::{ event::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -14,7 +13,7 @@ use tao::{ #[allow(clippy::single_match)] #[allow(clippy::ok_expect)] fn main() { - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); print!("Please choose the fullscreen mode: (1) exclusive, (2) borderless: "); diff --git a/examples/global_shortcut.rs b/examples/global_shortcut.rs index 029dfe1d39..0cd66a914f 100644 --- a/examples/global_shortcut.rs +++ b/examples/global_shortcut.rs @@ -3,7 +3,6 @@ #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] fn main() { - use simple_logger::SimpleLogger; use std::str::FromStr; use tao::{ accelerator::{Accelerator, AcceleratorId, RawMods, SysMods}, @@ -14,7 +13,7 @@ fn main() { window::WindowBuilder, }; - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); // create new shortcut manager instance diff --git a/examples/handling_close.rs b/examples/handling_close.rs index 04d5cc5c97..8fabf9e3fb 100644 --- a/examples/handling_close.rs +++ b/examples/handling_close.rs @@ -1,7 +1,6 @@ // Copyright 2019-2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -use simple_logger::SimpleLogger; use tao::{ event::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -11,7 +10,7 @@ use tao::{ #[allow(clippy::single_match)] fn main() { - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); let _window = WindowBuilder::new() diff --git a/examples/min_max_size.rs b/examples/min_max_size.rs index f71b2abed9..5da5dca853 100644 --- a/examples/min_max_size.rs +++ b/examples/min_max_size.rs @@ -1,7 +1,6 @@ // Copyright 2019-2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -use simple_logger::SimpleLogger; use tao::{ dpi::LogicalSize, event::{Event, WindowEvent}, @@ -11,7 +10,7 @@ use tao::{ #[allow(clippy::single_match)] fn main() { - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); diff --git a/examples/minimize.rs b/examples/minimize.rs index f2aebbc652..9c4f602934 100644 --- a/examples/minimize.rs +++ b/examples/minimize.rs @@ -3,7 +3,6 @@ extern crate tao; -use simple_logger::SimpleLogger; use tao::{ event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -13,7 +12,7 @@ use tao::{ #[allow(clippy::single_match)] fn main() { - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); let window = WindowBuilder::new() diff --git a/examples/monitor_list.rs b/examples/monitor_list.rs index 46ab0476da..468b192e11 100644 --- a/examples/monitor_list.rs +++ b/examples/monitor_list.rs @@ -1,11 +1,10 @@ // Copyright 2019-2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -use simple_logger::SimpleLogger; use tao::{event_loop::EventLoop, window::WindowBuilder}; fn main() { - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); diff --git a/examples/mouse_wheel.rs b/examples/mouse_wheel.rs index bc761de577..dd6a81eebb 100644 --- a/examples/mouse_wheel.rs +++ b/examples/mouse_wheel.rs @@ -1,7 +1,6 @@ // Copyright 2019-2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -use simple_logger::SimpleLogger; use tao::{ event::{DeviceEvent, Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -11,7 +10,7 @@ use tao::{ #[allow(clippy::collapsible_match)] #[allow(clippy::single_match)] fn main() { - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); let window = WindowBuilder::new() diff --git a/examples/multithreaded.rs b/examples/multithreaded.rs index 2827d5444a..66524b81a1 100644 --- a/examples/multithreaded.rs +++ b/examples/multithreaded.rs @@ -6,7 +6,6 @@ fn main() { use std::{collections::HashMap, sync::mpsc, thread, time::Duration}; - use simple_logger::SimpleLogger; use tao::{ dpi::{PhysicalPosition, PhysicalSize, Position, Size}, event::{ElementState, Event, KeyEvent, WindowEvent}, @@ -18,7 +17,7 @@ fn main() { const WINDOW_COUNT: usize = 3; const WINDOW_SIZE: PhysicalSize = PhysicalSize::new(600, 400); - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); let mut window_senders = HashMap::with_capacity(WINDOW_COUNT); for _ in 0..WINDOW_COUNT { diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index 14ade2dd1a..58e0073ad5 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -3,7 +3,6 @@ use std::collections::HashMap; -use simple_logger::SimpleLogger; use tao::{ event::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -11,7 +10,7 @@ use tao::{ }; fn main() { - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); let mut windows = HashMap::new(); diff --git a/examples/parentwindow.rs b/examples/parentwindow.rs index 72990de47d..5cfe60a01e 100644 --- a/examples/parentwindow.rs +++ b/examples/parentwindow.rs @@ -3,7 +3,6 @@ #[cfg(any(target_os = "windows", target_os = "macos"))] fn main() { - use simple_logger::SimpleLogger; use std::collections::HashMap; #[cfg(target_os = "macos")] use tao::platform::macos::{WindowBuilderExtMacOS, WindowExtMacOS}; @@ -16,8 +15,8 @@ fn main() { window::WindowBuilder, }; #[cfg(target_os = "windows")] - use winapi::shared::windef::HWND; - SimpleLogger::new().init().unwrap(); + use windows::Win32::Foundation::HWND; + env_logger::init(); let event_loop = EventLoop::new(); let mut windows = HashMap::new(); let main_window = WindowBuilder::new().build(&event_loop).unwrap(); @@ -25,7 +24,7 @@ fn main() { #[cfg(target_os = "macos")] let parent_window = main_window.ns_window(); #[cfg(target_os = "windows")] - let parent_window = main_window.hwnd() as HWND; + let parent_window = HWND(main_window.hwnd() as _); let child_window = WindowBuilder::new() .with_parent_window(parent_window) diff --git a/examples/request_redraw.rs b/examples/request_redraw.rs index a0227128fd..e3d9d0787c 100644 --- a/examples/request_redraw.rs +++ b/examples/request_redraw.rs @@ -1,7 +1,6 @@ // Copyright 2019-2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -use simple_logger::SimpleLogger; use tao::{ event::{ElementState, Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -10,7 +9,7 @@ use tao::{ #[allow(clippy::single_match)] fn main() { - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); let window = WindowBuilder::new() diff --git a/examples/request_redraw_threaded.rs b/examples/request_redraw_threaded.rs index 8c872db953..d55a7a329e 100644 --- a/examples/request_redraw_threaded.rs +++ b/examples/request_redraw_threaded.rs @@ -3,7 +3,6 @@ use std::{thread, time}; -use simple_logger::SimpleLogger; use tao::{ event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -13,7 +12,7 @@ use tao::{ #[allow(clippy::single_match)] #[allow(clippy::collapsible_match)] fn main() { - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); let window = WindowBuilder::new() diff --git a/examples/resizable.rs b/examples/resizable.rs index 045ccd813e..3696e8d6c9 100644 --- a/examples/resizable.rs +++ b/examples/resizable.rs @@ -1,7 +1,6 @@ // Copyright 2019-2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -use simple_logger::SimpleLogger; use tao::{ dpi::LogicalSize, event::{ElementState, Event, KeyEvent, WindowEvent}, @@ -12,7 +11,7 @@ use tao::{ #[allow(clippy::single_match)] fn main() { - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); let mut resizable = false; diff --git a/examples/set_ime_position.rs b/examples/set_ime_position.rs index 03dd07a626..18ff20572d 100644 --- a/examples/set_ime_position.rs +++ b/examples/set_ime_position.rs @@ -1,7 +1,6 @@ // Copyright 2019-2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -use simple_logger::SimpleLogger; use tao::{ dpi::PhysicalPosition, event::{ElementState, Event, WindowEvent}, @@ -10,7 +9,7 @@ use tao::{ }; fn main() { - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); diff --git a/examples/system_tray.rs b/examples/system_tray.rs index 9d28006941..5781d7b39d 100644 --- a/examples/system_tray.rs +++ b/examples/system_tray.rs @@ -6,7 +6,6 @@ #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] #[cfg(feature = "tray")] fn main() { - use simple_logger::SimpleLogger; use std::collections::HashMap; #[cfg(target_os = "linux")] use std::path::Path; @@ -20,7 +19,7 @@ fn main() { window::{Window, WindowId}, }; - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); let mut windows: HashMap = HashMap::new(); diff --git a/examples/system_tray_no_menu.rs b/examples/system_tray_no_menu.rs index 0d0a7cfb91..20e9247fbb 100644 --- a/examples/system_tray_no_menu.rs +++ b/examples/system_tray_no_menu.rs @@ -6,7 +6,6 @@ #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] #[cfg(feature = "tray")] fn main() { - use simple_logger::SimpleLogger; use std::collections::HashMap; #[cfg(target_os = "linux")] use std::path::Path; @@ -27,7 +26,7 @@ fn main() { window::WindowBuilder, }; - SimpleLogger::new().init().unwrap(); + env_logger::init(); #[cfg(target_os = "macos")] let mut event_loop = EventLoop::new(); diff --git a/examples/timer.rs b/examples/timer.rs index 3a492cd2ca..35874efdac 100644 --- a/examples/timer.rs +++ b/examples/timer.rs @@ -4,7 +4,6 @@ use instant::Instant; use std::time::Duration; -use simple_logger::SimpleLogger; use tao::{ event::{Event, StartCause, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -13,7 +12,7 @@ use tao::{ #[allow(clippy::single_match)] fn main() { - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); let _window = WindowBuilder::new() diff --git a/examples/transparent.rs b/examples/transparent.rs index baad4daf2e..6eee6bb4c3 100644 --- a/examples/transparent.rs +++ b/examples/transparent.rs @@ -1,7 +1,6 @@ // Copyright 2019-2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -use simple_logger::SimpleLogger; use tao::{ event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -10,7 +9,7 @@ use tao::{ #[allow(clippy::single_match)] fn main() { - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); let window = WindowBuilder::new() diff --git a/examples/video_modes.rs b/examples/video_modes.rs index 1e51740a17..5d3ceba387 100644 --- a/examples/video_modes.rs +++ b/examples/video_modes.rs @@ -1,12 +1,11 @@ // Copyright 2019-2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -use simple_logger::SimpleLogger; use tao::event_loop::EventLoop; #[allow(clippy::single_match)] fn main() { - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); let monitor = match event_loop.primary_monitor() { Some(monitor) => monitor, diff --git a/examples/window.rs b/examples/window.rs index 572380b62f..eae052b781 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -1,7 +1,6 @@ // Copyright 2019-2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -use simple_logger::SimpleLogger; use tao::{ event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -10,7 +9,7 @@ use tao::{ #[allow(clippy::single_match)] fn main() { - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); let window = WindowBuilder::new() diff --git a/examples/window_debug.rs b/examples/window_debug.rs index 26a05dff0e..7c00fc2b2f 100644 --- a/examples/window_debug.rs +++ b/examples/window_debug.rs @@ -3,7 +3,6 @@ // This example is used by developers to test various window functions. -use simple_logger::SimpleLogger; use tao::{ dpi::{LogicalSize, PhysicalSize}, event::{DeviceEvent, ElementState, Event, KeyEvent, RawKeyEvent, WindowEvent}, @@ -15,7 +14,7 @@ use tao::{ #[allow(clippy::single_match)] #[allow(clippy::collapsible_match)] fn main() { - SimpleLogger::new().init().unwrap(); + env_logger::init(); let event_loop = EventLoop::new(); let window = WindowBuilder::new() diff --git a/examples/window_icon.rs b/examples/window_icon.rs index b734637658..b245206231 100644 --- a/examples/window_icon.rs +++ b/examples/window_icon.rs @@ -4,7 +4,6 @@ extern crate image; use std::path::Path; -use simple_logger::SimpleLogger; use tao::{ event::Event, event_loop::{ControlFlow, EventLoop}, @@ -13,7 +12,7 @@ use tao::{ #[allow(clippy::single_match)] fn main() { - SimpleLogger::new().init().unwrap(); + env_logger::init(); // You'll have to choose an icon size at your own discretion. On Linux, the icon should be // provided in whatever size it was naturally drawn; that is, don’t scale the image before passing diff --git a/examples/window_run_return.rs b/examples/window_run_return.rs index bee1b0b12d..3b1fb4484c 100644 --- a/examples/window_run_return.rs +++ b/examples/window_run_return.rs @@ -7,7 +7,6 @@ fn main() { use std::{thread::sleep, time::Duration}; - use simple_logger::SimpleLogger; use tao::{ event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -16,7 +15,7 @@ fn main() { }; let mut event_loop = EventLoop::new(); - SimpleLogger::new().init().unwrap(); + env_logger::init(); let _window = WindowBuilder::new() .with_title("A fantastic window!") .build(&event_loop) diff --git a/src/accelerator.rs b/src/accelerator.rs index 10a50d12e5..396acb383d 100644 --- a/src/accelerator.rs +++ b/src/accelerator.rs @@ -1,9 +1,6 @@ //! The Accelerator struct and associated types. -use crate::{ - error::OsError, - keyboard::{KeyCode, ModifiersState, NativeKeyCode}, -}; +use crate::keyboard::{KeyCode, ModifiersState, NativeKeyCode}; use std::{ borrow::Borrow, collections::hash_map::DefaultHasher, @@ -62,9 +59,9 @@ impl Accelerator { // compatible with tauri and it also open the option // to generate accelerator from string impl FromStr for Accelerator { - type Err = OsError; + type Err = AcceleratorParseError; fn from_str(accelerator_string: &str) -> Result { - Ok(parse_accelerator(accelerator_string)) + parse_accelerator(accelerator_string) } } @@ -256,14 +253,37 @@ impl AcceleratorId { } } -fn parse_accelerator(accelerator_string: &str) -> Accelerator { +#[derive(Debug, Clone)] +pub struct AcceleratorParseError(String); + +impl std::fmt::Display for AcceleratorParseError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "[AcceleratorParseError]: {}", self.0) + } +} + +fn parse_accelerator(accelerator_string: &str) -> Result { let mut mods = ModifiersState::empty(); let mut key = KeyCode::Unidentified(NativeKeyCode::Unidentified); for raw in accelerator_string.to_uppercase().split('+') { let token = raw.trim().to_string(); if token.is_empty() { - continue; + return Err(AcceleratorParseError( + "Unexpected empty token while parsing accelerator".into(), + )); + } + + if key != KeyCode::Unidentified(NativeKeyCode::Unidentified) { + // at this point we already parsed the modifiers and found a main key but + // the function received more then one main key or it is not in the right order + // examples: + // 1. "Ctrl+Shift+C+A" => only one main key should be allowd. + // 2. "Ctrl+C+Shift" => wrong order + return Err(AcceleratorParseError(format!( + "Unexpected accelerator string format: \"{}\"", + accelerator_string + ))); } match token.as_str() { @@ -287,18 +307,26 @@ fn parse_accelerator(accelerator_string: &str) -> Accelerator { } _ => { if let Ok(keycode) = KeyCode::from_str(token.to_uppercase().as_str()) { - key = keycode; + match keycode { + KeyCode::Unidentified(_) => { + return Err(AcceleratorParseError(format!( + "Couldn't identify \"{}\" as a valid `KeyCode`", + token + ))) + } + _ => key = keycode, + } } } } } - Accelerator { + Ok(Accelerator { // use the accelerator string as id id: Some(AcceleratorId(hash_string_to_u16(accelerator_string))), key, mods, - } + }) } fn hash_string_to_u16(title: &str) -> u16 { @@ -319,7 +347,7 @@ fn hash_accelerator_to_u16(hotkey: Accelerator) -> u16 { #[test] fn test_parse_accelerator() { assert_eq!( - parse_accelerator("CTRL+X"), + parse_accelerator("CTRL+X").unwrap(), Accelerator { id: Some(AcceleratorId::new("CTRL+X")), mods: ModifiersState::CONTROL, @@ -327,7 +355,7 @@ fn test_parse_accelerator() { } ); assert_eq!( - parse_accelerator("SHIFT+C"), + parse_accelerator("SHIFT+C").unwrap(), Accelerator { id: Some(AcceleratorId::new("SHIFT+C")), mods: ModifiersState::SHIFT, @@ -335,7 +363,7 @@ fn test_parse_accelerator() { } ); assert_eq!( - parse_accelerator("CTRL+Z"), + parse_accelerator("CTRL+Z").unwrap(), Accelerator { id: Some(AcceleratorId::new("CTRL+Z")), mods: ModifiersState::CONTROL, @@ -343,7 +371,7 @@ fn test_parse_accelerator() { } ); assert_eq!( - parse_accelerator("super+ctrl+SHIFT+alt+Up"), + parse_accelerator("super+ctrl+SHIFT+alt+Up").unwrap(), Accelerator { id: Some(AcceleratorId::new("super+ctrl+SHIFT+alt+Up")), mods: ModifiersState::SUPER @@ -354,7 +382,7 @@ fn test_parse_accelerator() { } ); assert_eq!( - parse_accelerator("5"), + parse_accelerator("5").unwrap(), Accelerator { id: Some(AcceleratorId::new("5")), mods: ModifiersState::empty(), @@ -362,7 +390,7 @@ fn test_parse_accelerator() { } ); assert_eq!( - parse_accelerator("G"), + parse_accelerator("G").unwrap(), Accelerator { id: Some(AcceleratorId::new("G")), mods: ModifiersState::empty(), @@ -370,7 +398,7 @@ fn test_parse_accelerator() { } ); assert_eq!( - parse_accelerator("G"), + parse_accelerator("G").unwrap(), Accelerator { // id not with same uppercase should work id: Some(AcceleratorId::new("g")), @@ -378,24 +406,15 @@ fn test_parse_accelerator() { key: KeyCode::KeyG, } ); + + let acc = parse_accelerator("+G"); + assert!(acc.is_err()); + + let acc = parse_accelerator("SHGSH+G"); + assert!(acc.is_err()); + assert_eq!( - parse_accelerator("+G"), - Accelerator { - id: Some(AcceleratorId::new("+G")), - mods: ModifiersState::empty(), - key: KeyCode::KeyG, - } - ); - assert_eq!( - parse_accelerator("SHGSH+G"), - Accelerator { - id: Some(AcceleratorId::new("SHGSH+G")), - mods: ModifiersState::empty(), - key: KeyCode::KeyG, - } - ); - assert_eq!( - parse_accelerator("SHiFT+F12"), + parse_accelerator("SHiFT+F12").unwrap(), Accelerator { id: Some(AcceleratorId::new("SHIFT+F12")), mods: ModifiersState::SHIFT, @@ -403,7 +422,7 @@ fn test_parse_accelerator() { } ); assert_eq!( - parse_accelerator("CmdOrCtrl+Space"), + parse_accelerator("CmdOrCtrl+Space").unwrap(), Accelerator { id: Some(AcceleratorId::new("CmdOrCtrl+Space")), #[cfg(target_os = "macos")] @@ -413,12 +432,7 @@ fn test_parse_accelerator() { key: KeyCode::Space, } ); - assert_eq!( - parse_accelerator("CTRL+"), - Accelerator { - id: Some(AcceleratorId::new("CTRL+")), - mods: ModifiersState::CONTROL, - key: KeyCode::Unidentified(NativeKeyCode::Unidentified), - } - ); + + let acc = parse_accelerator("CTRL+"); + assert!(acc.is_err()); } diff --git a/src/clipboard.rs b/src/clipboard.rs index b33f25b80e..5294e53fe1 100644 --- a/src/clipboard.rs +++ b/src/clipboard.rs @@ -54,6 +54,7 @@ pub(crate) type FormatId = &'static str; /// Object that allows you to access the `ClipboardFormat`. #[derive(Debug, Clone)] +#[allow(dead_code)] pub(crate) struct ClipboardFormat { pub(crate) identifier: FormatId, pub(crate) data: Vec, diff --git a/src/event.rs b/src/event.rs index 722e00286a..f1a221d84c 100644 --- a/src/event.rs +++ b/src/event.rs @@ -138,6 +138,15 @@ pub enum Event<'a, T: 'static> { /// /// Mainly of interest to applications with mostly-static graphics that avoid redrawing unless /// something changes, like most non-game GUIs. + /// + /// ## Platform-specific + /// + /// - **Linux: This is triggered by `draw` signal of the gtk window. It can be used to detect if + /// the window is requested to redraw. But widgets it contains are usually not tied to its signal. + /// So if you really want to draw each component, please consider using `connect_draw` method + /// from [`WidgetExt`] directly.** + /// + /// [`WidgetExt`]: https://gtk-rs.org/gtk3-rs/stable/latest/docs/gtk/prelude/trait.WidgetExt.html RedrawRequested(WindowId), /// Emitted after all `RedrawRequested` events have been processed and control flow is about to diff --git a/src/event_loop.rs b/src/event_loop.rs index 9999cf0ef9..eafff94cb9 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -41,6 +41,7 @@ pub struct EventLoop { /// your callback. `EventLoop` will coerce into this type (`impl Deref for /// EventLoop`), so functions that take this as a parameter can also take /// `&EventLoop`. +#[derive(Clone)] pub struct EventLoopWindowTarget { pub(crate) p: platform_impl::EventLoopWindowTarget, pub(crate) _marker: ::std::marker::PhantomData<*mut ()>, // Not Send nor Sync @@ -65,9 +66,9 @@ impl fmt::Debug for EventLoopWindowTarget { /// /// ## Persistency /// Almost every change is persistent between multiple calls to the event loop closure within a -/// given run loop. The only exception to this is `Exit` which, once set, cannot be unset. Changes -/// are **not** persistent between multiple calls to `run_return` - issuing a new call will reset -/// the control flow to `Poll`. +/// given run loop. The only exception to this is `ExitWithCode` which, once set, cannot be unset. +/// Changes are **not** persistent between multiple calls to `run_return` - issuing a new call will +/// reset the control flow to `Poll`. /// /// [events_cleared]: crate::event::Event::RedrawEventsCleared #[non_exhaustive] @@ -82,9 +83,29 @@ pub enum ControlFlow { /// arrives or the given time is reached. WaitUntil(Instant), /// Send a `LoopDestroyed` event and stop the event loop. This variant is *sticky* - once set, - /// `control_flow` cannot be changed from `Exit`, and any future attempts to do so will result - /// in the `control_flow` parameter being reset to `Exit`. - Exit, + /// `control_flow` cannot be changed from `ExitWithCode`, and any future attempts to do so will + /// result in the `control_flow` parameter being reset to `ExitWithCode`. + /// + /// The contained number will be used as exit code. The [`Exit`] constant is a shortcut for this + /// with exit code 0. + /// + /// ## Platform-specific + /// + /// - **Android / iOS / WASM**: The supplied exit code is unused. + /// - **Unix**: On most Unix-like platforms, only the 8 least significant bits will be used, + /// which can cause surprises with negative exit values (`-42` would end up as `214`). See + /// [`std::process::exit`]. + /// + /// [`Exit`]: ControlFlow::Exit + ExitWithCode(i32), +} + +impl ControlFlow { + /// Alias for [`ExitWithCode`]`(0)`. + /// + /// [`ExitWithCode`]: ControlFlow::ExitWithCode + #[allow(non_upper_case_globals)] + pub const Exit: Self = Self::ExitWithCode(0); } impl Default for ControlFlow { @@ -142,6 +163,11 @@ impl EventLoop { /// /// Any values not passed to this function will *not* be dropped. /// + /// ## Platform-specific + /// + /// - **Unix**: The program terminates with exit code 1 if the display server + /// disconnects. + /// /// [`ControlFlow`]: crate::event_loop::ControlFlow #[inline] pub fn run(self, event_handler: F) -> ! diff --git a/src/keyboard.rs b/src/keyboard.rs index ee57cce198..cc50a9ba07 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -667,11 +667,11 @@ impl FromStr for KeyCode { type Err = OsError; fn from_str(accelerator_string: &str) -> Result { let keycode = match accelerator_string.to_uppercase().as_str() { - "BACKQUOTE" => KeyCode::Backquote, + "`" | "BACKQUOTE" => KeyCode::Backquote, "BACKSLASH" => KeyCode::Backslash, - "BRACKETLEFT" => KeyCode::BracketLeft, - "BRACKETRIGHT" => KeyCode::BracketRight, - "COMMA" => KeyCode::Comma, + "[" | "BRACKETLEFT" => KeyCode::BracketLeft, + "]" | "BRACKETRIGHT" => KeyCode::BracketRight, + "," | "COMMA" => KeyCode::Comma, "0" => KeyCode::Digit0, "1" => KeyCode::Digit1, "2" => KeyCode::Digit2, @@ -682,20 +682,20 @@ impl FromStr for KeyCode { "7" => KeyCode::Digit7, "8" => KeyCode::Digit8, "9" => KeyCode::Digit9, - "NUMPAD0" => KeyCode::Numpad0, - "NUMPAD1" => KeyCode::Numpad1, - "NUMPAD2" => KeyCode::Numpad2, - "NUMPAD3" => KeyCode::Numpad3, - "NUMPAD4" => KeyCode::Numpad4, - "NUMPAD5" => KeyCode::Numpad5, - "NUMPAD6" => KeyCode::Numpad6, - "NUMPAD7" => KeyCode::Numpad7, - "NUMPAD8" => KeyCode::Numpad8, - "NUMPAD9" => KeyCode::Numpad9, + "NUM0" | "NUMPAD0" => KeyCode::Numpad0, + "NUM1" | "NUMPAD1" => KeyCode::Numpad1, + "NUM2" | "NUMPAD2" => KeyCode::Numpad2, + "NUM3" | "NUMPAD3" => KeyCode::Numpad3, + "NUM4" | "NUMPAD4" => KeyCode::Numpad4, + "NUM5" | "NUMPAD5" => KeyCode::Numpad5, + "NUM6" | "NUMPAD6" => KeyCode::Numpad6, + "NUM7" | "NUMPAD7" => KeyCode::Numpad7, + "NUM8" | "NUMPAD8" => KeyCode::Numpad8, + "NUM9" | "NUMPAD9" => KeyCode::Numpad9, "=" => KeyCode::Equal, "-" => KeyCode::Minus, - "PERIOD" => KeyCode::Period, - "QUOTE" => KeyCode::Quote, + "." | "PERIOD" => KeyCode::Period, + "'" | "QUOTE" => KeyCode::Quote, "\\" => KeyCode::IntlBackslash, "A" => KeyCode::KeyA, "B" => KeyCode::KeyB, @@ -724,8 +724,8 @@ impl FromStr for KeyCode { "Y" => KeyCode::KeyY, "Z" => KeyCode::KeyZ, - "SEMICOLON" => KeyCode::Semicolon, - "SLASH" => KeyCode::Slash, + ";" | "SEMICOLON" => KeyCode::Semicolon, + "/" | "SLASH" => KeyCode::Slash, "BACKSPACE" => KeyCode::Backspace, "CAPSLOCK" => KeyCode::CapsLock, "CONTEXTMENU" => KeyCode::ContextMenu, @@ -747,16 +747,15 @@ impl FromStr for KeyCode { "RIGHT" => KeyCode::ArrowRight, "NUMLOCK" => KeyCode::NumLock, - "NUMPADADD" => KeyCode::NumpadAdd, - "NUMPADBACKSPACE" => KeyCode::NumpadBackspace, - "NUMPADCLEAR" => KeyCode::NumpadClear, - "NUMPADCOMMA" => KeyCode::NumpadComma, - "NUMPADDIVIDE" => KeyCode::NumpadDivide, - "NUMPADSUBSTRACT" => KeyCode::NumpadSubtract, - "NUMPADENTER" => KeyCode::NumpadEnter, + "NUMADD" | "NUMPADADD" => KeyCode::NumpadAdd, + "NUMBACKSPACE" | "NUMPADBACKSPACE" => KeyCode::NumpadBackspace, + "NUMCLEAR" | "NUMPADCLEAR" => KeyCode::NumpadClear, + "NUMCOMMA" | "NUMPADCOMMA" => KeyCode::NumpadComma, + "NUMDIVIDE" | "NUMPADDIVIDE" => KeyCode::NumpadDivide, + "NUMSUBSTRACT" | "NUMPADSUBSTRACT" => KeyCode::NumpadSubtract, + "NUMENTER" | "NUMPADENTER" => KeyCode::NumpadEnter, - "ESCAPE" => KeyCode::Escape, - "ESC" => KeyCode::Escape, + "ESC" | "ESCAPE" => KeyCode::Escape, "FN" => KeyCode::Fn, "FNLOCK" => KeyCode::FnLock, "PRINTSCREEN" => KeyCode::PrintScreen, diff --git a/src/lib.rs b/src/lib.rs index ec825c57f2..af0a8008d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,8 +34,8 @@ //! You can retrieve events by calling [`EventLoop::run`][event_loop_run]. This function will //! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and //! will run until the `control_flow` argument given to the closure is set to -//! [`ControlFlow`]`::`[`Exit`], at which point [`Event`]`::`[`LoopDestroyed`] is emitted and the -//! entire program terminates. +//! [`ControlFlow`]`::`[`ExitWithCode`] (which [`ControlFlow`]`::`[`Exit`] aliases to), at which +//! point [`Event`]`::`[`LoopDestroyed`] is emitted and the entire program terminates. //! //! Tao no longer uses a `EventLoop::poll_events() -> impl Iterator`-based event loop //! model, since that can't be implemented properly on some platforms (e.g web, iOS) and works poorly on @@ -117,6 +117,7 @@ //! [event_loop_run]: event_loop::EventLoop::run //! [`ControlFlow`]: event_loop::ControlFlow //! [`Exit`]: event_loop::ControlFlow::Exit +//! [`ExitWithCode`]: event_loop::ControlFlow::ExitWithCode //! [`Window`]: window::Window //! [`WindowId`]: window::WindowId //! [`WindowBuilder`]: window::WindowBuilder @@ -132,16 +133,18 @@ //! [`platform`]: platform //! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle #![allow( + clippy::match_str_case_mismatch, clippy::upper_case_acronyms, clippy::from_over_into, clippy::option_map_unit_fn, clippy::needless_lifetimes, clippy::type_complexity, clippy::identity_op, - clippy::wrong_self_convention + clippy::wrong_self_convention, + clippy::non_send_fields_in_send_ty )] #![deny(rust_2018_idioms)] -#![deny(broken_intra_doc_links)] +#![deny(rustdoc::broken_intra_doc_links)] #[allow(unused_imports)] #[macro_use] @@ -180,7 +183,7 @@ mod platform_impl; target_os = "netbsd", target_os = "openbsd" ))] -#[cfg(feature = "tray")] +#[cfg(any(feature = "tray", feature = "ayatana"))] pub mod system_tray; pub mod window; diff --git a/src/monitor.rs b/src/monitor.rs index 67267ebd19..45f795fb3b 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -152,6 +152,9 @@ impl MonitorHandle { } /// Returns all fullscreen video modes supported by this monitor. + /// + /// ## Platform-specific + /// - **Linux:** Unsupported. This will always return empty iterator. #[inline] pub fn video_modes(&self) -> impl Iterator { self.inner.video_modes() diff --git a/src/platform/macos.rs b/src/platform/macos.rs index ad34594e9f..57a3366888 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -445,6 +445,8 @@ impl MonitorHandleExtMacOS for MonitorHandle { pub trait EventLoopWindowTargetExtMacOS { /// Hide the entire application. In most applications this is typically triggered with Command-H. fn hide_application(&self); + /// Show the entire application. + fn show_application(&self); /// Hide the other applications. In most applications this is typically triggered with Command+Option-H. fn hide_other_applications(&self); } @@ -456,6 +458,12 @@ impl EventLoopWindowTargetExtMacOS for EventLoopWindowTarget { unsafe { msg_send![app, hide: 0] } } + fn show_application(&self) { + let cls = objc::runtime::Class::get("NSApplication").unwrap(); + let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] }; + unsafe { msg_send![app, unhide: 0] } + } + fn hide_other_applications(&self) { let cls = objc::runtime::Class::get("NSApplication").unwrap(); let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] }; diff --git a/src/platform/run_return.rs b/src/platform/run_return.rs index 02755e3deb..fe90cec05c 100644 --- a/src/platform/run_return.rs +++ b/src/platform/run_return.rs @@ -1,7 +1,7 @@ // Copyright 2019-2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -#![cfg(any(target_os = "windows", target_os = "macos", target_os = "android",))] +#![cfg(not(target_os = "ios"))] use crate::{ event::Event, @@ -27,7 +27,12 @@ pub trait EventLoopExtRunReturn { /// underlying OS APIs, which cannot be hidden by `tao` without severe stability repercussions. /// /// You are strongly encouraged to use `run`, unless the use of this is absolutely necessary. - fn run_return(&mut self, event_handler: F) + /// + /// ## Platform-specific + /// + /// - **Unix-alikes** (**X11** or **Wayland**): This function returns `1` upon disconnection from + /// the display server. + fn run_return(&mut self, event_handler: F) -> i32 where F: FnMut(Event<'_, Self::UserEvent>, &EventLoopWindowTarget, &mut ControlFlow); } @@ -35,7 +40,7 @@ pub trait EventLoopExtRunReturn { impl EventLoopExtRunReturn for EventLoop { type UserEvent = T; - fn run_return(&mut self, event_handler: F) + fn run_return(&mut self, event_handler: F) -> i32 where F: FnMut(Event<'_, Self::UserEvent>, &EventLoopWindowTarget, &mut ControlFlow), { diff --git a/src/platform/unix.rs b/src/platform/unix.rs index 4fc4adb70e..3e9579f719 100644 --- a/src/platform/unix.rs +++ b/src/platform/unix.rs @@ -9,8 +9,11 @@ target_os = "openbsd" ))] -pub use crate::platform_impl::hit_test; -use crate::window::{Window, WindowBuilder}; +pub use crate::platform_impl::{hit_test, EventLoop as UnixEventLoop}; +use crate::{ + event_loop::EventLoop, + window::{Window, WindowBuilder}, +}; /// Additional methods on `Window` that are specific to Unix. pub trait WindowExtUnix { @@ -42,3 +45,28 @@ impl WindowBuilderExtUnix for WindowBuilder { self } } + +/// Additional methods on `EventLoop` that are specific to Unix. +pub trait EventLoopExtUnix { + /// Builds a new `EventLoop` on any thread. + /// + /// This method bypasses the cross-platform compatibility requirement + /// that `EventLoop` be created on the main thread. + fn new_any_thread() -> Self + where + Self: Sized; +} + +fn wrap_ev(event_loop: UnixEventLoop) -> EventLoop { + EventLoop { + event_loop, + _marker: std::marker::PhantomData, + } +} + +impl EventLoopExtUnix for EventLoop { + #[inline] + fn new_any_thread() -> Self { + wrap_ev(UnixEventLoop::new_any_thread()) + } +} diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 85038449e8..76429665e9 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -3,7 +3,7 @@ #![cfg(target_os = "windows")] -use std::{os::raw::c_void, path::Path}; +use std::path::Path; pub use crate::platform_impl::hit_test; use crate::{ @@ -15,12 +15,9 @@ use crate::{ window::{BadIcon, Icon, Theme, Window, WindowBuilder}, }; use libc; -use winapi::{ - shared::{ - minwindef::{self, WORD}, - windef::{HMENU, HWND}, - }, - um::winuser, +use windows::Win32::{ + Foundation::HWND, + UI::{Input::KeyboardAndMouse::*, WindowsAndMessaging::*}, }; /// Additional methods on `EventLoop` that are specific to Windows. @@ -114,7 +111,7 @@ pub trait WindowExtWindows { fn reset_dead_keys(&self); /// Starts the resizing drag from given edge - fn begin_resize_drag(&self, edge: isize); + fn begin_resize_drag(&self, edge: isize, button: u32, x: i32, y: i32); /// Whether to show the window icon in the taskbar or not. fn set_skip_taskbar(&self, skip: bool); @@ -123,18 +120,18 @@ pub trait WindowExtWindows { impl WindowExtWindows for Window { #[inline] fn hinstance(&self) -> *mut libc::c_void { - self.window.hinstance() as *mut _ + self.window.hinstance().0 as _ } #[inline] fn hwnd(&self) -> *mut libc::c_void { - self.window.hwnd() as *mut _ + self.window.hwnd().0 as _ } #[inline] fn set_enable(&self, enabled: bool) { unsafe { - winapi::um::winuser::EnableWindow(self.hwnd() as _, enabled as _); + EnableWindow(self.window.hwnd(), enabled); } } @@ -154,27 +151,13 @@ impl WindowExtWindows for Window { } #[inline] - fn begin_resize_drag(&self, edge: isize) { - unsafe { - let point = { - let mut pos = std::mem::zeroed(); - winuser::GetCursorPos(&mut pos); - pos - }; - - winuser::ReleaseCapture(); - winuser::PostMessageW( - self.hwnd() as _, - winuser::WM_NCLBUTTONDOWN, - edge as minwindef::WPARAM, - &point as *const _ as minwindef::LPARAM, - ); - } + fn begin_resize_drag(&self, edge: isize, button: u32, x: i32, y: i32) { + self.window.begin_resize_drag(edge, button, x, y) } #[inline] fn set_skip_taskbar(&self, skip: bool) { - self.window.set_skip_taskbar(skip, true); + self.window.set_skip_taskbar(skip); } } @@ -203,7 +186,8 @@ pub trait WindowBuilderExtWindows { /// /// Parent and menu are mutually exclusive; a child window cannot have a menu! /// - /// The menu must have been manually created beforehand with [`winapi::um::winuser::CreateMenu`] or similar. + /// The menu must have been manually created beforehand with [`windows::Win32::UI::WindowsAndMessaging::CreateMenu`] + /// or similar. /// /// Note: Dark mode cannot be supported for win32 menus, it's simply not possible to change how the menus look. /// If you use this, it is recommended that you combine it with `with_theme(Some(Theme::Light))` to avoid a jarring effect. @@ -286,7 +270,7 @@ pub trait MonitorHandleExtWindows { fn native_id(&self) -> String; /// Returns the handle of the monitor - `HMONITOR`. - fn hmonitor(&self) -> *mut c_void; + fn hmonitor(&self) -> *mut libc::c_void; } impl MonitorHandleExtWindows for MonitorHandle { @@ -296,8 +280,8 @@ impl MonitorHandleExtWindows for MonitorHandle { } #[inline] - fn hmonitor(&self) -> *mut c_void { - self.inner.hmonitor() as *mut _ + fn hmonitor(&self) -> *mut libc::c_void { + self.inner.hmonitor().0 as _ } } @@ -334,7 +318,7 @@ pub trait IconExtWindows: Sized { /// /// In cases where the specified size does not exist in the file, Windows may perform scaling /// to get an icon of the desired size. - fn from_resource(ordinal: WORD, size: Option>) -> Result; + fn from_resource(ordinal: u16, size: Option>) -> Result; } impl IconExtWindows for Icon { @@ -343,7 +327,7 @@ impl IconExtWindows for Icon { Ok(Icon { inner: win_icon }) } - fn from_resource(ordinal: WORD, size: Option>) -> Result { + fn from_resource(ordinal: u16, size: Option>) -> Result { let win_icon = WinIcon::from_resource(ordinal, size)?; Ok(Icon { inner: win_icon }) } diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 44a1b8c577..47d0d434f6 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -18,6 +18,7 @@ use ndk::{ }; use ndk_glue::{Event, Rect}; use ndk_sys::AKeyEvent_getKeyCode; +use raw_window_handle::{AndroidNdkHandle, RawWindowHandle}; use std::{ collections::VecDeque, convert::TryInto, @@ -116,10 +117,10 @@ pub struct EventLoop { macro_rules! call_event_handler { ( $event_handler:expr, $window_target:expr, $cf:expr, $event:expr ) => {{ - if $cf != ControlFlow::Exit { - $event_handler($event, $window_target, &mut $cf); + if let ControlFlow::ExitWithCode(code) = $cf { + $event_handler($event, $window_target, &mut ControlFlow::ExitWithCode(code)); } else { - $event_handler($event, $window_target, &mut ControlFlow::Exit); + $event_handler($event, $window_target, &mut $cf); } }}; } @@ -146,11 +147,11 @@ impl EventLoop { F: 'static + FnMut(event::Event<'_, T>, &event_loop::EventLoopWindowTarget, &mut ControlFlow), { - self.run_return(event_handler); - ::std::process::exit(0); + let exit_code = self.run_return(event_handler); + ::std::process::exit(exit_code); } - pub fn run_return(&mut self, mut event_handler: F) + pub fn run_return(&mut self, mut event_handler: F) -> i32 where F: FnMut(event::Event<'_, T>, &event_loop::EventLoopWindowTarget, &mut ControlFlow), { @@ -376,7 +377,7 @@ impl EventLoop { ); match control_flow { - ControlFlow::Exit => { + ControlFlow::ExitWithCode(code) => { self.first_event = poll( self .looper @@ -387,7 +388,7 @@ impl EventLoop { start: Instant::now(), requested_resume: None, }; - break 'event_loop; + break 'event_loop code; } ControlFlow::Poll => { self.first_event = poll( @@ -463,6 +464,7 @@ impl Clone for EventLoopProxy { } } +#[derive(Clone)] pub struct EventLoopWindowTarget { _marker: std::marker::PhantomData, } @@ -657,15 +659,14 @@ impl Window { )) } - pub fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle { - let a_native_window = if let Some(native_window) = ndk_glue::native_window().as_ref() { - unsafe { native_window.ptr().as_mut() as *mut _ as *mut _ } + pub fn raw_window_handle(&self) -> RawWindowHandle { + let mut handle = AndroidNdkHandle::empty(); + if let Some(native_window) = ndk_glue::native_window().as_ref() { + handle.a_native_window = unsafe { native_window.ptr().as_mut() as *mut _ as *mut _ } } else { panic!("Cannot get the native window, it's null and will always be null before Event::Resumed and after Event::Suspended. Make sure you only call this function between those events."); }; - let mut handle = raw_window_handle::android::AndroidHandle::empty(); - handle.a_native_window = a_native_window; - raw_window_handle::RawWindowHandle::Android(handle) + RawWindowHandle::AndroidNdk(handle) } pub fn config(&self) -> Configuration { diff --git a/src/platform_impl/ios/app_state.rs b/src/platform_impl/ios/app_state.rs index 3fda3646ef..b59b586f1f 100644 --- a/src/platform_impl/ios/app_state.rs +++ b/src/platform_impl/ios/app_state.rs @@ -299,7 +299,7 @@ impl AppState { }; (waiting_event_handler, event) } - (ControlFlow::Exit, _) => bug!("unexpected `ControlFlow` `Exit`"), + (ControlFlow::ExitWithCode(_), _) => bug!("unexpected `ControlFlow` `Exit`"), s => bug!("`EventHandler` unexpectedly woke up {:?}", s), }; @@ -450,7 +450,7 @@ impl AppState { }); self.waker.start() } - (_, ControlFlow::Exit) => { + (_, ControlFlow::ExitWithCode(_)) => { // https://developer.apple.com/library/archive/qa/qa1561/_index.html // it is not possible to quit an iOS app gracefully and programatically warn!("`ControlFlow::Exit` ignored on iOS"); diff --git a/src/platform_impl/ios/event_loop.rs b/src/platform_impl/ios/event_loop.rs index a6a886446c..bef51d8810 100644 --- a/src/platform_impl/ios/event_loop.rs +++ b/src/platform_impl/ios/event_loop.rs @@ -49,6 +49,7 @@ pub enum EventProxy { }, } +#[derive(Clone)] pub struct EventLoopWindowTarget { receiver: Receiver, sender_to_clone: Sender, diff --git a/src/platform_impl/ios/mod.rs b/src/platform_impl/ios/mod.rs index 06ce41acef..ebd962b31e 100644 --- a/src/platform_impl/ios/mod.rs +++ b/src/platform_impl/ios/mod.rs @@ -65,7 +65,8 @@ // window size/position. macro_rules! assert_main_thread { ($($t:tt)*) => { - if !msg_send![class!(NSThread), isMainThread] { + let is_main_thread: ::objc::runtime::BOOL = msg_send!(class!(NSThread), isMainThread); + if is_main_thread == ::objc::runtime::NO { panic!($($t)*); } }; diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index f152e7415d..8fd8f28ba9 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -1,7 +1,7 @@ // Copyright 2019-2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -use raw_window_handle::{ios::IOSHandle, RawWindowHandle}; +use raw_window_handle::{RawWindowHandle, UiKitHandle}; use std::{ collections::VecDeque, ops::{Deref, DerefMut}, @@ -229,7 +229,7 @@ impl Inner { let uiscreen = match monitor { Some(Fullscreen::Exclusive(video_mode)) => { let uiscreen = video_mode.video_mode.monitor.ui_screen() as id; - let () = msg_send![uiscreen, setCurrentMode: video_mode.video_mode.screen_mode]; + let () = msg_send![uiscreen, setCurrentMode: video_mode.video_mode.screen_mode.0]; uiscreen } Some(Fullscreen::Borderless(monitor)) => monitor @@ -341,13 +341,11 @@ impl Inner { } pub fn raw_window_handle(&self) -> RawWindowHandle { - let handle = IOSHandle { - ui_window: self.window as _, - ui_view: self.view as _, - ui_view_controller: self.view_controller as _, - ..IOSHandle::empty() - }; - RawWindowHandle::IOS(handle) + let mut handle = UiKitHandle::empty(); + handle.ui_window = self.window as _; + handle.ui_view = self.view as _; + handle.ui_view_controller = self.view_controller as _; + RawWindowHandle::UiKit(handle) } } diff --git a/src/platform_impl/linux/event_loop.rs b/src/platform_impl/linux/event_loop.rs index 367b496cb0..0f73c8a55f 100644 --- a/src/platform_impl/linux/event_loop.rs +++ b/src/platform_impl/linux/event_loop.rs @@ -7,34 +7,34 @@ use std::{ error::Error, process, rc::Rc, - sync::{mpsc::SendError, Mutex}, + sync::mpsc::SendError, time::Instant, }; use gdk::{Cursor, CursorType, EventKey, EventMask, WindowEdge, WindowState}; use gio::{prelude::*, Cancellable}; use glib::{source::Priority, Continue, MainContext}; -use gtk::{prelude::*, AboutDialog, ApplicationWindow, Inhibit}; +use gtk::{prelude::*, AboutDialog, Inhibit}; use crate::{ accelerator::AcceleratorId, - dpi::{PhysicalPosition, PhysicalSize}, - event::{DeviceId as RootDeviceId, ElementState, Event, MouseButton, StartCause, WindowEvent}, + dpi::{LogicalPosition, LogicalSize}, + event::{ElementState, Event, MouseButton, StartCause, WindowEvent}, event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}, keyboard::ModifiersState, menu::{MenuItem, MenuType}, monitor::MonitorHandle as RootMonitorHandle, - platform_impl::platform::window::hit_test, - window::{CursorIcon, WindowId as RootWindowId}, + platform_impl::platform::{window::hit_test, DEVICE_ID}, + window::{CursorIcon, Fullscreen, WindowId as RootWindowId}, }; use super::{ keyboard, monitor::MonitorHandle, window::{WindowId, WindowRequest}, - DeviceId, }; +#[derive(Clone)] pub struct EventLoopWindowTarget { /// Gdk display pub(crate) display: gdk::Display, @@ -55,7 +55,7 @@ impl EventLoopWindowTarget { let numbers = display.n_monitors(); for i in 0..numbers { - let monitor = MonitorHandle::new(&display, i); + let monitor = MonitorHandle::new(display, i); handles.push_back(monitor); } @@ -77,25 +77,45 @@ pub struct EventLoop { window_target: RootELW, /// User event sender for EventLoopProxy user_event_tx: glib::Sender, - /// User event receiver - user_event_rx: glib::Receiver, - /// Window requests receiver - window_requests_rx: glib::Receiver<(WindowId, WindowRequest)>, + /// Event queue of EventLoop + events: crossbeam_channel::Receiver>, + /// Draw queue of EventLoop + draws: crossbeam_channel::Receiver, } impl EventLoop { pub fn new() -> EventLoop { assert_is_main_thread("new_any_thread"); - EventLoop::new_gtk().expect("Failed to initialize any backend!") + EventLoop::new_any_thread() + } + + pub fn new_any_thread() -> EventLoop { + let context = MainContext::default(); + context + .with_thread_default(|| EventLoop::new_gtk().expect("Failed to initialize gtk backend!")) + .expect("Failed to initialize gtk backend!") } fn new_gtk() -> Result, Box> { + let context = MainContext::default(); let app = gtk::Application::new(None, gio::ApplicationFlags::empty()); + let app_ = app.clone(); let cancellable: Option<&Cancellable> = None; app.register(cancellable)?; + // Send StartCause::Init event + let (event_tx, event_rx) = crossbeam_channel::unbounded(); + let (draw_tx, draw_rx) = crossbeam_channel::unbounded(); + let event_tx_ = event_tx.clone(); + app.connect_activate(move |_| { + if let Err(e) = event_tx_.send(Event::NewEvents(StartCause::Init)) { + log::warn!("Failed to send init event to event channel: {}", e); + } + }); + // Create event loop window target. let (window_requests_tx, window_requests_rx) = glib::MainContext::channel(Priority::default()); + let window_requests_tx_ = window_requests_tx.clone(); let display = gdk::Display::default() .expect("GdkDisplay not found. This usually means `gkt_init` hasn't called yet."); let window_target = EventLoopWindowTarget { @@ -108,53 +128,8 @@ impl EventLoop { // Create user event channel let (user_event_tx, user_event_rx) = glib::MainContext::channel(Priority::default()); - - // Create event loop itself. - let event_loop = Self { - window_target: RootELW { - p: window_target, - _marker: std::marker::PhantomData, - }, - user_event_tx, - user_event_rx, - window_requests_rx, - }; - - Ok(event_loop) - } - - #[inline] - pub fn run(self, callback: F) -> ! - where - F: FnMut(Event<'_, T>, &RootELW, &mut ControlFlow) + 'static, - { - self.run_return(callback); - process::exit(0) - } - - pub(crate) fn run_return(self, mut callback: F) - where - F: FnMut(Event<'_, T>, &RootELW, &mut ControlFlow) + 'static, - { - let mut control_flow = ControlFlow::default(); - let window_target = self.window_target; - let (event_tx, event_rx) = glib::MainContext::channel::>(Priority::default()); - - // Send StartCause::Init event let event_tx_ = event_tx.clone(); - window_target.p.app.connect_activate(move |_| { - if let Err(e) = event_tx_.send(Event::NewEvents(StartCause::Init)) { - log::warn!("Failed to send init event to event channel: {}", e); - } - }); - window_target.p.app.activate(); - - let context = MainContext::default(); - context.push_thread_default(); - - // User event - let event_tx_ = event_tx.clone(); - self.user_event_rx.attach(Some(&context), move |event| { + user_event_rx.attach(Some(&context), move |event| { if let Err(e) = event_tx_.send(Event::UserEvent(event)) { log::warn!("Failed to send user event to event channel: {}", e); } @@ -162,615 +137,790 @@ impl EventLoop { }); // Window Request - let app = window_target.p.app.clone(); - let window_requests_tx = window_target.p.window_requests_tx.clone(); - self - .window_requests_rx - .attach(Some(&context), move |(id, request)| { - if let Some(window) = app.window_by_id(id.0) { - match request { - WindowRequest::Title(title) => window.set_title(&title), - WindowRequest::Position((x, y)) => window.move_(x, y), - WindowRequest::Size((w, h)) => window.resize(w, h), - WindowRequest::MinSize((min_width, min_height)) => window - .set_geometry_hints::( - None, - Some(&gdk::Geometry { - min_width, - min_height, - max_width: 0, - max_height: 0, - base_width: 0, - base_height: 0, - width_inc: 0, - height_inc: 0, - min_aspect: 0f64, - max_aspect: 0f64, - win_gravity: gdk::Gravity::Center, - }), - gdk::WindowHints::MIN_SIZE, - ), - WindowRequest::MaxSize((max_width, max_height)) => window - .set_geometry_hints::( - None, - Some(&gdk::Geometry { - min_width: 0, - min_height: 0, - max_width, - max_height, - base_width: 0, - base_height: 0, - width_inc: 0, - height_inc: 0, - min_aspect: 0f64, - max_aspect: 0f64, - win_gravity: gdk::Gravity::Center, - }), - gdk::WindowHints::MAX_SIZE, - ), - WindowRequest::Visible(visible) => { - if visible { - window.show_all(); - } else { - window.hide(); - } - } - WindowRequest::Focus => { - // FIXME: replace with present_with_timestamp - window.present(); + window_requests_rx.attach(Some(&context), move |(id, request)| { + if let Some(window) = app_.window_by_id(id.0) { + match request { + WindowRequest::Title(title) => window.set_title(&title), + WindowRequest::Position((x, y)) => window.move_(x, y), + WindowRequest::Size((w, h)) => window.resize(w, h), + WindowRequest::MinSize((min_width, min_height)) => { + let picky_none: Option<>k::Window> = None; + window.set_geometry_hints( + picky_none, + Some(&gdk::Geometry::new( + min_width, + min_height, + 0, + 0, + 0, + 0, + 0, + 0, + 0f64, + 0f64, + gdk::Gravity::Center, + )), + gdk::WindowHints::MIN_SIZE, + ) + } + WindowRequest::MaxSize((max_width, max_height)) => { + let picky_none: Option<>k::Window> = None; + window.set_geometry_hints( + picky_none, + Some(&gdk::Geometry::new( + 0, + 0, + max_width, + max_height, + 0, + 0, + 0, + 0, + 0f64, + 0f64, + gdk::Gravity::Center, + )), + gdk::WindowHints::MAX_SIZE, + ) + } + WindowRequest::Visible(visible) => { + if visible { + window.show_all(); + } else { + window.hide(); } - WindowRequest::Resizable(resizable) => window.set_resizable(resizable), - WindowRequest::Minimized(minimized) => { - if minimized { - window.iconify(); - } else { - window.deiconify(); - } + } + WindowRequest::Focus => { + // FIXME: replace with present_with_timestamp + window.present(); + } + WindowRequest::Resizable(resizable) => window.set_resizable(resizable), + WindowRequest::Minimized(minimized) => { + if minimized { + window.iconify(); + } else { + window.deiconify(); } - WindowRequest::Maximized(maximized) => { - if maximized { - window.maximize(); - } else { - window.unmaximize(); - } + } + WindowRequest::Maximized(maximized) => { + if maximized { + window.maximize(); + } else { + window.unmaximize(); } - WindowRequest::DragWindow => { - let display = window.display(); - if let Some(cursor) = display - .default_seat() - .and_then(|device_manager| device_manager.pointer()) - { - let (_, x, y) = cursor.position(); - window.begin_move_drag(1, x, y, 0); - } + } + WindowRequest::DragWindow => { + let display = window.display(); + if let Some(cursor) = display + .default_seat() + .and_then(|device_manager| device_manager.pointer()) + { + let (_, x, y) = cursor.position(); + window.begin_move_drag(1, x, y, 0); } - WindowRequest::Fullscreen(fullscreen) => match fullscreen { - Some(_) => window.fullscreen(), - None => window.unfullscreen(), - }, - WindowRequest::Decorations(decorations) => window.set_decorated(decorations), - WindowRequest::AlwaysOnTop(always_on_top) => window.set_keep_above(always_on_top), - WindowRequest::WindowIcon(window_icon) => { - if let Some(icon) = window_icon { - window.set_icon(Some(&icon.inner.into())); + } + WindowRequest::Fullscreen(fullscreen) => match fullscreen { + Some(f) => { + if let Fullscreen::Borderless(Some(monitor)) = f { + let number = monitor.inner.number; + let screen = window.display().default_screen(); + window.fullscreen_on_monitor(&screen, number); } } - WindowRequest::UserAttention(request_type) => { - window.set_urgency_hint(request_type.is_some()) + None => window.unfullscreen(), + }, + WindowRequest::Decorations(decorations) => window.set_decorated(decorations), + WindowRequest::AlwaysOnTop(always_on_top) => window.set_keep_above(always_on_top), + WindowRequest::WindowIcon(window_icon) => { + if let Some(icon) = window_icon { + window.set_icon(Some(&icon.inner.into())); } - WindowRequest::SetSkipTaskbar(skip) => window.set_skip_taskbar_hint(skip), - WindowRequest::CursorIcon(cursor) => { - if let Some(gdk_window) = window.window() { - let display = window.display(); - match cursor { - Some(cr) => gdk_window.set_cursor( + } + WindowRequest::UserAttention(request_type) => { + window.set_urgency_hint(request_type.is_some()) + } + WindowRequest::SetSkipTaskbar(skip) => { + window.set_skip_taskbar_hint(skip); + window.set_skip_pager_hint(skip) + } + WindowRequest::CursorIcon(cursor) => { + if let Some(gdk_window) = window.window() { + let display = window.display(); + match cursor { + Some(cr) => gdk_window.set_cursor( + Cursor::from_name( + &display, + match cr { + CursorIcon::Crosshair => "crosshair", + CursorIcon::Hand => "pointer", + CursorIcon::Arrow => "crosshair", + CursorIcon::Move => "move", + CursorIcon::Text => "text", + CursorIcon::Wait => "wait", + CursorIcon::Help => "help", + CursorIcon::Progress => "progress", + CursorIcon::NotAllowed => "not-allowed", + CursorIcon::ContextMenu => "context-menu", + CursorIcon::Cell => "cell", + CursorIcon::VerticalText => "vertical-text", + CursorIcon::Alias => "alias", + CursorIcon::Copy => "copy", + CursorIcon::NoDrop => "no-drop", + CursorIcon::Grab => "grab", + CursorIcon::Grabbing => "grabbing", + CursorIcon::AllScroll => "all-scroll", + CursorIcon::ZoomIn => "zoom-in", + CursorIcon::ZoomOut => "zoom-out", + CursorIcon::EResize => "e-resize", + CursorIcon::NResize => "n-resize", + CursorIcon::NeResize => "ne-resize", + CursorIcon::NwResize => "nw-resize", + CursorIcon::SResize => "s-resize", + CursorIcon::SeResize => "se-resize", + CursorIcon::SwResize => "sw-resize", + CursorIcon::WResize => "w-resize", + CursorIcon::EwResize => "ew-resize", + CursorIcon::NsResize => "ns-resize", + CursorIcon::NeswResize => "nesw-resize", + CursorIcon::NwseResize => "nwse-resize", + CursorIcon::ColResize => "col-resize", + CursorIcon::RowResize => "row-resize", + CursorIcon::Default => "default", + }, + ) + .as_ref(), + ), + None => gdk_window + .set_cursor(Cursor::for_display(&display, CursorType::BlankCursor).as_ref()), + } + }; + } + WindowRequest::WireUpEvents => { + window.add_events( + EventMask::POINTER_MOTION_MASK + | EventMask::BUTTON1_MOTION_MASK + | EventMask::BUTTON_PRESS_MASK + | EventMask::TOUCH_MASK + | EventMask::STRUCTURE_MASK + | EventMask::FOCUS_CHANGE_MASK, + ); + + // Resizing `decorations: false` aka borderless + window.connect_motion_notify_event(|window, event| { + if !window.is_decorated() && window.is_resizable() { + if let Some(window) = window.window() { + let (cx, cy) = event.root(); + let edge = hit_test(&window, cx, cy); + window.set_cursor( Cursor::from_name( - &display, - match cr { - CursorIcon::Crosshair => "crosshair", - CursorIcon::Hand => "pointer", - CursorIcon::Arrow => "crosshair", - CursorIcon::Move => "move", - CursorIcon::Text => "text", - CursorIcon::Wait => "wait", - CursorIcon::Help => "help", - CursorIcon::Progress => "progress", - CursorIcon::NotAllowed => "not-allowed", - CursorIcon::ContextMenu => "context-menu", - CursorIcon::Cell => "cell", - CursorIcon::VerticalText => "vertical-text", - CursorIcon::Alias => "alias", - CursorIcon::Copy => "copy", - CursorIcon::NoDrop => "no-drop", - CursorIcon::Grab => "grab", - CursorIcon::Grabbing => "grabbing", - CursorIcon::AllScroll => "all-scroll", - CursorIcon::ZoomIn => "zoom-in", - CursorIcon::ZoomOut => "zoom-out", - CursorIcon::EResize => "e-resize", - CursorIcon::NResize => "n-resize", - CursorIcon::NeResize => "ne-resize", - CursorIcon::NwResize => "nw-resize", - CursorIcon::SResize => "s-resize", - CursorIcon::SeResize => "se-resize", - CursorIcon::SwResize => "sw-resize", - CursorIcon::WResize => "w-resize", - CursorIcon::EwResize => "ew-resize", - CursorIcon::NsResize => "ns-resize", - CursorIcon::NeswResize => "nesw-resize", - CursorIcon::NwseResize => "nwse-resize", - CursorIcon::ColResize => "col-resize", - CursorIcon::RowResize => "row-resize", - CursorIcon::Default => "default", + &window.display(), + match edge { + WindowEdge::North => "n-resize", + WindowEdge::South => "s-resize", + WindowEdge::East => "e-resize", + WindowEdge::West => "w-resize", + WindowEdge::NorthWest => "nw-resize", + WindowEdge::NorthEast => "ne-resize", + WindowEdge::SouthEast => "se-resize", + WindowEdge::SouthWest => "sw-resize", + _ => "default", }, ) .as_ref(), - ), - None => gdk_window.set_cursor(Some(&Cursor::for_display( - &display, - CursorType::BlankCursor, - ))), + ); } - }; - } - WindowRequest::WireUpEvents => { - // resizing `decorations: false` aka borderless - window.add_events(EventMask::POINTER_MOTION_MASK | EventMask::BUTTON_MOTION_MASK); - window.connect_motion_notify_event(|window, event| { - if window.is_decorated() && window.is_resizable() { - if let Some(window) = window.window() { - let (cx, cy) = event.root(); - let edge = hit_test(&window, cx, cy); - // FIXME: calling `window.begin_resize_drag` seems to revert the cursor back to normal style - window.set_cursor( - Cursor::from_name( - &window.display(), - match edge { - WindowEdge::North => "n-resize", - WindowEdge::South => "s-resize", - WindowEdge::East => "e-resize", - WindowEdge::West => "w-resize", - WindowEdge::NorthWest => "nw-resize", - WindowEdge::NorthEast => "ne-resize", - WindowEdge::SouthEast => "se-resize", - WindowEdge::SouthWest => "sw-resize", - _ => "default", - }, - ) - .as_ref(), - ); + } + Inhibit(false) + }); + window.connect_button_press_event(|window, event| { + if !window.is_decorated() && window.is_resizable() && event.button() == 1 { + if let Some(window) = window.window() { + let (cx, cy) = event.root(); + let result = hit_test(&window, cx, cy); + + // Ignore the `__Unknown` variant so the window receives the click correctly if it is not on the edges. + match result { + WindowEdge::__Unknown(_) => (), + _ => { + // FIXME: calling `window.begin_resize_drag` uses the default cursor, it should show a resizing cursor instead + window.begin_resize_drag(result, 1, cx as i32, cy as i32, event.time()) + } } } - Inhibit(false) - }); - window.connect_button_press_event(|window, event| { - if window.is_decorated() && window.is_resizable() { - if event.button() == 1 { - if let Some(window) = window.window() { - let (cx, cy) = event.root(); + } + + Inhibit(false) + }); + window.connect_touch_event(|window, event| { + if !window.is_decorated() && window.is_resizable() { + if let Some(window) = window.window() { + if let Some((cx, cy)) = event.root_coords() { + if let Some(device) = event.device() { let result = hit_test(&window, cx, cy); - // we ignore the `__Unknown` variant so the window receives the click correctly if it is not on the edges. + // Ignore the `__Unknown` variant so the window receives the click correctly if it is not on the edges. match result { WindowEdge::__Unknown(_) => (), - _ => { - window.begin_resize_drag(result, 1, cx as i32, cy as i32, event.time()) - } + _ => window.begin_resize_drag_for_device( + result, + &device, + 0, + cx as i32, + cy as i32, + event.time(), + ), } } } } + } - Inhibit(false) - }); + Inhibit(false) + }); - let tx_clone = event_tx.clone(); - window.connect_delete_event(move |_, _| { - if let Err(e) = tx_clone.send(Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::CloseRequested, - }) { - log::warn!("Failed to send window close event to event channel: {}", e); - } - Inhibit(true) - }); + let tx_clone = event_tx.clone(); + window.connect_delete_event(move |_, _| { + if let Err(e) = tx_clone.send(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::CloseRequested, + }) { + log::warn!("Failed to send window close event to event channel: {}", e); + } + Inhibit(true) + }); + + let tx_clone = event_tx.clone(); + window.connect_configure_event(move |window, event| { + let scale_factor = window.scale_factor(); + + let (x, y) = event.position(); + if let Err(e) = tx_clone.send(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::Moved( + LogicalPosition::new(x, y).to_physical(scale_factor as f64), + ), + }) { + log::warn!("Failed to send window moved event to event channel: {}", e); + } - let tx_clone = event_tx.clone(); - window.connect_configure_event(move |_, event| { - let (x, y) = event.position(); + let (w, h) = event.size(); + if let Err(e) = tx_clone.send(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::Resized( + LogicalSize::new(w, h).to_physical(scale_factor as f64), + ), + }) { + log::warn!( + "Failed to send window resized event to event channel: {}", + e + ); + } + false + }); + + let tx_clone = event_tx.clone(); + window.connect_focus_in_event(move |_, _| { + if let Err(e) = tx_clone.send(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::Focused(true), + }) { + log::warn!( + "Failed to send window focus-in event to event channel: {}", + e + ); + } + Inhibit(false) + }); + + let tx_clone = event_tx.clone(); + window.connect_focus_out_event(move |_, _| { + if let Err(e) = tx_clone.send(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::Focused(false), + }) { + log::warn!( + "Failed to send window focus-out event to event channel: {}", + e + ); + } + Inhibit(false) + }); + + let tx_clone = event_tx.clone(); + window.connect_destroy_event(move |_, _| { + if let Err(e) = tx_clone.send(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::Destroyed, + }) { + log::warn!( + "Failed to send window destroyed event to event channel: {}", + e + ); + } + Inhibit(false) + }); + + let tx_clone = event_tx.clone(); + window.connect_enter_notify_event(move |_, _| { + if let Err(e) = tx_clone.send(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::CursorEntered { + device_id: DEVICE_ID, + }, + }) { + log::warn!( + "Failed to send cursor entered event to event channel: {}", + e + ); + } + Inhibit(false) + }); + + let tx_clone = event_tx.clone(); + window.connect_motion_notify_event(move |window, motion| { + if let Some(cursor) = motion.device() { + let scale_factor = window.scale_factor(); + let (_, x, y) = cursor.window_at_position(); if let Err(e) = tx_clone.send(Event::WindowEvent { window_id: RootWindowId(id), - event: WindowEvent::Moved(PhysicalPosition::new(x, y)), + event: WindowEvent::CursorMoved { + position: LogicalPosition::new(x, y).to_physical(scale_factor as f64), + device_id: DEVICE_ID, + // this field is depracted so it is fine to pass empty state + modifiers: ModifiersState::empty(), + }, }) { - log::warn!("Failed to send window moved event to event channel: {}", e); + log::warn!("Failed to send cursor moved event to event channel: {}", e); } - - let (w, h) = event.size(); - if let Err(e) = tx_clone.send(Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::Resized(PhysicalSize::new(w, h)), - }) { - log::warn!( - "Failed to send window resized event to event channel: {}", - e - ); + } + Inhibit(false) + }); + + let tx_clone = event_tx.clone(); + window.connect_leave_notify_event(move |_, _| { + if let Err(e) = tx_clone.send(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::CursorLeft { + device_id: DEVICE_ID, + }, + }) { + log::warn!("Failed to send cursor left event to event channel: {}", e); + } + Inhibit(false) + }); + + let tx_clone = event_tx.clone(); + window.connect_button_press_event(move |_, event| { + let button = event.button(); + if let Err(e) = tx_clone.send(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::MouseInput { + button: match button { + 1 => MouseButton::Left, + 2 => MouseButton::Middle, + 3 => MouseButton::Right, + _ => MouseButton::Other(button as u16), + }, + state: ElementState::Pressed, + device_id: DEVICE_ID, + // this field is depracted so it is fine to pass empty state + modifiers: ModifiersState::empty(), + }, + }) { + log::warn!( + "Failed to send mouse input preseed event to event channel: {}", + e + ); + } + Inhibit(false) + }); + + let tx_clone = event_tx.clone(); + window.connect_button_release_event(move |_, event| { + let button = event.button(); + if let Err(e) = tx_clone.send(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::MouseInput { + button: match button { + 1 => MouseButton::Left, + 2 => MouseButton::Middle, + 3 => MouseButton::Right, + _ => MouseButton::Other(button as u16), + }, + state: ElementState::Released, + device_id: DEVICE_ID, + // this field is depracted so it is fine to pass empty state + modifiers: ModifiersState::empty(), + }, + }) { + log::warn!( + "Failed to send mouse input released event to event channel: {}", + e + ); + } + Inhibit(false) + }); + + let tx_clone = event_tx.clone(); + let keyboard_handler = Rc::new(move |event_key: EventKey, element_state| { + // if we have a modifier lets send it + let mut mods = keyboard::get_modifiers(event_key.clone()); + if !mods.is_empty() { + // if we release the modifier tell the world + if ElementState::Released == element_state { + mods = ModifiersState::empty(); } - false - }); - - let tx_clone = event_tx.clone(); - window.connect_window_state_event(move |_window, event| { - let state = event.new_window_state(); if let Err(e) = tx_clone.send(Event::WindowEvent { window_id: RootWindowId(id), - event: WindowEvent::Focused(state.contains(WindowState::FOCUSED)), + event: WindowEvent::ModifiersChanged(mods), }) { log::warn!( - "Failed to send window focused event to event channel: {}", + "Failed to send modifiers changed event to event channel: {}", e ); + } else { + // stop here we don't want to send the key event + // as we emit the `ModifiersChanged` + return Continue(true); } - Inhibit(false) - }); + } - let tx_clone = event_tx.clone(); - window.connect_destroy_event(move |_, _| { - if let Err(e) = tx_clone.send(Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::Destroyed, - }) { - log::warn!( - "Failed to send window destroyed event to event channel: {}", - e - ); - } - Inhibit(false) - }); + // todo: implement repeat? + let event = keyboard::make_key_event(&event_key, false, None, element_state); - let tx_clone = event_tx.clone(); - window.connect_enter_notify_event(move |_, _| { + if let Some(event) = event { if let Err(e) = tx_clone.send(Event::WindowEvent { window_id: RootWindowId(id), - event: WindowEvent::CursorEntered { - // FIXME: currently we use a dummy device id, find if we can get device id from gtk - device_id: RootDeviceId(DeviceId(0)), + event: WindowEvent::KeyboardInput { + device_id: DEVICE_ID, + event, + is_synthetic: false, }, }) { - log::warn!( - "Failed to send cursor entered event to event channel: {}", - e - ); + log::warn!("Failed to send keyboard event to event channel: {}", e); } - Inhibit(false) - }); - - let tx_clone = event_tx.clone(); - window.connect_motion_notify_event(move |window, _| { - let display = window.display(); - if let Some(cursor) = display - .default_seat() - .and_then(|device_manager| device_manager.pointer()) - { - let (_, x, y) = cursor.position(); - if let Err(e) = tx_clone.send(Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::CursorMoved { - position: PhysicalPosition::new(x as f64, y as f64), - // FIXME: currently we use a dummy device id, find if we can get device id from gtk - device_id: RootDeviceId(DeviceId(0)), - // this field is depracted so it is fine to pass empty state - modifiers: ModifiersState::empty(), - }, - }) { - log::warn!("Failed to send cursor moved event to event channel: {}", e); - } - } - Inhibit(false) - }); + } + Continue(true) + }); + + let tx_clone = event_tx.clone(); + // TODO Add actual IME from system + let ime = gtk::IMContextSimple::default(); + ime.set_client_window(window.window().as_ref()); + ime.focus_in(); + ime.connect_commit(move |_, s| { + if let Err(e) = tx_clone.send(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::ReceivedImeText(s.to_string()), + }) { + log::warn!( + "Failed to send received IME text event to event channel: {}", + e + ); + } + }); - let tx_clone = event_tx.clone(); - window.connect_leave_notify_event(move |_, _| { - if let Err(e) = tx_clone.send(Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::CursorLeft { - // FIXME: currently we use a dummy device id, find if we can get device id from gtk - device_id: RootDeviceId(DeviceId(0)), - }, - }) { - log::warn!("Failed to send cursor left event to event channel: {}", e); - } - Inhibit(false) - }); + let handler = keyboard_handler.clone(); + window.connect_key_press_event(move |_, event_key| { + handler(event_key.to_owned(), ElementState::Pressed); + ime.filter_keypress(event_key); + + Inhibit(false) + }); + + let handler = keyboard_handler.clone(); + window.connect_key_release_event(move |_, event_key| { + handler(event_key.to_owned(), ElementState::Released); + Inhibit(false) + }); - let tx_clone = event_tx.clone(); - window.connect_button_press_event(move |_, event| { - let button = event.button(); + let tx_clone = event_tx.clone(); + window.connect_window_state_event(move |window, event| { + let state = event.changed_mask(); + if state.contains(WindowState::ICONIFIED) || state.contains(WindowState::MAXIMIZED) { + let scale_factor = window.scale_factor(); + + let (x, y) = window.position(); if let Err(e) = tx_clone.send(Event::WindowEvent { window_id: RootWindowId(id), - event: WindowEvent::MouseInput { - button: match button { - 1 => MouseButton::Left, - 2 => MouseButton::Middle, - 3 => MouseButton::Right, - _ => MouseButton::Other(button as u16), - }, - state: ElementState::Pressed, - // FIXME: currently we use a dummy device id, find if we can get device id from gtk - device_id: RootDeviceId(DeviceId(0)), - // this field is depracted so it is fine to pass empty state - modifiers: ModifiersState::empty(), - }, + event: WindowEvent::Moved( + LogicalPosition::new(x, y).to_physical(scale_factor as f64), + ), }) { - log::warn!( - "Failed to send mouse input preseed event to event channel: {}", - e - ); + log::warn!("Failed to send window moved event to event channel: {}", e); } - Inhibit(false) - }); - let tx_clone = event_tx.clone(); - window.connect_button_release_event(move |_, event| { - let button = event.button(); + let (w, h) = window.size(); if let Err(e) = tx_clone.send(Event::WindowEvent { window_id: RootWindowId(id), - event: WindowEvent::MouseInput { - button: match button { - 1 => MouseButton::Left, - 2 => MouseButton::Middle, - 3 => MouseButton::Right, - _ => MouseButton::Other(button as u16), - }, - state: ElementState::Released, - // FIXME: currently we use a dummy device id, find if we can get device id from gtk - device_id: RootDeviceId(DeviceId(0)), - // this field is depracted so it is fine to pass empty state - modifiers: ModifiersState::empty(), - }, - }) { - log::warn!( - "Failed to send mouse input released event to event channel: {}", - e - ); - } - Inhibit(false) - }); - - let tx_clone = event_tx.clone(); - let keyboard_handler = Rc::new(move |event_key: EventKey, element_state| { - // if we have a modifier lets send it - let mut mods = keyboard::get_modifiers(event_key.clone()); - if !mods.is_empty() { - // if we release the modifier tell the world - if ElementState::Released == element_state { - mods = ModifiersState::empty(); - } - - if let Err(e) = tx_clone.send(Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::ModifiersChanged(mods), - }) { - log::warn!( - "Failed to send modifiers changed event to event channel: {}", - e - ); - } else { - // stop here we don't want to send the key event - // as we emit the `ModifiersChanged` - return Continue(true); - } - } - - // todo: implement repeat? - let event = keyboard::make_key_event(&event_key, false, None, element_state); - - if let Some(event) = event { - if let Err(e) = tx_clone.send(Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::KeyboardInput { - // FIXME: currently we use a dummy device id, find if we can get device id from gtk - device_id: RootDeviceId(DeviceId(0)), - event, - is_synthetic: false, - }, - }) { - log::warn!("Failed to send keyboard event to event channel: {}", e); - } - } - Continue(true) - }); - - let handler = keyboard_handler.clone(); - window.connect_key_press_event(move |_, event_key| { - handler(event_key.to_owned(), ElementState::Pressed); - Inhibit(false) - }); - - let handler = keyboard_handler.clone(); - window.connect_key_release_event(move |_, event_key| { - handler(event_key.to_owned(), ElementState::Released); - Inhibit(false) - }); - } - WindowRequest::Redraw => window.queue_draw(), - WindowRequest::Menu(m) => match m { - (None, Some(menu_id)) => { - if let Err(e) = event_tx.send(Event::MenuEvent { - window_id: Some(RootWindowId(id)), - menu_id, - origin: MenuType::MenuBar, + event: WindowEvent::Resized( + LogicalSize::new(w, h).to_physical(scale_factor as f64), + ), }) { - log::warn!("Failed to send menu event to event channel: {}", e); - } - } - (Some(MenuItem::About(_)), None) => { - let about = AboutDialog::new(); - about.show_all(); - app.add_window(&about); - } - (Some(MenuItem::Hide), None) => window.hide(), - (Some(MenuItem::CloseWindow), None) => window.close(), - (Some(MenuItem::Quit), None) => { - if let Err(e) = event_tx.send(Event::LoopDestroyed) { log::warn!( - "Failed to send loop destroyed event to event channel: {}", + "Failed to send window resized event to event channel: {}", e ); } } - (Some(MenuItem::EnterFullScreen), None) => { - let state = window.window().unwrap().state(); - if state.contains(WindowState::FULLSCREEN) { - window.unfullscreen(); - } else { - window.fullscreen(); - } - } - (Some(MenuItem::Minimize), None) => window.iconify(), - _ => {} - }, - WindowRequest::SetMenu((window_menu, accel_group, mut menubar)) => { - if let Some(window_menu) = window_menu { - // remove all existing elements as we overwrite - // but we keep same menubar reference - for i in menubar.children() { - menubar.remove(&i); - } - // create all new elements - window_menu.generate_menu(&mut menubar, &window_requests_tx, &accel_group, id); - // make sure all newly added elements are visible - menubar.show_all(); - } - } - WindowRequest::GlobalHotKey(_hotkey_id) => {} + Inhibit(false) + }); } - } else if id == WindowId::dummy() { - match request { - WindowRequest::GlobalHotKey(hotkey_id) => { - if let Err(e) = event_tx.send(Event::GlobalShortcutEvent(AcceleratorId(hotkey_id))) { - log::warn!("Failed to send global hotkey event to event channel: {}", e); - } + WindowRequest::Redraw => { + if let Err(e) = draw_tx.send(id) { + log::warn!("Failed to send redraw event to event channel: {}", e); } - WindowRequest::Menu((None, Some(menu_id))) => { + + window.queue_draw(); + } + WindowRequest::Menu(m) => match m { + (None, Some(menu_id)) => { if let Err(e) = event_tx.send(Event::MenuEvent { - window_id: None, + window_id: Some(RootWindowId(id)), menu_id, - origin: MenuType::ContextMenu, + origin: MenuType::MenuBar, }) { - log::warn!("Failed to send status bar event to event channel: {}", e); + log::warn!("Failed to send menu event to event channel: {}", e); } } - _ => {} - } - } - Continue(true) - }); - - // Event control flow - let events = Rc::new(Mutex::new(Vec::new())); - let events_ = events.clone(); - event_rx.attach(Some(&context), move |event| { - let mut e = events_.lock().unwrap(); - e.push(event); - Continue(true) - }); - - loop { - match control_flow { - ControlFlow::Exit => { - callback(Event::LoopDestroyed, &window_target, &mut control_flow); - break; - } - ControlFlow::Wait => { - let mut e = events.lock().unwrap(); - if !e.is_empty() { - callback( - Event::NewEvents(StartCause::WaitCancelled { - start: Instant::now(), - requested_resume: None, - }), - &window_target, - &mut control_flow, - ); - - for event in e.drain(..) { - match event { - Event::LoopDestroyed => control_flow = ControlFlow::Exit, - _ => callback(event, &window_target, &mut control_flow), - } - } - - if control_flow != ControlFlow::Exit { - callback(Event::MainEventsCleared, &window_target, &mut control_flow); + (Some(MenuItem::About(_)), None) => { + let about = AboutDialog::new(); + about.show_all(); + app_.add_window(&about); } - } - } - ControlFlow::WaitUntil(requested_resume) => { - let mut e = events.lock().unwrap(); - let start = Instant::now(); - if start >= requested_resume { - callback( - Event::NewEvents(StartCause::ResumeTimeReached { - start, - requested_resume, - }), - &window_target, - &mut control_flow, - ); - - for event in e.drain(..) { - match event { - Event::LoopDestroyed => control_flow = ControlFlow::Exit, - _ => callback(event, &window_target, &mut control_flow), + (Some(MenuItem::Hide), None) => window.hide(), + (Some(MenuItem::CloseWindow), None) => window.close(), + (Some(MenuItem::Quit), None) => { + if let Err(e) = event_tx.send(Event::LoopDestroyed) { + log::warn!( + "Failed to send loop destroyed event to event channel: {}", + e + ); } } - - if control_flow != ControlFlow::Exit { - callback(Event::MainEventsCleared, &window_target, &mut control_flow); - } - } else if !e.is_empty() { - callback( - Event::NewEvents(StartCause::WaitCancelled { - start, - requested_resume: Some(requested_resume), - }), - &window_target, - &mut control_flow, - ); - - for event in e.drain(..) { - match event { - Event::LoopDestroyed => control_flow = ControlFlow::Exit, - _ => callback(event, &window_target, &mut control_flow), + (Some(MenuItem::EnterFullScreen), None) => { + let state = window.window().unwrap().state(); + if state.contains(WindowState::FULLSCREEN) { + window.unfullscreen(); + } else { + window.fullscreen(); } } - - if control_flow != ControlFlow::Exit { - callback(Event::MainEventsCleared, &window_target, &mut control_flow); + (Some(MenuItem::Minimize), None) => window.iconify(), + _ => {} + }, + WindowRequest::SetMenu((window_menu, accel_group, mut menubar)) => { + if let Some(window_menu) = window_menu { + // remove all existing elements as we overwrite + // but we keep same menubar reference + for i in menubar.children() { + menubar.remove(&i); + } + // create all new elements + window_menu.generate_menu(&mut menubar, &window_requests_tx_, &accel_group, id); + // make sure all newly added elements are visible + menubar.show_all(); } } + WindowRequest::GlobalHotKey(_hotkey_id) => {} } - ControlFlow::Poll => { - let mut e = events.lock().unwrap(); - callback( - Event::NewEvents(StartCause::Poll), - &window_target, - &mut control_flow, - ); - for event in e.drain(..) { - match event { - Event::LoopDestroyed => control_flow = ControlFlow::Exit, - _ => callback(event, &window_target, &mut control_flow), + } else if id == WindowId::dummy() { + match request { + WindowRequest::GlobalHotKey(hotkey_id) => { + if let Err(e) = event_tx.send(Event::GlobalShortcutEvent(AcceleratorId(hotkey_id))) { + log::warn!("Failed to send global hotkey event to event channel: {}", e); + } + } + WindowRequest::Menu((None, Some(menu_id))) => { + if let Err(e) = event_tx.send(Event::MenuEvent { + window_id: None, + menu_id, + origin: MenuType::ContextMenu, + }) { + log::warn!("Failed to send status bar event to event channel: {}", e); } } - callback(Event::MainEventsCleared, &window_target, &mut control_flow); + _ => {} } } + Continue(true) + }); + + // Create event loop itself. + let event_loop = Self { + window_target: RootELW { + p: window_target, + _marker: std::marker::PhantomData, + }, + user_event_tx, + events: event_rx, + draws: draw_rx, + }; + + Ok(event_loop) + } - gtk::main_iteration(); + #[inline] + pub fn run(mut self, callback: F) -> ! + where + F: FnMut(Event<'_, T>, &RootELW, &mut ControlFlow) + 'static, + { + let exit_code = self.run_return(callback); + process::exit(exit_code) + } + + /// This is the core event loop logic. It basically loops on `gtk_main_iteration` and processes one + /// event along with that iteration. Depends on current control flow and what it should do, an + /// event state is defined. The whole state flow chart runs like following: + /// + /// ```ignore + /// Poll/Wait/WaitUntil + /// +-------------------------------------------------------------------------+ + /// | | + /// | Receiving event from event channel | Receiving event from draw channel + /// | +-------+ | +---+ + /// v v | | v | + /// +----------+ Poll/Wait/WaitUntil +------------+ Poll/Wait/WaitUntil +-----------+ | + /// | NewStart | ---------------------> | EventQueue | ---------------------> | DrawQueue | | + /// +----------+ +------------+ +-----------+ | + /// |ExitWithCode |ExitWithCode ExitWithCode| | | + /// +------------------------------------+------------------------------------+ +---+ + /// | + /// v + /// +---------------+ + /// | LoopDestroyed | + /// +---------------+ + /// ``` + /// + /// There are a dew notibale event will sent to callback when state is transisted: + /// - On any state moves to `LoopDestroyed`, a `LoopDestroyed` event is sent. + /// - On `NewStart` to `EventQueue`, a `NewEvents` with corresponding `StartCause` depends on + /// current control flow is sent. + /// - On `EventQueue` to `DrawQueue`, a `MainEventsCleared` event is sent. + /// - On `DrawQueue` back to `NewStart`, a `RedrawEventsCleared` event is sent. + pub(crate) fn run_return(&mut self, mut callback: F) -> i32 + where + F: FnMut(Event<'_, T>, &RootELW, &mut ControlFlow), + { + enum EventState { + NewStart, + EventQueue, + DrawQueue, } - context.pop_thread_default(); + + let context = MainContext::default(); + context + .with_thread_default(|| { + let mut control_flow = ControlFlow::default(); + let window_target = &self.window_target; + let events = &self.events; + let draws = &self.draws; + + window_target.p.app.activate(); + + let mut state = EventState::NewStart; + let exit_code = loop { + let mut blocking = false; + match state { + EventState::NewStart => match control_flow { + ControlFlow::ExitWithCode(code) => { + callback(Event::LoopDestroyed, window_target, &mut control_flow); + break code; + } + ControlFlow::Wait => { + if !events.is_empty() || !draws.is_empty() { + callback( + Event::NewEvents(StartCause::WaitCancelled { + start: Instant::now(), + requested_resume: None, + }), + window_target, + &mut control_flow, + ); + state = EventState::EventQueue; + } else { + blocking = true; + } + } + ControlFlow::WaitUntil(requested_resume) => { + let start = Instant::now(); + if start >= requested_resume { + callback( + Event::NewEvents(StartCause::ResumeTimeReached { + start, + requested_resume, + }), + window_target, + &mut control_flow, + ); + state = EventState::EventQueue; + } else if !events.is_empty() { + callback( + Event::NewEvents(StartCause::WaitCancelled { + start, + requested_resume: Some(requested_resume), + }), + window_target, + &mut control_flow, + ); + state = EventState::EventQueue; + } else { + blocking = true; + } + } + ControlFlow::Poll => { + callback( + Event::NewEvents(StartCause::Poll), + window_target, + &mut control_flow, + ); + state = EventState::EventQueue; + } + }, + EventState::EventQueue => match control_flow { + ControlFlow::ExitWithCode(code) => { + callback(Event::LoopDestroyed, window_target, &mut control_flow); + break (code); + } + _ => match events.try_recv() { + Ok(event) => match event { + Event::LoopDestroyed => control_flow = ControlFlow::ExitWithCode(1), + _ => callback(event, window_target, &mut control_flow), + }, + Err(_) => { + callback(Event::MainEventsCleared, window_target, &mut control_flow); + if draws.is_empty() { + state = EventState::NewStart; + } else { + state = EventState::DrawQueue; + } + } + }, + }, + EventState::DrawQueue => match control_flow { + ControlFlow::ExitWithCode(code) => { + callback(Event::LoopDestroyed, window_target, &mut control_flow); + break code; + } + _ => match draws.try_recv() { + Ok(id) => callback( + Event::RedrawRequested(RootWindowId(id)), + window_target, + &mut control_flow, + ), + Err(_) => { + callback(Event::RedrawEventsCleared, window_target, &mut control_flow); + state = EventState::NewStart; + } + }, + }, + } + gtk::main_iteration_do(blocking); + }; + exit_code + }) + .unwrap_or(1) } #[inline] @@ -815,14 +965,13 @@ impl EventLoopProxy { } fn assert_is_main_thread(suggested_method: &str) { - if !is_main_thread() { - panic!( - "Initializing the event loop outside of the main thread is a significant \ + assert!( + is_main_thread(), + "Initializing the event loop outside of the main thread is a significant \ cross-platform compatibility hazard. If you really, absolutely need to create an \ EventLoop on a different thread, please use the `EventLoopExtUnix::{}` function.", - suggested_method - ); - } + suggested_method + ); } #[cfg(target_os = "linux")] diff --git a/src/platform_impl/linux/global_shortcut.rs b/src/platform_impl/linux/global_shortcut.rs index db3984d302..e6b4f6e0ef 100644 --- a/src/platform_impl/linux/global_shortcut.rs +++ b/src/platform_impl/linux/global_shortcut.rs @@ -94,11 +94,13 @@ impl ShortcutManager { ), ))) { + #[cfg(debug_assertions)] eprintln!("hotkey: thread_sender.send error {}", err); } } else if let Err(err) = thread_sender.send(HotkeyMessage::RegisterHotkeyResult(Ok( (keycode, modifiers), ))) { + #[cfg(debug_assertions)] eprintln!("hotkey: thread_sender.send error {}", err); } } @@ -113,8 +115,14 @@ impl ShortcutManager { ), ))) { + #[cfg(debug_assertions)] eprintln!("hotkey: thread_sender.send error {}", err); } + } else if let Err(err) = + thread_sender.send(HotkeyMessage::UnregisterHotkeyResult(Ok(()))) + { + #[cfg(debug_assertions)] + eprintln!("hotkey: thread_sender.send error {}", err); } } Ok(HotkeyMessage::DropThread) => { @@ -123,6 +131,7 @@ impl ShortcutManager { } Err(err) => { if let TryRecvError::Disconnected = err { + #[cfg(debug_assertions)] eprintln!("hotkey: try_recv error {}", err); } } @@ -245,6 +254,7 @@ impl ShortcutManager { impl Drop for ShortcutManager { fn drop(&mut self) { if let Err(err) = self.method_sender.send(HotkeyMessage::DropThread) { + #[cfg(debug_assertions)] eprintln!("cant send close thread message {}", err); } } diff --git a/src/platform_impl/linux/keyboard.rs b/src/platform_impl/linux/keyboard.rs index 896024e86c..fbe01aa039 100644 --- a/src/platform_impl/linux/keyboard.rs +++ b/src/platform_impl/linux/keyboard.rs @@ -220,6 +220,7 @@ pub(crate) fn make_key_event( }, }); } else { + #[cfg(debug_assertions)] eprintln!("Couldn't get key from code: {:?}", physical_key); } None @@ -299,6 +300,18 @@ pub fn key_to_raw_key(src: &KeyCode) -> Option { KeyCode::F10 => F10, KeyCode::F11 => F11, KeyCode::F12 => F12, + KeyCode::F13 => F13, + KeyCode::F14 => F14, + KeyCode::F15 => F15, + KeyCode::F16 => F16, + KeyCode::F17 => F17, + KeyCode::F18 => F18, + KeyCode::F19 => F19, + KeyCode::F20 => F20, + KeyCode::F21 => F21, + KeyCode::F22 => F22, + KeyCode::F23 => F23, + KeyCode::F24 => F24, KeyCode::PrintScreen => Print, KeyCode::ScrollLock => Scroll_Lock, diff --git a/src/platform_impl/linux/menu.rs b/src/platform_impl/linux/menu.rs index ba34c90937..e30fd83ca7 100644 --- a/src/platform_impl/linux/menu.rs +++ b/src/platform_impl/linux/menu.rs @@ -18,10 +18,15 @@ use crate::{ }; macro_rules! menuitem { - ( $description:expr, $key:expr, $accel_group:ident ) => {{ + ( $description:expr, $key:expr, $accel_group:ident, $window_id:expr, $native_menu_item:expr, $tx:ident ) => {{ let item = GtkMenuItem::with_label($description); let (key, mods) = gtk::accelerator_parse($key); item.add_accelerator("activate", $accel_group, key, mods, AccelFlags::VISIBLE); + item.connect_activate(move |_| { + if let Err(e) = $tx.send(($window_id, WindowRequest::Menu(($native_menu_item, None)))) { + log::warn!("Fail to send native menu request: {}", e); + } + }); Some(item) }}; } @@ -57,6 +62,7 @@ unsafe impl Send for Menu {} unsafe impl Sync for Menu {} #[derive(Debug, Clone)] +#[allow(dead_code)] pub struct MenuItemAttributes { id: MenuId, key: Option, @@ -113,7 +119,7 @@ impl Menu { menu_type: MenuType, ) -> CustomMenuItem { let gtk_item = if selected { - let item = CheckMenuItem::with_label(&title); + let item = CheckMenuItem::with_label(title); item.set_active(true); item.upcast::() } else { @@ -245,17 +251,47 @@ impl Menu { menu_type: GtkMenuType::Native, menu_item: Some(MenuItem::Hide), .. - } => menuitem!("Hide", "H", accel_group), + } => { + let tx_clone = tx.clone(); + menuitem!( + "Hide", + "H", + accel_group, + window_id, + Some(MenuItem::Hide), + tx_clone + ) + } GtkMenuInfo { menu_type: GtkMenuType::Native, menu_item: Some(MenuItem::CloseWindow), .. - } => menuitem!("Close Window", "W", accel_group), + } => { + let tx_clone = tx.clone(); + menuitem!( + "Close Window", + "W", + accel_group, + window_id, + Some(MenuItem::CloseWindow), + tx_clone + ) + } GtkMenuInfo { menu_type: GtkMenuType::Native, menu_item: Some(MenuItem::Quit), .. - } => menuitem!("Quit", "Q", accel_group), + } => { + let tx_clone = tx.clone(); + menuitem!( + "Quit", + "Q", + accel_group, + window_id, + Some(MenuItem::Quit), + tx_clone + ) + } // TODO add others _ => None, }; @@ -305,6 +341,18 @@ fn register_accelerator(item: &GtkMenuItem, accel_group: &AccelGroup, menu_key: KeyCode::Digit7 => '7' as u32, KeyCode::Digit8 => '8' as u32, KeyCode::Digit9 => '9' as u32, + KeyCode::Comma => ',' as u32, + KeyCode::Minus => '-' as u32, + KeyCode::Period => '.' as u32, + KeyCode::Space => ' ' as u32, + KeyCode::Equal => '=' as u32, + KeyCode::Semicolon => ';' as u32, + KeyCode::Slash => '/' as u32, + KeyCode::Backslash => '\\' as u32, + KeyCode::Quote => '\'' as u32, + KeyCode::Backquote => '`' as u32, + KeyCode::BracketLeft => '[' as u32, + KeyCode::BracketRight => ']' as u32, k => { if let Some(gdk_key) = key_to_raw_key(k) { *gdk_key diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index b801875db2..784707f910 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -16,11 +16,11 @@ mod keyboard; mod keycode; mod menu; mod monitor; -#[cfg(feature = "tray")] +#[cfg(any(feature = "tray", feature = "ayatana"))] mod system_tray; mod window; -#[cfg(feature = "tray")] +#[cfg(any(feature = "tray", feature = "ayatana"))] pub use self::system_tray::{SystemTray, SystemTrayBuilder}; pub use self::{ clipboard::Clipboard, @@ -32,7 +32,7 @@ pub use event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget}; pub use monitor::{MonitorHandle, VideoMode}; pub use window::{hit_test, PlatformIcon, Window, WindowId}; -use crate::keyboard::Key; +use crate::{event::DeviceId as RootDeviceId, keyboard::Key}; #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct KeyEventExtra { @@ -40,19 +40,11 @@ pub struct KeyEventExtra { pub key_without_modifiers: Key<'static>, } -#[derive(Clone)] +#[derive(Clone, Default)] pub struct PlatformSpecificWindowBuilderAttributes { pub skip_taskbar: bool, } -impl Default for PlatformSpecificWindowBuilderAttributes { - fn default() -> Self { - Self { - skip_taskbar: false, - } - } -} - unsafe impl Send for PlatformSpecificWindowBuilderAttributes {} unsafe impl Sync for PlatformSpecificWindowBuilderAttributes {} @@ -73,3 +65,6 @@ impl DeviceId { Self(0) } } + +// FIXME: currently we use a dummy device id, find if we can get device id from gtk +pub(crate) const DEVICE_ID: RootDeviceId = RootDeviceId(DeviceId(0)); diff --git a/src/platform_impl/linux/monitor.rs b/src/platform_impl/linux/monitor.rs index efb585af2c..ed084ae226 100644 --- a/src/platform_impl/linux/monitor.rs +++ b/src/platform_impl/linux/monitor.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ - dpi::{PhysicalPosition, PhysicalSize}, + dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}, monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}, }; @@ -12,7 +12,7 @@ pub struct MonitorHandle { // We have to store the monitor number in GdkScreen despite // it's deprecated. Otherwise, there's no way to set it in // GtkWindow in Gtk3. - number: i32, + pub(crate) number: i32, } impl MonitorHandle { @@ -29,19 +29,21 @@ impl MonitorHandle { #[inline] pub fn size(&self) -> PhysicalSize { let rect = self.monitor.geometry(); - PhysicalSize { - width: rect.width as u32, - height: rect.height as u32, + LogicalSize { + width: rect.width() as u32, + height: rect.height() as u32, } + .to_physical(self.scale_factor()) } #[inline] pub fn position(&self) -> PhysicalPosition { let rect = self.monitor.geometry(); - PhysicalPosition { - x: rect.x, - y: rect.y, + LogicalPosition { + x: rect.x(), + y: rect.y(), } + .to_physical(self.scale_factor()) } #[inline] @@ -51,7 +53,7 @@ impl MonitorHandle { #[inline] pub fn video_modes(&self) -> Box> { - todo!() + Box::new(Vec::new().into_iter()) } } @@ -64,21 +66,21 @@ pub struct VideoMode; impl VideoMode { #[inline] pub fn size(&self) -> PhysicalSize { - todo!() + panic!("VideoMode is unsupported on Linux.") } #[inline] pub fn bit_depth(&self) -> u16 { - todo!() + panic!("VideoMode is unsupported on Linux.") } #[inline] pub fn refresh_rate(&self) -> u16 { - todo!() + panic!("VideoMode is unsupported on Linux.") } #[inline] pub fn monitor(&self) -> RootMonitorHandle { - todo!() + panic!("VideoMode is unsupported on Linux.") } } diff --git a/src/platform_impl/linux/window.rs b/src/platform_impl/linux/window.rs index da955380dc..0bc3193680 100644 --- a/src/platform_impl/linux/window.rs +++ b/src/platform_impl/linux/window.rs @@ -10,10 +10,11 @@ use std::{ use gdk::{WindowEdge, WindowState}; use gdk_pixbuf::{Colorspace, Pixbuf}; -use gtk::{prelude::*, AccelGroup, ApplicationWindow, Orientation}; +use gtk::{prelude::*, AccelGroup, Orientation}; +use raw_window_handle::{RawWindowHandle, XlibHandle}; use crate::{ - dpi::{PhysicalPosition, PhysicalSize, Position, Size}, + dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, error::{ExternalError, NotSupportedError, OsError as RootOsError}, icon::{BadIcon, Icon}, menu::{MenuId, MenuItem}, @@ -143,27 +144,28 @@ impl Window { .max_inner_size .map(|size| size.to_logical::(win_scale_factor as f64).into()) .unwrap_or_default(); - window.set_geometry_hints::( - None, - Some(&gdk::Geometry { + let picky_none: Option<>k::Window> = None; + window.set_geometry_hints( + picky_none, + Some(&gdk::Geometry::new( min_width, min_height, max_width, max_height, - base_width: 0, - base_height: 0, - width_inc: 0, - height_inc: 0, - min_aspect: 0f64, - max_aspect: 0f64, - win_gravity: gdk::Gravity::Center, - }), + 0, + 0, + 0, + 0, + 0f64, + 0f64, + gdk::Gravity::Center, + )), geom_mask, ); // Set Position if let Some(position) = attributes.position { - let (x, y): (i32, i32) = position.to_physical::(win_scale_factor as f64).into(); + let (x, y): (i32, i32) = position.to_logical::(win_scale_factor as f64).into(); window.move_(x, y); } @@ -198,9 +200,10 @@ impl Window { // Rest attributes window.set_title(&attributes.title); - // TODO set it with Fullscreen enum - if attributes.fullscreen.is_some() { - window.fullscreen(); + if let Some(Fullscreen::Borderless(Some(f))) = &attributes.fullscreen { + let number = f.inner.number; + let screen = window.display().default_screen(); + window.fullscreen_on_monitor(&screen, number); } if attributes.maximized { window.maximize(); @@ -245,7 +248,7 @@ impl Window { let minimized = Rc::new(AtomicBool::new(false)); let min_clone = minimized.clone(); - window.connect_window_state_event(move |_window, event| { + window.connect_window_state_event(move |_, event| { let state = event.new_window_state(); max_clone.store(state.contains(WindowState::MAXIMIZED), Ordering::Release); min_clone.store(state.contains(WindowState::ICONIFIED), Ordering::Release); @@ -262,7 +265,9 @@ impl Window { log::warn!("Fail to send wire up events request: {}", e); } - window.queue_draw(); + if let Err(e) = window_requests_tx.send((window_id, WindowRequest::Redraw)) { + log::warn!("Fail to send redraw request: {}", e); + } let win = Self { window_id, @@ -302,24 +307,24 @@ impl Window { pub fn inner_position(&self) -> Result, NotSupportedError> { let (x, y) = &*self.position; - Ok(PhysicalPosition::new( - x.load(Ordering::Acquire), - y.load(Ordering::Acquire), - )) + Ok( + LogicalPosition::new(x.load(Ordering::Acquire), y.load(Ordering::Acquire)) + .to_physical(self.scale_factor.load(Ordering::Acquire) as f64), + ) } pub fn outer_position(&self) -> Result, NotSupportedError> { let (x, y) = &*self.position; - Ok(PhysicalPosition::new( - x.load(Ordering::Acquire), - y.load(Ordering::Acquire), - )) + Ok( + LogicalPosition::new(x.load(Ordering::Acquire), y.load(Ordering::Acquire)) + .to_physical(self.scale_factor.load(Ordering::Acquire) as f64), + ) } pub fn set_outer_position>(&self, position: P) { let (x, y): (i32, i32) = position .into() - .to_physical::(self.scale_factor()) + .to_logical::(self.scale_factor()) .into(); if let Err(e) = self @@ -333,10 +338,11 @@ impl Window { pub fn inner_size(&self) -> PhysicalSize { let (width, height) = &*self.size; - PhysicalSize::new( + LogicalSize::new( width.load(Ordering::Acquire) as u32, height.load(Ordering::Acquire) as u32, ) + .to_physical(self.scale_factor.load(Ordering::Acquire) as f64) } pub fn set_inner_size>(&self, size: S) { @@ -353,10 +359,11 @@ impl Window { pub fn outer_size(&self) -> PhysicalSize { let (width, height) = &*self.size; - PhysicalSize::new( + LogicalSize::new( width.load(Ordering::Acquire) as u32, height.load(Ordering::Acquire) as u32, ) + .to_physical(self.scale_factor.load(Ordering::Acquire) as f64) } pub fn set_min_inner_size>(&self, min_size: Option) { @@ -518,7 +525,7 @@ impl Window { } pub fn set_ime_position>(&self, _position: P) { - todo!() + //TODO } pub fn request_user_attention(&self, request_type: Option) { @@ -604,8 +611,18 @@ impl Window { Some(RootMonitorHandle { inner: handle }) } - pub fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle { - todo!() + pub fn raw_window_handle(&self) -> RawWindowHandle { + // TODO: add wayland support + let mut handle = XlibHandle::empty(); + unsafe { + if let Some(window) = self.window.window() { + handle.window = gdk_x11_sys::gdk_x11_window_get_xid(window.as_ptr() as *mut _); + } + if let Ok(xlib) = x11_dl::xlib::Xlib::open() { + handle.display = (xlib.XOpenDisplay)(std::ptr::null()) as _; + } + } + RawWindowHandle::Xlib(handle) } pub(crate) fn set_skip_taskbar(&self, skip: bool) { diff --git a/src/platform_impl/macos/app_state.rs b/src/platform_impl/macos/app_state.rs index 4fcc01f4ba..39b8676efd 100644 --- a/src/platform_impl/macos/app_state.rs +++ b/src/platform_impl/macos/app_state.rs @@ -20,9 +20,7 @@ use cocoa::{ base::{id, nil}, foundation::{NSAutoreleasePool, NSSize}, }; -use objc::runtime::YES; - -use objc::runtime::Object; +use objc::runtime::{Object, BOOL, NO, YES}; use crate::{ dpi::LogicalSize, @@ -64,7 +62,6 @@ pub trait EventHandler: Debug { struct EventLoopHandler { callback: Weak, &RootWindowTarget, &mut ControlFlow)>>, - will_exit: bool, window_target: Rc>, } @@ -100,25 +97,25 @@ impl Debug for EventLoopHandler { impl EventHandler for EventLoopHandler { fn handle_nonuser_event(&mut self, event: Event<'_, Never>, control_flow: &mut ControlFlow) { self.with_callback(|this, mut callback| { - (callback)(event.userify(), &this.window_target, control_flow); - this.will_exit |= *control_flow == ControlFlow::Exit; - if this.will_exit { - *control_flow = ControlFlow::Exit; + if let ControlFlow::ExitWithCode(code) = *control_flow { + let dummy = &mut ControlFlow::ExitWithCode(code); + (callback)(event.userify(), &this.window_target, dummy); + } else { + (callback)(event.userify(), &this.window_target, control_flow); } }); } fn handle_user_events(&mut self, control_flow: &mut ControlFlow) { self.with_callback(|this, mut callback| { - let mut will_exit = this.will_exit; for event in this.window_target.p.receiver.try_iter() { - (callback)(Event::UserEvent(event), &this.window_target, control_flow); - will_exit |= *control_flow == ControlFlow::Exit; - if will_exit { - *control_flow = ControlFlow::Exit; + if let ControlFlow::ExitWithCode(code) = *control_flow { + let dummy = &mut ControlFlow::ExitWithCode(code); + (callback)(Event::UserEvent(event), &this.window_target, dummy); + } else { + (callback)(Event::UserEvent(event), &this.window_target, control_flow); } } - this.will_exit = will_exit; }); } } @@ -162,7 +159,10 @@ impl Handler { } fn should_exit(&self) -> bool { - *self.control_flow.lock().unwrap() == ControlFlow::Exit + matches!( + *self.control_flow.lock().unwrap(), + ControlFlow::ExitWithCode(_) + ) } fn get_control_flow_and_update_prev(&self) -> ControlFlow { @@ -267,16 +267,20 @@ impl AppState { ) { *HANDLER.callback.lock().unwrap() = Some(Box::new(EventLoopHandler { callback, - will_exit: false, window_target, })); } - pub fn exit() { + pub fn exit() -> i32 { HANDLER.set_in_callback(true); HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::LoopDestroyed)); HANDLER.set_in_callback(false); HANDLER.callback.lock().unwrap().take(); + if let ControlFlow::ExitWithCode(code) = HANDLER.get_old_and_new_control_flow().1 { + code + } else { + 0 + } } pub fn launched(app_delegate: &Object) { @@ -323,7 +327,7 @@ impl AppState { } } } - ControlFlow::Exit => StartCause::Poll, //panic!("unexpected `ControlFlow::Exit`"), + ControlFlow::ExitWithCode(_) => StartCause::Poll, //panic!("unexpected `ControlFlow::Exit`"), }; HANDLER.set_in_callback(true); HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::NewEvents(cause))); @@ -347,14 +351,16 @@ impl AppState { } pub fn queue_event(wrapper: EventWrapper) { - if !unsafe { msg_send![class!(NSThread), isMainThread] } { + let is_main_thread: BOOL = unsafe { msg_send!(class!(NSThread), isMainThread) }; + if is_main_thread == NO { panic!("Event queued from different thread: {:#?}", wrapper); } HANDLER.events().push_back(wrapper); } pub fn queue_events(mut wrappers: VecDeque) { - if !unsafe { msg_send![class!(NSThread), isMainThread] } { + let is_main_thread: BOOL = unsafe { msg_send!(class!(NSThread), isMainThread) }; + if is_main_thread == NO { panic!("Events queued from different thread: {:#?}", wrappers); } HANDLER.events().append(&mut wrappers); @@ -388,8 +394,9 @@ impl AppState { let dialog_open = if window_count > 1 { let dialog: id = msg_send![windows, lastObject]; - let is_main_window: bool = msg_send![dialog, isMainWindow]; - msg_send![dialog, isVisible] && !is_main_window + let is_main_window: BOOL = msg_send![dialog, isMainWindow]; + let is_visible: BOOL = msg_send![dialog, isVisible]; + is_visible != NO && is_main_window == NO } else { false }; @@ -404,9 +411,9 @@ impl AppState { pool.drain(); if window_count > 0 { - let window: id = msg_send![windows, objectAtIndex:0]; - let window_has_focus = msg_send![window, isKeyWindow]; - if !dialog_open && window_has_focus && dialog_is_closing { + let window: id = msg_send![windows, firstObject]; + let window_has_focus: BOOL = msg_send![window, isKeyWindow]; + if !dialog_open && window_has_focus != NO && dialog_is_closing { HANDLER.dialog_is_closing.store(false, Ordering::SeqCst); } if dialog_open { @@ -417,7 +424,7 @@ impl AppState { } HANDLER.update_start_time(); match HANDLER.get_old_and_new_control_flow() { - (ControlFlow::Exit, _) | (_, ControlFlow::Exit) => (), + (ControlFlow::ExitWithCode(_), _) | (_, ControlFlow::ExitWithCode(_)) => (), (old, new) if old == new => (), (_, ControlFlow::Wait) => HANDLER.waker().stop(), (_, ControlFlow::WaitUntil(instant)) => HANDLER.waker().start_at(instant), diff --git a/src/platform_impl/macos/clipboard.rs b/src/platform_impl/macos/clipboard.rs index 912408fa2d..bace3382ef 100644 --- a/src/platform_impl/macos/clipboard.rs +++ b/src/platform_impl/macos/clipboard.rs @@ -20,6 +20,7 @@ impl Clipboard { let _: NSInteger = msg_send![pasteboard, clearContents]; let result: BOOL = msg_send![pasteboard, setString: nsstring forType: NSPasteboardTypeString]; if result != YES { + #[cfg(debug_assertions)] println!("failed to set clipboard"); } } diff --git a/src/platform_impl/macos/event.rs b/src/platform_impl/macos/event.rs index 312188ec44..60275d123b 100644 --- a/src/platform_impl/macos/event.rs +++ b/src/platform_impl/macos/event.rs @@ -163,7 +163,7 @@ pub fn create_key_event( logical_key = key_from_code.clone(); key_without_modifiers = key_from_code; } else { - //println!("Couldn't get key from code: {:?}", physical_key); + //#[cfg(debug_assertions)] println!("Couldn't get key from code: {:?}", physical_key); key_without_modifiers = get_modifierless_char(scancode); let modifiers = unsafe { NSEvent::modifierFlags(ns_event) }; diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index 6a16d326d5..d9ccceb135 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -14,9 +14,9 @@ use std::{ }; use cocoa::{ - appkit::{NSApp, NSEventType::NSApplicationDefined}, - base::{id, nil, YES}, - foundation::{NSAutoreleasePool, NSPoint}, + appkit::{NSApp, NSEventModifierFlags, NSEventSubtype, NSEventType::NSApplicationDefined}, + base::{id, nil, BOOL, NO, YES}, + foundation::{NSAutoreleasePool, NSInteger, NSPoint, NSTimeInterval}, }; use crossbeam_channel::{self as channel, Receiver, Sender}; use scopeguard::defer; @@ -63,6 +63,7 @@ impl PanicInfo { } } +#[derive(Clone)] pub struct EventLoopWindowTarget { pub sender: Sender, // this is only here to be cloned elsewhere pub receiver: Receiver, @@ -106,7 +107,8 @@ pub struct EventLoop { impl EventLoop { pub fn new() -> Self { let delegate = unsafe { - if !msg_send![class!(NSThread), isMainThread] { + let is_main_thread: BOOL = msg_send!(class!(NSThread), isMainThread); + if is_main_thread == NO { panic!("On macOS, `EventLoop` must be created on the main thread!"); } @@ -143,11 +145,11 @@ impl EventLoop { where F: 'static + FnMut(Event<'_, T>, &RootWindowTarget, &mut ControlFlow), { - self.run_return(callback); - process::exit(0); + let exit_code = self.run_return(callback); + process::exit(exit_code); } - pub fn run_return(&mut self, callback: F) + pub fn run_return(&mut self, callback: F) -> i32 where F: FnMut(Event<'_, T>, &RootWindowTarget, &mut ControlFlow), { @@ -164,7 +166,7 @@ impl EventLoop { self._callback = Some(Rc::clone(&callback)); - unsafe { + let exit_code = unsafe { let pool = NSAutoreleasePool::new(nil); defer!(pool.drain()); let app = NSApp(); @@ -182,9 +184,11 @@ impl EventLoop { drop(self._callback.take()); resume_unwind(panic); } - AppState::exit(); - } + AppState::exit() + }; drop(self._callback.take()); + + exit_code } pub fn create_proxy(&self) -> Proxy { @@ -199,13 +203,13 @@ pub unsafe fn post_dummy_event(target: id) { event_class, otherEventWithType: NSApplicationDefined location: NSPoint::new(0.0, 0.0) - modifierFlags: 0 - timestamp: 0 - windowNumber: 0 + modifierFlags: NSEventModifierFlags::empty() + timestamp: 0 as NSTimeInterval + windowNumber: 0 as NSInteger context: nil - subtype: 0 - data1: 0 - data2: 0 + subtype: NSEventSubtype::NSWindowExposedEventType + data1: 0 as NSInteger + data2: 0 as NSInteger ]; let () = msg_send![target, postEvent: dummy_event atStart: YES]; } diff --git a/src/platform_impl/macos/ffi.rs b/src/platform_impl/macos/ffi.rs index ba195bfd24..f3cd433289 100644 --- a/src/platform_impl/macos/ffi.rs +++ b/src/platform_impl/macos/ffi.rs @@ -112,6 +112,7 @@ pub const kCGCursorWindowLevelKey: NSInteger = 19; pub const kCGNumberOfWindowLevelKeys: NSInteger = 20; #[derive(Debug, Clone, Copy)] +#[repr(isize)] pub enum NSWindowLevel { NSNormalWindowLevel = kCGBaseWindowLevelKey as _, NSFloatingWindowLevel = kCGFloatingWindowLevelKey as _, @@ -168,9 +169,18 @@ pub const IO8BitOverlayPixels: &str = "O8"; pub type CGWindowLevel = i32; pub type CGDisplayModeRef = *mut libc::c_void; +// `CGDisplayCreateUUIDFromDisplayID` comes from the `ColorSync` framework. +// However, that framework was only introduced "publicly" in macOS 10.13. +// +// Since we want to support older versions, we can't link to `ColorSync` +// directly. Fortunately, it has always been available as a subframework of +// `ApplicationServices`, see: +// https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/OSX_Technology_Overview/SystemFrameworks/SystemFrameworks.html#//apple_ref/doc/uid/TP40001067-CH210-BBCFFIEG +// +// TODO: Remove the WINIT_LINK_COLORSYNC hack, it is probably not needed. #[cfg_attr( not(use_colorsync_cgdisplaycreateuuidfromdisplayid), - link(name = "CoreGraphics", kind = "framework") + link(name = "ApplicationServices", kind = "framework") )] #[cfg_attr( use_colorsync_cgdisplaycreateuuidfromdisplayid, diff --git a/src/platform_impl/macos/global_shortcut.rs b/src/platform_impl/macos/global_shortcut.rs index c9ae240bce..0232bd0098 100644 --- a/src/platform_impl/macos/global_shortcut.rs +++ b/src/platform_impl/macos/global_shortcut.rs @@ -63,6 +63,7 @@ impl ShortcutManager { // we get only 1 keycode as we don't generate it for the modifier // it's safe to use first() if let Some(scan_code) = accelerator.key.to_scancode() { + #[cfg(debug_assertions)] println!("register {:?}", accelerator); // register hotkey let handler_ref = register_hotkey( diff --git a/src/platform_impl/macos/menu.rs b/src/platform_impl/macos/menu.rs index aa23a4e554..ba02233514 100644 --- a/src/platform_impl/macos/menu.rs +++ b/src/platform_impl/macos/menu.rs @@ -469,8 +469,6 @@ impl Accelerator { /// Returns the empty string if no key equivalent is known. fn key_equivalent(self) -> String { match self.key { - KeyCode::Minus => "-".into(), - KeyCode::Equal => "=".into(), KeyCode::KeyA => "a".into(), KeyCode::KeyB => "b".into(), KeyCode::KeyC => "c".into(), @@ -507,8 +505,20 @@ impl Accelerator { KeyCode::Digit7 => "7".into(), KeyCode::Digit8 => "8".into(), KeyCode::Digit9 => "9".into(), - KeyCode::Escape => "\u{1b}".into(), + KeyCode::Comma => ",".into(), + KeyCode::Minus => "-".into(), + KeyCode::Period => ".".into(), KeyCode::Space => "\u{0020}".into(), + KeyCode::Equal => "=".into(), + KeyCode::Semicolon => ";".into(), + KeyCode::Slash => "/".into(), + KeyCode::Backslash => "\\".into(), + KeyCode::Quote => "\'".into(), + KeyCode::Backquote => "`".into(), + KeyCode::BracketLeft => "[".into(), + KeyCode::BracketRight => "]".into(), + KeyCode::Tab => "⇥".into(), + KeyCode::Escape => "\u{001b}".into(), // from NSText.h KeyCode::Enter => "\u{0003}".into(), KeyCode::Backspace => "\u{0008}".into(), @@ -537,7 +547,20 @@ impl Accelerator { KeyCode::F10 => "\u{F70D}".into(), KeyCode::F11 => "\u{F70E}".into(), KeyCode::F12 => "\u{F70F}".into(), + KeyCode::F13 => "\u{F710}".into(), + KeyCode::F14 => "\u{F711}".into(), + KeyCode::F15 => "\u{F712}".into(), + KeyCode::F16 => "\u{F713}".into(), + KeyCode::F17 => "\u{F714}".into(), + KeyCode::F18 => "\u{F715}".into(), + KeyCode::F19 => "\u{F716}".into(), + KeyCode::F20 => "\u{F717}".into(), + KeyCode::F21 => "\u{F718}".into(), + KeyCode::F22 => "\u{F719}".into(), + KeyCode::F23 => "\u{F71A}".into(), + KeyCode::F24 => "\u{F71B}".into(), _ => { + #[cfg(debug_assertions)] eprintln!("no key equivalent for {:?}", self); "".into() } diff --git a/src/platform_impl/macos/monitor.rs b/src/platform_impl/macos/monitor.rs index d5445f4863..2d3c63ec37 100644 --- a/src/platform_impl/macos/monitor.rs +++ b/src/platform_impl/macos/monitor.rs @@ -165,6 +165,7 @@ impl fmt::Debug for MonitorHandle { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // TODO: Do this using the proper fmt API #[derive(Debug)] + #[allow(dead_code)] // none of the member reads in Debug::fmt are detected struct MonitorHandle { name: Option, native_identifier: u32, diff --git a/src/platform_impl/macos/observer.rs b/src/platform_impl/macos/observer.rs index 135f720912..09b91cee27 100644 --- a/src/platform_impl/macos/observer.rs +++ b/src/platform_impl/macos/observer.rs @@ -142,7 +142,10 @@ where // However we want to keep that weak reference around after the function. std::mem::forget(info_from_raw); - stop_app_on_panic(Weak::clone(&panic_info), move || f(panic_info.0)); + stop_app_on_panic(Weak::clone(&panic_info), move || { + let _ = &panic_info; + f(panic_info.0) + }); } // begin is queued with the highest priority to ensure it is processed before other observers diff --git a/src/platform_impl/macos/system_tray.rs b/src/platform_impl/macos/system_tray.rs index b8140a1530..ebb6e68161 100644 --- a/src/platform_impl/macos/system_tray.rs +++ b/src/platform_impl/macos/system_tray.rs @@ -24,7 +24,7 @@ use cocoa::{ }; use objc::{ declare::ClassDecl, - runtime::{Class, Object, Sel}, + runtime::{Class, Object, Protocol, Sel}, }; use std::sync::Once; @@ -34,10 +34,6 @@ pub struct SystemTrayBuilder { impl SystemTrayBuilder { /// Creates a new SystemTray for platforms where this is appropriate. - /// ## Platform-specific - /// - /// - **macOS / Windows:**: receive icon as bytes (`Vec`) - /// - **Linux:**: receive icon's path (`PathBuf`) #[inline] pub fn new(icon: Vec, tray_menu: Option) -> Self { unsafe { @@ -57,8 +53,6 @@ impl SystemTrayBuilder { } /// Builds the system tray. - /// - /// Possible causes of error include denied permission, incompatible system, and lack of memory. #[inline] pub fn build( self, @@ -71,16 +65,12 @@ impl SystemTrayBuilder { // set our icon self.system_tray.create_button_with_icon(); - // attach menu only if provided - if let Some(menu) = self.system_tray.tray_menu.clone() { - // set tray menu - status_bar.setMenu_(menu.menu); - } - // attach click event to our button let button = status_bar.button(); let tray_target: id = msg_send![make_tray_class(), alloc]; let tray_target: id = msg_send![tray_target, init]; + (*tray_target).set_ivar("status_bar", status_bar); + (*tray_target).set_ivar("menu", nil); let _: () = msg_send![button, setAction: sel!(click:)]; let _: () = msg_send![button, setTarget: tray_target]; let _: () = msg_send![ @@ -89,6 +79,15 @@ impl SystemTrayBuilder { | NSEventMask::NSRightMouseDownMask | NSEventMask::NSKeyDownMask ]; + + // attach menu only if provided + if let Some(menu) = self.system_tray.tray_menu.clone() { + // We set the tray menu to tray_target instead of status bar + // Because setting directly to status bar will overwrite the event callback of the button + // See `make_tray_class` for more information. + (*tray_target).set_ivar("menu", menu.menu); + let () = msg_send![menu.menu, setDelegate: tray_target]; + } } Ok(RootSystemTray(self.system_tray)) @@ -96,10 +95,6 @@ impl SystemTrayBuilder { } /// System tray is a status icon that can show popup menu. It is usually displayed on top right or bottom right of the screen. -/// -/// ## Platform-specific -/// -/// - **Linux:**: require `menu` feature flag. Otherwise, it's a no-op. #[derive(Debug, Clone)] pub struct SystemTray { pub(crate) icon: Vec, @@ -154,6 +149,12 @@ impl SystemTray { } } +/// Create a `TrayHandler` Class that handle button click event and also menu opening and closing. +/// +/// We set the tray menu to tray_target instead of status bar, because setting directly to status bar +/// will overwrite the event callback of the button. When `perform_tray_click` called, it will set +/// the menu to status bar in the end. And when the menu is closed `menu_did_close` will set it to +/// nil again. fn make_tray_class() -> *const Class { static mut TRAY_CLASS: *const Class = 0 as *const Class; static INIT: Once = Once::new(); @@ -161,11 +162,20 @@ fn make_tray_class() -> *const Class { INIT.call_once(|| unsafe { let superclass = class!(NSObject); let mut decl = ClassDecl::new("TaoTrayHandler", superclass).unwrap(); + decl.add_ivar::("status_bar"); + decl.add_ivar::("menu"); decl.add_method( sel!(click:), perform_tray_click as extern "C" fn(&mut Object, _, id), ); + let delegate = Protocol::get("NSMenuDelegate").unwrap(); + decl.add_protocol(&delegate); + decl.add_method( + sel!(menuDidClose:), + menu_did_close as extern "C" fn(&mut Object, _, id), + ); + TRAY_CLASS = decl.register(); }); @@ -173,7 +183,7 @@ fn make_tray_class() -> *const Class { } /// This will fire for an NSButton callback. -extern "C" fn perform_tray_click(_this: &mut Object, _: Sel, _sender: id) { +extern "C" fn perform_tray_click(this: &mut Object, _: Sel, button: id) { unsafe { let app: id = msg_send![class!(NSApplication), sharedApplication]; let current_event: id = msg_send![app, currentEvent]; @@ -219,6 +229,21 @@ extern "C" fn perform_tray_click(_this: &mut Object, _: Sel, _sender: id) { }; AppState::queue_event(EventWrapper::StaticEvent(event)); + + let menu = this.get_ivar::("menu"); + if *menu != nil { + let status_bar = this.get_ivar::("status_bar"); + status_bar.setMenu_(*menu); + let () = msg_send![button, performClick: nil]; + } } } } + +// Set the menu of the status bar to nil, so it won't overwrite the button events. +extern "C" fn menu_did_close(this: &mut Object, _: Sel, _menu: id) { + unsafe { + let status_bar = this.get_ivar::("status_bar"); + status_bar.setMenu_(nil); + } +} diff --git a/src/platform_impl/macos/util/async.rs b/src/platform_impl/macos/util/async.rs index d6e2b3165b..00521e13ca 100644 --- a/src/platform_impl/macos/util/async.rs +++ b/src/platform_impl/macos/util/async.rs @@ -15,7 +15,7 @@ use dispatch::Queue; use menu::Menu; use objc::{ rc::autoreleasepool, - runtime::{NO, YES}, + runtime::{BOOL, NO, YES}, }; use crate::{ @@ -57,7 +57,8 @@ pub unsafe fn set_style_mask_async(ns_window: id, ns_view: id, mask: NSWindowSty }); } pub unsafe fn set_style_mask_sync(ns_window: id, ns_view: id, mask: NSWindowStyleMask) { - if msg_send![class!(NSThread), isMainThread] { + let is_main_thread: BOOL = msg_send!(class!(NSThread), isMainThread); + if is_main_thread != NO { set_style_mask(ns_window, ns_view, mask); } else { let ns_window = MainThreadSafe(ns_window); diff --git a/src/platform_impl/macos/util/mod.rs b/src/platform_impl/macos/util/mod.rs index ee904f6e6f..05b7581093 100644 --- a/src/platform_impl/macos/util/mod.rs +++ b/src/platform_impl/macos/util/mod.rs @@ -48,7 +48,7 @@ impl IdRef { #[allow(dead_code)] pub fn retain(inner: id) -> IdRef { if inner != nil { - let () = unsafe { msg_send![inner, retain] }; + let _: id = unsafe { msg_send![inner, retain] }; } IdRef(inner) } @@ -84,10 +84,7 @@ impl Deref for IdRef { impl Clone for IdRef { fn clone(&self) -> IdRef { - if self.0 != nil { - let _: id = unsafe { msg_send![self.0, retain] }; - } - IdRef(self.0) + IdRef::retain(self.0) } } @@ -147,8 +144,8 @@ pub unsafe fn app_name() -> Option { } pub unsafe fn superclass<'a>(this: &'a Object) -> &'a Class { - let superclass: id = msg_send![this, superclass]; - &*(superclass as *const _) + let superclass: *const Class = msg_send![this, superclass]; + &*superclass } pub unsafe fn create_input_context(view: id) -> IdRef { diff --git a/src/platform_impl/macos/view.rs b/src/platform_impl/macos/view.rs index 8577a53b6b..764260aadc 100644 --- a/src/platform_impl/macos/view.rs +++ b/src/platform_impl/macos/view.rs @@ -5,7 +5,7 @@ use std::{ boxed::Box, collections::{HashSet, VecDeque}, os::raw::*, - slice, str, + ptr, slice, str, sync::{Arc, Mutex, Weak}, }; @@ -349,7 +349,7 @@ extern "C" fn view_did_move_to_window(this: &Object, _sel: Sel) { let tracking_rect: NSInteger = msg_send![this, addTrackingRect:rect owner:this - userData:nil + userData:ptr::null_mut::() assumeInside:NO ]; state.tracking_rect = Some(tracking_rect); @@ -370,7 +370,7 @@ extern "C" fn frame_did_change(this: &Object, _sel: Sel, _event: id) { let tracking_rect: NSInteger = msg_send![this, addTrackingRect:rect owner:this - userData:nil + userData:ptr::null_mut::() assumeInside:NO ]; @@ -462,8 +462,8 @@ extern "C" fn set_marked_text( trace!("Triggered `setMarkedText`"); unsafe { let marked_text_ref = clear_marked_text(this); - let has_attr = msg_send![string, isKindOfClass: class!(NSAttributedString)]; - if has_attr { + let has_attr: BOOL = msg_send![string, isKindOfClass: class!(NSAttributedString)]; + if has_attr != NO { marked_text_ref.initWithAttributedString(string); } else { marked_text_ref.initWithString(string); @@ -538,8 +538,8 @@ extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_ran let state_ptr: *mut c_void = *this.get_ivar("taoState"); let state = &mut *(state_ptr as *mut ViewState); - let has_attr = msg_send![string, isKindOfClass: class!(NSAttributedString)]; - let characters = if has_attr { + let has_attr: BOOL = msg_send![string, isKindOfClass: class!(NSAttributedString)]; + let characters = if has_attr != NO { // This is a *mut NSAttributedString msg_send![string, string] } else { @@ -557,13 +557,11 @@ extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_ran // We don't need this now, but it's here if that changes. //let event: id = msg_send![NSApp(), currentEvent]; - // We only send the IME text input here. The text coming from the - // keyboard is handled by `key_down` + AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { + window_id: WindowId(get_window_id(state.ns_window)), + event: WindowEvent::ReceivedImeText(string), + })); if state.in_ime_preedit { - AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { - window_id: WindowId(get_window_id(state.ns_window)), - event: WindowEvent::ReceivedImeText(string), - })); state.in_ime_preedit = false; state.key_triggered_ime = true; } @@ -1008,7 +1006,7 @@ fn mouse_motion(this: &Object, event: id) { || view_point.x > view_rect.size.width || view_point.y > view_rect.size.height { - let mouse_buttons_down: NSInteger = msg_send![class!(NSEvent), pressedMouseButtons]; + let mouse_buttons_down: NSUInteger = msg_send![class!(NSEvent), pressedMouseButtons]; if mouse_buttons_down == 0 { // Point is outside of the client area (view) and no buttons are pressed return; diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index afb7ec0a42..c5f18fdf26 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -1,9 +1,10 @@ // Copyright 2019-2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -use raw_window_handle::{macos::MacOSHandle, RawWindowHandle}; +use raw_window_handle::{AppKitHandle, RawWindowHandle}; use std::{ collections::VecDeque, + convert::TryInto, f64, os::raw::c_void, sync::{ @@ -38,7 +39,7 @@ use cocoa::{ NSWindowStyleMask, }, base::{id, nil}, - foundation::{NSAutoreleasePool, NSDictionary, NSPoint, NSRect, NSSize}, + foundation::{NSAutoreleasePool, NSDictionary, NSPoint, NSRect, NSSize, NSUInteger}, }; use core_graphics::display::{CGDisplay, CGDisplayMode}; use objc::{ @@ -295,7 +296,10 @@ pub struct SharedState { is_simple_fullscreen: bool, pub saved_style: Option, /// Presentation options saved before entering `set_simple_fullscreen`, and - /// restored upon exiting it + /// restored upon exiting it. Also used when transitioning from Borderless to + /// Exclusive fullscreen in `set_fullscreen` because we need to disable the menu + /// bar in exclusive fullscreen but want to restore the original options when + /// transitioning back to borderless fullscreen. save_presentation_opts: Option, pub saved_desktop_display_mode: Option<(CGDisplay, CGDisplayMode)>, } @@ -344,7 +348,8 @@ impl UnownedWindow { pl_attribs: PlatformSpecificWindowBuilderAttributes, ) -> Result<(Arc, IdRef), RootOsError> { unsafe { - if !msg_send![class!(NSThread), isMainThread] { + let is_main_thread: BOOL = msg_send!(class!(NSThread), isMainThread); + if is_main_thread == NO { panic!("Windows can only be created on the main thread on macOS"); } } @@ -843,6 +848,15 @@ impl UnownedWindow { let mut fade_token = ffi::kCGDisplayFadeReservationInvalidToken; + if matches!(old_fullscreen, Some(Fullscreen::Borderless(_))) { + unsafe { + let app = NSApp(); + trace!("Locked shared state in `set_fullscreen`"); + let mut shared_state_lock = self.shared_state.lock().unwrap(); + shared_state_lock.save_presentation_opts = Some(app.presentationOptions_()); + } + } + unsafe { // Fade to black (and wait for the fade to complete) to hide the // flicker from capturing the display and switching display mode @@ -932,16 +946,41 @@ impl UnownedWindow { // of the menu bar, and this looks broken, so we must make sure // that the menu bar is disabled. This is done in the window // delegate in `window:willUseFullScreenPresentationOptions:`. + let app = NSApp(); + trace!("Locked shared state in `set_fullscreen`"); + shared_state_lock.save_presentation_opts = Some(app.presentationOptions_()); + + let presentation_options = + NSApplicationPresentationOptions::NSApplicationPresentationFullScreen + | NSApplicationPresentationOptions::NSApplicationPresentationHideDock + | NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar; + app.setPresentationOptions_(presentation_options); + let () = msg_send![*self.ns_window, setLevel: ffi::CGShieldingWindowLevel() + 1]; }, ( &Some(Fullscreen::Exclusive(RootVideoMode { ref video_mode })), &Some(Fullscreen::Borderless(_)), ) => unsafe { + let presentation_options = shared_state_lock.save_presentation_opts.unwrap_or_else(|| { + NSApplicationPresentationOptions::NSApplicationPresentationFullScreen + | NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock + | NSApplicationPresentationOptions::NSApplicationPresentationAutoHideMenuBar + }); + NSApp().setPresentationOptions_(presentation_options); + util::restore_display_mode_async(video_mode.monitor().inner.native_identifier()); + + // Restore the normal window level following the Borderless fullscreen + // `CGShieldingWindowLevel() + 1` hack. + let () = msg_send![ + *self.ns_window, + setLevel: ffi::NSWindowLevel::NSNormalWindowLevel + ]; }, _ => INTERRUPT_EVENT_LOOP_EXIT.store(false, Ordering::SeqCst), } + trace!("Unlocked shared state in `set_fullscreen`"); } #[inline] @@ -1052,9 +1091,9 @@ impl UnownedWindow { let desc = NSScreen::deviceDescription(screen); let key = util::ns_string_id_ref("NSScreenNumber"); let value = NSDictionary::valueForKey_(desc, *key); - let display_id = msg_send![value, unsignedIntegerValue]; + let display_id: NSUInteger = msg_send![value, unsignedIntegerValue]; RootMonitorHandle { - inner: MonitorHandle::new(display_id), + inner: MonitorHandle::new(display_id.try_into().unwrap()), } } } @@ -1077,12 +1116,10 @@ impl UnownedWindow { #[inline] pub fn raw_window_handle(&self) -> RawWindowHandle { - let handle = MacOSHandle { - ns_window: *self.ns_window as *mut _, - ns_view: *self.ns_view as *mut _, - ..MacOSHandle::empty() - }; - RawWindowHandle::MacOS(handle) + let mut handle = AppKitHandle::empty(); + handle.ns_window = *self.ns_window as *mut _; + handle.ns_view = *self.ns_view as *mut _; + RawWindowHandle::AppKit(handle) } } diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index 87200edb4e..bb5c103bf3 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -483,10 +483,10 @@ extern "C" fn window_will_exit_fullscreen(this: &Object, _: Sel, _: id) { } extern "C" fn window_will_use_fullscreen_presentation_options( - _this: &Object, + this: &Object, _: Sel, _: id, - _proposed_options: NSUInteger, + proposed_options: NSUInteger, ) -> NSUInteger { // Generally, games will want to disable the menu bar and the dock. Ideally, // this would be configurable by the user. Unfortunately because of our @@ -496,10 +496,22 @@ extern "C" fn window_will_use_fullscreen_presentation_options( // still want to make this configurable for borderless fullscreen. Right now // we don't, for consistency. If we do, it should be documented that the // user-provided options are ignored in exclusive fullscreen. - (NSApplicationPresentationOptions::NSApplicationPresentationFullScreen - | NSApplicationPresentationOptions::NSApplicationPresentationHideDock - | NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar) - .bits() + let mut options: NSUInteger = proposed_options; + with_state(this, |state| { + state.with_window(|window| { + trace!("Locked shared state in `window_will_use_fullscreen_presentation_options`"); + let shared_state = window.shared_state.lock().unwrap(); + if let Some(Fullscreen::Exclusive(_)) = shared_state.fullscreen { + options = (NSApplicationPresentationOptions::NSApplicationPresentationFullScreen + | NSApplicationPresentationOptions::NSApplicationPresentationHideDock + | NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar) + .bits(); + } + trace!("Unlocked shared state in `window_will_use_fullscreen_presentation_options`"); + }) + }); + + options } /// Invoked when entered fullscreen diff --git a/src/platform_impl/windows/accelerator.rs b/src/platform_impl/windows/accelerator.rs index 704e22592a..cd482d7786 100644 --- a/src/platform_impl/windows/accelerator.rs +++ b/src/platform_impl/windows/accelerator.rs @@ -7,7 +7,8 @@ use std::{ }; use lazy_static::lazy_static; -use winapi::{ctypes::c_int, shared::windef::*, um::winuser::*}; + +use windows::Win32::{Foundation::HWND, UI::WindowsAndMessaging::*}; // NOTE: // https://docs.microsoft.com/en-us/windows/win32/wsw/thread-safety @@ -15,11 +16,11 @@ use winapi::{ctypes::c_int, shared::windef::*, um::winuser::*}; // unless the MSDN Library article for the function explicitly mentions it is not. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -struct WindowHandle(HWND); +struct WindowHandle(isize); unsafe impl Send for WindowHandle {} #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -struct AccelHandle(HACCEL); +struct AccelHandle(isize); unsafe impl Send for AccelHandle {} unsafe impl Sync for AccelHandle {} @@ -36,32 +37,31 @@ pub(crate) struct AccelTable { impl AccelTable { fn new(accel: &[ACCEL]) -> AccelTable { - let accel = - unsafe { CreateAcceleratorTableW(accel as *const _ as *mut _, accel.len() as c_int) }; + let accel = unsafe { CreateAcceleratorTableW(accel as *const _ as *mut _, accel.len() as i32) }; AccelTable { - accel: AccelHandle(accel), + accel: AccelHandle(accel.0), } } pub(crate) fn handle(&self) -> HACCEL { - self.accel.0 + HACCEL(self.accel.0) } } pub(crate) fn register_accel(hwnd: HWND, accel: &[ACCEL]) { let mut table = ACCEL_TABLES.lock().unwrap(); - table.insert(WindowHandle(hwnd), Arc::new(AccelTable::new(accel))); + table.insert(WindowHandle(hwnd.0), Arc::new(AccelTable::new(accel))); } impl Drop for AccelTable { fn drop(&mut self) { unsafe { - DestroyAcceleratorTable(self.accel.0); + DestroyAcceleratorTable(self.handle()); } } } pub(crate) fn find_accels(hwnd: HWND) -> Option> { let table = ACCEL_TABLES.lock().unwrap(); - table.get(&WindowHandle(hwnd)).cloned() + table.get(&WindowHandle(hwnd.0)).cloned() } diff --git a/src/platform_impl/windows/clipboard.rs b/src/platform_impl/windows/clipboard.rs index 4e3508b5a1..35a9af9bfd 100644 --- a/src/platform_impl/windows/clipboard.rs +++ b/src/platform_impl/windows/clipboard.rs @@ -2,23 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 use crate::clipboard::{ClipboardFormat, FormatId}; -use std::{ - ffi::{CString, OsStr}, - os::windows::ffi::OsStrExt, - ptr, -}; -use winapi::{ - shared::{ - minwindef::{FALSE, UINT}, - ntdef::{CHAR, HANDLE, LPWSTR, WCHAR}, - }, - um::{ - errhandlingapi::GetLastError, - winbase::{GlobalAlloc, GlobalLock, GlobalUnlock, GMEM_MOVEABLE}, - winuser::{ +use std::{ffi::OsStr, os::windows::ffi::OsStrExt, ptr}; +use windows::Win32::{ + Foundation::{HANDLE, HWND, PSTR, PWSTR}, + System::{ + DataExchange::{ CloseClipboard, EmptyClipboard, GetClipboardData, OpenClipboard, RegisterClipboardFormatA, - SetClipboardData, CF_UNICODETEXT, + SetClipboardData, }, + Memory::{GlobalAlloc, GlobalLock, GlobalUnlock, GMEM_MOVEABLE}, + SystemServices::CF_UNICODETEXT, }, }; @@ -35,18 +28,18 @@ impl Clipboard { pub(crate) fn read_text(&self) -> Option { with_clipboard(|| unsafe { let handle = GetClipboardData(CF_UNICODETEXT); - if handle.is_null() { + if handle.0 == 0 { None } else { - let unic_str = GlobalLock(handle) as LPWSTR; + let unic_str = PWSTR(GlobalLock(handle.0) as *mut _); let mut len = 0; - while *unic_str.offset(len) != 0 { + while *unic_str.0.offset(len) != 0 { len += 1; } - let utf16_slice = std::slice::from_raw_parts(unic_str, len as usize); + let utf16_slice = std::slice::from_raw_parts(unic_str.0, len as usize); let result = String::from_utf16(utf16_slice); if let Ok(result) = result { - GlobalUnlock(handle); + GlobalUnlock(handle.0); return Some(result); } @@ -61,20 +54,22 @@ impl Clipboard { EmptyClipboard(); for format in formats { - let handle = make_handle(&format); - let format_id = match get_format_id(&format.identifier) { + let handle = make_handle(format); + let format_id = match get_format_id(format.identifier) { Some(id) => id, None => { + #[cfg(debug_assertions)] println!("failed to register clipboard format {}", &format.identifier); continue; } }; let result = SetClipboardData(format_id, handle); - if result.is_null() { + if result.0 == 0 { + #[cfg(debug_assertions)] println!( "failed to set clipboard for fmt {}, error: {}", &format.identifier, - GetLastError() + windows::core::Error::from_win32().code().0 ); } } @@ -82,7 +77,7 @@ impl Clipboard { } } -fn get_format_id(format: FormatId) -> Option { +fn get_format_id(format: FormatId) -> Option { if let Some((id, _)) = STANDARD_FORMATS.iter().find(|(_, s)| s == &format) { return Some(*id); } @@ -92,22 +87,15 @@ fn get_format_id(format: FormatId) -> Option { } } -fn register_identifier(ident: &str) -> Option { - let cstr = match CString::new(ident) { - Ok(s) => s, - Err(_) => { - // granted this should happen _never_, but unwrap feels bad - println!("Null byte in clipboard identifier '{}'", ident); - return None; - } - }; +fn register_identifier(ident: &str) -> Option { unsafe { - let pb_format = RegisterClipboardFormatA(cstr.as_ptr()); + let pb_format = RegisterClipboardFormatA(ident); if pb_format == 0 { - let err = GetLastError(); + #[cfg(debug_assertions)] println!( "failed to register clipboard format '{}'; error {}.", - ident, err + ident, + windows::core::Error::from_win32().code().0 ); return None; } @@ -116,29 +104,26 @@ fn register_identifier(ident: &str) -> Option { } unsafe fn make_handle(format: &ClipboardFormat) -> HANDLE { - if format.identifier == ClipboardFormat::TEXT { + HANDLE(if format.identifier == ClipboardFormat::TEXT { let s: &OsStr = std::str::from_utf8_unchecked(&format.data).as_ref(); let wstr: Vec = s.encode_wide().chain(Some(0)).collect(); - let handle = GlobalAlloc(GMEM_MOVEABLE, wstr.len() * std::mem::size_of::()); - let locked = GlobalLock(handle) as LPWSTR; - ptr::copy_nonoverlapping(wstr.as_ptr(), locked, wstr.len()); + let handle = GlobalAlloc(GMEM_MOVEABLE, wstr.len() * std::mem::size_of::()); + let locked = PWSTR(GlobalLock(handle) as *mut _); + ptr::copy_nonoverlapping(wstr.as_ptr(), locked.0, wstr.len()); GlobalUnlock(handle); handle } else { - let handle = GlobalAlloc( - GMEM_MOVEABLE, - format.data.len() * std::mem::size_of::(), - ); - let locked = GlobalLock(handle) as *mut u8; - ptr::copy_nonoverlapping(format.data.as_ptr(), locked, format.data.len()); + let handle = GlobalAlloc(GMEM_MOVEABLE, format.data.len() * std::mem::size_of::()); + let locked = PSTR(GlobalLock(handle) as *mut _); + ptr::copy_nonoverlapping(format.data.as_ptr(), locked.0, format.data.len()); GlobalUnlock(handle); handle - } + }) } fn with_clipboard(f: impl FnOnce() -> V) -> Option { unsafe { - if OpenClipboard(ptr::null_mut()) == FALSE { + if !OpenClipboard(HWND::default()).as_bool() { return None; } @@ -151,7 +136,7 @@ fn with_clipboard(f: impl FnOnce() -> V) -> Option { } // https://docs.microsoft.com/en-ca/windows/win32/dataxchg/standard-clipboard-formats -static STANDARD_FORMATS: &[(UINT, &str)] = &[ +static STANDARD_FORMATS: &[(u32, &str)] = &[ (1, "CF_TEXT"), (2, "CF_BITMAP"), (3, "CF_METAFILEPICT"), diff --git a/src/platform_impl/windows/dark_mode.rs b/src/platform_impl/windows/dark_mode.rs index 8abea88eb1..41a661625b 100644 --- a/src/platform_impl/windows/dark_mode.rs +++ b/src/platform_impl/windows/dark_mode.rs @@ -3,36 +3,33 @@ /// This is a simple implementation of support for Windows Dark Mode, /// which is inspired by the solution in https://github.com/ysc3839/win32-darkmode -use winapi::{ - shared::{ - basetsd::SIZE_T, - minwindef::{BOOL, DWORD, FALSE, UINT, ULONG, WORD}, - ntdef::{LPSTR, NTSTATUS, NT_SUCCESS, PVOID, WCHAR}, - windef::HWND, - winerror::S_OK, - }, - um::{libloaderapi, uxtheme, winuser}, +use windows::Win32::{ + Foundation::{BOOL, HWND, PSTR, PWSTR}, + System::LibraryLoader::*, + UI::{Accessibility::*, Controls::*, WindowsAndMessaging::*}, }; +use std::ffi::c_void; + use crate::{platform_impl::platform::util, window::Theme}; lazy_static! { - static ref WIN10_BUILD_VERSION: Option = { + static ref WIN10_BUILD_VERSION: Option = { // FIXME: RtlGetVersion is a documented windows API, - // should be part of winapi! + // should be part of win32metadata! #[allow(non_snake_case)] #[repr(C)] struct OSVERSIONINFOW { - dwOSVersionInfoSize: ULONG, - dwMajorVersion: ULONG, - dwMinorVersion: ULONG, - dwBuildNumber: ULONG, - dwPlatformId: ULONG, - szCSDVersion: [WCHAR; 128], + dwOSVersionInfoSize: u32, + dwMajorVersion: u32, + dwMinorVersion: u32, + dwBuildNumber: u32, + dwPlatformId: u32, + szCSDVersion: [u16; 128], } - type RtlGetVersion = unsafe extern "system" fn (*mut OSVERSIONINFOW) -> NTSTATUS; + type RtlGetVersion = unsafe extern "system" fn (*mut OSVERSIONINFOW) -> i32; let handle = get_function!("ntdll.dll", RtlGetVersion); if let Some(rtl_get_version) = handle { @@ -48,7 +45,7 @@ lazy_static! { let status = (rtl_get_version)(&mut vi as _); - if NT_SUCCESS(status) && vi.dwMajorVersion == 10 && vi.dwMinorVersion == 0 { + if status >= 0 && vi.dwMajorVersion == 10 && vi.dwMinorVersion == 0 { Some(vi.dwBuildNumber) } else { None @@ -86,14 +83,17 @@ pub fn try_theme(hwnd: HWND, preferred_theme: Option) -> Theme { } else { Theme::Light }; - let theme_name = match theme { - Theme::Dark => DARK_THEME_NAME.as_ptr(), - Theme::Light => LIGHT_THEME_NAME.as_ptr(), - }; + let theme_name = PWSTR( + match theme { + Theme::Dark => DARK_THEME_NAME.clone(), + Theme::Light => LIGHT_THEME_NAME.clone(), + } + .as_mut_ptr(), + ); - let status = unsafe { uxtheme::SetWindowTheme(hwnd, theme_name as _, std::ptr::null()) }; + let status = unsafe { SetWindowTheme(hwnd, theme_name, PWSTR::default()) }; - if status == S_OK && set_dark_mode_for_window(hwnd, is_dark_mode) { + if status.is_ok() && set_dark_mode_for_window(hwnd, is_dark_mode) { return theme; } } @@ -116,8 +116,8 @@ fn set_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) -> bool { #[repr(C)] struct WINDOWCOMPOSITIONATTRIBDATA { Attrib: WINDOWCOMPOSITIONATTRIB, - pvData: PVOID, - cbData: SIZE_T, + pvData: *mut c_void, + cbData: usize, } lazy_static! { @@ -128,7 +128,7 @@ fn set_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) -> bool { if let Some(set_window_composition_attribute) = *SET_WINDOW_COMPOSITION_ATTRIBUTE { unsafe { // SetWindowCompositionAttribute needs a bigbool (i32), not bool. - let mut is_dark_mode_bigbool = is_dark_mode as BOOL; + let mut is_dark_mode_bigbool: BOOL = is_dark_mode.into(); let mut data = WINDOWCOMPOSITIONATTRIBDATA { Attrib: WCA_USEDARKMODECOLORS, @@ -138,7 +138,7 @@ fn set_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) -> bool { let status = set_window_composition_attribute(hwnd, &mut data as *mut _); - status != FALSE + status.as_bool() } } else { false @@ -154,24 +154,20 @@ fn should_apps_use_dark_mode() -> bool { lazy_static! { static ref SHOULD_APPS_USE_DARK_MODE: Option = { unsafe { - const UXTHEME_SHOULDAPPSUSEDARKMODE_ORDINAL: WORD = 132; + const UXTHEME_SHOULDAPPSUSEDARKMODE_ORDINAL: u16 = 132; - let module = libloaderapi::LoadLibraryA("uxtheme.dll\0".as_ptr() as _); + let module = LoadLibraryA("uxtheme.dll"); - if module.is_null() { + if module.is_invalid() { return None; } - let handle = libloaderapi::GetProcAddress( + let handle = GetProcAddress( module, - winuser::MAKEINTRESOURCEA(UXTHEME_SHOULDAPPSUSEDARKMODE_ORDINAL), + PSTR(UXTHEME_SHOULDAPPSUSEDARKMODE_ORDINAL as usize as *mut _), ); - if handle.is_null() { - None - } else { - Some(std::mem::transmute(handle)) - } + handle.map(|handle| std::mem::transmute(handle)) } }; } @@ -181,34 +177,23 @@ fn should_apps_use_dark_mode() -> bool { .unwrap_or(false) } -// FIXME: This definition was missing from winapi. Can remove from -// here and use winapi once the following PR is released: -// https://github.com/retep998/winapi-rs/pull/815 -#[repr(C)] -#[allow(non_snake_case)] -struct HIGHCONTRASTA { - cbSize: UINT, - dwFlags: DWORD, - lpszDefaultScheme: LPSTR, -} - -const HCF_HIGHCONTRASTON: DWORD = 1; +const HCF_HIGHCONTRASTON: u32 = 1; fn is_high_contrast() -> bool { let mut hc = HIGHCONTRASTA { cbSize: 0, dwFlags: 0, - lpszDefaultScheme: std::ptr::null_mut(), + lpszDefaultScheme: PSTR::default(), }; let ok = unsafe { - winuser::SystemParametersInfoA( - winuser::SPI_GETHIGHCONTRAST, + SystemParametersInfoA( + SPI_GETHIGHCONTRAST, std::mem::size_of_val(&hc) as _, &mut hc as *mut _ as _, 0, ) }; - ok != FALSE && (HCF_HIGHCONTRASTON & hc.dwFlags) == 1 + ok.as_bool() && (HCF_HIGHCONTRASTON & hc.dwFlags) != 0 } diff --git a/src/platform_impl/windows/dpi.rs b/src/platform_impl/windows/dpi.rs index cb12043d50..7e6faac80e 100644 --- a/src/platform_impl/windows/dpi.rs +++ b/src/platform_impl/windows/dpi.rs @@ -5,24 +5,16 @@ use std::sync::Once; +use windows::Win32::{ + Foundation::HWND, + Graphics::Gdi::*, + UI::{HiDpi::*, WindowsAndMessaging::*}, +}; + use crate::platform_impl::platform::util::{ ENABLE_NON_CLIENT_DPI_SCALING, GET_DPI_FOR_MONITOR, GET_DPI_FOR_WINDOW, SET_PROCESS_DPI_AWARE, SET_PROCESS_DPI_AWARENESS, SET_PROCESS_DPI_AWARENESS_CONTEXT, }; -use winapi::{ - shared::{ - minwindef::FALSE, - windef::{DPI_AWARENESS_CONTEXT, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE, HMONITOR, HWND}, - winerror::S_OK, - }, - um::{ - shellscalingapi::{MDT_EFFECTIVE_DPI, PROCESS_PER_MONITOR_DPI_AWARE}, - wingdi::{GetDeviceCaps, LOGPIXELSX}, - winuser::{self, MONITOR_DEFAULTTONEAREST}, - }, -}; - -const DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2: DPI_AWARENESS_CONTEXT = -4isize as _; pub fn become_dpi_aware() { static ENABLE_DPI_AWARENESS: Once = Once::new(); @@ -30,14 +22,14 @@ pub fn become_dpi_aware() { unsafe { if let Some(SetProcessDpiAwarenessContext) = *SET_PROCESS_DPI_AWARENESS_CONTEXT { // We are on Windows 10 Anniversary Update (1607) or later. - if SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) == FALSE { + if !SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2).as_bool() { // V2 only works with Windows 10 Creators Update (1703). Try using the older // V1 if we can't set V2. SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); } } else if let Some(SetProcessDpiAwareness) = *SET_PROCESS_DPI_AWARENESS { // We are on Windows 8.1 or later. - SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); + let _ = SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); } else if let Some(SetProcessDPIAware) = *SET_PROCESS_DPI_AWARE { // We are on Vista or later. SetProcessDPIAware(); @@ -60,7 +52,7 @@ pub fn get_monitor_dpi(hmonitor: HMONITOR) -> Option { // We are on Windows 8.1 or later. let mut dpi_x = 0; let mut dpi_y = 0; - if GetDpiForMonitor(hmonitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut dpi_y) == S_OK { + if GetDpiForMonitor(hmonitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut dpi_y).is_ok() { // MSDN says that "the values of *dpiX and *dpiY are identical. You only need to // record one of the values to determine the DPI and respond appropriately". // https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510(v=vs.85).aspx @@ -77,8 +69,8 @@ pub fn dpi_to_scale_factor(dpi: u32) -> f64 { } pub unsafe fn hwnd_dpi(hwnd: HWND) -> u32 { - let hdc = winuser::GetDC(hwnd); - if hdc.is_null() { + let hdc = GetDC(hwnd); + if hdc.is_invalid() { panic!("[tao] `GetDC` returned null!"); } if let Some(GetDpiForWindow) = *GET_DPI_FOR_WINDOW { @@ -89,21 +81,21 @@ pub unsafe fn hwnd_dpi(hwnd: HWND) -> u32 { } } else if let Some(GetDpiForMonitor) = *GET_DPI_FOR_MONITOR { // We are on Windows 8.1 or later. - let monitor = winuser::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); - if monitor.is_null() { + let monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + if monitor.is_invalid() { return BASE_DPI; } let mut dpi_x = 0; let mut dpi_y = 0; - if GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut dpi_y) == S_OK { + if GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut dpi_y).is_ok() { dpi_x as u32 } else { BASE_DPI } } else { // We are on Vista or later. - if winuser::IsProcessDPIAware() != FALSE { + if IsProcessDPIAware().as_bool() { // If the process is DPI aware, then scaling must be handled by the application using // this DPI value. GetDeviceCaps(hdc, LOGPIXELSX) as u32 diff --git a/src/platform_impl/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs index d20541fcc0..3b11392791 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -1,256 +1,166 @@ // Copyright 2019-2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -use std::{ - ffi::OsString, - os::windows::ffi::OsStringExt, - path::PathBuf, - ptr, - sync::atomic::{AtomicUsize, Ordering}, -}; - -use winapi::{ - ctypes::c_void, - shared::{ - guiddef::REFIID, - minwindef::{DWORD, UINT, ULONG}, - windef::{HWND, POINTL}, - winerror::S_OK, - }, - um::{ - objidl::IDataObject, - oleidl::{IDropTarget, IDropTargetVtbl, DROPEFFECT_COPY, DROPEFFECT_NONE}, - shellapi, unknwnbase, - winnt::HRESULT, +use std::{ffi::OsString, os::windows::ffi::OsStringExt, path::PathBuf, ptr}; + +use windows::{ + self as Windows, + Win32::{ + Foundation::{self as win32f, HWND, POINTL, PWSTR}, + System::{ + Com::{IDataObject, DVASPECT_CONTENT, FORMATETC, TYMED_HGLOBAL}, + Ole::{DROPEFFECT_COPY, DROPEFFECT_NONE}, + SystemServices::CF_HDROP, + }, + UI::Shell::{DragFinish, DragQueryFileW, HDROP}, }, }; +use windows_macros::implement; + use crate::platform_impl::platform::WindowId; use crate::{event::Event, window::WindowId as SuperWindowId}; -#[repr(C)] -pub struct FileDropHandlerData { - pub interface: IDropTarget, - refcount: AtomicUsize, +#[implement(Windows::Win32::System::Ole::IDropTarget)] +pub struct FileDropHandler { window: HWND, send_event: Box)>, - cursor_effect: DWORD, + cursor_effect: u32, hovered_is_valid: bool, /* If the currently hovered item is not valid there must not be any `HoveredFileCancelled` emitted */ } -pub struct FileDropHandler { - pub data: *mut FileDropHandlerData, -} - #[allow(non_snake_case)] impl FileDropHandler { pub fn new(window: HWND, send_event: Box)>) -> FileDropHandler { - let data = Box::new(FileDropHandlerData { - interface: IDropTarget { - lpVtbl: &DROP_TARGET_VTBL as *const IDropTargetVtbl, - }, - refcount: AtomicUsize::new(1), + Self { window, send_event, cursor_effect: DROPEFFECT_NONE, hovered_is_valid: false, - }); - FileDropHandler { - data: Box::into_raw(data), } } - // Implement IUnknown - pub unsafe extern "system" fn QueryInterface( - _this: *mut unknwnbase::IUnknown, - _riid: REFIID, - _ppvObject: *mut *mut c_void, - ) -> HRESULT { - // This function doesn't appear to be required for an `IDropTarget`. - // An implementation would be nice however. - unimplemented!(); - } - - pub unsafe extern "system" fn AddRef(this: *mut unknwnbase::IUnknown) -> ULONG { - let drop_handler_data = Self::from_interface(this); - let count = drop_handler_data.refcount.fetch_add(1, Ordering::Release) + 1; - count as ULONG - } - - pub unsafe extern "system" fn Release(this: *mut unknwnbase::IUnknown) -> ULONG { - let drop_handler = Self::from_interface(this); - let count = drop_handler.refcount.fetch_sub(1, Ordering::Release) - 1; - if count == 0 { - // Destroy the underlying data - Box::from_raw(drop_handler as *mut FileDropHandlerData); - } - count as ULONG - } - - pub unsafe extern "system" fn DragEnter( - this: *mut IDropTarget, - pDataObj: *const IDataObject, - _grfKeyState: DWORD, - _pt: *const POINTL, - pdwEffect: *mut DWORD, - ) -> HRESULT { + unsafe fn DragEnter( + &mut self, + pDataObj: &Option, + _grfKeyState: u32, + _pt: POINTL, + pdwEffect: *mut u32, + ) -> windows::core::Result<()> { use crate::event::WindowEvent::HoveredFile; - let drop_handler = Self::from_interface(this); let hdrop = Self::iterate_filenames(pDataObj, |filename| { - drop_handler.send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(drop_handler.window)), + (self.send_event)(Event::WindowEvent { + window_id: SuperWindowId(WindowId(self.window.0)), event: HoveredFile(filename), }); }); - drop_handler.hovered_is_valid = hdrop.is_some(); - drop_handler.cursor_effect = if drop_handler.hovered_is_valid { + self.hovered_is_valid = hdrop.is_some(); + self.cursor_effect = if self.hovered_is_valid { DROPEFFECT_COPY } else { DROPEFFECT_NONE }; - *pdwEffect = drop_handler.cursor_effect; - - S_OK + *pdwEffect = self.cursor_effect; + Ok(()) } - pub unsafe extern "system" fn DragOver( - this: *mut IDropTarget, - _grfKeyState: DWORD, - _pt: *const POINTL, - pdwEffect: *mut DWORD, - ) -> HRESULT { - let drop_handler = Self::from_interface(this); - *pdwEffect = drop_handler.cursor_effect; - - S_OK + unsafe fn DragOver( + &self, + _grfKeyState: u32, + _pt: POINTL, + pdwEffect: *mut u32, + ) -> windows::core::Result<()> { + *pdwEffect = self.cursor_effect; + Ok(()) } - pub unsafe extern "system" fn DragLeave(this: *mut IDropTarget) -> HRESULT { + unsafe fn DragLeave(&self) -> windows::core::Result<()> { use crate::event::WindowEvent::HoveredFileCancelled; - let drop_handler = Self::from_interface(this); - if drop_handler.hovered_is_valid { - drop_handler.send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(drop_handler.window)), + if self.hovered_is_valid { + (self.send_event)(Event::WindowEvent { + window_id: SuperWindowId(WindowId(self.window.0)), event: HoveredFileCancelled, }); } - - S_OK + Ok(()) } - pub unsafe extern "system" fn Drop( - this: *mut IDropTarget, - pDataObj: *const IDataObject, - _grfKeyState: DWORD, - _pt: *const POINTL, - _pdwEffect: *mut DWORD, - ) -> HRESULT { + unsafe fn Drop( + &self, + pDataObj: &Option, + _grfKeyState: u32, + _pt: POINTL, + _pdwEffect: *mut u32, + ) -> windows::core::Result<()> { use crate::event::WindowEvent::DroppedFile; - let drop_handler = Self::from_interface(this); let hdrop = Self::iterate_filenames(pDataObj, |filename| { - drop_handler.send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(drop_handler.window)), + (self.send_event)(Event::WindowEvent { + window_id: SuperWindowId(WindowId(self.window.0)), event: DroppedFile(filename), }); }); if let Some(hdrop) = hdrop { - shellapi::DragFinish(hdrop); + DragFinish(hdrop); } - - S_OK + Ok(()) } - unsafe fn from_interface<'a, InterfaceT>(this: *mut InterfaceT) -> &'a mut FileDropHandlerData { - &mut *(this as *mut _) - } - - unsafe fn iterate_filenames( - data_obj: *const IDataObject, - callback: F, - ) -> Option + unsafe fn iterate_filenames(data_obj: &Option, callback: F) -> Option where F: Fn(PathBuf), { - use winapi::{ - shared::{ - winerror::{DV_E_FORMATETC, SUCCEEDED}, - wtypes::{CLIPFORMAT, DVASPECT_CONTENT}, - }, - um::{ - objidl::{FORMATETC, TYMED_HGLOBAL}, - shellapi::DragQueryFileW, - winuser::CF_HDROP, - }, - }; - - let mut drop_format = FORMATETC { - cfFormat: CF_HDROP as CLIPFORMAT, - ptd: ptr::null(), - dwAspect: DVASPECT_CONTENT, + let drop_format = FORMATETC { + cfFormat: CF_HDROP as u16, + ptd: ptr::null_mut(), + dwAspect: DVASPECT_CONTENT as u32, lindex: -1, - tymed: TYMED_HGLOBAL, + tymed: TYMED_HGLOBAL as u32, }; - let mut medium = std::mem::zeroed(); - let get_data_result = (*data_obj).GetData(&mut drop_format, &mut medium); - if SUCCEEDED(get_data_result) { - let hglobal = (*medium.u).hGlobal(); - let hdrop = (*hglobal) as shellapi::HDROP; - - // The second parameter (0xFFFFFFFF) instructs the function to return the item count - let item_count = DragQueryFileW(hdrop, 0xFFFFFFFF, ptr::null_mut(), 0); - - for i in 0..item_count { - // Get the length of the path string NOT including the terminating null character. - // Previously, this was using a fixed size array of MAX_PATH length, but the - // Windows API allows longer paths under certain circumstances. - let character_count = DragQueryFileW(hdrop, i, ptr::null_mut(), 0) as usize; - let str_len = character_count + 1; - - // Fill path_buf with the null-terminated file name - let mut path_buf = Vec::with_capacity(str_len); - DragQueryFileW(hdrop, i, path_buf.as_mut_ptr(), str_len as UINT); - path_buf.set_len(str_len); - - callback(OsString::from_wide(&path_buf[0..character_count]).into()); + match data_obj + .as_ref() + .expect("Received null IDataObject") + .GetData(&drop_format) + { + Ok(medium) => { + let hglobal = medium.Anonymous.hGlobal; + let hdrop = HDROP(hglobal); + + // The second parameter (0xFFFFFFFF) instructs the function to return the item count + let item_count = DragQueryFileW(hdrop, 0xFFFFFFFF, PWSTR::default(), 0); + + for i in 0..item_count { + // Get the length of the path string NOT including the terminating null character. + // Previously, this was using a fixed size array of MAX_PATH length, but the + // Windows API allows longer paths under certain circumstances. + let character_count = DragQueryFileW(hdrop, i, PWSTR::default(), 0) as usize; + let str_len = character_count + 1; + + // Fill path_buf with the null-terminated file name + let mut path_buf = Vec::with_capacity(str_len); + DragQueryFileW(hdrop, i, PWSTR(path_buf.as_mut_ptr()), str_len as u32); + path_buf.set_len(str_len); + + callback(OsString::from_wide(&path_buf[0..character_count]).into()); + } + + Some(hdrop) + } + Err(error) => { + debug!( + "{}", + match error.code() { + win32f::DV_E_FORMATETC => { + // If the dropped item is not a file this error will occur. + // In this case it is OK to return without taking further action. + "Error occured while processing dropped/hovered item: item is not a file." + } + _ => "Unexpected error occured while processing dropped/hovered item.", + } + ); + None } - - Some(hdrop) - } else if get_data_result == DV_E_FORMATETC { - // If the dropped item is not a file this error will occur. - // In this case it is OK to return without taking further action. - debug!("Error occured while processing dropped/hovered item: item is not a file."); - None - } else { - debug!("Unexpected error occured while processing dropped/hovered item."); - None - } - } -} - -impl FileDropHandlerData { - fn send_event(&self, event: Event<'static, ()>) { - (self.send_event)(event); - } -} - -impl Drop for FileDropHandler { - fn drop(&mut self) { - unsafe { - FileDropHandler::Release(self.data as *mut unknwnbase::IUnknown); } } } - -static DROP_TARGET_VTBL: IDropTargetVtbl = IDropTargetVtbl { - parent: unknwnbase::IUnknownVtbl { - QueryInterface: FileDropHandler::QueryInterface, - AddRef: FileDropHandler::AddRef, - Release: FileDropHandler::Release, - }, - DragEnter: FileDropHandler::DragEnter, - DragOver: FileDropHandler::DragOver, - DragLeave: FileDropHandler::DragLeave, - Drop: FileDropHandler::Drop, -}; diff --git a/src/platform_impl/windows/event.rs b/src/platform_impl/windows/event.rs index 5406655fed..219cab060c 100644 --- a/src/platform_impl/windows/event.rs +++ b/src/platform_impl/windows/event.rs @@ -10,31 +10,35 @@ use std::{ use crate::event::{ModifiersState, ScanCode, VirtualKeyCode}; -use winapi::{ - shared::minwindef::{HKL, HKL__, LPARAM, UINT, WPARAM}, - um::winuser, +use windows::Win32::{ + Foundation::{HWND, LPARAM, WPARAM}, + UI::{ + Input::KeyboardAndMouse::*, + TextServices::HKL, + WindowsAndMessaging::{self as win32wm, *}, + }, }; fn key_pressed(vkey: c_int) -> bool { - unsafe { (winuser::GetKeyState(vkey) & (1 << 15)) == (1 << 15) } + unsafe { (GetKeyState(vkey) & (1 << 15)) == (1 << 15) } } pub fn get_key_mods() -> ModifiersState { - let filter_out_altgr = layout_uses_altgr() && key_pressed(winuser::VK_RMENU); + let filter_out_altgr = layout_uses_altgr() && key_pressed(VK_RMENU); let mut mods = ModifiersState::empty(); - mods.set(ModifiersState::SHIFT, key_pressed(winuser::VK_SHIFT)); + mods.set(ModifiersState::SHIFT, key_pressed(VK_SHIFT)); mods.set( ModifiersState::CTRL, - key_pressed(winuser::VK_CONTROL) && !filter_out_altgr, + key_pressed(VK_CONTROL) && !filter_out_altgr, ); mods.set( ModifiersState::ALT, - key_pressed(winuser::VK_MENU) && !filter_out_altgr, + key_pressed(VK_MENU) && !filter_out_altgr, ); mods.set( ModifiersState::LOGO, - key_pressed(winuser::VK_LWIN) || key_pressed(winuser::VK_RWIN), + key_pressed(VK_LWIN) || key_pressed(VK_RWIN), ); mods } @@ -90,7 +94,7 @@ impl From for ModifiersState { pub fn get_pressed_keys() -> impl Iterator { let mut keyboard_state = vec![0u8; 256]; - unsafe { winuser::GetKeyboardState(keyboard_state.as_mut_ptr()) }; + unsafe { GetKeyboardState(keyboard_state.as_mut_ptr()) }; keyboard_state .into_iter() .enumerate() @@ -100,7 +104,7 @@ pub fn get_pressed_keys() -> impl Iterator { unsafe fn get_char(keyboard_state: &[u8; 256], v_key: u32, hkl: HKL) -> Option { let mut unicode_bytes = [0u16; 5]; - let len = winuser::ToUnicodeEx( + let len = ToUnicodeEx( v_key, 0, keyboard_state.as_ptr(), @@ -132,7 +136,7 @@ fn layout_uses_altgr() -> bool { static ACTIVE_LAYOUT: AtomicPtr = AtomicPtr::new(ptr::null_mut()); static USES_ALTGR: AtomicBool = AtomicBool::new(false); - let hkl = winuser::GetKeyboardLayout(0); + let hkl = GetKeyboardLayout(0); let old_hkl = ACTIVE_LAYOUT.swap(hkl, Ordering::SeqCst); if hkl == old_hkl { @@ -142,8 +146,8 @@ fn layout_uses_altgr() -> bool { let mut keyboard_state_altgr = [0u8; 256]; // AltGr is an alias for Ctrl+Alt for... some reason. Whatever it is, those are the keypresses // we have to emulate to do an AltGr test. - keyboard_state_altgr[winuser::VK_MENU as usize] = 0x80; - keyboard_state_altgr[winuser::VK_CONTROL as usize] = 0x80; + keyboard_state_altgr[VK_MENU as usize] = 0x80; + keyboard_state_altgr[VK_CONTROL as usize] = 0x80; let keyboard_state_empty = [0u8; 256]; @@ -163,55 +167,55 @@ fn layout_uses_altgr() -> bool { } } -pub fn vkey_to_tao_vkey(vkey: c_int) -> Option { +pub fn vkey_to_tao_vkey(vkey: u32) -> Option { // VK_* codes are documented here https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx match vkey { - //winuser::VK_LBUTTON => Some(VirtualKeyCode::Lbutton), - //winuser::VK_RBUTTON => Some(VirtualKeyCode::Rbutton), - //winuser::VK_CANCEL => Some(VirtualKeyCode::Cancel), - //winuser::VK_MBUTTON => Some(VirtualKeyCode::Mbutton), - //winuser::VK_XBUTTON1 => Some(VirtualKeyCode::Xbutton1), - //winuser::VK_XBUTTON2 => Some(VirtualKeyCode::Xbutton2), - winuser::VK_BACK => Some(VirtualKeyCode::Back), - winuser::VK_TAB => Some(VirtualKeyCode::Tab), - //winuser::VK_CLEAR => Some(VirtualKeyCode::Clear), - winuser::VK_RETURN => Some(VirtualKeyCode::Return), - winuser::VK_LSHIFT => Some(VirtualKeyCode::LShift), - winuser::VK_RSHIFT => Some(VirtualKeyCode::RShift), - winuser::VK_LCONTROL => Some(VirtualKeyCode::LControl), - winuser::VK_RCONTROL => Some(VirtualKeyCode::RControl), - winuser::VK_LMENU => Some(VirtualKeyCode::LAlt), - winuser::VK_RMENU => Some(VirtualKeyCode::RAlt), - winuser::VK_PAUSE => Some(VirtualKeyCode::Pause), - winuser::VK_CAPITAL => Some(VirtualKeyCode::Capital), - winuser::VK_KANA => Some(VirtualKeyCode::Kana), - //winuser::VK_HANGUEL => Some(VirtualKeyCode::Hanguel), - //winuser::VK_HANGUL => Some(VirtualKeyCode::Hangul), - //winuser::VK_JUNJA => Some(VirtualKeyCode::Junja), - //winuser::VK_FINAL => Some(VirtualKeyCode::Final), - //winuser::VK_HANJA => Some(VirtualKeyCode::Hanja), - winuser::VK_KANJI => Some(VirtualKeyCode::Kanji), - winuser::VK_ESCAPE => Some(VirtualKeyCode::Escape), - winuser::VK_CONVERT => Some(VirtualKeyCode::Convert), - winuser::VK_NONCONVERT => Some(VirtualKeyCode::NoConvert), - //winuser::VK_ACCEPT => Some(VirtualKeyCode::Accept), - //winuser::VK_MODECHANGE => Some(VirtualKeyCode::Modechange), - winuser::VK_SPACE => Some(VirtualKeyCode::Space), - winuser::VK_PRIOR => Some(VirtualKeyCode::PageUp), - winuser::VK_NEXT => Some(VirtualKeyCode::PageDown), - winuser::VK_END => Some(VirtualKeyCode::End), - winuser::VK_HOME => Some(VirtualKeyCode::Home), - winuser::VK_LEFT => Some(VirtualKeyCode::Left), - winuser::VK_UP => Some(VirtualKeyCode::Up), - winuser::VK_RIGHT => Some(VirtualKeyCode::Right), - winuser::VK_DOWN => Some(VirtualKeyCode::Down), - //winuser::VK_SELECT => Some(VirtualKeyCode::Select), - //winuser::VK_PRINT => Some(VirtualKeyCode::Print), - //winuser::VK_EXECUTE => Some(VirtualKeyCode::Execute), - winuser::VK_SNAPSHOT => Some(VirtualKeyCode::Snapshot), - winuser::VK_INSERT => Some(VirtualKeyCode::Insert), - winuser::VK_DELETE => Some(VirtualKeyCode::Delete), - //winuser::VK_HELP => Some(VirtualKeyCode::Help), + //win32wm::VK_LBUTTON => Some(VirtualKeyCode::Lbutton), + //win32wm::VK_RBUTTON => Some(VirtualKeyCode::Rbutton), + //win32wm::VK_CANCEL => Some(VirtualKeyCode::Cancel), + //win32wm::VK_MBUTTON => Some(VirtualKeyCode::Mbutton), + //win32wm::VK_XBUTTON1 => Some(VirtualKeyCode::Xbutton1), + //win32wm::VK_XBUTTON2 => Some(VirtualKeyCode::Xbutton2), + win32wm::VK_BACK => Some(VirtualKeyCode::Back), + win32wm::VK_TAB => Some(VirtualKeyCode::Tab), + //win32wm::VK_CLEAR => Some(VirtualKeyCode::Clear), + win32wm::VK_RETURN => Some(VirtualKeyCode::Return), + win32wm::VK_LSHIFT => Some(VirtualKeyCode::LShift), + win32wm::VK_RSHIFT => Some(VirtualKeyCode::RShift), + win32wm::VK_LCONTROL => Some(VirtualKeyCode::LControl), + win32wm::VK_RCONTROL => Some(VirtualKeyCode::RControl), + win32wm::VK_LMENU => Some(VirtualKeyCode::LAlt), + win32wm::VK_RMENU => Some(VirtualKeyCode::RAlt), + win32wm::VK_PAUSE => Some(VirtualKeyCode::Pause), + win32wm::VK_CAPITAL => Some(VirtualKeyCode::Capital), + win32wm::VK_KANA => Some(VirtualKeyCode::Kana), + //win32wm::VK_HANGUEL => Some(VirtualKeyCode::Hanguel), + //win32wm::VK_HANGUL => Some(VirtualKeyCode::Hangul), + //win32wm::VK_JUNJA => Some(VirtualKeyCode::Junja), + //win32wm::VK_FINAL => Some(VirtualKeyCode::Final), + //win32wm::VK_HANJA => Some(VirtualKeyCode::Hanja), + win32wm::VK_KANJI => Some(VirtualKeyCode::Kanji), + win32wm::VK_ESCAPE => Some(VirtualKeyCode::Escape), + win32wm::VK_CONVERT => Some(VirtualKeyCode::Convert), + win32wm::VK_NONCONVERT => Some(VirtualKeyCode::NoConvert), + //win32wm::VK_ACCEPT => Some(VirtualKeyCode::Accept), + //win32wm::VK_MODECHANGE => Some(VirtualKeyCode::Modechange), + win32wm::VK_SPACE => Some(VirtualKeyCode::Space), + win32wm::VK_PRIOR => Some(VirtualKeyCode::PageUp), + win32wm::VK_NEXT => Some(VirtualKeyCode::PageDown), + win32wm::VK_END => Some(VirtualKeyCode::End), + win32wm::VK_HOME => Some(VirtualKeyCode::Home), + win32wm::VK_LEFT => Some(VirtualKeyCode::Left), + win32wm::VK_UP => Some(VirtualKeyCode::Up), + win32wm::VK_RIGHT => Some(VirtualKeyCode::Right), + win32wm::VK_DOWN => Some(VirtualKeyCode::Down), + //win32wm::VK_SELECT => Some(VirtualKeyCode::Select), + //win32wm::VK_PRINT => Some(VirtualKeyCode::Print), + //win32wm::VK_EXECUTE => Some(VirtualKeyCode::Execute), + win32wm::VK_SNAPSHOT => Some(VirtualKeyCode::Snapshot), + win32wm::VK_INSERT => Some(VirtualKeyCode::Insert), + win32wm::VK_DELETE => Some(VirtualKeyCode::Delete), + //win32wm::VK_HELP => Some(VirtualKeyCode::Help), 0x30 => Some(VirtualKeyCode::Key0), 0x31 => Some(VirtualKeyCode::Key1), 0x32 => Some(VirtualKeyCode::Key2), @@ -248,121 +252,119 @@ pub fn vkey_to_tao_vkey(vkey: c_int) -> Option { 0x58 => Some(VirtualKeyCode::X), 0x59 => Some(VirtualKeyCode::Y), 0x5A => Some(VirtualKeyCode::Z), - winuser::VK_LWIN => Some(VirtualKeyCode::LWin), - winuser::VK_RWIN => Some(VirtualKeyCode::RWin), - winuser::VK_APPS => Some(VirtualKeyCode::Apps), - winuser::VK_SLEEP => Some(VirtualKeyCode::Sleep), - winuser::VK_NUMPAD0 => Some(VirtualKeyCode::Numpad0), - winuser::VK_NUMPAD1 => Some(VirtualKeyCode::Numpad1), - winuser::VK_NUMPAD2 => Some(VirtualKeyCode::Numpad2), - winuser::VK_NUMPAD3 => Some(VirtualKeyCode::Numpad3), - winuser::VK_NUMPAD4 => Some(VirtualKeyCode::Numpad4), - winuser::VK_NUMPAD5 => Some(VirtualKeyCode::Numpad5), - winuser::VK_NUMPAD6 => Some(VirtualKeyCode::Numpad6), - winuser::VK_NUMPAD7 => Some(VirtualKeyCode::Numpad7), - winuser::VK_NUMPAD8 => Some(VirtualKeyCode::Numpad8), - winuser::VK_NUMPAD9 => Some(VirtualKeyCode::Numpad9), - winuser::VK_MULTIPLY => Some(VirtualKeyCode::NumpadMultiply), - winuser::VK_ADD => Some(VirtualKeyCode::NumpadAdd), - //winuser::VK_SEPARATOR => Some(VirtualKeyCode::Separator), - winuser::VK_SUBTRACT => Some(VirtualKeyCode::NumpadSubtract), - winuser::VK_DECIMAL => Some(VirtualKeyCode::NumpadDecimal), - winuser::VK_DIVIDE => Some(VirtualKeyCode::NumpadDivide), - winuser::VK_F1 => Some(VirtualKeyCode::F1), - winuser::VK_F2 => Some(VirtualKeyCode::F2), - winuser::VK_F3 => Some(VirtualKeyCode::F3), - winuser::VK_F4 => Some(VirtualKeyCode::F4), - winuser::VK_F5 => Some(VirtualKeyCode::F5), - winuser::VK_F6 => Some(VirtualKeyCode::F6), - winuser::VK_F7 => Some(VirtualKeyCode::F7), - winuser::VK_F8 => Some(VirtualKeyCode::F8), - winuser::VK_F9 => Some(VirtualKeyCode::F9), - winuser::VK_F10 => Some(VirtualKeyCode::F10), - winuser::VK_F11 => Some(VirtualKeyCode::F11), - winuser::VK_F12 => Some(VirtualKeyCode::F12), - winuser::VK_F13 => Some(VirtualKeyCode::F13), - winuser::VK_F14 => Some(VirtualKeyCode::F14), - winuser::VK_F15 => Some(VirtualKeyCode::F15), - winuser::VK_F16 => Some(VirtualKeyCode::F16), - winuser::VK_F17 => Some(VirtualKeyCode::F17), - winuser::VK_F18 => Some(VirtualKeyCode::F18), - winuser::VK_F19 => Some(VirtualKeyCode::F19), - winuser::VK_F20 => Some(VirtualKeyCode::F20), - winuser::VK_F21 => Some(VirtualKeyCode::F21), - winuser::VK_F22 => Some(VirtualKeyCode::F22), - winuser::VK_F23 => Some(VirtualKeyCode::F23), - winuser::VK_F24 => Some(VirtualKeyCode::F24), - winuser::VK_NUMLOCK => Some(VirtualKeyCode::Numlock), - winuser::VK_SCROLL => Some(VirtualKeyCode::Scroll), - winuser::VK_BROWSER_BACK => Some(VirtualKeyCode::NavigateBackward), - winuser::VK_BROWSER_FORWARD => Some(VirtualKeyCode::NavigateForward), - winuser::VK_BROWSER_REFRESH => Some(VirtualKeyCode::WebRefresh), - winuser::VK_BROWSER_STOP => Some(VirtualKeyCode::WebStop), - winuser::VK_BROWSER_SEARCH => Some(VirtualKeyCode::WebSearch), - winuser::VK_BROWSER_FAVORITES => Some(VirtualKeyCode::WebFavorites), - winuser::VK_BROWSER_HOME => Some(VirtualKeyCode::WebHome), - winuser::VK_VOLUME_MUTE => Some(VirtualKeyCode::Mute), - winuser::VK_VOLUME_DOWN => Some(VirtualKeyCode::VolumeDown), - winuser::VK_VOLUME_UP => Some(VirtualKeyCode::VolumeUp), - winuser::VK_MEDIA_NEXT_TRACK => Some(VirtualKeyCode::NextTrack), - winuser::VK_MEDIA_PREV_TRACK => Some(VirtualKeyCode::PrevTrack), - winuser::VK_MEDIA_STOP => Some(VirtualKeyCode::MediaStop), - winuser::VK_MEDIA_PLAY_PAUSE => Some(VirtualKeyCode::PlayPause), - winuser::VK_LAUNCH_MAIL => Some(VirtualKeyCode::Mail), - winuser::VK_LAUNCH_MEDIA_SELECT => Some(VirtualKeyCode::MediaSelect), - /*winuser::VK_LAUNCH_APP1 => Some(VirtualKeyCode::Launch_app1), - winuser::VK_LAUNCH_APP2 => Some(VirtualKeyCode::Launch_app2),*/ - winuser::VK_OEM_PLUS => Some(VirtualKeyCode::Equals), - winuser::VK_OEM_COMMA => Some(VirtualKeyCode::Comma), - winuser::VK_OEM_MINUS => Some(VirtualKeyCode::Minus), - winuser::VK_OEM_PERIOD => Some(VirtualKeyCode::Period), - winuser::VK_OEM_1 => map_text_keys(vkey), - winuser::VK_OEM_2 => map_text_keys(vkey), - winuser::VK_OEM_3 => map_text_keys(vkey), - winuser::VK_OEM_4 => map_text_keys(vkey), - winuser::VK_OEM_5 => map_text_keys(vkey), - winuser::VK_OEM_6 => map_text_keys(vkey), - winuser::VK_OEM_7 => map_text_keys(vkey), - /* winuser::VK_OEM_8 => Some(VirtualKeyCode::Oem_8), */ - winuser::VK_OEM_102 => Some(VirtualKeyCode::OEM102), - /*winuser::VK_PROCESSKEY => Some(VirtualKeyCode::Processkey), - winuser::VK_PACKET => Some(VirtualKeyCode::Packet), - winuser::VK_ATTN => Some(VirtualKeyCode::Attn), - winuser::VK_CRSEL => Some(VirtualKeyCode::Crsel), - winuser::VK_EXSEL => Some(VirtualKeyCode::Exsel), - winuser::VK_EREOF => Some(VirtualKeyCode::Ereof), - winuser::VK_PLAY => Some(VirtualKeyCode::Play), - winuser::VK_ZOOM => Some(VirtualKeyCode::Zoom), - winuser::VK_NONAME => Some(VirtualKeyCode::Noname), - winuser::VK_PA1 => Some(VirtualKeyCode::Pa1), - winuser::VK_OEM_CLEAR => Some(VirtualKeyCode::Oem_clear),*/ + win32wm::VK_LWIN => Some(VirtualKeyCode::LWin), + win32wm::VK_RWIN => Some(VirtualKeyCode::RWin), + win32wm::VK_APPS => Some(VirtualKeyCode::Apps), + win32wm::VK_SLEEP => Some(VirtualKeyCode::Sleep), + win32wm::VK_NUMPAD0 => Some(VirtualKeyCode::Numpad0), + win32wm::VK_NUMPAD1 => Some(VirtualKeyCode::Numpad1), + win32wm::VK_NUMPAD2 => Some(VirtualKeyCode::Numpad2), + win32wm::VK_NUMPAD3 => Some(VirtualKeyCode::Numpad3), + win32wm::VK_NUMPAD4 => Some(VirtualKeyCode::Numpad4), + win32wm::VK_NUMPAD5 => Some(VirtualKeyCode::Numpad5), + win32wm::VK_NUMPAD6 => Some(VirtualKeyCode::Numpad6), + win32wm::VK_NUMPAD7 => Some(VirtualKeyCode::Numpad7), + win32wm::VK_NUMPAD8 => Some(VirtualKeyCode::Numpad8), + win32wm::VK_NUMPAD9 => Some(VirtualKeyCode::Numpad9), + win32wm::VK_MULTIPLY => Some(VirtualKeyCode::NumpadMultiply), + win32wm::VK_ADD => Some(VirtualKeyCode::NumpadAdd), + //win32wm::VK_SEPARATOR => Some(VirtualKeyCode::Separator), + win32wm::VK_SUBTRACT => Some(VirtualKeyCode::NumpadSubtract), + win32wm::VK_DECIMAL => Some(VirtualKeyCode::NumpadDecimal), + win32wm::VK_DIVIDE => Some(VirtualKeyCode::NumpadDivide), + win32wm::VK_F1 => Some(VirtualKeyCode::F1), + win32wm::VK_F2 => Some(VirtualKeyCode::F2), + win32wm::VK_F3 => Some(VirtualKeyCode::F3), + win32wm::VK_F4 => Some(VirtualKeyCode::F4), + win32wm::VK_F5 => Some(VirtualKeyCode::F5), + win32wm::VK_F6 => Some(VirtualKeyCode::F6), + win32wm::VK_F7 => Some(VirtualKeyCode::F7), + win32wm::VK_F8 => Some(VirtualKeyCode::F8), + win32wm::VK_F9 => Some(VirtualKeyCode::F9), + win32wm::VK_F10 => Some(VirtualKeyCode::F10), + win32wm::VK_F11 => Some(VirtualKeyCode::F11), + win32wm::VK_F12 => Some(VirtualKeyCode::F12), + win32wm::VK_F13 => Some(VirtualKeyCode::F13), + win32wm::VK_F14 => Some(VirtualKeyCode::F14), + win32wm::VK_F15 => Some(VirtualKeyCode::F15), + win32wm::VK_F16 => Some(VirtualKeyCode::F16), + win32wm::VK_F17 => Some(VirtualKeyCode::F17), + win32wm::VK_F18 => Some(VirtualKeyCode::F18), + win32wm::VK_F19 => Some(VirtualKeyCode::F19), + win32wm::VK_F20 => Some(VirtualKeyCode::F20), + win32wm::VK_F21 => Some(VirtualKeyCode::F21), + win32wm::VK_F22 => Some(VirtualKeyCode::F22), + win32wm::VK_F23 => Some(VirtualKeyCode::F23), + win32wm::VK_F24 => Some(VirtualKeyCode::F24), + win32wm::VK_NUMLOCK => Some(VirtualKeyCode::Numlock), + win32wm::VK_SCROLL => Some(VirtualKeyCode::Scroll), + win32wm::VK_BROWSER_BACK => Some(VirtualKeyCode::NavigateBackward), + win32wm::VK_BROWSER_FORWARD => Some(VirtualKeyCode::NavigateForward), + win32wm::VK_BROWSER_REFRESH => Some(VirtualKeyCode::WebRefresh), + win32wm::VK_BROWSER_STOP => Some(VirtualKeyCode::WebStop), + win32wm::VK_BROWSER_SEARCH => Some(VirtualKeyCode::WebSearch), + win32wm::VK_BROWSER_FAVORITES => Some(VirtualKeyCode::WebFavorites), + win32wm::VK_BROWSER_HOME => Some(VirtualKeyCode::WebHome), + win32wm::VK_VOLUME_MUTE => Some(VirtualKeyCode::Mute), + win32wm::VK_VOLUME_DOWN => Some(VirtualKeyCode::VolumeDown), + win32wm::VK_VOLUME_UP => Some(VirtualKeyCode::VolumeUp), + win32wm::VK_MEDIA_NEXT_TRACK => Some(VirtualKeyCode::NextTrack), + win32wm::VK_MEDIA_PREV_TRACK => Some(VirtualKeyCode::PrevTrack), + win32wm::VK_MEDIA_STOP => Some(VirtualKeyCode::MediaStop), + win32wm::VK_MEDIA_PLAY_PAUSE => Some(VirtualKeyCode::PlayPause), + win32wm::VK_LAUNCH_MAIL => Some(VirtualKeyCode::Mail), + win32wm::VK_LAUNCH_MEDIA_SELECT => Some(VirtualKeyCode::MediaSelect), + /*win32wm::VK_LAUNCH_APP1 => Some(VirtualKeyCode::Launch_app1), + win32wm::VK_LAUNCH_APP2 => Some(VirtualKeyCode::Launch_app2),*/ + win32wm::VK_OEM_PLUS => Some(VirtualKeyCode::Equals), + win32wm::VK_OEM_COMMA => Some(VirtualKeyCode::Comma), + win32wm::VK_OEM_MINUS => Some(VirtualKeyCode::Minus), + win32wm::VK_OEM_PERIOD => Some(VirtualKeyCode::Period), + win32wm::VK_OEM_1 => map_text_keys(vkey), + win32wm::VK_OEM_2 => map_text_keys(vkey), + win32wm::VK_OEM_3 => map_text_keys(vkey), + win32wm::VK_OEM_4 => map_text_keys(vkey), + win32wm::VK_OEM_5 => map_text_keys(vkey), + win32wm::VK_OEM_6 => map_text_keys(vkey), + win32wm::VK_OEM_7 => map_text_keys(vkey), + /* win32wm::VK_OEM_8 => Some(VirtualKeyCode::Oem_8), */ + win32wm::VK_OEM_102 => Some(VirtualKeyCode::OEM102), + /*win32wm::VK_PROCESSKEY => Some(VirtualKeyCode::Processkey), + win32wm::VK_PACKET => Some(VirtualKeyCode::Packet), + win32wm::VK_ATTN => Some(VirtualKeyCode::Attn), + win32wm::VK_CRSEL => Some(VirtualKeyCode::Crsel), + win32wm::VK_EXSEL => Some(VirtualKeyCode::Exsel), + win32wm::VK_EREOF => Some(VirtualKeyCode::Ereof), + win32wm::VK_PLAY => Some(VirtualKeyCode::Play), + win32wm::VK_ZOOM => Some(VirtualKeyCode::Zoom), + win32wm::VK_NONAME => Some(VirtualKeyCode::Noname), + win32wm::VK_PA1 => Some(VirtualKeyCode::Pa1), + win32wm::VK_OEM_CLEAR => Some(VirtualKeyCode::Oem_clear),*/ _ => None, } } pub fn handle_extended_keys( - vkey: c_int, + vkey: u32, mut scancode: UINT, extended: bool, ) -> Option<(c_int, UINT)> { // Welcome to hell https://blog.molecular-matters.com/2011/09/05/properly-handling-keyboard-input/ scancode = if extended { 0xE000 } else { 0x0000 } | scancode; let vkey = match vkey { - winuser::VK_SHIFT => unsafe { - winuser::MapVirtualKeyA(scancode, winuser::MAPVK_VSC_TO_VK_EX) as _ - }, - winuser::VK_CONTROL => { + win32wm::VK_SHIFT => unsafe { MapVirtualKeyA(scancode, MAPVK_VSC_TO_VK_EX) as _ }, + win32wm::VK_CONTROL => { if extended { - winuser::VK_RCONTROL + VK_RCONTROL } else { - winuser::VK_LCONTROL + VK_LCONTROL } } - winuser::VK_MENU => { + win32wm::VK_MENU => { if extended { - winuser::VK_RMENU + VK_RMENU } else { - winuser::VK_LMENU + VK_LMENU } } _ => { @@ -370,20 +372,20 @@ pub fn handle_extended_keys( // When VK_PAUSE is pressed it emits a LeftControl + NumLock scancode event sequence, but reports VK_PAUSE // as the virtual key on both events, or VK_PAUSE on the first event or 0xFF when using raw input. // Don't emit anything for the LeftControl event in the pair... - 0xE01D if vkey == winuser::VK_PAUSE => return None, + 0xE01D if vkey == VK_PAUSE => return None, // ...and emit the Pause event for the second event in the pair. - 0x45 if vkey == winuser::VK_PAUSE || vkey == 0xFF as _ => { + 0x45 if vkey == VK_PAUSE || vkey == 0xFF as _ => { scancode = 0xE059; - winuser::VK_PAUSE + VK_PAUSE } // VK_PAUSE has an incorrect vkey value when used with modifiers. VK_PAUSE also reports a different // scancode when used with modifiers than when used without 0xE046 => { scancode = 0xE059; - winuser::VK_PAUSE + VK_PAUSE } // VK_SCROLL has an incorrect vkey value when used with modifiers. - 0x46 => winuser::VK_SCROLL, + 0x46 => VK_SCROLL, _ => vkey, } } @@ -404,8 +406,7 @@ pub fn process_key_params( // This is needed as windows doesn't properly distinguish // some virtual key codes for different keyboard layouts fn map_text_keys(win_virtual_key: i32) -> Option { - let char_key = - unsafe { winuser::MapVirtualKeyA(win_virtual_key as u32, winuser::MAPVK_VK_TO_CHAR) } & 0x7FFF; + let char_key = unsafe { MapVirtualKeyA(win_virtual_key as u32, MAPVK_VK_TO_CHAR) } & 0x7FFF; match char::from_u32(char_key) { Some(';') => Some(VirtualKeyCode::Semicolon), Some('/') => Some(VirtualKeyCode::Slash), diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index a78ad27be6..1d8b952a20 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -17,18 +17,23 @@ use std::{ thread, time::{Duration, Instant}, }; -use winapi::{ - ctypes::c_int, - shared::{ - basetsd::{DWORD_PTR, UINT_PTR}, - minwindef::{BOOL, DWORD, HIWORD, INT, LOWORD, LPARAM, LRESULT, UINT, WORD, WPARAM}, - windef::{HWND, POINT, RECT}, - windowsx, winerror, +use windows::Win32::{ + Devices::HumanInterfaceDevice::*, + Foundation::{ + BOOL, HANDLE, HINSTANCE, HWND, LPARAM, LRESULT, POINT, PWSTR, RECT, WAIT_TIMEOUT, WPARAM, }, - um::{ - commctrl, libloaderapi, ole2, processthreadsapi, winbase, - winnt::{HANDLE, LONG, LPCSTR, SHORT}, - winuser::{self, RAWINPUT}, + Graphics::Gdi::*, + System::{ + LibraryLoader::GetModuleHandleW, + Ole::{IDropTarget, RevokeDragDrop}, + Threading::GetCurrentThreadId, + WindowsProgramming::INFINITE, + }, + UI::{ + Controls::{self as win32c, HOVER_DEFAULT}, + Input::{KeyboardAndMouse::*, Pointer::*, Touch::*, *}, + Shell::{DefSubclassProc, RemoveWindowSubclass, SetWindowSubclass}, + WindowsAndMessaging::{self as win32wm, *}, }, }; @@ -43,7 +48,6 @@ use crate::{ accelerator, dark_mode::try_theme, dpi::{become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling}, - drop_handler::FileDropHandler, keyboard::is_msg_keyboard_related, keyboard_layout::LAYOUT_CACHE, minimal_ime::is_msg_ime_related, @@ -57,13 +61,14 @@ use crate::{ use runner::{EventLoopRunner, EventLoopRunnerShared}; type GetPointerFrameInfoHistory = unsafe extern "system" fn( - pointerId: UINT, - entriesCount: *mut UINT, - pointerCount: *mut UINT, - pointerInfo: *mut winuser::POINTER_INFO, + pointerId: u32, + entriesCount: *mut u32, + pointerCount: *mut u32, + pointerInfo: *mut POINTER_INFO, ) -> BOOL; -type SkipPointerFrameMessages = unsafe extern "system" fn(pointerId: UINT) -> BOOL; +type SkipPointerFrameMessages = unsafe extern "system" fn(pointerId: u32) -> BOOL; + type GetPointerDeviceRects = unsafe extern "system" fn( device: HANDLE, pointerDeviceRect: *mut RECT, @@ -71,10 +76,10 @@ type GetPointerDeviceRects = unsafe extern "system" fn( ) -> BOOL; type GetPointerTouchInfo = - unsafe extern "system" fn(pointerId: UINT, touchInfo: *mut winuser::POINTER_TOUCH_INFO) -> BOOL; + unsafe extern "system" fn(pointerId: u32, touchInfo: *mut POINTER_TOUCH_INFO) -> BOOL; type GetPointerPenInfo = - unsafe extern "system" fn(pointId: UINT, penInfo: *mut winuser::POINTER_PEN_INFO) -> BOOL; + unsafe extern "system" fn(pointId: u32, penInfo: *mut POINTER_PEN_INFO) -> BOOL; lazy_static! { static ref GET_POINTER_FRAME_INFO_HISTORY: Option = @@ -92,7 +97,7 @@ lazy_static! { pub(crate) struct SubclassInput { pub window_state: Arc>, pub event_loop_runner: EventLoopRunnerShared, - pub file_drop_handler: Option, + pub _file_drop_handler: Option, pub subclass_removed: Cell, pub recurse_depth: Cell, } @@ -118,7 +123,7 @@ impl ThreadMsgTargetSubclassInput { pub(crate) enum ProcResult { DefSubclassProc, // <- this should be the default value DefWindowProc, - Value(isize), + Value(LRESULT), } pub struct EventLoop { @@ -126,15 +131,16 @@ pub struct EventLoop { window_target: RootELW, } +#[derive(Clone)] pub struct EventLoopWindowTarget { - thread_id: DWORD, + thread_id: u32, thread_msg_target: HWND, pub(crate) runner_shared: EventLoopRunnerShared, } macro_rules! main_thread_check { ($fn_name:literal) => {{ - let thread_id = unsafe { processthreadsapi::GetCurrentThreadId() }; + let thread_id = unsafe { GetCurrentThreadId() }; if thread_id != main_thread_id() { panic!(concat!( "Initializing the event loop outside of the main thread is a significant \ @@ -166,12 +172,12 @@ impl EventLoop { } pub fn new_dpi_unaware_any_thread() -> EventLoop { - let thread_id = unsafe { processthreadsapi::GetCurrentThreadId() }; + let thread_id = unsafe { GetCurrentThreadId() }; let thread_msg_target = create_event_target_window(); - let send_thread_msg_target = thread_msg_target as usize; - thread::spawn(move || wait_thread(thread_id, send_thread_msg_target as HWND)); + let send_thread_msg_target = thread_msg_target; + thread::spawn(move || wait_thread(thread_id, send_thread_msg_target)); let wait_thread_id = get_wait_thread_id(); let runner_shared = Rc::new(EventLoopRunner::new(thread_msg_target, wait_thread_id)); @@ -200,11 +206,11 @@ impl EventLoop { where F: 'static + FnMut(Event<'_, T>, &RootELW, &mut ControlFlow), { - self.run_return(event_handler); - ::std::process::exit(0); + let exit_code = self.run_return(event_handler); + ::std::process::exit(exit_code); } - pub fn run_return(&mut self, mut event_handler: F) + pub fn run_return(&mut self, mut event_handler: F) -> i32 where F: FnMut(Event<'_, T>, &RootELW, &mut ControlFlow), { @@ -216,36 +222,37 @@ impl EventLoop { .p .runner_shared .set_event_handler(move |event, control_flow| { - event_handler(event, event_loop_windows_ref, control_flow) + event_handler(event, event_loop_windows_ref, control_flow); }); } let runner = &self.window_target.p.runner_shared; - unsafe { - let mut msg = mem::zeroed(); + let exit_code = unsafe { + let mut msg = MSG::default(); runner.poll(); 'main: loop { - if 0 == winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) { - break 'main; + if !GetMessageW(&mut msg, HWND::default(), 0, 0).as_bool() { + break 'main 0; } // global accelerator - if msg.message == winuser::WM_HOTKEY { + if msg.message == WM_HOTKEY { let event_loop_runner = self.window_target.p.runner_shared.clone(); - event_loop_runner - .send_event(Event::GlobalShortcutEvent(AcceleratorId(msg.wParam as u16))); + event_loop_runner.send_event(Event::GlobalShortcutEvent(AcceleratorId( + msg.wParam.0 as u16, + ))); } // window accelerator - let accels = accelerator::find_accels(winuser::GetAncestor(msg.hwnd, winuser::GA_ROOT)); + let accels = accelerator::find_accels(GetAncestor(msg.hwnd, GA_ROOT)); let translated = accels.map_or(false, |it| { - winuser::TranslateAcceleratorW(msg.hwnd, it.handle(), &mut msg) != 0 + TranslateAcceleratorW(msg.hwnd, it.handle(), &msg) != 0 }); if !translated { - winuser::TranslateMessage(&mut msg); - winuser::DispatchMessageW(&mut msg); + TranslateMessage(&msg); + DispatchMessageW(&msg); } if let Err(payload) = runner.take_panic_error() { @@ -253,16 +260,19 @@ impl EventLoop { panic::resume_unwind(payload); } - if runner.control_flow() == ControlFlow::Exit && !runner.handling_events() { - break 'main; + if let ControlFlow::ExitWithCode(code) = runner.control_flow() { + if !runner.handling_events() { + break 'main code; + } } } - } + }; unsafe { runner.loop_destroyed(); } runner.reset_runner(); + exit_code } pub fn create_proxy(&self) -> EventLoopProxy { @@ -293,14 +303,14 @@ impl EventLoopWindowTarget { } } -fn main_thread_id() -> DWORD { - static mut MAIN_THREAD_ID: DWORD = 0; +fn main_thread_id() -> u32 { + static mut MAIN_THREAD_ID: u32 = 0; #[used] #[allow(non_upper_case_globals)] #[link_section = ".CRT$XCU"] static INIT_MAIN_THREAD_ID: unsafe fn() = { unsafe fn initer() { - MAIN_THREAD_ID = processthreadsapi::GetCurrentThreadId(); + MAIN_THREAD_ID = GetCurrentThreadId(); } initer }; @@ -308,34 +318,34 @@ fn main_thread_id() -> DWORD { unsafe { MAIN_THREAD_ID } } -fn get_wait_thread_id() -> DWORD { +fn get_wait_thread_id() -> u32 { unsafe { - let mut msg = mem::zeroed(); - let result = winuser::GetMessageW( + let mut msg = MSG::default(); + let result = GetMessageW( &mut msg, - -1 as _, + HWND::default(), *SEND_WAIT_THREAD_ID_MSG_ID, *SEND_WAIT_THREAD_ID_MSG_ID, ); assert_eq!( msg.message, *SEND_WAIT_THREAD_ID_MSG_ID, "this shouldn't be possible. please open an issue with Tauri. error code: {}", - result + result.0 ); - msg.lParam as DWORD + msg.lParam.0 as u32 } } -fn wait_thread(parent_thread_id: DWORD, msg_window_id: HWND) { +fn wait_thread(parent_thread_id: u32, msg_window_id: HWND) { unsafe { - let mut msg: winuser::MSG; + let mut msg: MSG; - let cur_thread_id = processthreadsapi::GetCurrentThreadId(); - winuser::PostThreadMessageW( + let cur_thread_id = GetCurrentThreadId(); + PostThreadMessageW( parent_thread_id, *SEND_WAIT_THREAD_ID_MSG_ID, - 0, - cur_thread_id as LPARAM, + WPARAM(0), + LPARAM(cur_thread_id as _), ); let mut wait_until_opt = None; @@ -343,24 +353,22 @@ fn wait_thread(parent_thread_id: DWORD, msg_window_id: HWND) { // Zeroing out the message ensures that the `WaitUntilInstantBox` doesn't get // double-freed if `MsgWaitForMultipleObjectsEx` returns early and there aren't // additional messages to process. - msg = mem::zeroed(); + msg = MSG::default(); if wait_until_opt.is_some() { - if 0 != winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, winuser::PM_REMOVE) { - winuser::TranslateMessage(&mut msg); - winuser::DispatchMessageW(&mut msg); + if PeekMessageW(&mut msg, HWND::default(), 0, 0, PM_REMOVE).as_bool() { + TranslateMessage(&msg); + DispatchMessageW(&msg); } + } else if !GetMessageW(&mut msg, HWND::default(), 0, 0).as_bool() { + break 'main; } else { - if 0 == winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) { - break 'main; - } else { - winuser::TranslateMessage(&mut msg); - winuser::DispatchMessageW(&mut msg); - } + TranslateMessage(&msg); + DispatchMessageW(&msg); } if msg.message == *WAIT_UNTIL_MSG_ID { - wait_until_opt = Some(*WaitUntilInstantBox::from_raw(msg.lParam as *mut _)); + wait_until_opt = Some(*WaitUntilInstantBox::from_raw(msg.lParam.0 as *mut _)); } else if msg.message == *CANCEL_WAIT_UNTIL_MSG_ID { wait_until_opt = None; } @@ -371,19 +379,29 @@ fn wait_thread(parent_thread_id: DWORD, msg_window_id: HWND) { // MsgWaitForMultipleObjects tends to overshoot just a little bit. We subtract // 1 millisecond from the requested time and spinlock for the remainder to // compensate for that. - let resume_reason = winuser::MsgWaitForMultipleObjectsEx( + let resume_reason = MsgWaitForMultipleObjectsEx( 0, ptr::null(), dur2timeout(wait_until - now).saturating_sub(1), - winuser::QS_ALLEVENTS, - winuser::MWMO_INPUTAVAILABLE, + QS_ALLEVENTS, + MWMO_INPUTAVAILABLE, ); - if resume_reason == winerror::WAIT_TIMEOUT { - winuser::PostMessageW(msg_window_id, *PROCESS_NEW_EVENTS_MSG_ID, 0, 0); + if resume_reason == WAIT_TIMEOUT { + PostMessageW( + msg_window_id, + *PROCESS_NEW_EVENTS_MSG_ID, + WPARAM(0), + LPARAM(0), + ); wait_until_opt = None; } } else { - winuser::PostMessageW(msg_window_id, *PROCESS_NEW_EVENTS_MSG_ID, 0, 0); + PostMessageW( + msg_window_id, + *PROCESS_NEW_EVENTS_MSG_ID, + WPARAM(0), + LPARAM(0), + ); wait_until_opt = None; } } @@ -392,7 +410,7 @@ fn wait_thread(parent_thread_id: DWORD, msg_window_id: HWND) { } // Implementation taken from https://github.com/rust-lang/rust/blob/db5476571d9b27c862b95c1e64764b0ac8980e23/src/libstd/sys/windows/mod.rs -fn dur2timeout(dur: Duration) -> DWORD { +fn dur2timeout(dur: Duration) -> u32 { // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the // timeouts in windows APIs are typically u32 milliseconds. To translate, we // have two pieces to take care of: @@ -412,25 +430,25 @@ fn dur2timeout(dur: Duration) -> DWORD { }) }) .map(|ms| { - if ms > DWORD::max_value() as u64 { - winbase::INFINITE + if ms > u32::max_value() as u64 { + INFINITE } else { - ms as DWORD + ms as u32 } }) - .unwrap_or(winbase::INFINITE) + .unwrap_or(INFINITE) } impl Drop for EventLoop { fn drop(&mut self) { unsafe { - winuser::DestroyWindow(self.window_target.p.thread_msg_target); + DestroyWindow(self.window_target.p.thread_msg_target); } } } pub(crate) struct EventLoopThreadExecutor { - thread_id: DWORD, + thread_id: u32, target_window: HWND, } @@ -440,7 +458,7 @@ unsafe impl Sync for EventLoopThreadExecutor {} impl EventLoopThreadExecutor { /// Check to see if we're in the parent event loop's thread. pub(super) fn in_event_loop_thread(&self) -> bool { - let cur_thread_id = unsafe { processthreadsapi::GetCurrentThreadId() }; + let cur_thread_id = unsafe { GetCurrentThreadId() }; self.thread_id == cur_thread_id } @@ -471,13 +489,16 @@ impl EventLoopThreadExecutor { let raw = Box::into_raw(boxed2); - let res = winuser::PostMessageW( + let res = PostMessageW( self.target_window, *EXEC_MSG_ID, - raw as *mut () as usize as WPARAM, - 0, + WPARAM(raw as _), + LPARAM(0), + ); + assert!( + res.as_bool(), + "PostMessage failed ; is the messages queue full?" ); - assert!(res != 0, "PostMessage failed ; is the messages queue full?"); } } } @@ -504,7 +525,7 @@ impl Clone for EventLoopProxy { impl EventLoopProxy { pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { unsafe { - if winuser::PostMessageW(self.target_window, *USER_EVENT_MSG_ID, 0, 0) != 0 { + if PostMessageW(self.target_window, *USER_EVENT_MSG_ID, WPARAM(0), LPARAM(0)).as_bool() { self.event_send.send(event).ok(); Ok(()) } else { @@ -521,7 +542,7 @@ lazy_static! { // WPARAM and LPARAM are unused. static ref USER_EVENT_MSG_ID: u32 = { unsafe { - winuser::RegisterWindowMessageA("Tao::WakeupMsg\0".as_ptr() as LPCSTR) + RegisterWindowMessageA("Tao::WakeupMsg") } }; // Message sent when we want to execute a closure in the thread. @@ -529,95 +550,94 @@ lazy_static! { // and LPARAM is unused. static ref EXEC_MSG_ID: u32 = { unsafe { - winuser::RegisterWindowMessageA("Tao::ExecMsg\0".as_ptr() as *const i8) + RegisterWindowMessageA("Tao::ExecMsg") } }; static ref PROCESS_NEW_EVENTS_MSG_ID: u32 = { unsafe { - winuser::RegisterWindowMessageA("Tao::ProcessNewEvents\0".as_ptr() as *const i8) + RegisterWindowMessageA("Tao::ProcessNewEvents") } }; /// lparam is the wait thread's message id. static ref SEND_WAIT_THREAD_ID_MSG_ID: u32 = { unsafe { - winuser::RegisterWindowMessageA("Tao::SendWaitThreadId\0".as_ptr() as *const i8) + RegisterWindowMessageA("Tao::SendWaitThreadId") } }; /// lparam points to a `Box` signifying the time `PROCESS_NEW_EVENTS_MSG_ID` should /// be sent. static ref WAIT_UNTIL_MSG_ID: u32 = { unsafe { - winuser::RegisterWindowMessageA("Tao::WaitUntil\0".as_ptr() as *const i8) + RegisterWindowMessageA("Tao::WaitUntil") } }; static ref CANCEL_WAIT_UNTIL_MSG_ID: u32 = { unsafe { - winuser::RegisterWindowMessageA("Tao::CancelWaitUntil\0".as_ptr() as *const i8) + RegisterWindowMessageA("Tao::CancelWaitUntil") } }; // Message sent by a `Window` when it wants to be destroyed by the main thread. // WPARAM and LPARAM are unused. pub static ref DESTROY_MSG_ID: u32 = { unsafe { - winuser::RegisterWindowMessageA("Tao::DestroyMsg\0".as_ptr() as LPCSTR) + RegisterWindowMessageA("Tao::DestroyMsg") } }; // WPARAM is a bool specifying the `WindowFlags::MARKER_RETAIN_STATE_ON_SIZE` flag. See the // documentation in the `window_state` module for more information. pub static ref SET_RETAIN_STATE_ON_SIZE_MSG_ID: u32 = unsafe { - winuser::RegisterWindowMessageA("Tao::SetRetainMaximized\0".as_ptr() as LPCSTR) + RegisterWindowMessageA("Tao::SetRetainMaximized") }; static ref THREAD_EVENT_TARGET_WINDOW_CLASS: Vec = unsafe { + let mut class_name= util::to_wstring("Tao Thread Event Target"); - let class_name= util::to_wstring("Tao Thread Event Target"); - - let class = winuser::WNDCLASSEXW { - cbSize: mem::size_of::() as UINT, + let class = WNDCLASSEXW { + cbSize: mem::size_of::() as u32, style: 0, - lpfnWndProc: Some(winuser::DefWindowProcW), + lpfnWndProc: Some(util::call_default_window_proc), cbClsExtra: 0, cbWndExtra: 0, - hInstance: libloaderapi::GetModuleHandleW(ptr::null()), - hIcon: ptr::null_mut(), - hCursor: ptr::null_mut(), // must be null in order for cursor state to work properly - hbrBackground: ptr::null_mut(), - lpszMenuName: ptr::null(), - lpszClassName: class_name.as_ptr(), - hIconSm: ptr::null_mut(), + hInstance: GetModuleHandleW(PWSTR::default()), + hIcon: HICON::default(), + hCursor: HCURSOR::default(), // must be null in order for cursor state to work properly + hbrBackground: HBRUSH::default(), + lpszMenuName: PWSTR::default(), + lpszClassName: PWSTR(class_name.as_mut_ptr()), + hIconSm: HICON::default(), }; - winuser::RegisterClassExW(&class); + RegisterClassExW(&class); class_name }; } fn create_event_target_window() -> HWND { - unsafe { - let window = winuser::CreateWindowExW( - winuser::WS_EX_NOACTIVATE | winuser::WS_EX_TRANSPARENT | winuser::WS_EX_LAYERED, - THREAD_EVENT_TARGET_WINDOW_CLASS.as_ptr(), - ptr::null_mut(), + let window = unsafe { + CreateWindowExW( + WS_EX_NOACTIVATE | WS_EX_TRANSPARENT | WS_EX_LAYERED, + PWSTR(THREAD_EVENT_TARGET_WINDOW_CLASS.clone().as_mut_ptr()), + PWSTR::default(), 0, 0, 0, 0, 0, + HWND::default(), + HMENU::default(), + GetModuleHandleW(PWSTR::default()), ptr::null_mut(), - ptr::null_mut(), - libloaderapi::GetModuleHandleW(ptr::null()), - ptr::null_mut(), - ); - winuser::SetWindowLongPtrW( - window, - winuser::GWL_STYLE, - // The window technically has to be visible to receive WM_PAINT messages (which are used - // for delivering events during resizes), but it isn't displayed to the user because of - // the LAYERED style. - (winuser::WS_VISIBLE | winuser::WS_POPUP) as _, - ); - window - } + ) + }; + util::SetWindowLongPtrW( + window, + GWL_STYLE, + // The window technically has to be visible to receive WM_PAINT messages (which are used + // for delivering events during resizes), but it isn't displayed to the user because of + // the LAYERED style. + (WS_VISIBLE | WS_POPUP) as isize, + ); + window } fn subclass_event_target_window( @@ -632,13 +652,13 @@ fn subclass_event_target_window( user_event_receiver: rx, }; let input_ptr = Box::into_raw(Box::new(subclass_input)); - let subclass_result = commctrl::SetWindowSubclass( + let subclass_result = SetWindowSubclass( window, Some(thread_event_target_callback::), THREAD_EVENT_TARGET_SUBCLASS_ID, - input_ptr as DWORD_PTR, + input_ptr as usize, ); - assert_eq!(subclass_result, 1); + assert!(subclass_result.as_bool()); tx } @@ -646,20 +666,20 @@ fn subclass_event_target_window( fn remove_event_target_window_subclass(window: HWND) { let removal_result = unsafe { - commctrl::RemoveWindowSubclass( + RemoveWindowSubclass( window, Some(thread_event_target_callback::), THREAD_EVENT_TARGET_SUBCLASS_ID, ) }; - assert_eq!(removal_result, 1); + assert!(removal_result.as_bool()); } /// Capture mouse input, allowing `window` to receive mouse events when the cursor is outside of /// the window. unsafe fn capture_mouse(window: HWND, window_state: &mut WindowState) { window_state.mouse.capture_count += 1; - winuser::SetCapture(window); + SetCapture(window); } /// Release mouse input, stopping windows on this thread from receiving mouse input when the cursor @@ -669,35 +689,35 @@ unsafe fn release_mouse(mut window_state: parking_lot::MutexGuard<'_, WindowStat if window_state.mouse.capture_count == 0 { // ReleaseCapture() causes a WM_CAPTURECHANGED where we lock the window_state. drop(window_state); - winuser::ReleaseCapture(); + ReleaseCapture(); } } -const WINDOW_SUBCLASS_ID: UINT_PTR = 0; -const THREAD_EVENT_TARGET_SUBCLASS_ID: UINT_PTR = 1; +const WINDOW_SUBCLASS_ID: usize = 0; +const THREAD_EVENT_TARGET_SUBCLASS_ID: usize = 1; pub(crate) fn subclass_window(window: HWND, subclass_input: SubclassInput) { subclass_input.event_loop_runner.register_window(window); let input_ptr = Box::into_raw(Box::new(subclass_input)); let subclass_result = unsafe { - commctrl::SetWindowSubclass( + SetWindowSubclass( window, Some(public_window_callback::), WINDOW_SUBCLASS_ID, - input_ptr as DWORD_PTR, + input_ptr as usize, ) }; - assert_eq!(subclass_result, 1); + assert!(subclass_result.as_bool()); } fn remove_window_subclass(window: HWND) { let removal_result = unsafe { - commctrl::RemoveWindowSubclass( + RemoveWindowSubclass( window, Some(public_window_callback::), WINDOW_SUBCLASS_ID, ) }; - assert_eq!(removal_result, 1); + assert!(removal_result.as_bool()); } fn normalize_pointer_pressure(pressure: u32) -> Option { @@ -726,26 +746,26 @@ unsafe fn flush_paint_messages( ) -> bool { if !runner.redrawing() { runner.main_events_cleared(); - let mut msg = mem::zeroed(); + let mut msg = MSG::default(); runner.owned_windows(|redraw_window| { if Some(redraw_window) == except { return; } - if 0 - == winuser::PeekMessageW( - &mut msg, - redraw_window, - winuser::WM_PAINT, - winuser::WM_PAINT, - winuser::PM_REMOVE | winuser::PM_QS_PAINT, - ) + if !PeekMessageW( + &mut msg, + redraw_window, + WM_PAINT, + WM_PAINT, + PM_REMOVE | PM_QS_PAINT, + ) + .as_bool() { return; } - winuser::TranslateMessage(&mut msg); - winuser::DispatchMessageW(&mut msg); + TranslateMessage(&msg); + DispatchMessageW(&msg); }); true } else { @@ -756,18 +776,23 @@ unsafe fn flush_paint_messages( unsafe fn process_control_flow(runner: &EventLoopRunner) { match runner.control_flow() { ControlFlow::Poll => { - winuser::PostMessageW(runner.thread_msg_target(), *PROCESS_NEW_EVENTS_MSG_ID, 0, 0); + PostMessageW( + runner.thread_msg_target(), + *PROCESS_NEW_EVENTS_MSG_ID, + WPARAM(0), + LPARAM(0), + ); } ControlFlow::Wait => (), ControlFlow::WaitUntil(until) => { - winuser::PostThreadMessageW( + PostThreadMessageW( runner.wait_thread_id(), *WAIT_UNTIL_MSG_ID, - 0, - Box::into_raw(WaitUntilInstantBox::new(until)) as LPARAM, + WPARAM(0), + LPARAM(Box::into_raw(WaitUntilInstantBox::new(until)) as _), ); } - ControlFlow::Exit => (), + ControlFlow::ExitWithCode(_) => (), } } @@ -790,7 +815,7 @@ fn update_modifiers(window: HWND, subclass_input: &SubclassInput) -> Modif unsafe { subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: ModifiersChanged(modifiers), }); } @@ -807,11 +832,11 @@ fn update_modifiers(window: HWND, subclass_input: &SubclassInput) -> Modif // FIXME: detect WM_DWMCOMPOSITIONCHANGED and call DwmEnableBlurBehindWindow if necessary unsafe extern "system" fn public_window_callback( window: HWND, - msg: UINT, + msg: u32, wparam: WPARAM, lparam: LPARAM, - uidsubclass: UINT_PTR, - subclass_input_ptr: DWORD_PTR, + uidsubclass: usize, + subclass_input_ptr: usize, ) -> LRESULT { let subclass_input_ptr = subclass_input_ptr as *mut SubclassInput; let (result, subclass_removed, recurse_depth) = { @@ -821,7 +846,7 @@ unsafe extern "system" fn public_window_callback( .set(subclass_input.recurse_depth.get() + 1); // Clear userdata - winuser::SetWindowLongPtrW(window, winuser::GWL_USERDATA, 0); + util::SetWindowLongPtrW(window, GWL_USERDATA, 0); let result = public_window_callback_inner(window, msg, wparam, lparam, uidsubclass, subclass_input); @@ -842,33 +867,33 @@ unsafe extern "system" fn public_window_callback( unsafe fn public_window_callback_inner( window: HWND, - msg: UINT, + msg: u32, wparam: WPARAM, lparam: LPARAM, - _: UINT_PTR, + _: usize, subclass_input: &SubclassInput, ) -> LRESULT { - winuser::RedrawWindow( + RedrawWindow( subclass_input.event_loop_runner.thread_msg_target(), ptr::null(), - ptr::null_mut(), - winuser::RDW_INTERNALPAINT, + HRGN::default(), + RDW_INTERNALPAINT, ); let mut result = ProcResult::DefSubclassProc; // Send new modifiers before sending key events. let mods_changed_callback = || match msg { - winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN | winuser::WM_KEYUP | winuser::WM_SYSKEYUP => { + win32wm::WM_KEYDOWN | win32wm::WM_SYSKEYDOWN | win32wm::WM_KEYUP | win32wm::WM_SYSKEYUP => { update_modifiers(window, subclass_input); - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } _ => (), }; subclass_input .event_loop_runner .catch_unwind(mods_changed_callback) - .unwrap_or_else(|| result = ProcResult::Value(-1)); + .unwrap_or_else(|| result = ProcResult::Value(LRESULT(-1))); let keyboard_callback = || { use crate::event::WindowEvent::KeyboardInput; @@ -886,7 +911,7 @@ unsafe fn public_window_callback_inner( }; for event in events { subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: KeyboardInput { device_id: DEVICE_ID, event: event.event, @@ -898,7 +923,7 @@ unsafe fn public_window_callback_inner( subclass_input .event_loop_runner .catch_unwind(keyboard_callback) - .unwrap_or_else(|| result = ProcResult::Value(-1)); + .unwrap_or_else(|| result = ProcResult::Value(LRESULT(-1))); let ime_callback = || { use crate::event::WindowEvent::ReceivedImeText; @@ -914,7 +939,7 @@ unsafe fn public_window_callback_inner( }; if let Some(str) = text { subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: ReceivedImeText(str), }); } @@ -922,76 +947,71 @@ unsafe fn public_window_callback_inner( subclass_input .event_loop_runner .catch_unwind(ime_callback) - .unwrap_or_else(|| result = ProcResult::Value(-1)); + .unwrap_or_else(|| result = ProcResult::Value(LRESULT(-1))); // I decided to bind the closure to `callback` and pass it to catch_unwind rather than passing // the closure to catch_unwind directly so that the match body indendation wouldn't change and // the git blame and history would be preserved. let callback = || match msg { - winuser::WM_ENTERSIZEMOVE => { + win32wm::WM_ENTERSIZEMOVE => { subclass_input .window_state .lock() .set_window_flags_in_place(|f| f.insert(WindowFlags::MARKER_IN_SIZE_MOVE)); - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_EXITSIZEMOVE => { + win32wm::WM_EXITSIZEMOVE => { subclass_input .window_state .lock() .set_window_flags_in_place(|f| f.remove(WindowFlags::MARKER_IN_SIZE_MOVE)); - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_NCCREATE => { + win32wm::WM_NCCREATE => { enable_non_client_dpi_scaling(window); } - winuser::WM_NCLBUTTONDOWN => { - if wparam == winuser::HTCAPTION as _ { - winuser::PostMessageW(window, winuser::WM_MOUSEMOVE, 0, lparam); + win32wm::WM_NCLBUTTONDOWN => { + if wparam.0 == HTCAPTION as _ { + PostMessageW(window, WM_MOUSEMOVE, WPARAM(0), lparam); } } - winuser::WM_CLOSE => { + win32wm::WM_CLOSE => { use crate::event::WindowEvent::CloseRequested; subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: CloseRequested, }); - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_DESTROY => { + win32wm::WM_DESTROY => { use crate::event::WindowEvent::Destroyed; - ole2::RevokeDragDrop(window); + let _ = RevokeDragDrop(window); subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: Destroyed, }); subclass_input.event_loop_runner.remove_window(window); - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_NCDESTROY => { + win32wm::WM_NCDESTROY => { remove_window_subclass::(window); subclass_input.subclass_removed.set(true); - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_PAINT => { + win32wm::WM_PAINT => { if subclass_input.event_loop_runner.should_buffer() { // this branch can happen in response to `UpdateWindow`, if win32 decides to // redraw the window outside the normal flow of the event loop. - winuser::RedrawWindow( - window, - ptr::null(), - ptr::null_mut(), - winuser::RDW_INTERNALPAINT, - ); + RedrawWindow(window, ptr::null(), HRGN::default(), RDW_INTERNALPAINT); } else { let managing_redraw = flush_paint_messages(Some(window), &subclass_input.event_loop_runner); - subclass_input.send_event(Event::RedrawRequested(RootWindowId(WindowId(window)))); + subclass_input.send_event(Event::RedrawRequested(RootWindowId(WindowId(window.0)))); if managing_redraw { subclass_input.event_loop_runner.redraw_events_cleared(); process_control_flow(&subclass_input.event_loop_runner); @@ -999,27 +1019,27 @@ unsafe fn public_window_callback_inner( } } - winuser::WM_WINDOWPOSCHANGING => { + win32wm::WM_WINDOWPOSCHANGING => { let mut window_state = subclass_input.window_state.lock(); if let Some(ref mut fullscreen) = window_state.fullscreen { - let window_pos = &mut *(lparam as *mut winuser::WINDOWPOS); + let window_pos = &mut *(lparam.0 as *mut WINDOWPOS); let new_rect = RECT { left: window_pos.x, top: window_pos.y, right: window_pos.x + window_pos.cx, bottom: window_pos.y + window_pos.cy, }; - let new_monitor = winuser::MonitorFromRect(&new_rect, winuser::MONITOR_DEFAULTTONULL); + let new_monitor = MonitorFromRect(&new_rect, MONITOR_DEFAULTTONULL); match fullscreen { Fullscreen::Borderless(ref mut fullscreen_monitor) => { - if !new_monitor.is_null() + if !new_monitor.is_invalid() && fullscreen_monitor .as_ref() .map(|monitor| new_monitor != monitor.inner.hmonitor()) .unwrap_or(true) { if let Ok(new_monitor_info) = monitor::get_monitor_info(new_monitor) { - let new_monitor_rect = new_monitor_info.rcMonitor; + let new_monitor_rect = new_monitor_info.monitorInfo.rcMonitor; window_pos.x = new_monitor_rect.left; window_pos.y = new_monitor_rect.top; window_pos.cx = new_monitor_rect.right - new_monitor_rect.left; @@ -1033,7 +1053,7 @@ unsafe fn public_window_callback_inner( Fullscreen::Exclusive(ref video_mode) => { let old_monitor = video_mode.video_mode.monitor.hmonitor(); if let Ok(old_monitor_info) = monitor::get_monitor_info(old_monitor) { - let old_monitor_rect = old_monitor_info.rcMonitor; + let old_monitor_rect = old_monitor_info.monitorInfo.rcMonitor; window_pos.x = old_monitor_rect.left; window_pos.y = old_monitor_rect.top; window_pos.cx = old_monitor_rect.right - old_monitor_rect.left; @@ -1043,18 +1063,18 @@ unsafe fn public_window_callback_inner( } } - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } // WM_MOVE supplies client area positions, so we send Moved here instead. - winuser::WM_WINDOWPOSCHANGED => { + win32wm::WM_WINDOWPOSCHANGED => { use crate::event::WindowEvent::Moved; - let windowpos = lparam as *const winuser::WINDOWPOS; - if (*windowpos).flags & winuser::SWP_NOMOVE != winuser::SWP_NOMOVE { + let windowpos = lparam.0 as *const WINDOWPOS; + if (*windowpos).flags & SWP_NOMOVE != SWP_NOMOVE { let physical_position = PhysicalPosition::new((*windowpos).x as i32, (*windowpos).y as i32); subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: Moved(physical_position), }); } @@ -1063,37 +1083,49 @@ unsafe fn public_window_callback_inner( result = ProcResult::DefSubclassProc; } - winuser::WM_SIZE => { + win32wm::WM_SIZE => { use crate::event::WindowEvent::Resized; - let w = LOWORD(lparam as DWORD) as u32; - let h = HIWORD(lparam as DWORD) as u32; + let w = u32::from(util::LOWORD(lparam.0 as u32)); + let h = u32::from(util::HIWORD(lparam.0 as u32)); let physical_size = PhysicalSize::new(w, h); let event = Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: Resized(physical_size), }; + { + let mut w = subclass_input.window_state.lock(); + // See WindowFlags::MARKER_RETAIN_STATE_ON_SIZE docs for info on why this `if` check exists. + if !w + .window_flags() + .contains(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE) + { + let maximized = wparam.0 == win32wm::SIZE_MAXIMIZED as _; + w.set_window_flags_in_place(|f| f.set(WindowFlags::MAXIMIZED, maximized)); + } + } + subclass_input.send_event(event); - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } // this is necessary for us to maintain minimize/restore state - winuser::WM_SYSCOMMAND => { - if wparam == winuser::SC_RESTORE { + win32wm::WM_SYSCOMMAND => { + if wparam.0 == SC_RESTORE as _ { let mut w = subclass_input.window_state.lock(); w.set_window_flags_in_place(|f| f.set(WindowFlags::MINIMIZED, false)); } - if wparam == winuser::SC_MINIMIZE { + if wparam.0 == SC_MINIMIZE as _ { let mut w = subclass_input.window_state.lock(); w.set_window_flags_in_place(|f| f.set(WindowFlags::MINIMIZED, true)); } // Send `WindowEvent::Minimized` here if we decide to implement one - if wparam == winuser::SC_SCREENSAVE { + if wparam.0 == SC_SCREENSAVE as _ { let window_state = subclass_input.window_state.lock(); if window_state.fullscreen.is_some() { - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); return; } } @@ -1101,7 +1133,7 @@ unsafe fn public_window_callback_inner( result = ProcResult::DefWindowProc; } - winuser::WM_MOUSEMOVE => { + win32wm::WM_MOUSEMOVE => { use crate::event::WindowEvent::{CursorEntered, CursorMoved}; let mouse_was_outside_window = { let mut w = subclass_input.window_state.lock(); @@ -1115,23 +1147,23 @@ unsafe fn public_window_callback_inner( if mouse_was_outside_window { subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: CursorEntered { device_id: DEVICE_ID, }, }); // Calling TrackMouseEvent in order to receive mouse leave events. - winuser::TrackMouseEvent(&mut winuser::TRACKMOUSEEVENT { - cbSize: mem::size_of::() as DWORD, - dwFlags: winuser::TME_LEAVE, + TrackMouseEvent(&mut TRACKMOUSEEVENT { + cbSize: mem::size_of::() as u32, + dwFlags: TME_LEAVE, hwndTrack: window, - dwHoverTime: winuser::HOVER_DEFAULT, + dwHoverTime: HOVER_DEFAULT, }); } - let x = windowsx::GET_X_LPARAM(lparam) as f64; - let y = windowsx::GET_Y_LPARAM(lparam) as f64; + let x = f64::from(util::GET_X_LPARAM(lparam)); + let y = f64::from(util::GET_Y_LPARAM(lparam)); let position = PhysicalPosition::new(x, y); let cursor_moved; { @@ -1145,7 +1177,7 @@ unsafe fn public_window_callback_inner( if cursor_moved { let modifiers = update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: CursorMoved { device_id: DEVICE_ID, position, @@ -1154,10 +1186,10 @@ unsafe fn public_window_callback_inner( }); } - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_MOUSELEAVE => { + win32c::WM_MOUSELEAVE => { use crate::event::WindowEvent::CursorLeft; { let mut w = subclass_input.window_state.lock(); @@ -1167,26 +1199,25 @@ unsafe fn public_window_callback_inner( } subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: CursorLeft { device_id: DEVICE_ID, }, }); - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_MOUSEWHEEL => { + win32wm::WM_MOUSEWHEEL => { use crate::event::MouseScrollDelta::LineDelta; - let value = (wparam >> 16) as i16; - let value = value as i32; - let value = value as f32 / winuser::WHEEL_DELTA as f32; + let value = f32::from(util::GET_WHEEL_DELTA_WPARAM(wparam)); + let value = value / WHEEL_DELTA as f32; let modifiers = update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: WindowEvent::MouseWheel { device_id: DEVICE_ID, delta: LineDelta(0.0, value), @@ -1195,20 +1226,19 @@ unsafe fn public_window_callback_inner( }, }); - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_MOUSEHWHEEL => { + win32wm::WM_MOUSEHWHEEL => { use crate::event::MouseScrollDelta::LineDelta; - let value = (wparam >> 16) as i16; - let value = value as i32; - let value = value as f32 / winuser::WHEEL_DELTA as f32; + let value = f32::from(util::GET_WHEEL_DELTA_WPARAM(wparam)); + let value = value / WHEEL_DELTA as f32; let modifiers = update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: WindowEvent::MouseWheel { device_id: DEVICE_ID, delta: LineDelta(value, 0.0), @@ -1217,16 +1247,16 @@ unsafe fn public_window_callback_inner( }, }); - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN => { - if msg == winuser::WM_SYSKEYDOWN && wparam as i32 == winuser::VK_F4 { + win32wm::WM_KEYDOWN | win32wm::WM_SYSKEYDOWN => { + if msg == WM_SYSKEYDOWN && wparam.0 as VIRTUAL_KEY == VK_F4 { result = ProcResult::DefSubclassProc; } } - winuser::WM_LBUTTONDOWN => { + win32wm::WM_LBUTTONDOWN => { use crate::event::{ElementState::Pressed, MouseButton::Left, WindowEvent::MouseInput}; capture_mouse(window, &mut *subclass_input.window_state.lock()); @@ -1234,7 +1264,7 @@ unsafe fn public_window_callback_inner( let modifiers = update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: MouseInput { device_id: DEVICE_ID, state: Pressed, @@ -1242,10 +1272,10 @@ unsafe fn public_window_callback_inner( modifiers, }, }); - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_LBUTTONUP => { + win32wm::WM_LBUTTONUP => { use crate::event::{ElementState::Released, MouseButton::Left, WindowEvent::MouseInput}; release_mouse(subclass_input.window_state.lock()); @@ -1253,7 +1283,7 @@ unsafe fn public_window_callback_inner( let modifiers = update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: MouseInput { device_id: DEVICE_ID, state: Released, @@ -1261,10 +1291,10 @@ unsafe fn public_window_callback_inner( modifiers, }, }); - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_RBUTTONDOWN => { + win32wm::WM_RBUTTONDOWN => { use crate::event::{ElementState::Pressed, MouseButton::Right, WindowEvent::MouseInput}; capture_mouse(window, &mut *subclass_input.window_state.lock()); @@ -1272,7 +1302,7 @@ unsafe fn public_window_callback_inner( let modifiers = update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: MouseInput { device_id: DEVICE_ID, state: Pressed, @@ -1280,10 +1310,10 @@ unsafe fn public_window_callback_inner( modifiers, }, }); - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_RBUTTONUP => { + win32wm::WM_RBUTTONUP => { use crate::event::{ElementState::Released, MouseButton::Right, WindowEvent::MouseInput}; release_mouse(subclass_input.window_state.lock()); @@ -1291,7 +1321,7 @@ unsafe fn public_window_callback_inner( let modifiers = update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: MouseInput { device_id: DEVICE_ID, state: Released, @@ -1299,10 +1329,10 @@ unsafe fn public_window_callback_inner( modifiers, }, }); - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_MBUTTONDOWN => { + win32wm::WM_MBUTTONDOWN => { use crate::event::{ElementState::Pressed, MouseButton::Middle, WindowEvent::MouseInput}; capture_mouse(window, &mut *subclass_input.window_state.lock()); @@ -1310,7 +1340,7 @@ unsafe fn public_window_callback_inner( let modifiers = update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: MouseInput { device_id: DEVICE_ID, state: Pressed, @@ -1318,10 +1348,10 @@ unsafe fn public_window_callback_inner( modifiers, }, }); - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_MBUTTONUP => { + win32wm::WM_MBUTTONUP => { use crate::event::{ElementState::Released, MouseButton::Middle, WindowEvent::MouseInput}; release_mouse(subclass_input.window_state.lock()); @@ -1329,7 +1359,7 @@ unsafe fn public_window_callback_inner( let modifiers = update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: MouseInput { device_id: DEVICE_ID, state: Released, @@ -1337,19 +1367,19 @@ unsafe fn public_window_callback_inner( modifiers, }, }); - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_XBUTTONDOWN => { + win32wm::WM_XBUTTONDOWN => { use crate::event::{ElementState::Pressed, MouseButton::Other, WindowEvent::MouseInput}; - let xbutton = winuser::GET_XBUTTON_WPARAM(wparam); + let xbutton = util::GET_XBUTTON_WPARAM(wparam); capture_mouse(window, &mut *subclass_input.window_state.lock()); let modifiers = update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: MouseInput { device_id: DEVICE_ID, state: Pressed, @@ -1357,19 +1387,19 @@ unsafe fn public_window_callback_inner( modifiers, }, }); - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_XBUTTONUP => { + win32wm::WM_XBUTTONUP => { use crate::event::{ElementState::Released, MouseButton::Other, WindowEvent::MouseInput}; - let xbutton = winuser::GET_XBUTTON_WPARAM(wparam); + let xbutton = util::GET_XBUTTON_WPARAM(wparam); release_mouse(subclass_input.window_state.lock()); let modifiers = update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: MouseInput { device_id: DEVICE_ID, state: Released, @@ -1377,31 +1407,32 @@ unsafe fn public_window_callback_inner( modifiers, }, }); - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_CAPTURECHANGED => { + win32wm::WM_CAPTURECHANGED => { // lparam here is a handle to the window which is gaining mouse capture. // If it is the same as our window, then we're essentially retaining the capture. This // can happen if `SetCapture` is called on our window when it already has the mouse // capture. - if lparam != window as isize { + if lparam.0 != window.0 { subclass_input.window_state.lock().mouse.capture_count = 0; } - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_TOUCH => { - let pcount = LOWORD(wparam as DWORD) as usize; + win32wm::WM_TOUCH => { + let pcount = usize::from(util::LOWORD(wparam.0 as u32)); let mut inputs = Vec::with_capacity(pcount); inputs.set_len(pcount); - let htouch = lparam as winuser::HTOUCHINPUT; - if winuser::GetTouchInputInfo( + let htouch = HTOUCHINPUT(lparam.0); + if GetTouchInputInfo( htouch, - pcount as UINT, + pcount as u32, inputs.as_mut_ptr(), - mem::size_of::() as INT, - ) > 0 + mem::size_of::() as i32, + ) + .as_bool() { for input in &inputs { let mut location = POINT { @@ -1409,7 +1440,7 @@ unsafe fn public_window_callback_inner( y: input.y / 100, }; - if winuser::ScreenToClient(window, &mut location as *mut _) == 0 { + if !ScreenToClient(window, &mut location as *mut _).as_bool() { continue; } @@ -1417,13 +1448,13 @@ unsafe fn public_window_callback_inner( let y = location.y as f64 + (input.y % 100) as f64 / 100f64; let location = PhysicalPosition::new(x, y); subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: WindowEvent::Touch(Touch { - phase: if input.dwFlags & winuser::TOUCHEVENTF_DOWN != 0 { + phase: if (input.dwFlags & TOUCHEVENTF_DOWN) != 0 { TouchPhase::Started - } else if input.dwFlags & winuser::TOUCHEVENTF_UP != 0 { + } else if (input.dwFlags & TOUCHEVENTF_UP) != 0 { TouchPhase::Ended - } else if input.dwFlags & winuser::TOUCHEVENTF_MOVE != 0 { + } else if (input.dwFlags & TOUCHEVENTF_MOVE) != 0 { TouchPhase::Moved } else { continue; @@ -1436,11 +1467,11 @@ unsafe fn public_window_callback_inner( }); } } - winuser::CloseTouchInputHandle(htouch); - result = ProcResult::Value(0); + CloseTouchInputHandle(htouch); + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_POINTERDOWN | winuser::WM_POINTERUPDATE | winuser::WM_POINTERUP => { + win32wm::WM_POINTERDOWN | win32wm::WM_POINTERUPDATE | win32wm::WM_POINTERUP => { if let ( Some(GetPointerFrameInfoHistory), Some(SkipPointerFrameMessages), @@ -1450,31 +1481,33 @@ unsafe fn public_window_callback_inner( *SKIP_POINTER_FRAME_MESSAGES, *GET_POINTER_DEVICE_RECTS, ) { - let pointer_id = LOWORD(wparam as DWORD) as UINT; - let mut entries_count = 0 as UINT; - let mut pointers_count = 0 as UINT; - if GetPointerFrameInfoHistory( + let pointer_id = u32::from(util::LOWORD(wparam.0 as u32)); + let mut entries_count = 0_u32; + let mut pointers_count = 0_u32; + if !GetPointerFrameInfoHistory( pointer_id, &mut entries_count as *mut _, &mut pointers_count as *mut _, std::ptr::null_mut(), - ) == 0 + ) + .as_bool() { - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); return; } let pointer_info_count = (entries_count * pointers_count) as usize; let mut pointer_infos = Vec::with_capacity(pointer_info_count); pointer_infos.set_len(pointer_info_count); - if GetPointerFrameInfoHistory( + if !GetPointerFrameInfoHistory( pointer_id, &mut entries_count as *mut _, &mut pointers_count as *mut _, pointer_infos.as_mut_ptr(), - ) == 0 + ) + .as_bool() { - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); return; } @@ -1485,11 +1518,12 @@ unsafe fn public_window_callback_inner( let mut device_rect = mem::MaybeUninit::uninit(); let mut display_rect = mem::MaybeUninit::uninit(); - if (GetPointerDeviceRects( + if !(GetPointerDeviceRects( pointer_info.sourceDevice, device_rect.as_mut_ptr(), display_rect.as_mut_ptr(), - )) == 0 + )) + .as_bool() { continue; } @@ -1517,26 +1551,28 @@ unsafe fn public_window_callback_inner( y: y.floor() as i32, }; - if winuser::ScreenToClient(window, &mut location as *mut _) == 0 { + if !ScreenToClient(window, &mut location as *mut _).as_bool() { continue; } let force = match pointer_info.pointerType { - winuser::PT_TOUCH => { + win32wm::PT_TOUCH => { let mut touch_info = mem::MaybeUninit::uninit(); GET_POINTER_TOUCH_INFO.and_then(|GetPointerTouchInfo| { - match GetPointerTouchInfo(pointer_info.pointerId, touch_info.as_mut_ptr()) { - 0 => None, - _ => normalize_pointer_pressure(touch_info.assume_init().pressure), + if GetPointerTouchInfo(pointer_info.pointerId, touch_info.as_mut_ptr()).as_bool() { + normalize_pointer_pressure(touch_info.assume_init().pressure) + } else { + None } }) } - winuser::PT_PEN => { + win32wm::PT_PEN => { let mut pen_info = mem::MaybeUninit::uninit(); GET_POINTER_PEN_INFO.and_then(|GetPointerPenInfo| { - match GetPointerPenInfo(pointer_info.pointerId, pen_info.as_mut_ptr()) { - 0 => None, - _ => normalize_pointer_pressure(pen_info.assume_init().pressure), + if GetPointerPenInfo(pointer_info.pointerId, pen_info.as_mut_ptr()).as_bool() { + normalize_pointer_pressure(pen_info.assume_init().pressure) + } else { + None } }) } @@ -1547,13 +1583,13 @@ unsafe fn public_window_callback_inner( let y = location.y as f64 + y.fract(); let location = PhysicalPosition::new(x, y); subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: WindowEvent::Touch(Touch { - phase: if pointer_info.pointerFlags & winuser::POINTER_FLAG_DOWN != 0 { + phase: if (pointer_info.pointerFlags & POINTER_FLAG_DOWN) != 0 { TouchPhase::Started - } else if pointer_info.pointerFlags & winuser::POINTER_FLAG_UP != 0 { + } else if (pointer_info.pointerFlags & POINTER_FLAG_UP) != 0 { TouchPhase::Ended - } else if pointer_info.pointerFlags & winuser::POINTER_FLAG_UPDATE != 0 { + } else if (pointer_info.pointerFlags & POINTER_FLAG_UPDATE) != 0 { TouchPhase::Moved } else { continue; @@ -1568,44 +1604,45 @@ unsafe fn public_window_callback_inner( SkipPointerFrameMessages(pointer_id); } - result = ProcResult::Value(0); + + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_SETFOCUS => { + win32wm::WM_SETFOCUS => { use crate::event::WindowEvent::Focused; update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: Focused(true), }); - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_KILLFOCUS => { + win32wm::WM_KILLFOCUS => { use crate::event::WindowEvent::{Focused, ModifiersChanged}; subclass_input.window_state.lock().modifiers_state = ModifiersState::empty(); subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: ModifiersChanged(ModifiersState::empty()), }); subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: Focused(false), }); - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_SETCURSOR => { + win32wm::WM_SETCURSOR => { let set_cursor_to = { let window_state = subclass_input.window_state.lock(); // The return value for the preceding `WM_NCHITTEST` message is conveniently // provided through the low-order word of lParam. We use that here since // `WM_MOUSEMOVE` seems to come after `WM_SETCURSOR` for a given cursor movement. - let in_client_area = LOWORD(lparam as DWORD) == winuser::HTCLIENT as WORD; + let in_client_area = u32::from(util::LOWORD(lparam.0 as u32)) == HTCLIENT; if in_client_area { Some(window_state.mouse.cursor) } else { @@ -1615,16 +1652,16 @@ unsafe fn public_window_callback_inner( match set_cursor_to { Some(cursor) => { - let cursor = winuser::LoadCursorW(ptr::null_mut(), cursor.to_windows_cursor()); - winuser::SetCursor(cursor); - result = ProcResult::Value(0); + let cursor = LoadCursorW(HINSTANCE::default(), cursor.to_windows_cursor()); + SetCursor(cursor); + result = ProcResult::Value(LRESULT(0)); } None => result = ProcResult::DefWindowProc, } } - winuser::WM_GETMINMAXINFO => { - let mmi = lparam as *mut winuser::MINMAXINFO; + win32wm::WM_GETMINMAXINFO => { + let mmi = lparam.0 as *mut MINMAXINFO; let window_state = subclass_input.window_state.lock(); @@ -1647,19 +1684,19 @@ unsafe fn public_window_callback_inner( } } - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } // Only sent on Windows 8.1 or newer. On Windows 7 and older user has to log out to change // DPI, therefore all applications are closed while DPI is changing. - winuser::WM_DPICHANGED => { + win32wm::WM_DPICHANGED => { use crate::event::WindowEvent::ScaleFactorChanged; // This message actually provides two DPI values - x and y. However MSDN says that // "you only need to use either the X-axis or the Y-axis value when scaling your // application since they are the same". // https://msdn.microsoft.com/en-us/library/windows/desktop/dn312083(v=vs.85).aspx - let new_dpi_x = u32::from(LOWORD(wparam as DWORD)); + let new_dpi_x = u32::from(util::LOWORD(wparam.0 as u32)); let new_scale_factor = dpi_to_scale_factor(new_dpi_x); let old_scale_factor: f64; @@ -1668,19 +1705,20 @@ unsafe fn public_window_callback_inner( old_scale_factor = window_state.scale_factor; window_state.scale_factor = new_scale_factor; - if new_scale_factor == old_scale_factor { - result = ProcResult::Value(0); + if (new_scale_factor - old_scale_factor).abs() < f64::EPSILON { + result = ProcResult::Value(LRESULT(0)); return; } - window_state.fullscreen.is_none() && !util::is_maximized(window) + window_state.fullscreen.is_none() + && !window_state.window_flags().contains(WindowFlags::MAXIMIZED) }; - let style = winuser::GetWindowLongW(window, winuser::GWL_STYLE) as _; - let style_ex = winuser::GetWindowLongW(window, winuser::GWL_EXSTYLE) as _; + let style = GetWindowLongW(window, GWL_STYLE) as WINDOW_STYLE; + let style_ex = GetWindowLongW(window, GWL_EXSTYLE) as WINDOW_EX_STYLE; // New size as suggested by Windows. - let suggested_rect = *(lparam as *const RECT); + let suggested_rect = *(lparam.0 as *const RECT); // The window rect provided is the window's outer size, not it's inner size. However, // win32 doesn't provide an `UnadjustWindowRectEx` function to get the client rect from @@ -1701,10 +1739,10 @@ unsafe fn public_window_callback_inner( } let old_physical_inner_rect = { - let mut old_physical_inner_rect = mem::zeroed(); - winuser::GetClientRect(window, &mut old_physical_inner_rect); - let mut origin = mem::zeroed(); - winuser::ClientToScreen(window, &mut origin); + let mut old_physical_inner_rect = RECT::default(); + GetClientRect(window, &mut old_physical_inner_rect); + let mut origin = POINT::default(); + ClientToScreen(window, &mut origin); old_physical_inner_rect.left += origin.x; old_physical_inner_rect.right += origin.x; @@ -1730,7 +1768,7 @@ unsafe fn public_window_callback_inner( }; let _ = subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: ScaleFactorChanged { scale_factor: new_scale_factor, new_inner_size: &mut new_physical_inner_size, @@ -1746,7 +1784,9 @@ unsafe fn public_window_callback_inner( .contains(WindowFlags::MARKER_IN_SIZE_MOVE); // Unset maximized if we're changing the window's size. if new_physical_inner_size != old_physical_inner_size { - util::set_maximized(window, false); + WindowState::set_window_flags(window_state, window, |f| { + f.set(WindowFlags::MAXIMIZED, false) + }); } } @@ -1760,8 +1800,8 @@ unsafe fn public_window_callback_inner( let mut conservative_rect = RECT { left: suggested_ul.0, top: suggested_ul.1, - right: suggested_ul.0 + new_physical_inner_size.width as LONG, - bottom: suggested_ul.1 + new_physical_inner_size.height as LONG, + right: suggested_ul.0 + new_physical_inner_size.width as i32, + bottom: suggested_ul.1 + new_physical_inner_size.height as i32, }; conservative_rect = @@ -1773,8 +1813,8 @@ unsafe fn public_window_callback_inner( if dragging_window { let bias = { let cursor_pos = { - let mut pos = mem::zeroed(); - winuser::GetCursorPos(&mut pos); + let mut pos = POINT::default(); + GetCursorPos(&mut pos); pos }; let suggested_cursor_horizontal_ratio = (cursor_pos.x - suggested_rect.left) as f64 @@ -1782,8 +1822,7 @@ unsafe fn public_window_callback_inner( (cursor_pos.x - (suggested_cursor_horizontal_ratio - * (conservative_rect.right - conservative_rect.left) as f64) - as LONG) + * (conservative_rect.right - conservative_rect.left) as f64) as i32) - conservative_rect.left }; conservative_rect.left += bias; @@ -1792,54 +1831,54 @@ unsafe fn public_window_callback_inner( // Check to see if the new window rect is on the monitor with the new DPI factor. // If it isn't, offset the window so that it is. - let new_dpi_monitor = winuser::MonitorFromWindow(window, 0); - let conservative_rect_monitor = winuser::MonitorFromRect(&conservative_rect, 0); - new_outer_rect = if conservative_rect_monitor == new_dpi_monitor { - conservative_rect - } else { - let get_monitor_rect = |monitor| { - let mut monitor_info = winuser::MONITORINFO { - cbSize: mem::size_of::() as _, - ..mem::zeroed() + let new_dpi_monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); + let conservative_rect_monitor = MonitorFromRect(&conservative_rect, MONITOR_DEFAULTTONULL); + new_outer_rect = { + if conservative_rect_monitor != new_dpi_monitor { + let get_monitor_rect = |monitor| { + let mut monitor_info = MONITORINFO { + cbSize: mem::size_of::() as _, + ..MONITORINFO::default() + }; + GetMonitorInfoW(monitor, &mut monitor_info); + monitor_info.rcMonitor }; - winuser::GetMonitorInfoW(monitor, &mut monitor_info); - monitor_info.rcMonitor - }; - let wrong_monitor = conservative_rect_monitor; - let wrong_monitor_rect = get_monitor_rect(wrong_monitor); - let new_monitor_rect = get_monitor_rect(new_dpi_monitor); - - // The direction to nudge the window in to get the window onto the monitor with - // the new DPI factor. We calculate this by seeing which monitor edges are - // shared and nudging away from the wrong monitor based on those. - let delta_nudge_to_dpi_monitor = ( - if wrong_monitor_rect.left == new_monitor_rect.right { - -1 - } else if wrong_monitor_rect.right == new_monitor_rect.left { - 1 - } else { - 0 - }, - if wrong_monitor_rect.bottom == new_monitor_rect.top { - 1 - } else if wrong_monitor_rect.top == new_monitor_rect.bottom { - -1 - } else { - 0 - }, - ); - - let abort_after_iterations = new_monitor_rect.right - new_monitor_rect.left - + new_monitor_rect.bottom - - new_monitor_rect.top; - for _ in 0..abort_after_iterations { - conservative_rect.left += delta_nudge_to_dpi_monitor.0; - conservative_rect.right += delta_nudge_to_dpi_monitor.0; - conservative_rect.top += delta_nudge_to_dpi_monitor.1; - conservative_rect.bottom += delta_nudge_to_dpi_monitor.1; - - if winuser::MonitorFromRect(&conservative_rect, 0) == new_dpi_monitor { - break; + let wrong_monitor = conservative_rect_monitor; + let wrong_monitor_rect = get_monitor_rect(wrong_monitor); + let new_monitor_rect = get_monitor_rect(new_dpi_monitor); + + // The direction to nudge the window in to get the window onto the monitor with + // the new DPI factor. We calculate this by seeing which monitor edges are + // shared and nudging away from the wrong monitor based on those. + let delta_nudge_to_dpi_monitor = ( + if wrong_monitor_rect.left == new_monitor_rect.right { + -1 + } else if wrong_monitor_rect.right == new_monitor_rect.left { + 1 + } else { + 0 + }, + if wrong_monitor_rect.bottom == new_monitor_rect.top { + 1 + } else if wrong_monitor_rect.top == new_monitor_rect.bottom { + -1 + } else { + 0 + }, + ); + + let abort_after_iterations = new_monitor_rect.right - new_monitor_rect.left + + new_monitor_rect.bottom + - new_monitor_rect.top; + for _ in 0..abort_after_iterations { + conservative_rect.left += delta_nudge_to_dpi_monitor.0; + conservative_rect.right += delta_nudge_to_dpi_monitor.0; + conservative_rect.top += delta_nudge_to_dpi_monitor.1; + conservative_rect.bottom += delta_nudge_to_dpi_monitor.1; + + if MonitorFromRect(&conservative_rect, MONITOR_DEFAULTTONULL) == new_dpi_monitor { + break; + } } } @@ -1847,20 +1886,20 @@ unsafe fn public_window_callback_inner( }; } - winuser::SetWindowPos( + SetWindowPos( window, - ptr::null_mut(), + HWND::default(), new_outer_rect.left, new_outer_rect.top, new_outer_rect.right - new_outer_rect.left, new_outer_rect.bottom - new_outer_rect.top, - winuser::SWP_NOZORDER | winuser::SWP_NOACTIVATE, + SWP_NOZORDER | SWP_NOACTIVATE, ); - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } - winuser::WM_SETTINGCHANGE => { + win32wm::WM_WININICHANGE => { use crate::event::WindowEvent::ThemeChanged; let preferred_theme = subclass_input.window_state.lock().preferred_theme; @@ -1873,14 +1912,14 @@ unsafe fn public_window_callback_inner( window_state.current_theme = new_theme; mem::drop(window_state); subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), + window_id: RootWindowId(WindowId(window.0)), event: ThemeChanged(new_theme), }); } } } - winuser::WM_NCCALCSIZE => { + win32wm::WM_NCCALCSIZE => { let win_flags = subclass_input.window_state.lock().window_flags(); if !win_flags.contains(WindowFlags::DECORATIONS) { @@ -1888,17 +1927,17 @@ unsafe fn public_window_callback_inner( if util::is_maximized(window) { let monitor = monitor::current_monitor(window); if let Ok(monitor_info) = monitor::get_monitor_info(monitor.hmonitor()) { - let params = &mut *(lparam as *mut winuser::NCCALCSIZE_PARAMS); - params.rgrc[0] = monitor_info.rcWork; + let params = &mut *(lparam.0 as *mut NCCALCSIZE_PARAMS); + params.rgrc[0] = monitor_info.monitorInfo.rcWork; } } - result = ProcResult::Value(0); // return 0 here to make the windowo borderless + result = ProcResult::Value(LRESULT(0)); // return 0 here to make the windowo borderless } else { result = ProcResult::DefSubclassProc; } } - winuser::WM_NCHITTEST => { + win32wm::WM_NCHITTEST => { if let Some(state) = subclass_input.window_state.try_lock() { let win_flags = state.window_flags(); @@ -1906,8 +1945,8 @@ unsafe fn public_window_callback_inner( if !win_flags.contains(WindowFlags::DECORATIONS) { // cursor location let (cx, cy) = ( - windowsx::GET_X_LPARAM(lparam), - windowsx::GET_Y_LPARAM(lparam), + i32::from(util::GET_X_LPARAM(lparam)), + i32::from(util::GET_Y_LPARAM(lparam)), ); result = ProcResult::Value(crate::platform_impl::hit_test(window, cx, cy)); @@ -1919,14 +1958,14 @@ unsafe fn public_window_callback_inner( _ => { if msg == *DESTROY_MSG_ID { - winuser::DestroyWindow(window); - result = ProcResult::Value(0); + DestroyWindow(window); + result = ProcResult::Value(LRESULT(0)); } else if msg == *SET_RETAIN_STATE_ON_SIZE_MSG_ID { let mut window_state = subclass_input.window_state.lock(); window_state.set_window_flags_in_place(|f| { - f.set(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE, wparam != 0) + f.set(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE, wparam.0 != 0) }); - result = ProcResult::Value(0); + result = ProcResult::Value(LRESULT(0)); } } }; @@ -1934,32 +1973,27 @@ unsafe fn public_window_callback_inner( subclass_input .event_loop_runner .catch_unwind(callback) - .unwrap_or_else(|| result = ProcResult::Value(-1)); + .unwrap_or_else(|| result = ProcResult::Value(LRESULT(-1))); match result { - ProcResult::DefSubclassProc => commctrl::DefSubclassProc(window, msg, wparam, lparam), - ProcResult::DefWindowProc => winuser::DefWindowProcW(window, msg, wparam, lparam), + ProcResult::DefSubclassProc => DefSubclassProc(window, msg, wparam, lparam), + ProcResult::DefWindowProc => DefWindowProcW(window, msg, wparam, lparam), ProcResult::Value(val) => val, } } unsafe extern "system" fn thread_event_target_callback( window: HWND, - msg: UINT, + msg: u32, wparam: WPARAM, lparam: LPARAM, - _: UINT_PTR, - subclass_input_ptr: DWORD_PTR, + _: usize, + subclass_input_ptr: usize, ) -> LRESULT { let subclass_input = Box::from_raw(subclass_input_ptr as *mut ThreadMsgTargetSubclassInput); - if msg != winuser::WM_PAINT { - winuser::RedrawWindow( - window, - ptr::null(), - ptr::null_mut(), - winuser::RDW_INTERNALPAINT, - ); + if msg != WM_PAINT { + RedrawWindow(window, ptr::null(), HRGN::default(), RDW_INTERNALPAINT); } let mut subclass_removed = false; @@ -1968,15 +2002,15 @@ unsafe extern "system" fn thread_event_target_callback( // the closure to catch_unwind directly so that the match body indendation wouldn't change and // the git blame and history would be preserved. let callback = || match msg { - winuser::WM_NCDESTROY => { + win32wm::WM_NCDESTROY => { remove_event_target_window_subclass::(window); subclass_removed = true; - 0 + LRESULT(0) } // Because WM_PAINT comes after all other messages, we use it during modal loops to detect // when the event queue has been emptied. See `process_event` for more details. - winuser::WM_PAINT => { - winuser::ValidateRect(window, ptr::null()); + win32wm::WM_PAINT => { + ValidateRect(window, ptr::null()); // If the WM_PAINT handler in `public_window_callback` has already flushed the redraw // events, `handling_events` will return false and we won't emit a second // `RedrawEventsCleared` event. @@ -1984,12 +2018,7 @@ unsafe extern "system" fn thread_event_target_callback( if subclass_input.event_loop_runner.should_buffer() { // This branch can be triggered when a nested win32 event loop is triggered // inside of the `event_handler` callback. - winuser::RedrawWindow( - window, - ptr::null(), - ptr::null_mut(), - winuser::RDW_INTERNALPAINT, - ); + RedrawWindow(window, ptr::null(), HRGN::default(), RDW_INTERNALPAINT); } else { // This WM_PAINT handler will never be re-entrant because `flush_paint_messages` // doesn't call WM_PAINT for the thread event target (i.e. this window). @@ -2003,72 +2032,67 @@ unsafe extern "system" fn thread_event_target_callback( } // Default WM_PAINT behaviour. This makes sure modals and popups are shown immediatly when opening them. - commctrl::DefSubclassProc(window, msg, wparam, lparam) + DefSubclassProc(window, msg, wparam, lparam) } - winuser::WM_INPUT_DEVICE_CHANGE => { - let event = match wparam as _ { - winuser::GIDC_ARRIVAL => DeviceEvent::Added, - winuser::GIDC_REMOVAL => DeviceEvent::Removed, + win32wm::WM_INPUT_DEVICE_CHANGE => { + let event = match wparam.0 as u32 { + win32wm::GIDC_ARRIVAL => DeviceEvent::Added, + win32wm::GIDC_REMOVAL => DeviceEvent::Removed, _ => unreachable!(), }; subclass_input.send_event(Event::DeviceEvent { - device_id: wrap_device_id(lparam as _), + device_id: wrap_device_id(lparam.0), event, }); - 0 + LRESULT(0) } - winuser::WM_INPUT => { - if let Some(data) = raw_input::get_raw_input_data(lparam as _) { + win32wm::WM_INPUT => { + if let Some(data) = raw_input::get_raw_input_data(HRAWINPUT(lparam.0)) { handle_raw_input(&subclass_input, data); } - commctrl::DefSubclassProc(window, msg, wparam, lparam) + DefSubclassProc(window, msg, wparam, lparam) } _ if msg == *USER_EVENT_MSG_ID => { if let Ok(event) = subclass_input.user_event_receiver.recv() { subclass_input.send_event(Event::UserEvent(event)); } - 0 + LRESULT(0) } _ if msg == *EXEC_MSG_ID => { - let mut function: ThreadExecFn = Box::from_raw(wparam as usize as *mut _); + let mut function: ThreadExecFn = Box::from_raw(wparam.0 as *mut _); function(); - 0 + LRESULT(0) } _ if msg == *PROCESS_NEW_EVENTS_MSG_ID => { - winuser::PostThreadMessageW( + PostThreadMessageW( subclass_input.event_loop_runner.wait_thread_id(), *CANCEL_WAIT_UNTIL_MSG_ID, - 0, - 0, + WPARAM(0), + LPARAM(0), ); // if the control_flow is WaitUntil, make sure the given moment has actually passed // before emitting NewEvents if let ControlFlow::WaitUntil(wait_until) = subclass_input.event_loop_runner.control_flow() { - let mut msg = mem::zeroed(); + let mut msg = MSG::default(); while Instant::now() < wait_until { - if 0 != winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 0) { + if PeekMessageW(&mut msg, HWND::default(), 0, 0, PM_NOREMOVE).as_bool() { // This works around a "feature" in PeekMessageW. If the message PeekMessageW // gets is a WM_PAINT message that had RDW_INTERNALPAINT set (i.e. doesn't // have an update region), PeekMessageW will remove that window from the // redraw queue even though we told it not to remove messages from the // queue. We fix it by re-dispatching an internal paint message to that // window. - if msg.message == winuser::WM_PAINT { - let mut rect = mem::zeroed(); - if 0 == winuser::GetUpdateRect(msg.hwnd, &mut rect, 0) { - winuser::RedrawWindow( - msg.hwnd, - ptr::null(), - ptr::null_mut(), - winuser::RDW_INTERNALPAINT, - ); + if msg.message == WM_PAINT { + let mut rect = RECT::default(); + if !GetUpdateRect(msg.hwnd, &mut rect, false).as_bool() { + RedrawWindow(msg.hwnd, ptr::null(), HRGN::default(), RDW_INTERNALPAINT); } } @@ -2077,15 +2101,15 @@ unsafe extern "system" fn thread_event_target_callback( } } subclass_input.event_loop_runner.poll(); - 0 + LRESULT(0) } - _ => commctrl::DefSubclassProc(window, msg, wparam, lparam), + _ => DefSubclassProc(window, msg, wparam, lparam), }; let result = subclass_input .event_loop_runner .catch_unwind(callback) - .unwrap_or(-1); + .unwrap_or(LRESULT(-1)); if subclass_removed { mem::drop(subclass_input); } else { @@ -2095,7 +2119,7 @@ unsafe extern "system" fn thread_event_target_callback( } unsafe fn handle_raw_input( - subclass_input: &Box>, + subclass_input: &ThreadMsgTargetSubclassInput, data: RAWINPUT, ) { use crate::event::{ @@ -2104,12 +2128,12 @@ unsafe fn handle_raw_input( MouseScrollDelta::LineDelta, }; - let device_id = wrap_device_id(data.header.hDevice as _); + let device_id = wrap_device_id(data.header.hDevice.0 as _); - if data.header.dwType == winuser::RIM_TYPEMOUSE { - let mouse = data.data.mouse(); + if data.header.dwType == RIM_TYPEMOUSE { + let mouse = data.data.mouse; - if util::has_flag(mouse.usFlags, winuser::MOUSE_MOVE_RELATIVE) { + if util::has_flag(mouse.usFlags, MOUSE_MOVE_RELATIVE as u16) { let x = mouse.lLastX as f64; let y = mouse.lLastY as f64; @@ -2135,9 +2159,12 @@ unsafe fn handle_raw_input( } } - if util::has_flag(mouse.usButtonFlags, winuser::RI_MOUSE_WHEEL) { + if util::has_flag( + mouse.Anonymous.Anonymous.usButtonFlags, + RI_MOUSE_WHEEL as u16, + ) { // We must cast to SHORT first, becaues `usButtonData` must be interpreted as signed. - let delta = mouse.usButtonData as SHORT as f32 / winuser::WHEEL_DELTA as f32; + let delta = mouse.Anonymous.Anonymous.usButtonData as f32 / WHEEL_DELTA as f32; subclass_input.send_event(Event::DeviceEvent { device_id, event: MouseWheel { @@ -2146,7 +2173,8 @@ unsafe fn handle_raw_input( }); } - let button_state = raw_input::get_raw_mouse_button_state(mouse.usButtonFlags); + let button_state = + raw_input::get_raw_mouse_button_state(mouse.Anonymous.Anonymous.usButtonFlags); // Left, middle, and right, respectively. for (index, state) in button_state.iter().enumerate() { if let Some(state) = *state { @@ -2160,13 +2188,11 @@ unsafe fn handle_raw_input( }); } } - } else if data.header.dwType == winuser::RIM_TYPEKEYBOARD { - let keyboard = data.data.keyboard(); + } else if data.header.dwType == RIM_TYPEKEYBOARD { + let keyboard = data.data.keyboard; - let pressed = - keyboard.Message == winuser::WM_KEYDOWN || keyboard.Message == winuser::WM_SYSKEYDOWN; - let released = - keyboard.Message == winuser::WM_KEYUP || keyboard.Message == winuser::WM_SYSKEYUP; + let pressed = keyboard.Message == WM_KEYDOWN || keyboard.Message == WM_SYSKEYDOWN; + let released = keyboard.Message == WM_KEYUP || keyboard.Message == WM_SYSKEYUP; if !pressed && !released { return; @@ -2174,9 +2200,9 @@ unsafe fn handle_raw_input( let state = if pressed { Pressed } else { Released }; let extension = { - if util::has_flag(keyboard.Flags, winuser::RI_KEY_E0 as _) { + if util::has_flag(keyboard.Flags, RI_KEY_E0 as _) { 0xE000 - } else if util::has_flag(keyboard.Flags, winuser::RI_KEY_E1 as _) { + } else if util::has_flag(keyboard.Flags, RI_KEY_E1 as _) { 0xE100 } else { 0x0000 @@ -2186,7 +2212,7 @@ unsafe fn handle_raw_input( if keyboard.MakeCode == 0 { // In some cases (often with media keys) the device reports a scancode of 0 but a // valid virtual key. In these cases we obtain the scancode from the virtual key. - scancode = winuser::MapVirtualKeyW(keyboard.VKey as u32, winuser::MAPVK_VK_TO_VSC_EX) as u16; + scancode = MapVirtualKeyW(keyboard.VKey as u32, MAPVK_VK_TO_VSC_EX) as u16; } else { scancode = keyboard.MakeCode | extension; } @@ -2214,7 +2240,7 @@ unsafe fn handle_raw_input( return; } let code; - if keyboard.VKey as c_int == winuser::VK_NUMLOCK { + if keyboard.VKey == VK_NUMLOCK { // Historically, the NumLock and the Pause key were one and the same physical key. // The user could trigger Pause by pressing Ctrl+NumLock. // Now these are often physically separate and the two keys can be differentiated by @@ -2231,7 +2257,7 @@ unsafe fn handle_raw_input( } else { code = KeyCode::from_scancode(scancode as u32); } - if keyboard.VKey as c_int == winuser::VK_SHIFT { + if keyboard.VKey == VK_SHIFT { match code { KeyCode::NumpadDecimal | KeyCode::Numpad0 diff --git a/src/platform_impl/windows/event_loop/runner.rs b/src/platform_impl/windows/event_loop/runner.rs index 529277c1ce..a8146cb411 100644 --- a/src/platform_impl/windows/event_loop/runner.rs +++ b/src/platform_impl/windows/event_loop/runner.rs @@ -10,9 +10,9 @@ use std::{ time::Instant, }; -use winapi::{ - shared::{minwindef::DWORD, windef::HWND}, - um::winuser, +use windows::Win32::{ + Foundation::HWND, + Graphics::Gdi::{RedrawWindow, HRGN, RDW_INTERNALPAINT}, }; use crate::{ @@ -27,7 +27,7 @@ pub(crate) type EventLoopRunnerShared = Rc>; pub(crate) struct EventLoopRunner { // The event loop's win32 handles thread_msg_target: HWND, - wait_thread_id: DWORD, + wait_thread_id: u32, control_flow: Cell, runner_state: Cell, @@ -36,7 +36,7 @@ pub(crate) struct EventLoopRunner { event_handler: Cell, &mut ControlFlow)>>>, event_buffer: RefCell>>, - owned_windows: Cell>, + owned_windows: Cell>, panic_error: Cell>, } @@ -66,7 +66,7 @@ enum BufferedEvent { } impl EventLoopRunner { - pub(crate) fn new(thread_msg_target: HWND, wait_thread_id: DWORD) -> EventLoopRunner { + pub(crate) fn new(thread_msg_target: HWND, wait_thread_id: u32) -> EventLoopRunner { EventLoopRunner { thread_msg_target, wait_thread_id, @@ -116,7 +116,7 @@ impl EventLoopRunner { self.thread_msg_target } - pub fn wait_thread_id(&self) -> DWORD { + pub fn wait_thread_id(&self) -> u32 { self.wait_thread_id } @@ -177,20 +177,20 @@ impl EventLoopRunner { } pub fn register_window(&self, window: HWND) { let mut owned_windows = self.owned_windows.take(); - owned_windows.insert(window); + owned_windows.insert(window.0); self.owned_windows.set(owned_windows); } pub fn remove_window(&self, window: HWND) { let mut owned_windows = self.owned_windows.take(); - owned_windows.remove(&window); + owned_windows.remove(&window.0); self.owned_windows.set(owned_windows); } pub fn owned_windows(&self, mut f: impl FnMut(HWND)) { let mut owned_windows = self.owned_windows.take(); for hwnd in &owned_windows { - f(*hwnd); + f(HWND(*hwnd)); } let new_owned_windows = self.owned_windows.take(); owned_windows.extend(&new_owned_windows); @@ -211,19 +211,17 @@ impl EventLoopRunner { self.move_state_to(RunnerState::HandlingRedrawEvents); } self.call_event_handler(event); + } else if self.should_buffer() { + // If the runner is already borrowed, we're in the middle of an event loop invocation. Add + // the event to a buffer to be processed later. + self + .event_buffer + .borrow_mut() + .push_back(BufferedEvent::from_event(event)) } else { - if self.should_buffer() { - // If the runner is already borrowed, we're in the middle of an event loop invocation. Add - // the event to a buffer to be processed later. - self - .event_buffer - .borrow_mut() - .push_back(BufferedEvent::from_event(event)) - } else { - self.move_state_to(RunnerState::HandlingMainEvents); - self.call_event_handler(event); - self.dispatch_buffered_events(); - } + self.move_state_to(RunnerState::HandlingMainEvents); + self.call_event_handler(event); + self.dispatch_buffered_events(); } } @@ -245,10 +243,10 @@ impl EventLoopRunner { let mut event_handler = self.event_handler.take() .expect("either event handler is re-entrant (likely), or no event handler is registered (very unlikely)"); - if control_flow != ControlFlow::Exit { - event_handler(event, &mut control_flow); + if let ControlFlow::ExitWithCode(code) = control_flow { + event_handler(event, &mut ControlFlow::ExitWithCode(code)); } else { - event_handler(event, &mut ControlFlow::Exit); + event_handler(event, &mut control_flow); } assert!(self.event_handler.replace(Some(event_handler)).is_none()); @@ -374,10 +372,12 @@ impl EventLoopRunner { let start_cause = match (init, self.control_flow()) { (true, _) => StartCause::Init, (false, ControlFlow::Poll) => StartCause::Poll, - (false, ControlFlow::Exit) | (false, ControlFlow::Wait) => StartCause::WaitCancelled { - requested_resume: None, - start: self.last_events_cleared.get(), - }, + (false, ControlFlow::ExitWithCode(_)) | (false, ControlFlow::Wait) => { + StartCause::WaitCancelled { + requested_resume: None, + start: self.last_events_cleared.get(), + } + } (false, ControlFlow::WaitUntil(requested_resume)) => { if Instant::now() < requested_resume { StartCause::WaitCancelled { @@ -394,11 +394,11 @@ impl EventLoopRunner { }; self.call_event_handler(Event::NewEvents(start_cause)); self.dispatch_buffered_events(); - winuser::RedrawWindow( + RedrawWindow( self.thread_msg_target, ptr::null(), - ptr::null_mut(), - winuser::RDW_INTERNALPAINT, + HRGN::default(), + RDW_INTERNALPAINT, ); } @@ -435,7 +435,7 @@ impl BufferedEvent { }, }); util::set_inner_size_physical( - (window_id.0).0, + HWND(window_id.0 .0), new_inner_size.width as _, new_inner_size.height as _, ); diff --git a/src/platform_impl/windows/global_shortcut.rs b/src/platform_impl/windows/global_shortcut.rs index 16aedc26ef..5fe0586a68 100644 --- a/src/platform_impl/windows/global_shortcut.rs +++ b/src/platform_impl/windows/global_shortcut.rs @@ -5,8 +5,7 @@ use crate::{ global_shortcut::{GlobalShortcut as RootGlobalShortcut, ShortcutManagerError}, keyboard::ModifiersState, }; -use std::ptr; -use winapi::{shared::windef::HWND, um::winuser}; +use windows::Win32::{Foundation::HWND, UI::Input::KeyboardAndMouse::*}; #[derive(Debug, Clone)] pub struct ShortcutManager { @@ -25,33 +24,33 @@ impl ShortcutManager { accelerator: Accelerator, ) -> Result { unsafe { - let mut converted_modifiers: u32 = 0; + let mut converted_modifiers = 0; let modifiers: ModifiersState = accelerator.mods; if modifiers.shift_key() { - converted_modifiers |= winuser::MOD_SHIFT as u32; + converted_modifiers |= MOD_SHIFT; } if modifiers.super_key() { - converted_modifiers |= winuser::MOD_WIN as u32; + converted_modifiers |= MOD_WIN; } if modifiers.alt_key() { - converted_modifiers |= winuser::MOD_ALT as u32; + converted_modifiers |= MOD_ALT; } if modifiers.control_key() { - converted_modifiers |= winuser::MOD_CONTROL as u32; + converted_modifiers |= MOD_CONTROL; } // get key scan code match key_to_vk(&accelerator.key) { Some(vk_code) => { - let result = winuser::RegisterHotKey( - ptr::null_mut(), + let result = RegisterHotKey( + HWND::default(), accelerator.clone().id().0 as i32, converted_modifiers, - vk_code as u32, + u32::from(vk_code), ); - if result == 0 { + if !result.as_bool() { return Err(ShortcutManagerError::InvalidAccelerator( - "Unable to register accelerator with `winuser::RegisterHotKey`.".into(), + "Unable to register accelerator with `RegisterHotKey`.".into(), )); } let shortcut = GlobalShortcut { accelerator }; @@ -92,7 +91,7 @@ impl GlobalShortcut { } pub(crate) fn unregister(&self) { unsafe { - winuser::UnregisterHotKey(0 as HWND, self.accelerator.clone().id().0 as i32); + UnregisterHotKey(HWND::default(), self.accelerator.clone().id().0 as i32); } } } diff --git a/src/platform_impl/windows/icon.rs b/src/platform_impl/windows/icon.rs index 5491998b98..e426d9009d 100644 --- a/src/platform_impl/windows/icon.rs +++ b/src/platform_impl/windows/icon.rs @@ -1,15 +1,12 @@ // Copyright 2019-2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -use std::{fmt, io, iter::once, mem, os::windows::ffi::OsStrExt, path::Path, ptr, sync::Arc}; - -use winapi::{ - ctypes::{c_int, wchar_t}, - shared::{ - minwindef::{BYTE, LPARAM, WORD, WPARAM}, - windef::{HICON, HWND}, - }, - um::{libloaderapi, winuser}, +use std::{fmt, io, iter::once, mem, os::windows::ffi::OsStrExt, path::Path, sync::Arc}; + +use windows::Win32::{ + Foundation::{HINSTANCE, HWND, LPARAM, PWSTR, WPARAM}, + System::LibraryLoader::*, + UI::WindowsAndMessaging::*, }; use crate::{dpi::PhysicalSize, icon::*}; @@ -33,29 +30,29 @@ impl RgbaIcon { } assert_eq!(and_mask.len(), pixel_count); let handle = unsafe { - winuser::CreateIcon( - ptr::null_mut(), - self.width as c_int, - self.height as c_int, + CreateIcon( + HINSTANCE::default(), + self.width as i32, + self.height as i32, 1, - (PIXEL_SIZE * 8) as BYTE, - and_mask.as_ptr() as *const BYTE, - rgba.as_ptr() as *const BYTE, + (PIXEL_SIZE * 8) as u8, + and_mask.as_ptr() as *const u8, + rgba.as_ptr() as *const u8, ) as HICON }; - if !handle.is_null() { - Ok(WinIcon::from_handle(handle)) - } else { - Err(BadIcon::OsError(io::Error::last_os_error())) - } + Ok(WinIcon::from_handle( + handle + .ok() + .map_err(|_| BadIcon::OsError(io::Error::last_os_error()))?, + )) } } #[non_exhaustive] #[derive(Debug)] pub enum IconType { - Small = winuser::ICON_SMALL as isize, - Big = winuser::ICON_BIG as isize, + Small = ICON_SMALL as isize, + Big = ICON_BIG as isize, } #[derive(Debug)] @@ -79,7 +76,7 @@ impl WinIcon { path: P, size: Option>, ) -> Result { - let wide_path: Vec = path + let mut wide_path: Vec = path .as_ref() .as_os_str() .encode_wide() @@ -89,44 +86,45 @@ impl WinIcon { // width / height of 0 along with LR_DEFAULTSIZE tells windows to load the default icon size let (width, height) = size.map(Into::into).unwrap_or((0, 0)); - let handle = unsafe { - winuser::LoadImageW( - ptr::null_mut(), - wide_path.as_ptr() as *const wchar_t, - winuser::IMAGE_ICON, - width as c_int, - height as c_int, - winuser::LR_DEFAULTSIZE | winuser::LR_LOADFROMFILE, - ) as HICON - }; - if !handle.is_null() { - Ok(WinIcon::from_handle(handle)) - } else { - Err(BadIcon::OsError(io::Error::last_os_error())) - } + let handle = HICON( + unsafe { + LoadImageW( + HINSTANCE::default(), + PWSTR(wide_path.as_mut_ptr()), + IMAGE_ICON, + width as i32, + height as i32, + LR_DEFAULTSIZE | LR_LOADFROMFILE, + ) + } + .0, + ); + Ok(WinIcon::from_handle( + handle + .ok() + .map_err(|_| BadIcon::OsError(io::Error::last_os_error()))?, + )) } - pub fn from_resource( - resource_id: WORD, - size: Option>, - ) -> Result { + pub fn from_resource(resource_id: u16, size: Option>) -> Result { // width / height of 0 along with LR_DEFAULTSIZE tells windows to load the default icon size let (width, height) = size.map(Into::into).unwrap_or((0, 0)); - let handle = unsafe { - winuser::LoadImageW( - libloaderapi::GetModuleHandleW(ptr::null_mut()), - winuser::MAKEINTRESOURCEW(resource_id), - winuser::IMAGE_ICON, - width as c_int, - height as c_int, - winuser::LR_DEFAULTSIZE, - ) as HICON - }; - if !handle.is_null() { - Ok(WinIcon::from_handle(handle)) - } else { - Err(BadIcon::OsError(io::Error::last_os_error())) - } + let handle = HICON(unsafe { + LoadImageW( + GetModuleHandleW(PWSTR::default()), + PWSTR(resource_id as usize as *mut u16), + IMAGE_ICON, + width as i32, + height as i32, + LR_DEFAULTSIZE, + ) + .0 + }); + Ok(WinIcon::from_handle( + handle + .ok() + .map_err(|_| BadIcon::OsError(io::Error::last_os_error()))?, + )) } pub fn from_rgba(rgba: Vec, width: u32, height: u32) -> Result { @@ -136,11 +134,11 @@ impl WinIcon { pub fn set_for_window(&self, hwnd: HWND, icon_type: IconType) { unsafe { - winuser::SendMessageW( + SendMessageW( hwnd, - winuser::WM_SETICON, - icon_type as WPARAM, - self.as_raw_handle() as LPARAM, + WM_SETICON, + WPARAM(icon_type as _), + LPARAM(self.as_raw_handle().0), ); } } @@ -154,7 +152,7 @@ impl WinIcon { impl Drop for RaiiIcon { fn drop(&mut self) { - unsafe { winuser::DestroyIcon(self.handle) }; + unsafe { DestroyIcon(self.handle) }; } } @@ -166,6 +164,6 @@ impl fmt::Debug for WinIcon { pub fn unset_for_window(hwnd: HWND, icon_type: IconType) { unsafe { - winuser::SendMessageW(hwnd, winuser::WM_SETICON, icon_type as WPARAM, 0 as LPARAM); + SendMessageW(hwnd, WM_SETICON, WPARAM(icon_type as _), LPARAM(0)); } } diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 79e0acf470..b78408be4c 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -1,18 +1,15 @@ use std::{ - char, - collections::HashSet, - ffi::OsString, - mem::MaybeUninit, - os::{raw::c_int, windows::ffi::OsStringExt}, + char, collections::HashSet, ffi::OsString, mem::MaybeUninit, os::windows::ffi::OsStringExt, sync::MutexGuard, }; -use winapi::{ - shared::{ - minwindef::{HKL, LPARAM, UINT, WPARAM}, - windef::HWND, +use windows::Win32::{ + Foundation::{HWND, LPARAM, LRESULT, WPARAM}, + UI::{ + Input::KeyboardAndMouse::{self as win32km, *}, + TextServices::HKL, + WindowsAndMessaging::{self as win32wm, *}, }, - um::winuser, }; use unicode_segmentation::UnicodeSegmentation; @@ -28,7 +25,6 @@ use crate::{ }; pub fn is_msg_keyboard_related(msg: u32) -> bool { - use winuser::{WM_KEYFIRST, WM_KEYLAST, WM_KILLFOCUS, WM_SETFOCUS}; let is_keyboard_msg = WM_KEYFIRST <= msg && msg <= WM_KEYLAST; is_keyboard_msg || msg == WM_SETFOCUS || msg == WM_KILLFOCUS @@ -61,14 +57,10 @@ pub struct MessageAsKeyEvent { /// /// Key release messages are a bit different due to the fact that they don't contribute to /// text input. The "sequence" only consists of one WM_KEYUP / WM_SYSKEYUP event. +#[derive(Default)] pub struct KeyEventBuilder { event_info: Option, } -impl Default for KeyEventBuilder { - fn default() -> Self { - KeyEventBuilder { event_info: None } - } -} impl KeyEventBuilder { /// Call this function for every window message. /// Returns Some() if this window message completes a KeyEvent. @@ -82,7 +74,7 @@ impl KeyEventBuilder { result: &mut ProcResult, ) -> Vec { match msg_kind { - winuser::WM_SETFOCUS => { + win32wm::WM_SETFOCUS => { // synthesize keydown events let kbd_state = get_async_kbd_state(); let key_events = self.synthesize_kbd_state(ElementState::Pressed, &kbd_state); @@ -90,7 +82,7 @@ impl KeyEventBuilder { return key_events; } } - winuser::WM_KILLFOCUS => { + win32wm::WM_KILLFOCUS => { // sythesize keyup events let kbd_state = get_kbd_state(); let key_events = self.synthesize_kbd_state(ElementState::Released, &kbd_state); @@ -98,13 +90,13 @@ impl KeyEventBuilder { return key_events; } } - winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN => { - if msg_kind == winuser::WM_SYSKEYDOWN && wparam as i32 == winuser::VK_F4 { + win32wm::WM_KEYDOWN | win32wm::WM_SYSKEYDOWN => { + if msg_kind == WM_SYSKEYDOWN && wparam.0 == usize::from(VK_F4) { // Don't dispatch Alt+F4 to the application. // This is handled in `event_loop.rs` return vec![]; } - *result = ProcResult::Value(0); + *result = ProcResult::Value(LRESULT(0)); let mut layouts = LAYOUT_CACHE.lock().unwrap(); let event_info = @@ -112,15 +104,15 @@ impl KeyEventBuilder { let mut next_msg = MaybeUninit::uninit(); let peek_retval = unsafe { - winuser::PeekMessageW( + PeekMessageW( next_msg.as_mut_ptr(), hwnd, - winuser::WM_KEYFIRST, - winuser::WM_KEYLAST, - winuser::PM_NOREMOVE, + WM_KEYFIRST, + WM_KEYLAST, + PM_NOREMOVE, ) }; - let has_next_key_message = peek_retval != 0; + let has_next_key_message = peek_retval.as_bool(); self.event_info = None; let mut finished_event_info = Some(event_info); if has_next_key_message { @@ -128,7 +120,7 @@ impl KeyEventBuilder { let next_msg_kind = next_msg.message; let next_belongs_to_this = !matches!( next_msg_kind, - winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN | winuser::WM_KEYUP | winuser::WM_SYSKEYUP + win32wm::WM_KEYDOWN | win32wm::WM_SYSKEYDOWN | win32wm::WM_KEYUP | win32wm::WM_SYSKEYUP ); if next_belongs_to_this { self.event_info = finished_event_info.take(); @@ -151,8 +143,8 @@ impl KeyEventBuilder { }]; } } - winuser::WM_DEADCHAR | winuser::WM_SYSDEADCHAR => { - *result = ProcResult::Value(0); + win32wm::WM_DEADCHAR | win32wm::WM_SYSDEADCHAR => { + *result = ProcResult::Value(LRESULT(0)); // At this point, we know that there isn't going to be any more events related to // this key press let event_info = self.event_info.take().unwrap(); @@ -163,33 +155,33 @@ impl KeyEventBuilder { is_synthetic: false, }]; } - winuser::WM_CHAR | winuser::WM_SYSCHAR => { + win32wm::WM_CHAR | win32wm::WM_SYSCHAR => { if self.event_info.is_none() { trace!("Received a CHAR message but no `event_info` was available. The message is probably IME, returning."); return vec![]; } - *result = ProcResult::Value(0); - let is_high_surrogate = 0xD800 <= wparam && wparam <= 0xDBFF; - let is_low_surrogate = 0xDC00 <= wparam && wparam <= 0xDFFF; + *result = ProcResult::Value(LRESULT(0)); + let is_high_surrogate = (0xD800..=0xDBFF).contains(&wparam.0); + let is_low_surrogate = (0xDC00..=0xDFFF).contains(&wparam.0); let is_utf16 = is_high_surrogate || is_low_surrogate; let more_char_coming; unsafe { let mut next_msg = MaybeUninit::uninit(); - let has_message = winuser::PeekMessageW( + let has_message = PeekMessageW( next_msg.as_mut_ptr(), hwnd, - winuser::WM_KEYFIRST, - winuser::WM_KEYLAST, - winuser::PM_NOREMOVE, + WM_KEYFIRST, + WM_KEYLAST, + PM_NOREMOVE, ); - let has_message = has_message != 0; + let has_message = has_message.as_bool(); if !has_message { more_char_coming = false; } else { let next_msg = next_msg.assume_init().message; - if next_msg == winuser::WM_CHAR || next_msg == winuser::WM_SYSCHAR { + if next_msg == WM_CHAR || next_msg == WM_SYSCHAR { more_char_coming = true; } else { more_char_coming = false; @@ -199,7 +191,7 @@ impl KeyEventBuilder { if is_utf16 { if let Some(ev_info) = self.event_info.as_mut() { - ev_info.utf16parts.push(wparam as u16); + ev_info.utf16parts.push(wparam.0 as u16); } } else { // In this case, wparam holds a UTF-32 character. @@ -214,7 +206,7 @@ impl KeyEventBuilder { let start_offset = utf16parts.len(); let new_size = utf16parts.len() + 2; utf16parts.resize(new_size, 0); - if let Some(ch) = char::from_u32(wparam as u32) { + if let Some(ch) = char::from_u32(wparam.0 as u32) { let encode_len = ch.encode_utf16(&mut utf16parts[start_offset..]).len(); let new_size = start_offset + encode_len; utf16parts.resize(new_size, 0); @@ -250,7 +242,7 @@ impl KeyEventBuilder { event_info.text = PartialText::System(event_info.utf16parts.clone()); } else { let mod_no_ctrl = mod_state.remove_only_ctrl(); - let num_lock_on = kbd_state[winuser::VK_NUMLOCK as usize] & 1 != 0; + let num_lock_on = kbd_state[usize::from(VK_NUMLOCK)] & 1 != 0; let vkey = event_info.vkey; let scancode = event_info.scancode; let keycode = event_info.code; @@ -264,30 +256,30 @@ impl KeyEventBuilder { }]; } } - winuser::WM_KEYUP | winuser::WM_SYSKEYUP => { - *result = ProcResult::Value(0); + win32wm::WM_KEYUP | win32wm::WM_SYSKEYUP => { + *result = ProcResult::Value(LRESULT(0)); let mut layouts = LAYOUT_CACHE.lock().unwrap(); let event_info = PartialKeyEventInfo::from_message(wparam, lparam, ElementState::Released, &mut layouts); let mut next_msg = MaybeUninit::uninit(); let peek_retval = unsafe { - winuser::PeekMessageW( + PeekMessageW( next_msg.as_mut_ptr(), hwnd, - winuser::WM_KEYFIRST, - winuser::WM_KEYLAST, - winuser::PM_NOREMOVE, + WM_KEYFIRST, + WM_KEYLAST, + PM_NOREMOVE, ) }; - let has_next_key_message = peek_retval != 0; + let has_next_key_message = peek_retval.as_bool(); let mut valid_event_info = Some(event_info); if has_next_key_message { let next_msg = unsafe { next_msg.assume_init() }; let (_, layout) = layouts.get_current_layout(); let is_fake = { let event_info = valid_event_info.as_ref().unwrap(); - is_current_fake(&event_info, next_msg, layout) + is_current_fake(event_info, next_msg, layout) }; if is_fake { valid_event_info = None; @@ -317,16 +309,12 @@ impl KeyEventBuilder { let mut layouts = LAYOUT_CACHE.lock().unwrap(); let (locale_id, _) = layouts.get_current_layout(); - macro_rules! is_key_pressed { - ($vk:expr) => { - kbd_state[$vk as usize] & 0x80 != 0 - }; - } + let is_key_pressed = |vk: VIRTUAL_KEY| &kbd_state[usize::from(vk)] & 0x80 != 0; // Is caps-lock active? Note that this is different from caps-lock // being held down. - let caps_lock_on = kbd_state[winuser::VK_CAPITAL as usize] & 1 != 0; - let num_lock_on = kbd_state[winuser::VK_NUMLOCK as usize] & 1 != 0; + let caps_lock_on = kbd_state[usize::from(VK_CAPITAL)] & 1 != 0; + let num_lock_on = kbd_state[usize::from(VK_NUMLOCK)] & 1 != 0; // We are synthesizing the press event for caps-lock first for the following reasons: // 1. If caps-lock is *not* held down but *is* active, then we have to @@ -339,13 +327,13 @@ impl KeyEventBuilder { // For the sake of simplicity we are choosing to always sythesize // caps-lock first, and always use the current caps-lock state // to determine the produced text - if is_key_pressed!(winuser::VK_CAPITAL) { + if is_key_pressed(VK_CAPITAL) { let event = self.create_synthetic( - winuser::VK_CAPITAL, + VK_CAPITAL, key_state, caps_lock_on, num_lock_on, - locale_id as HKL, + locale_id, &mut layouts, ); if let Some(event) = event { @@ -354,20 +342,24 @@ impl KeyEventBuilder { } let do_non_modifier = |key_events: &mut Vec<_>, layouts: &mut _| { for vk in 0..256 { + let vk = vk as VIRTUAL_KEY; match vk { - winuser::VK_CONTROL - | winuser::VK_LCONTROL - | winuser::VK_RCONTROL - | winuser::VK_SHIFT - | winuser::VK_LSHIFT - | winuser::VK_RSHIFT - | winuser::VK_MENU - | winuser::VK_LMENU - | winuser::VK_RMENU - | winuser::VK_CAPITAL => continue, + _ if vk == VK_CONTROL + || vk == VK_LCONTROL + || vk == VK_RCONTROL + || vk == VK_SHIFT + || vk == VK_LSHIFT + || vk == VK_RSHIFT + || vk == VK_MENU + || vk == VK_LMENU + || vk == VK_RMENU + || vk == VK_CAPITAL => + { + continue + } _ => (), } - if !is_key_pressed!(vk) { + if !is_key_pressed(vk) { continue; } let event = self.create_synthetic( @@ -384,16 +376,16 @@ impl KeyEventBuilder { } }; let do_modifier = |key_events: &mut Vec<_>, layouts: &mut _| { - const CLEAR_MODIFIER_VKS: [i32; 6] = [ - winuser::VK_LCONTROL, - winuser::VK_LSHIFT, - winuser::VK_LMENU, - winuser::VK_RCONTROL, - winuser::VK_RSHIFT, - winuser::VK_RMENU, + const CLEAR_MODIFIER_VKS: [VIRTUAL_KEY; 6] = [ + VK_LCONTROL, + VK_LSHIFT, + VK_LMENU, + VK_RCONTROL, + VK_RSHIFT, + VK_RMENU, ]; for vk in CLEAR_MODIFIER_VKS.iter() { - if is_key_pressed!(*vk) { + if is_key_pressed(*vk) { let event = self.create_synthetic( *vk, key_state, @@ -428,15 +420,14 @@ impl KeyEventBuilder { fn create_synthetic( &self, - vk: i32, + vk: VIRTUAL_KEY, key_state: ElementState, caps_lock_on: bool, num_lock_on: bool, locale_id: HKL, layouts: &mut MutexGuard<'_, LayoutCache>, ) -> Option { - let scancode = - unsafe { winuser::MapVirtualKeyExW(vk as UINT, winuser::MAPVK_VK_TO_VSC_EX, locale_id) }; + let scancode = unsafe { MapVirtualKeyExW(u32::from(vk), MAPVK_VK_TO_VSC_EX, locale_id) }; if scancode == 0 { return None; } @@ -447,7 +438,7 @@ impl KeyEventBuilder { } else { WindowsModifiers::empty() }; - let layout = layouts.layouts.get(&(locale_id as u64)).unwrap(); + let layout = layouts.layouts.get(&locale_id.0).unwrap(); let logical_key = layout.get_key(mods, num_lock_on, vk, scancode, code); let key_without_modifiers = layout.get_key(WindowsModifiers::empty(), false, vk, scancode, code); @@ -498,7 +489,7 @@ enum PartialLogicalKey { } struct PartialKeyEventInfo { - vkey: c_int, + vkey: VIRTUAL_KEY, scancode: ExScancode, key_state: ElementState, is_repeat: bool, @@ -527,24 +518,22 @@ impl PartialKeyEventInfo { let (_, layout) = layouts.get_current_layout(); let lparam_struct = destructure_key_lparam(lparam); let scancode; - let vkey = wparam as c_int; + let vkey = wparam.0 as VIRTUAL_KEY; if lparam_struct.scancode == 0 { // In some cases (often with media keys) the device reports a scancode of 0 but a // valid virtual key. In these cases we obtain the scancode from the virtual key. - scancode = unsafe { - winuser::MapVirtualKeyExW(vkey as u32, winuser::MAPVK_VK_TO_VSC_EX, layout.hkl as HKL) - as u16 - }; + scancode = + unsafe { MapVirtualKeyExW(u32::from(vkey), MAPVK_VK_TO_VSC_EX, layout.hkl) as u16 }; } else { scancode = new_ex_scancode(lparam_struct.scancode, lparam_struct.extended); } let code = KeyCode::from_scancode(scancode as u32); - let location = get_location(scancode, layout.hkl as HKL); + let location = get_location(scancode, layout.hkl); let kbd_state = get_kbd_state(); let mods = WindowsModifiers::active_modifiers(&kbd_state); let mods_without_ctrl = mods.remove_only_ctrl(); - let num_lock_on = kbd_state[winuser::VK_NUMLOCK as usize] & 1 != 0; + let num_lock_on = kbd_state[VK_NUMLOCK as usize] & 1 != 0; // On Windows Ctrl+NumLock = Pause (and apparently Ctrl+Pause -> NumLock). In these cases // the KeyCode still stores the real key, so in the name of consistency across platforms, we @@ -680,11 +669,11 @@ struct KeyLParam { } fn destructure_key_lparam(lparam: LPARAM) -> KeyLParam { - let previous_state = (lparam >> 30) & 0x01; - let transition_state = (lparam >> 31) & 0x01; + let previous_state = (lparam.0 >> 30) & 0x01; + let transition_state = (lparam.0 >> 31) & 0x01; KeyLParam { - scancode: ((lparam >> 16) & 0xFF) as u8, - extended: ((lparam >> 24) & 0x01) != 0, + scancode: ((lparam.0 >> 16) & 0xFF) as u8, + extended: ((lparam.0 >> 24) & 0x01) != 0, is_repeat: (previous_state ^ transition_state) != 0, } } @@ -705,7 +694,7 @@ fn ex_scancode_from_lparam(lparam: LPARAM) -> ExScancode { fn get_kbd_state() -> [u8; 256] { unsafe { let mut kbd_state: MaybeUninit<[u8; 256]> = MaybeUninit::uninit(); - winuser::GetKeyboardState(kbd_state.as_mut_ptr() as *mut u8); + GetKeyboardState(kbd_state.as_mut_ptr() as *mut u8); kbd_state.assume_init() } } @@ -714,19 +703,21 @@ fn get_kbd_state() -> [u8; 256] { /// been removed from the event queue. See also: get_kbd_state fn get_async_kbd_state() -> [u8; 256] { unsafe { - let mut kbd_state: [u8; 256] = MaybeUninit::uninit().assume_init(); + let mut kbd_state: [u8; 256] = [0; 256]; for (vk, state) in kbd_state.iter_mut().enumerate() { - let vk = vk as c_int; - let async_state = winuser::GetAsyncKeyState(vk as c_int); + let vk = vk as VIRTUAL_KEY; + let async_state = GetAsyncKeyState(i32::from(vk)); let is_down = (async_state & (1 << 15)) != 0; - *state = if is_down { 0x80 } else { 0 }; + if is_down { + *state = 0x80; + } if matches!( vk, - winuser::VK_CAPITAL | winuser::VK_NUMLOCK | winuser::VK_SCROLL + win32km::VK_CAPITAL | win32km::VK_NUMLOCK | win32km::VK_SCROLL ) { // Toggle states aren't reported by `GetAsyncKeyState` - let toggle_state = winuser::GetKeyState(vk); + let toggle_state = GetKeyState(i32::from(vk)); let is_active = (toggle_state & 1) != 0; *state |= if is_active { 1 } else { 0 }; } @@ -741,11 +732,7 @@ fn get_async_kbd_state() -> [u8; 256] { /// every AltGr key-press (and key-release). We check if the current event is a Ctrl event and if /// the next event is a right Alt (AltGr) event. If this is the case, the current event must be the /// fake Ctrl event. -fn is_current_fake( - curr_info: &PartialKeyEventInfo, - next_msg: winuser::MSG, - layout: &Layout, -) -> bool { +fn is_current_fake(curr_info: &PartialKeyEventInfo, next_msg: MSG, layout: &Layout) -> bool { let curr_is_ctrl = matches!(curr_info.logical_key, PartialLogicalKey::This(Key::Control)); if layout.has_alt_graph { let next_code = ex_scancode_from_lparam(next_msg.lParam); @@ -758,133 +745,174 @@ fn is_current_fake( } fn get_location(scancode: ExScancode, hkl: HKL) -> KeyLocation { - use winuser::*; - const VK_ABNT_C2: c_int = 0xc2; + const VK_ABNT_C2: VIRTUAL_KEY = win32km::VK_ABNT_C2 as VIRTUAL_KEY; let extension = 0xE000; let extended = (scancode & extension) == extension; - let vkey = - unsafe { winuser::MapVirtualKeyExW(scancode as u32, winuser::MAPVK_VSC_TO_VK_EX, hkl) as i32 }; + let vkey = unsafe { MapVirtualKeyExW(scancode as u32, MAPVK_VSC_TO_VK_EX, hkl) as u16 }; // Use the native VKEY and the extended flag to cover most cases // This is taken from the `druid` GUI library, specifically // druid-shell/src/platform/windows/keyboard.rs match vkey { - VK_LSHIFT | VK_LCONTROL | VK_LMENU | VK_LWIN => KeyLocation::Left, - VK_RSHIFT | VK_RCONTROL | VK_RMENU | VK_RWIN => KeyLocation::Right, - VK_RETURN if extended => KeyLocation::Numpad, - VK_INSERT | VK_DELETE | VK_END | VK_DOWN | VK_NEXT | VK_LEFT | VK_CLEAR | VK_RIGHT - | VK_HOME | VK_UP | VK_PRIOR => { + win32km::VK_LSHIFT | win32km::VK_LCONTROL | win32km::VK_LMENU | win32km::VK_LWIN => { + KeyLocation::Left + } + win32km::VK_RSHIFT | win32km::VK_RCONTROL | win32km::VK_RMENU | win32km::VK_RWIN => { + KeyLocation::Right + } + win32km::VK_RETURN if extended => KeyLocation::Numpad, + win32km::VK_INSERT + | win32km::VK_DELETE + | win32km::VK_END + | win32km::VK_DOWN + | win32km::VK_NEXT + | win32km::VK_LEFT + | win32km::VK_CLEAR + | win32km::VK_RIGHT + | win32km::VK_HOME + | win32km::VK_UP + | win32km::VK_PRIOR => { if extended { KeyLocation::Standard } else { KeyLocation::Numpad } } - VK_NUMPAD0 | VK_NUMPAD1 | VK_NUMPAD2 | VK_NUMPAD3 | VK_NUMPAD4 | VK_NUMPAD5 | VK_NUMPAD6 - | VK_NUMPAD7 | VK_NUMPAD8 | VK_NUMPAD9 | VK_DECIMAL | VK_DIVIDE | VK_MULTIPLY | VK_SUBTRACT - | VK_ADD | VK_ABNT_C2 => KeyLocation::Numpad, + win32km::VK_NUMPAD0 + | win32km::VK_NUMPAD1 + | win32km::VK_NUMPAD2 + | win32km::VK_NUMPAD3 + | win32km::VK_NUMPAD4 + | win32km::VK_NUMPAD5 + | win32km::VK_NUMPAD6 + | win32km::VK_NUMPAD7 + | win32km::VK_NUMPAD8 + | win32km::VK_NUMPAD9 + | win32km::VK_DECIMAL + | win32km::VK_DIVIDE + | win32km::VK_MULTIPLY + | win32km::VK_SUBTRACT + | win32km::VK_ADD + | VK_ABNT_C2 => KeyLocation::Numpad, _ => KeyLocation::Standard, } } // used to build accelerators table from Key -pub(crate) fn key_to_vk(key: &KeyCode) -> Option { +pub(crate) fn key_to_vk(key: &KeyCode) -> Option { Some(match key { - KeyCode::KeyA => unsafe { winuser::VkKeyScanW('a' as u16) as i32 }, - KeyCode::KeyB => unsafe { winuser::VkKeyScanW('b' as u16) as i32 }, - KeyCode::KeyC => unsafe { winuser::VkKeyScanW('c' as u16) as i32 }, - KeyCode::KeyD => unsafe { winuser::VkKeyScanW('d' as u16) as i32 }, - KeyCode::KeyE => unsafe { winuser::VkKeyScanW('e' as u16) as i32 }, - KeyCode::KeyF => unsafe { winuser::VkKeyScanW('f' as u16) as i32 }, - KeyCode::KeyG => unsafe { winuser::VkKeyScanW('g' as u16) as i32 }, - KeyCode::KeyH => unsafe { winuser::VkKeyScanW('h' as u16) as i32 }, - KeyCode::KeyI => unsafe { winuser::VkKeyScanW('i' as u16) as i32 }, - KeyCode::KeyJ => unsafe { winuser::VkKeyScanW('j' as u16) as i32 }, - KeyCode::KeyK => unsafe { winuser::VkKeyScanW('k' as u16) as i32 }, - KeyCode::KeyL => unsafe { winuser::VkKeyScanW('l' as u16) as i32 }, - KeyCode::KeyM => unsafe { winuser::VkKeyScanW('m' as u16) as i32 }, - KeyCode::KeyN => unsafe { winuser::VkKeyScanW('n' as u16) as i32 }, - KeyCode::KeyO => unsafe { winuser::VkKeyScanW('o' as u16) as i32 }, - KeyCode::KeyP => unsafe { winuser::VkKeyScanW('p' as u16) as i32 }, - KeyCode::KeyQ => unsafe { winuser::VkKeyScanW('q' as u16) as i32 }, - KeyCode::KeyR => unsafe { winuser::VkKeyScanW('r' as u16) as i32 }, - KeyCode::KeyS => unsafe { winuser::VkKeyScanW('s' as u16) as i32 }, - KeyCode::KeyT => unsafe { winuser::VkKeyScanW('t' as u16) as i32 }, - KeyCode::KeyU => unsafe { winuser::VkKeyScanW('u' as u16) as i32 }, - KeyCode::KeyV => unsafe { winuser::VkKeyScanW('v' as u16) as i32 }, - KeyCode::KeyW => unsafe { winuser::VkKeyScanW('w' as u16) as i32 }, - KeyCode::KeyX => unsafe { winuser::VkKeyScanW('x' as u16) as i32 }, - KeyCode::KeyY => unsafe { winuser::VkKeyScanW('y' as u16) as i32 }, - KeyCode::KeyZ => unsafe { winuser::VkKeyScanW('z' as u16) as i32 }, - KeyCode::Digit0 => unsafe { winuser::VkKeyScanW('0' as u16) as i32 }, - KeyCode::Digit1 => unsafe { winuser::VkKeyScanW('1' as u16) as i32 }, - KeyCode::Digit2 => unsafe { winuser::VkKeyScanW('2' as u16) as i32 }, - KeyCode::Digit3 => unsafe { winuser::VkKeyScanW('3' as u16) as i32 }, - KeyCode::Digit4 => unsafe { winuser::VkKeyScanW('4' as u16) as i32 }, - KeyCode::Digit5 => unsafe { winuser::VkKeyScanW('5' as u16) as i32 }, - KeyCode::Digit6 => unsafe { winuser::VkKeyScanW('6' as u16) as i32 }, - KeyCode::Digit7 => unsafe { winuser::VkKeyScanW('7' as u16) as i32 }, - KeyCode::Digit8 => unsafe { winuser::VkKeyScanW('8' as u16) as i32 }, - KeyCode::Digit9 => unsafe { winuser::VkKeyScanW('9' as u16) as i32 }, - KeyCode::Backspace => winuser::VK_BACK, - KeyCode::Tab => winuser::VK_TAB, - KeyCode::Enter => winuser::VK_RETURN, - KeyCode::Pause => winuser::VK_PAUSE, - KeyCode::CapsLock => winuser::VK_CAPITAL, - KeyCode::KanaMode => winuser::VK_KANA, - KeyCode::Escape => winuser::VK_ESCAPE, - KeyCode::NonConvert => winuser::VK_NONCONVERT, - KeyCode::PageUp => winuser::VK_PRIOR, - KeyCode::PageDown => winuser::VK_NEXT, - KeyCode::End => winuser::VK_END, - KeyCode::Home => winuser::VK_HOME, - KeyCode::ArrowLeft => winuser::VK_LEFT, - KeyCode::ArrowUp => winuser::VK_UP, - KeyCode::ArrowRight => winuser::VK_RIGHT, - KeyCode::ArrowDown => winuser::VK_DOWN, - KeyCode::PrintScreen => winuser::VK_SNAPSHOT, - KeyCode::Insert => winuser::VK_INSERT, - KeyCode::Delete => winuser::VK_DELETE, - KeyCode::Help => winuser::VK_HELP, - KeyCode::ContextMenu => winuser::VK_APPS, - KeyCode::F1 => winuser::VK_F1, - KeyCode::F2 => winuser::VK_F2, - KeyCode::F3 => winuser::VK_F3, - KeyCode::F4 => winuser::VK_F4, - KeyCode::F5 => winuser::VK_F5, - KeyCode::F6 => winuser::VK_F6, - KeyCode::F7 => winuser::VK_F7, - KeyCode::F8 => winuser::VK_F8, - KeyCode::F9 => winuser::VK_F9, - KeyCode::F10 => winuser::VK_F10, - KeyCode::F11 => winuser::VK_F11, - KeyCode::F12 => winuser::VK_F12, - KeyCode::F13 => winuser::VK_F13, - KeyCode::F14 => winuser::VK_F14, - KeyCode::F15 => winuser::VK_F15, - KeyCode::F16 => winuser::VK_F16, - KeyCode::F17 => winuser::VK_F17, - KeyCode::F18 => winuser::VK_F18, - KeyCode::F19 => winuser::VK_F19, - KeyCode::NumLock => winuser::VK_NUMLOCK, - KeyCode::ScrollLock => winuser::VK_SCROLL, - KeyCode::BrowserBack => winuser::VK_BROWSER_BACK, - KeyCode::BrowserForward => winuser::VK_BROWSER_FORWARD, - KeyCode::BrowserRefresh => winuser::VK_BROWSER_REFRESH, - KeyCode::BrowserStop => winuser::VK_BROWSER_STOP, - KeyCode::BrowserSearch => winuser::VK_BROWSER_SEARCH, - KeyCode::BrowserFavorites => winuser::VK_BROWSER_FAVORITES, - KeyCode::BrowserHome => winuser::VK_BROWSER_HOME, - KeyCode::AudioVolumeMute => winuser::VK_VOLUME_MUTE, - KeyCode::AudioVolumeDown => winuser::VK_VOLUME_DOWN, - KeyCode::AudioVolumeUp => winuser::VK_VOLUME_UP, - KeyCode::MediaTrackNext => winuser::VK_MEDIA_NEXT_TRACK, - KeyCode::MediaTrackPrevious => winuser::VK_MEDIA_PREV_TRACK, - KeyCode::MediaStop => winuser::VK_MEDIA_STOP, - KeyCode::MediaPlayPause => winuser::VK_MEDIA_PLAY_PAUSE, - KeyCode::LaunchMail => winuser::VK_LAUNCH_MAIL, - KeyCode::Convert => winuser::VK_CONVERT, + KeyCode::KeyA => unsafe { VkKeyScanW('a' as u16) as VIRTUAL_KEY }, + KeyCode::KeyB => unsafe { VkKeyScanW('b' as u16) as VIRTUAL_KEY }, + KeyCode::KeyC => unsafe { VkKeyScanW('c' as u16) as VIRTUAL_KEY }, + KeyCode::KeyD => unsafe { VkKeyScanW('d' as u16) as VIRTUAL_KEY }, + KeyCode::KeyE => unsafe { VkKeyScanW('e' as u16) as VIRTUAL_KEY }, + KeyCode::KeyF => unsafe { VkKeyScanW('f' as u16) as VIRTUAL_KEY }, + KeyCode::KeyG => unsafe { VkKeyScanW('g' as u16) as VIRTUAL_KEY }, + KeyCode::KeyH => unsafe { VkKeyScanW('h' as u16) as VIRTUAL_KEY }, + KeyCode::KeyI => unsafe { VkKeyScanW('i' as u16) as VIRTUAL_KEY }, + KeyCode::KeyJ => unsafe { VkKeyScanW('j' as u16) as VIRTUAL_KEY }, + KeyCode::KeyK => unsafe { VkKeyScanW('k' as u16) as VIRTUAL_KEY }, + KeyCode::KeyL => unsafe { VkKeyScanW('l' as u16) as VIRTUAL_KEY }, + KeyCode::KeyM => unsafe { VkKeyScanW('m' as u16) as VIRTUAL_KEY }, + KeyCode::KeyN => unsafe { VkKeyScanW('n' as u16) as VIRTUAL_KEY }, + KeyCode::KeyO => unsafe { VkKeyScanW('o' as u16) as VIRTUAL_KEY }, + KeyCode::KeyP => unsafe { VkKeyScanW('p' as u16) as VIRTUAL_KEY }, + KeyCode::KeyQ => unsafe { VkKeyScanW('q' as u16) as VIRTUAL_KEY }, + KeyCode::KeyR => unsafe { VkKeyScanW('r' as u16) as VIRTUAL_KEY }, + KeyCode::KeyS => unsafe { VkKeyScanW('s' as u16) as VIRTUAL_KEY }, + KeyCode::KeyT => unsafe { VkKeyScanW('t' as u16) as VIRTUAL_KEY }, + KeyCode::KeyU => unsafe { VkKeyScanW('u' as u16) as VIRTUAL_KEY }, + KeyCode::KeyV => unsafe { VkKeyScanW('v' as u16) as VIRTUAL_KEY }, + KeyCode::KeyW => unsafe { VkKeyScanW('w' as u16) as VIRTUAL_KEY }, + KeyCode::KeyX => unsafe { VkKeyScanW('x' as u16) as VIRTUAL_KEY }, + KeyCode::KeyY => unsafe { VkKeyScanW('y' as u16) as VIRTUAL_KEY }, + KeyCode::KeyZ => unsafe { VkKeyScanW('z' as u16) as VIRTUAL_KEY }, + KeyCode::Digit0 => unsafe { VkKeyScanW('0' as u16) as VIRTUAL_KEY }, + KeyCode::Digit1 => unsafe { VkKeyScanW('1' as u16) as VIRTUAL_KEY }, + KeyCode::Digit2 => unsafe { VkKeyScanW('2' as u16) as VIRTUAL_KEY }, + KeyCode::Digit3 => unsafe { VkKeyScanW('3' as u16) as VIRTUAL_KEY }, + KeyCode::Digit4 => unsafe { VkKeyScanW('4' as u16) as VIRTUAL_KEY }, + KeyCode::Digit5 => unsafe { VkKeyScanW('5' as u16) as VIRTUAL_KEY }, + KeyCode::Digit6 => unsafe { VkKeyScanW('6' as u16) as VIRTUAL_KEY }, + KeyCode::Digit7 => unsafe { VkKeyScanW('7' as u16) as VIRTUAL_KEY }, + KeyCode::Digit8 => unsafe { VkKeyScanW('8' as u16) as VIRTUAL_KEY }, + KeyCode::Digit9 => unsafe { VkKeyScanW('9' as u16) as VIRTUAL_KEY }, + KeyCode::Comma => VK_OEM_COMMA, + KeyCode::Minus => VK_OEM_MINUS, + KeyCode::Period => VK_OEM_PERIOD, + KeyCode::Equal => unsafe { VkKeyScanW('=' as u16) as VIRTUAL_KEY }, + KeyCode::Semicolon => unsafe { VkKeyScanW(';' as u16) as VIRTUAL_KEY }, + KeyCode::Slash => unsafe { VkKeyScanW('/' as u16) as VIRTUAL_KEY }, + KeyCode::Backslash => unsafe { VkKeyScanW('\\' as u16) as VIRTUAL_KEY }, + KeyCode::Quote => unsafe { VkKeyScanW('\'' as u16) as VIRTUAL_KEY }, + KeyCode::Backquote => unsafe { VkKeyScanW('`' as u16) as VIRTUAL_KEY }, + KeyCode::BracketLeft => unsafe { VkKeyScanW('[' as u16) as VIRTUAL_KEY }, + KeyCode::BracketRight => unsafe { VkKeyScanW(']' as u16) as VIRTUAL_KEY }, + KeyCode::Backspace => VK_BACK, + KeyCode::Tab => VK_TAB, + KeyCode::Space => VK_SPACE, + KeyCode::Enter => VK_RETURN, + KeyCode::Pause => VK_PAUSE, + KeyCode::CapsLock => VK_CAPITAL, + KeyCode::KanaMode => VK_KANA, + KeyCode::Escape => VK_ESCAPE, + KeyCode::NonConvert => VK_NONCONVERT, + KeyCode::PageUp => VK_PRIOR, + KeyCode::PageDown => VK_NEXT, + KeyCode::End => VK_END, + KeyCode::Home => VK_HOME, + KeyCode::ArrowLeft => VK_LEFT, + KeyCode::ArrowUp => VK_UP, + KeyCode::ArrowRight => VK_RIGHT, + KeyCode::ArrowDown => VK_DOWN, + KeyCode::PrintScreen => VK_SNAPSHOT, + KeyCode::Insert => VK_INSERT, + KeyCode::Delete => VK_DELETE, + KeyCode::Help => VK_HELP, + KeyCode::ContextMenu => VK_APPS, + KeyCode::F1 => VK_F1, + KeyCode::F2 => VK_F2, + KeyCode::F3 => VK_F3, + KeyCode::F4 => VK_F4, + KeyCode::F5 => VK_F5, + KeyCode::F6 => VK_F6, + KeyCode::F7 => VK_F7, + KeyCode::F8 => VK_F8, + KeyCode::F9 => VK_F9, + KeyCode::F10 => VK_F10, + KeyCode::F11 => VK_F11, + KeyCode::F12 => VK_F12, + KeyCode::F13 => VK_F13, + KeyCode::F14 => VK_F14, + KeyCode::F15 => VK_F15, + KeyCode::F16 => VK_F16, + KeyCode::F17 => VK_F17, + KeyCode::F18 => VK_F18, + KeyCode::F19 => VK_F19, + KeyCode::F20 => VK_F20, + KeyCode::F21 => VK_F21, + KeyCode::F22 => VK_F22, + KeyCode::F23 => VK_F23, + KeyCode::F24 => VK_F24, + KeyCode::NumLock => VK_NUMLOCK, + KeyCode::ScrollLock => VK_SCROLL, + KeyCode::BrowserBack => VK_BROWSER_BACK, + KeyCode::BrowserForward => VK_BROWSER_FORWARD, + KeyCode::BrowserRefresh => VK_BROWSER_REFRESH, + KeyCode::BrowserStop => VK_BROWSER_STOP, + KeyCode::BrowserSearch => VK_BROWSER_SEARCH, + KeyCode::BrowserFavorites => VK_BROWSER_FAVORITES, + KeyCode::BrowserHome => VK_BROWSER_HOME, + KeyCode::AudioVolumeMute => VK_VOLUME_MUTE, + KeyCode::AudioVolumeDown => VK_VOLUME_DOWN, + KeyCode::AudioVolumeUp => VK_VOLUME_UP, + KeyCode::MediaTrackNext => VK_MEDIA_NEXT_TRACK, + KeyCode::MediaTrackPrevious => VK_MEDIA_PREV_TRACK, + KeyCode::MediaStop => VK_MEDIA_STOP, + KeyCode::MediaPlayPause => VK_MEDIA_PLAY_PAUSE, + KeyCode::LaunchMail => VK_LAUNCH_MAIL, + KeyCode::Convert => VK_CONVERT, _ => return None, }) } diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index b4f15cfb01..f02e29e424 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -7,43 +7,47 @@ use std::{ use lazy_static::lazy_static; -use winapi::{ - ctypes::c_int, - shared::minwindef::{HKL, LOWORD}, - um::{ - winnt::{LANG_JAPANESE, LANG_KOREAN, PRIMARYLANGID}, - winuser, +use windows::Win32::{ + Foundation::PWSTR, + System::SystemServices::{LANG_JAPANESE, LANG_KOREAN}, + UI::{ + Input::KeyboardAndMouse::{self as win32km, *}, + TextServices::HKL, + WindowsAndMessaging::*, }, }; use super::keyboard::ExScancode; -use crate::keyboard::{Key, KeyCode, ModifiersState, NativeKeyCode}; +use crate::{ + keyboard::{Key, KeyCode, ModifiersState, NativeKeyCode}, + platform_impl::platform::util, +}; lazy_static! { pub(crate) static ref LAYOUT_CACHE: Mutex = Mutex::new(LayoutCache::default()); } -fn key_pressed(vkey: c_int) -> bool { - unsafe { (winuser::GetKeyState(vkey) & (1 << 15)) == (1 << 15) } +fn key_pressed(vkey: VIRTUAL_KEY) -> bool { + unsafe { (GetKeyState(u32::from(vkey) as i32) & (1 << 15)) == (1 << 15) } } -const NUMPAD_VKEYS: [c_int; 16] = [ - winuser::VK_NUMPAD0, - winuser::VK_NUMPAD1, - winuser::VK_NUMPAD2, - winuser::VK_NUMPAD3, - winuser::VK_NUMPAD4, - winuser::VK_NUMPAD5, - winuser::VK_NUMPAD6, - winuser::VK_NUMPAD7, - winuser::VK_NUMPAD8, - winuser::VK_NUMPAD9, - winuser::VK_MULTIPLY, - winuser::VK_ADD, - winuser::VK_SEPARATOR, - winuser::VK_SUBTRACT, - winuser::VK_DECIMAL, - winuser::VK_DIVIDE, +const NUMPAD_VKEYS: [VIRTUAL_KEY; 16] = [ + VK_NUMPAD0, + VK_NUMPAD1, + VK_NUMPAD2, + VK_NUMPAD3, + VK_NUMPAD4, + VK_NUMPAD5, + VK_NUMPAD6, + VK_NUMPAD7, + VK_NUMPAD8, + VK_NUMPAD9, + VK_MULTIPLY, + VK_ADD, + VK_SEPARATOR, + VK_SUBTRACT, + VK_DECIMAL, + VK_DIVIDE, ]; lazy_static! { @@ -81,19 +85,19 @@ bitflags! { impl WindowsModifiers { pub fn active_modifiers(key_state: &[u8; 256]) -> WindowsModifiers { - let shift = key_state[winuser::VK_SHIFT as usize] & 0x80 != 0; - let lshift = key_state[winuser::VK_LSHIFT as usize] & 0x80 != 0; - let rshift = key_state[winuser::VK_RSHIFT as usize] & 0x80 != 0; + let shift = key_state[usize::from(VK_SHIFT)] & 0x80 != 0; + let lshift = key_state[usize::from(VK_LSHIFT)] & 0x80 != 0; + let rshift = key_state[usize::from(VK_RSHIFT)] & 0x80 != 0; - let control = key_state[winuser::VK_CONTROL as usize] & 0x80 != 0; - let lcontrol = key_state[winuser::VK_LCONTROL as usize] & 0x80 != 0; - let rcontrol = key_state[winuser::VK_RCONTROL as usize] & 0x80 != 0; + let control = key_state[usize::from(VK_CONTROL)] & 0x80 != 0; + let lcontrol = key_state[usize::from(VK_LCONTROL)] & 0x80 != 0; + let rcontrol = key_state[usize::from(VK_RCONTROL)] & 0x80 != 0; - let alt = key_state[winuser::VK_MENU as usize] & 0x80 != 0; - let lalt = key_state[winuser::VK_LMENU as usize] & 0x80 != 0; - let ralt = key_state[winuser::VK_RMENU as usize] & 0x80 != 0; + let alt = key_state[usize::from(VK_MENU)] & 0x80 != 0; + let lalt = key_state[usize::from(VK_LMENU)] & 0x80 != 0; + let ralt = key_state[usize::from(VK_RMENU)] & 0x80 != 0; - let caps = key_state[winuser::VK_CAPITAL as usize] & 0x01 != 0; + let caps = key_state[usize::from(VK_CAPITAL)] & 0x01 != 0; let mut result = WindowsModifiers::empty(); if shift || lshift || rshift { @@ -114,30 +118,30 @@ impl WindowsModifiers { pub fn apply_to_kbd_state(self, key_state: &mut [u8; 256]) { if self.intersects(Self::SHIFT) { - key_state[winuser::VK_SHIFT as usize] |= 0x80; + key_state[usize::from(VK_SHIFT)] |= 0x80; } else { - key_state[winuser::VK_SHIFT as usize] &= !0x80; - key_state[winuser::VK_LSHIFT as usize] &= !0x80; - key_state[winuser::VK_RSHIFT as usize] &= !0x80; + key_state[usize::from(VK_SHIFT)] &= !0x80; + key_state[usize::from(VK_LSHIFT)] &= !0x80; + key_state[usize::from(VK_RSHIFT)] &= !0x80; } if self.intersects(Self::CONTROL) { - key_state[winuser::VK_CONTROL as usize] |= 0x80; + key_state[usize::from(VK_CONTROL)] |= 0x80; } else { - key_state[winuser::VK_CONTROL as usize] &= !0x80; - key_state[winuser::VK_LCONTROL as usize] &= !0x80; - key_state[winuser::VK_RCONTROL as usize] &= !0x80; + key_state[usize::from(VK_CONTROL)] &= !0x80; + key_state[usize::from(VK_LCONTROL)] &= !0x80; + key_state[usize::from(VK_RCONTROL)] &= !0x80; } if self.intersects(Self::ALT) { - key_state[winuser::VK_MENU as usize] |= 0x80; + key_state[usize::from(VK_MENU)] |= 0x80; } else { - key_state[winuser::VK_MENU as usize] &= !0x80; - key_state[winuser::VK_LMENU as usize] &= !0x80; - key_state[winuser::VK_RMENU as usize] &= !0x80; + key_state[usize::from(VK_MENU)] &= !0x80; + key_state[usize::from(VK_LMENU)] &= !0x80; + key_state[usize::from(VK_RMENU)] &= !0x80; } if self.intersects(Self::CAPS_LOCK) { - key_state[winuser::VK_CAPITAL as usize] |= 0x01; + key_state[usize::from(VK_CAPITAL)] |= 0x01; } else { - key_state[winuser::VK_CAPITAL as usize] &= !0x01; + key_state[usize::from(VK_CAPITAL)] &= !0x01; } } @@ -153,7 +157,7 @@ impl WindowsModifiers { } pub(crate) struct Layout { - pub hkl: u64, + pub hkl: HKL, /// Maps numpad keys from Windows virtual key to a `Key`. /// @@ -163,10 +167,10 @@ pub(crate) struct Layout { /// /// Making this field separate from the `keys` field saves having to add NumLock as a modifier /// to `WindowsModifiers`, which would double the number of items in keys. - pub numlock_on_keys: HashMap>, + pub numlock_on_keys: HashMap>, /// Like `numlock_on_keys` but this will map to the key that would be produced if numlock was /// off. The keys of this map are identical to the keys of `numlock_on_keys`. - pub numlock_off_keys: HashMap>, + pub numlock_off_keys: HashMap>, /// Maps a modifier state to group of key strings /// We're not using `ModifiersState` here because that object cannot express caps lock, @@ -187,13 +191,13 @@ impl Layout { &self, mods: WindowsModifiers, num_lock_on: bool, - vkey: c_int, + vkey: VIRTUAL_KEY, scancode: ExScancode, keycode: KeyCode, ) -> Key<'static> { let native_code = NativeKeyCode::Windows(scancode); - let unknown_alt = vkey == winuser::VK_MENU; + let unknown_alt = vkey == VK_MENU; if !unknown_alt { // Here we try using the virtual key directly but if the virtual key doesn't distinguish // between left and right alt, we can't report AltGr. Therefore, we only do this if the @@ -213,11 +217,10 @@ impl Layout { if let Some(key) = self.numlock_on_keys.get(&vkey) { return key.clone(); } - } else { - if let Some(key) = self.numlock_off_keys.get(&vkey) { - return key.clone(); - } + } else if let Some(key) = self.numlock_off_keys.get(&vkey) { + return key.clone(); } + if let Some(keys) = self.keys.get(&mods) { if let Some(key) = keys.get(&keycode) { return key.clone(); @@ -230,7 +233,7 @@ impl Layout { #[derive(Default)] pub(crate) struct LayoutCache { /// Maps locale identifiers (HKL) to layouts - pub layouts: HashMap, + pub layouts: HashMap, pub strings: HashSet<&'static str>, } @@ -238,9 +241,9 @@ impl LayoutCache { /// Checks whether the current layout is already known and /// prepares the layout if it isn't known. /// The current layout is then returned. - pub fn get_current_layout<'a>(&'a mut self) -> (u64, &'a Layout) { - let locale_id = unsafe { winuser::GetKeyboardLayout(0) } as u64; - match self.layouts.entry(locale_id) { + pub fn get_current_layout<'a>(&'a mut self) -> (HKL, &'a Layout) { + let locale_id = unsafe { GetKeyboardLayout(0) }; + match self.layouts.entry(locale_id.0) { Entry::Occupied(entry) => (locale_id, entry.into_mut()), Entry::Vacant(entry) => { let layout = Self::prepare_layout(&mut self.strings, locale_id); @@ -251,25 +254,25 @@ impl LayoutCache { pub fn get_agnostic_mods(&mut self) -> ModifiersState { let (_, layout) = self.get_current_layout(); - let filter_out_altgr = layout.has_alt_graph && key_pressed(winuser::VK_RMENU); + let filter_out_altgr = layout.has_alt_graph && key_pressed(VK_RMENU); let mut mods = ModifiersState::empty(); - mods.set(ModifiersState::SHIFT, key_pressed(winuser::VK_SHIFT)); + mods.set(ModifiersState::SHIFT, key_pressed(VK_SHIFT)); mods.set( ModifiersState::CONTROL, - key_pressed(winuser::VK_CONTROL) && !filter_out_altgr, + key_pressed(VK_CONTROL) && !filter_out_altgr, ); mods.set( ModifiersState::ALT, - key_pressed(winuser::VK_MENU) && !filter_out_altgr, + key_pressed(VK_MENU) && !filter_out_altgr, ); mods.set( ModifiersState::SUPER, - key_pressed(winuser::VK_LWIN) || key_pressed(winuser::VK_RWIN), + key_pressed(VK_LWIN) || key_pressed(VK_RWIN), ); mods } - fn prepare_layout(strings: &mut HashSet<&'static str>, locale_id: u64) -> Layout { + fn prepare_layout(strings: &mut HashSet<&'static str>, locale_id: HKL) -> Layout { let mut layout = Layout { hkl: locale_id, numlock_on_keys: Default::default(), @@ -296,20 +299,20 @@ impl LayoutCache { // \/ \/ // map_value: Key <- map_vkey: VK layout.numlock_off_keys.reserve(NUMPAD_KEYCODES.len()); - for vk in 0..256 { + for vk in 0_u16..256 { let scancode = - unsafe { winuser::MapVirtualKeyExW(vk, winuser::MAPVK_VK_TO_VSC_EX, locale_id as HKL) }; + unsafe { MapVirtualKeyExW(u32::from(vk), MAPVK_VK_TO_VSC_EX, locale_id as HKL) }; if scancode == 0 { continue; } let keycode = KeyCode::from_scancode(scancode); - if !is_numpad_specific(vk as i32) && NUMPAD_KEYCODES.contains(&keycode) { + if !is_numpad_specific(vk) && NUMPAD_KEYCODES.contains(&keycode) { let native_code = NativeKeyCode::Windows(scancode as u16); let map_vkey = keycode_to_vkey(keycode, locale_id); if map_vkey == 0 { continue; } - let map_value = vkey_to_non_char_key(vk as i32, native_code, locale_id, false); + let map_value = vkey_to_non_char_key(vk, native_code, locale_id, false); if matches!(map_value, Key::Unidentified(_)) { continue; } @@ -319,15 +322,14 @@ impl LayoutCache { layout.numlock_on_keys.reserve(NUMPAD_VKEYS.len()); for vk in NUMPAD_VKEYS.iter() { - let vk = (*vk) as u32; let scancode = - unsafe { winuser::MapVirtualKeyExW(vk, winuser::MAPVK_VK_TO_VSC_EX, locale_id as HKL) }; - let unicode = Self::to_unicode_string(&key_state, vk, scancode, locale_id); + unsafe { MapVirtualKeyExW(u32::from(*vk), MAPVK_VK_TO_VSC_EX, locale_id as HKL) }; + let unicode = Self::to_unicode_string(&key_state, *vk, scancode, locale_id); if let ToUnicodeResult::Str(s) = unicode { let static_str = get_or_insert_str(strings, s); layout .numlock_on_keys - .insert(vk as i32, Key::Character(static_str)); + .insert(*vk, Key::Character(static_str)); } } @@ -343,20 +345,18 @@ impl LayoutCache { // This is reinforced by the fact that the keyboard state array has 256 // elements. This array is allowed to be indexed by virtual key values // giving the key state for the virtual key used for indexing. - for vk in 0..256 { - let scancode = - unsafe { winuser::MapVirtualKeyExW(vk, winuser::MAPVK_VK_TO_VSC_EX, locale_id as HKL) }; + for vk in 0_u16..256 { + let scancode = unsafe { MapVirtualKeyExW(u32::from(vk), MAPVK_VK_TO_VSC_EX, locale_id) }; if scancode == 0 { continue; } - let native_code = NativeKeyCode::Windows(scancode as ExScancode); let key_code = KeyCode::from_scancode(scancode); // Let's try to get the key from just the scancode and vk // We don't necessarily know yet if AltGraph is present on this layout so we'll // assume it isn't. Then we'll do a second pass where we set the "AltRight" keys to // "AltGr" in case we find out that there's an AltGraph. - let preliminary_key = vkey_to_non_char_key(vk as i32, native_code, locale_id, false); + let preliminary_key = vkey_to_non_char_key(vk, native_code, locale_id, false); match preliminary_key { Key::Unidentified(_) => (), _ => { @@ -372,7 +372,7 @@ impl LayoutCache { Key::Character(static_str) } ToUnicodeResult::Dead(dead_char) => { - //println!("{:?} - {:?} produced dead {:?}", key_code, mod_state, dead_char); + //#[cfg(debug_assertions)] println!("{:?} - {:?} produced dead {:?}", key_code, mod_state, dead_char); Key::Dead(dead_char) } ToUnicodeResult::None => { @@ -428,31 +428,31 @@ impl LayoutCache { fn to_unicode_string( key_state: &[u8; 256], - vkey: u32, + vkey: VIRTUAL_KEY, scancode: u32, - locale_id: u64, + locale_id: HKL, ) -> ToUnicodeResult { unsafe { let mut label_wide = [0u16; 8]; - let mut wide_len = winuser::ToUnicodeEx( - vkey, + let mut wide_len = ToUnicodeEx( + u32::from(vkey), scancode, (&key_state[0]) as *const _, - (&mut label_wide[0]) as *mut _, + PWSTR((&mut label_wide[0]) as *mut _), label_wide.len() as i32, 0, - locale_id as HKL, + locale_id, ); if wide_len < 0 { // If it's dead, we run `ToUnicode` again to consume the dead-key - wide_len = winuser::ToUnicodeEx( - vkey, + wide_len = ToUnicodeEx( + u32::from(vkey), scancode, (&key_state[0]) as *const _, - (&mut label_wide[0]) as *mut _, + PWSTR((&mut label_wide[0]) as *mut _), label_wide.len() as i32, 0, - locale_id as HKL, + locale_id, ); if wide_len > 0 { let os_string = OsString::from_wide(&label_wide[0..wide_len as usize]); @@ -498,230 +498,230 @@ enum ToUnicodeResult { None, } -fn is_numpad_specific(vk: i32) -> bool { - match vk { - winuser::VK_NUMPAD0 => true, - winuser::VK_NUMPAD1 => true, - winuser::VK_NUMPAD2 => true, - winuser::VK_NUMPAD3 => true, - winuser::VK_NUMPAD4 => true, - winuser::VK_NUMPAD5 => true, - winuser::VK_NUMPAD6 => true, - winuser::VK_NUMPAD7 => true, - winuser::VK_NUMPAD8 => true, - winuser::VK_NUMPAD9 => true, - winuser::VK_ADD => true, - winuser::VK_SUBTRACT => true, - winuser::VK_DIVIDE => true, - winuser::VK_DECIMAL => true, - winuser::VK_SEPARATOR => true, - _ => false, - } +fn is_numpad_specific(vk: VIRTUAL_KEY) -> bool { + matches!( + vk, + win32km::VK_NUMPAD0 + | win32km::VK_NUMPAD1 + | win32km::VK_NUMPAD2 + | win32km::VK_NUMPAD3 + | win32km::VK_NUMPAD4 + | win32km::VK_NUMPAD5 + | win32km::VK_NUMPAD6 + | win32km::VK_NUMPAD7 + | win32km::VK_NUMPAD8 + | win32km::VK_NUMPAD9 + | win32km::VK_ADD + | win32km::VK_SUBTRACT + | win32km::VK_DIVIDE + | win32km::VK_DECIMAL + | win32km::VK_SEPARATOR + ) } -fn keycode_to_vkey(keycode: KeyCode, hkl: u64) -> i32 { - let primary_lang_id = PRIMARYLANGID(LOWORD(hkl as u32)); +fn keycode_to_vkey(keycode: KeyCode, hkl: HKL) -> VIRTUAL_KEY { + let primary_lang_id = util::PRIMARYLANGID(hkl); let is_korean = primary_lang_id == LANG_KOREAN; let is_japanese = primary_lang_id == LANG_JAPANESE; match keycode { - KeyCode::Backquote => 0, - KeyCode::Backslash => 0, - KeyCode::BracketLeft => 0, - KeyCode::BracketRight => 0, - KeyCode::Comma => 0, - KeyCode::Digit0 => 0, - KeyCode::Digit1 => 0, - KeyCode::Digit2 => 0, - KeyCode::Digit3 => 0, - KeyCode::Digit4 => 0, - KeyCode::Digit5 => 0, - KeyCode::Digit6 => 0, - KeyCode::Digit7 => 0, - KeyCode::Digit8 => 0, - KeyCode::Digit9 => 0, - KeyCode::Equal => 0, - KeyCode::IntlBackslash => 0, - KeyCode::IntlRo => 0, - KeyCode::IntlYen => 0, - KeyCode::KeyA => 0, - KeyCode::KeyB => 0, - KeyCode::KeyC => 0, - KeyCode::KeyD => 0, - KeyCode::KeyE => 0, - KeyCode::KeyF => 0, - KeyCode::KeyG => 0, - KeyCode::KeyH => 0, - KeyCode::KeyI => 0, - KeyCode::KeyJ => 0, - KeyCode::KeyK => 0, - KeyCode::KeyL => 0, - KeyCode::KeyM => 0, - KeyCode::KeyN => 0, - KeyCode::KeyO => 0, - KeyCode::KeyP => 0, - KeyCode::KeyQ => 0, - KeyCode::KeyR => 0, - KeyCode::KeyS => 0, - KeyCode::KeyT => 0, - KeyCode::KeyU => 0, - KeyCode::KeyV => 0, - KeyCode::KeyW => 0, - KeyCode::KeyX => 0, - KeyCode::KeyY => 0, - KeyCode::KeyZ => 0, - KeyCode::Minus => 0, - KeyCode::Period => 0, - KeyCode::Quote => 0, - KeyCode::Semicolon => 0, - KeyCode::Slash => 0, - KeyCode::AltLeft => winuser::VK_LMENU, - KeyCode::AltRight => winuser::VK_RMENU, - KeyCode::Backspace => winuser::VK_BACK, - KeyCode::CapsLock => winuser::VK_CAPITAL, - KeyCode::ContextMenu => winuser::VK_APPS, - KeyCode::ControlLeft => winuser::VK_LCONTROL, - KeyCode::ControlRight => winuser::VK_RCONTROL, - KeyCode::Enter => winuser::VK_RETURN, - KeyCode::SuperLeft => winuser::VK_LWIN, - KeyCode::SuperRight => winuser::VK_RWIN, - KeyCode::ShiftLeft => winuser::VK_RSHIFT, - KeyCode::ShiftRight => winuser::VK_LSHIFT, - KeyCode::Space => winuser::VK_SPACE, - KeyCode::Tab => winuser::VK_TAB, - KeyCode::Convert => winuser::VK_CONVERT, - KeyCode::KanaMode => winuser::VK_KANA, - KeyCode::Lang1 if is_korean => winuser::VK_HANGUL, - KeyCode::Lang1 if is_japanese => winuser::VK_KANA, - KeyCode::Lang2 if is_korean => winuser::VK_HANJA, - KeyCode::Lang2 if is_japanese => 0, - KeyCode::Lang3 if is_japanese => winuser::VK_OEM_FINISH, - KeyCode::Lang4 if is_japanese => 0, - KeyCode::Lang5 if is_japanese => 0, - KeyCode::NonConvert => winuser::VK_NONCONVERT, - KeyCode::Delete => winuser::VK_DELETE, - KeyCode::End => winuser::VK_END, - KeyCode::Help => winuser::VK_HELP, - KeyCode::Home => winuser::VK_HOME, - KeyCode::Insert => winuser::VK_INSERT, - KeyCode::PageDown => winuser::VK_NEXT, - KeyCode::PageUp => winuser::VK_PRIOR, - KeyCode::ArrowDown => winuser::VK_DOWN, - KeyCode::ArrowLeft => winuser::VK_LEFT, - KeyCode::ArrowRight => winuser::VK_RIGHT, - KeyCode::ArrowUp => winuser::VK_UP, - KeyCode::NumLock => winuser::VK_NUMLOCK, - KeyCode::Numpad0 => winuser::VK_NUMPAD0, - KeyCode::Numpad1 => winuser::VK_NUMPAD1, - KeyCode::Numpad2 => winuser::VK_NUMPAD2, - KeyCode::Numpad3 => winuser::VK_NUMPAD3, - KeyCode::Numpad4 => winuser::VK_NUMPAD4, - KeyCode::Numpad5 => winuser::VK_NUMPAD5, - KeyCode::Numpad6 => winuser::VK_NUMPAD6, - KeyCode::Numpad7 => winuser::VK_NUMPAD7, - KeyCode::Numpad8 => winuser::VK_NUMPAD8, - KeyCode::Numpad9 => winuser::VK_NUMPAD9, - KeyCode::NumpadAdd => winuser::VK_ADD, - KeyCode::NumpadBackspace => winuser::VK_BACK, - KeyCode::NumpadClear => winuser::VK_CLEAR, - KeyCode::NumpadClearEntry => 0, - KeyCode::NumpadComma => winuser::VK_SEPARATOR, - KeyCode::NumpadDecimal => winuser::VK_DECIMAL, - KeyCode::NumpadDivide => winuser::VK_DIVIDE, - KeyCode::NumpadEnter => winuser::VK_RETURN, - KeyCode::NumpadEqual => 0, - KeyCode::NumpadHash => 0, - KeyCode::NumpadMemoryAdd => 0, - KeyCode::NumpadMemoryClear => 0, - KeyCode::NumpadMemoryRecall => 0, - KeyCode::NumpadMemoryStore => 0, - KeyCode::NumpadMemorySubtract => 0, - KeyCode::NumpadMultiply => winuser::VK_MULTIPLY, - KeyCode::NumpadParenLeft => 0, - KeyCode::NumpadParenRight => 0, - KeyCode::NumpadStar => 0, - KeyCode::NumpadSubtract => winuser::VK_SUBTRACT, - KeyCode::Escape => winuser::VK_ESCAPE, - KeyCode::Fn => 0, - KeyCode::FnLock => 0, - KeyCode::PrintScreen => winuser::VK_SNAPSHOT, - KeyCode::ScrollLock => winuser::VK_SCROLL, - KeyCode::Pause => winuser::VK_PAUSE, - KeyCode::BrowserBack => winuser::VK_BROWSER_BACK, - KeyCode::BrowserFavorites => winuser::VK_BROWSER_FAVORITES, - KeyCode::BrowserForward => winuser::VK_BROWSER_FORWARD, - KeyCode::BrowserHome => winuser::VK_BROWSER_HOME, - KeyCode::BrowserRefresh => winuser::VK_BROWSER_REFRESH, - KeyCode::BrowserSearch => winuser::VK_BROWSER_SEARCH, - KeyCode::BrowserStop => winuser::VK_BROWSER_STOP, - KeyCode::Eject => 0, - KeyCode::LaunchApp1 => winuser::VK_LAUNCH_APP1, - KeyCode::LaunchApp2 => winuser::VK_LAUNCH_APP2, - KeyCode::LaunchMail => winuser::VK_LAUNCH_MAIL, - KeyCode::MediaPlayPause => winuser::VK_MEDIA_PLAY_PAUSE, - KeyCode::MediaSelect => winuser::VK_LAUNCH_MEDIA_SELECT, - KeyCode::MediaStop => winuser::VK_MEDIA_STOP, - KeyCode::MediaTrackNext => winuser::VK_MEDIA_NEXT_TRACK, - KeyCode::MediaTrackPrevious => winuser::VK_MEDIA_PREV_TRACK, - KeyCode::Power => 0, - KeyCode::Sleep => 0, - KeyCode::AudioVolumeDown => winuser::VK_VOLUME_DOWN, - KeyCode::AudioVolumeMute => winuser::VK_VOLUME_MUTE, - KeyCode::AudioVolumeUp => winuser::VK_VOLUME_UP, - KeyCode::WakeUp => 0, - KeyCode::Hyper => 0, - KeyCode::Turbo => 0, - KeyCode::Abort => 0, - KeyCode::Resume => 0, - KeyCode::Suspend => 0, - KeyCode::Again => 0, - KeyCode::Copy => 0, - KeyCode::Cut => 0, - KeyCode::Find => 0, - KeyCode::Open => 0, - KeyCode::Paste => 0, - KeyCode::Props => 0, - KeyCode::Select => winuser::VK_SELECT, - KeyCode::Undo => 0, - KeyCode::Hiragana => 0, - KeyCode::Katakana => 0, - KeyCode::F1 => winuser::VK_F1, - KeyCode::F2 => winuser::VK_F2, - KeyCode::F3 => winuser::VK_F3, - KeyCode::F4 => winuser::VK_F4, - KeyCode::F5 => winuser::VK_F5, - KeyCode::F6 => winuser::VK_F6, - KeyCode::F7 => winuser::VK_F7, - KeyCode::F8 => winuser::VK_F8, - KeyCode::F9 => winuser::VK_F9, - KeyCode::F10 => winuser::VK_F10, - KeyCode::F11 => winuser::VK_F11, - KeyCode::F12 => winuser::VK_F12, - KeyCode::F13 => winuser::VK_F13, - KeyCode::F14 => winuser::VK_F14, - KeyCode::F15 => winuser::VK_F15, - KeyCode::F16 => winuser::VK_F16, - KeyCode::F17 => winuser::VK_F17, - KeyCode::F18 => winuser::VK_F18, - KeyCode::F19 => winuser::VK_F19, - KeyCode::F20 => winuser::VK_F20, - KeyCode::F21 => winuser::VK_F21, - KeyCode::F22 => winuser::VK_F22, - KeyCode::F23 => winuser::VK_F23, - KeyCode::F24 => winuser::VK_F24, - KeyCode::F25 => 0, - KeyCode::F26 => 0, - KeyCode::F27 => 0, - KeyCode::F28 => 0, - KeyCode::F29 => 0, - KeyCode::F30 => 0, - KeyCode::F31 => 0, - KeyCode::F32 => 0, - KeyCode::F33 => 0, - KeyCode::F34 => 0, - KeyCode::F35 => 0, - KeyCode::Unidentified(_) => 0, - _ => 0, + KeyCode::Backquote => VIRTUAL_KEY::default(), + KeyCode::Backslash => VIRTUAL_KEY::default(), + KeyCode::BracketLeft => VIRTUAL_KEY::default(), + KeyCode::BracketRight => VIRTUAL_KEY::default(), + KeyCode::Comma => VIRTUAL_KEY::default(), + KeyCode::Digit0 => VIRTUAL_KEY::default(), + KeyCode::Digit1 => VIRTUAL_KEY::default(), + KeyCode::Digit2 => VIRTUAL_KEY::default(), + KeyCode::Digit3 => VIRTUAL_KEY::default(), + KeyCode::Digit4 => VIRTUAL_KEY::default(), + KeyCode::Digit5 => VIRTUAL_KEY::default(), + KeyCode::Digit6 => VIRTUAL_KEY::default(), + KeyCode::Digit7 => VIRTUAL_KEY::default(), + KeyCode::Digit8 => VIRTUAL_KEY::default(), + KeyCode::Digit9 => VIRTUAL_KEY::default(), + KeyCode::Equal => VIRTUAL_KEY::default(), + KeyCode::IntlBackslash => VIRTUAL_KEY::default(), + KeyCode::IntlRo => VIRTUAL_KEY::default(), + KeyCode::IntlYen => VIRTUAL_KEY::default(), + KeyCode::KeyA => VIRTUAL_KEY::default(), + KeyCode::KeyB => VIRTUAL_KEY::default(), + KeyCode::KeyC => VIRTUAL_KEY::default(), + KeyCode::KeyD => VIRTUAL_KEY::default(), + KeyCode::KeyE => VIRTUAL_KEY::default(), + KeyCode::KeyF => VIRTUAL_KEY::default(), + KeyCode::KeyG => VIRTUAL_KEY::default(), + KeyCode::KeyH => VIRTUAL_KEY::default(), + KeyCode::KeyI => VIRTUAL_KEY::default(), + KeyCode::KeyJ => VIRTUAL_KEY::default(), + KeyCode::KeyK => VIRTUAL_KEY::default(), + KeyCode::KeyL => VIRTUAL_KEY::default(), + KeyCode::KeyM => VIRTUAL_KEY::default(), + KeyCode::KeyN => VIRTUAL_KEY::default(), + KeyCode::KeyO => VIRTUAL_KEY::default(), + KeyCode::KeyP => VIRTUAL_KEY::default(), + KeyCode::KeyQ => VIRTUAL_KEY::default(), + KeyCode::KeyR => VIRTUAL_KEY::default(), + KeyCode::KeyS => VIRTUAL_KEY::default(), + KeyCode::KeyT => VIRTUAL_KEY::default(), + KeyCode::KeyU => VIRTUAL_KEY::default(), + KeyCode::KeyV => VIRTUAL_KEY::default(), + KeyCode::KeyW => VIRTUAL_KEY::default(), + KeyCode::KeyX => VIRTUAL_KEY::default(), + KeyCode::KeyY => VIRTUAL_KEY::default(), + KeyCode::KeyZ => VIRTUAL_KEY::default(), + KeyCode::Minus => VIRTUAL_KEY::default(), + KeyCode::Period => VIRTUAL_KEY::default(), + KeyCode::Quote => VIRTUAL_KEY::default(), + KeyCode::Semicolon => VIRTUAL_KEY::default(), + KeyCode::Slash => VIRTUAL_KEY::default(), + KeyCode::AltLeft => VK_LMENU, + KeyCode::AltRight => VK_RMENU, + KeyCode::Backspace => VK_BACK, + KeyCode::CapsLock => VK_CAPITAL, + KeyCode::ContextMenu => VK_APPS, + KeyCode::ControlLeft => VK_LCONTROL, + KeyCode::ControlRight => VK_RCONTROL, + KeyCode::Enter => VK_RETURN, + KeyCode::SuperLeft => VK_LWIN, + KeyCode::SuperRight => VK_RWIN, + KeyCode::ShiftLeft => VK_RSHIFT, + KeyCode::ShiftRight => VK_LSHIFT, + KeyCode::Space => VK_SPACE, + KeyCode::Tab => VK_TAB, + KeyCode::Convert => VK_CONVERT, + KeyCode::KanaMode => VK_KANA, + KeyCode::Lang1 if is_korean => VK_HANGUL, + KeyCode::Lang1 if is_japanese => VK_KANA, + KeyCode::Lang2 if is_korean => VK_HANJA, + KeyCode::Lang2 if is_japanese => VIRTUAL_KEY::default(), + KeyCode::Lang3 if is_japanese => VK_OEM_FINISH, + KeyCode::Lang4 if is_japanese => VIRTUAL_KEY::default(), + KeyCode::Lang5 if is_japanese => VIRTUAL_KEY::default(), + KeyCode::NonConvert => VK_NONCONVERT, + KeyCode::Delete => VK_DELETE, + KeyCode::End => VK_END, + KeyCode::Help => VK_HELP, + KeyCode::Home => VK_HOME, + KeyCode::Insert => VK_INSERT, + KeyCode::PageDown => VK_NEXT, + KeyCode::PageUp => VK_PRIOR, + KeyCode::ArrowDown => VK_DOWN, + KeyCode::ArrowLeft => VK_LEFT, + KeyCode::ArrowRight => VK_RIGHT, + KeyCode::ArrowUp => VK_UP, + KeyCode::NumLock => VK_NUMLOCK, + KeyCode::Numpad0 => VK_NUMPAD0, + KeyCode::Numpad1 => VK_NUMPAD1, + KeyCode::Numpad2 => VK_NUMPAD2, + KeyCode::Numpad3 => VK_NUMPAD3, + KeyCode::Numpad4 => VK_NUMPAD4, + KeyCode::Numpad5 => VK_NUMPAD5, + KeyCode::Numpad6 => VK_NUMPAD6, + KeyCode::Numpad7 => VK_NUMPAD7, + KeyCode::Numpad8 => VK_NUMPAD8, + KeyCode::Numpad9 => VK_NUMPAD9, + KeyCode::NumpadAdd => VK_ADD, + KeyCode::NumpadBackspace => VK_BACK, + KeyCode::NumpadClear => VK_CLEAR, + KeyCode::NumpadClearEntry => VIRTUAL_KEY::default(), + KeyCode::NumpadComma => VK_SEPARATOR, + KeyCode::NumpadDecimal => VK_DECIMAL, + KeyCode::NumpadDivide => VK_DIVIDE, + KeyCode::NumpadEnter => VK_RETURN, + KeyCode::NumpadEqual => VIRTUAL_KEY::default(), + KeyCode::NumpadHash => VIRTUAL_KEY::default(), + KeyCode::NumpadMemoryAdd => VIRTUAL_KEY::default(), + KeyCode::NumpadMemoryClear => VIRTUAL_KEY::default(), + KeyCode::NumpadMemoryRecall => VIRTUAL_KEY::default(), + KeyCode::NumpadMemoryStore => VIRTUAL_KEY::default(), + KeyCode::NumpadMemorySubtract => VIRTUAL_KEY::default(), + KeyCode::NumpadMultiply => VK_MULTIPLY, + KeyCode::NumpadParenLeft => VIRTUAL_KEY::default(), + KeyCode::NumpadParenRight => VIRTUAL_KEY::default(), + KeyCode::NumpadStar => VIRTUAL_KEY::default(), + KeyCode::NumpadSubtract => VK_SUBTRACT, + KeyCode::Escape => VK_ESCAPE, + KeyCode::Fn => VIRTUAL_KEY::default(), + KeyCode::FnLock => VIRTUAL_KEY::default(), + KeyCode::PrintScreen => VK_SNAPSHOT, + KeyCode::ScrollLock => VK_SCROLL, + KeyCode::Pause => VK_PAUSE, + KeyCode::BrowserBack => VK_BROWSER_BACK, + KeyCode::BrowserFavorites => VK_BROWSER_FAVORITES, + KeyCode::BrowserForward => VK_BROWSER_FORWARD, + KeyCode::BrowserHome => VK_BROWSER_HOME, + KeyCode::BrowserRefresh => VK_BROWSER_REFRESH, + KeyCode::BrowserSearch => VK_BROWSER_SEARCH, + KeyCode::BrowserStop => VK_BROWSER_STOP, + KeyCode::Eject => VIRTUAL_KEY::default(), + KeyCode::LaunchApp1 => VK_LAUNCH_APP1, + KeyCode::LaunchApp2 => VK_LAUNCH_APP2, + KeyCode::LaunchMail => VK_LAUNCH_MAIL, + KeyCode::MediaPlayPause => VK_MEDIA_PLAY_PAUSE, + KeyCode::MediaSelect => VK_LAUNCH_MEDIA_SELECT, + KeyCode::MediaStop => VK_MEDIA_STOP, + KeyCode::MediaTrackNext => VK_MEDIA_NEXT_TRACK, + KeyCode::MediaTrackPrevious => VK_MEDIA_PREV_TRACK, + KeyCode::Power => VIRTUAL_KEY::default(), + KeyCode::Sleep => VIRTUAL_KEY::default(), + KeyCode::AudioVolumeDown => VK_VOLUME_DOWN, + KeyCode::AudioVolumeMute => VK_VOLUME_MUTE, + KeyCode::AudioVolumeUp => VK_VOLUME_UP, + KeyCode::WakeUp => VIRTUAL_KEY::default(), + KeyCode::Hyper => VIRTUAL_KEY::default(), + KeyCode::Turbo => VIRTUAL_KEY::default(), + KeyCode::Abort => VIRTUAL_KEY::default(), + KeyCode::Resume => VIRTUAL_KEY::default(), + KeyCode::Suspend => VIRTUAL_KEY::default(), + KeyCode::Again => VIRTUAL_KEY::default(), + KeyCode::Copy => VIRTUAL_KEY::default(), + KeyCode::Cut => VIRTUAL_KEY::default(), + KeyCode::Find => VIRTUAL_KEY::default(), + KeyCode::Open => VIRTUAL_KEY::default(), + KeyCode::Paste => VIRTUAL_KEY::default(), + KeyCode::Props => VIRTUAL_KEY::default(), + KeyCode::Select => VK_SELECT, + KeyCode::Undo => VIRTUAL_KEY::default(), + KeyCode::Hiragana => VIRTUAL_KEY::default(), + KeyCode::Katakana => VIRTUAL_KEY::default(), + KeyCode::F1 => VK_F1, + KeyCode::F2 => VK_F2, + KeyCode::F3 => VK_F3, + KeyCode::F4 => VK_F4, + KeyCode::F5 => VK_F5, + KeyCode::F6 => VK_F6, + KeyCode::F7 => VK_F7, + KeyCode::F8 => VK_F8, + KeyCode::F9 => VK_F9, + KeyCode::F10 => VK_F10, + KeyCode::F11 => VK_F11, + KeyCode::F12 => VK_F12, + KeyCode::F13 => VK_F13, + KeyCode::F14 => VK_F14, + KeyCode::F15 => VK_F15, + KeyCode::F16 => VK_F16, + KeyCode::F17 => VK_F17, + KeyCode::F18 => VK_F18, + KeyCode::F19 => VK_F19, + KeyCode::F20 => VK_F20, + KeyCode::F21 => VK_F21, + KeyCode::F22 => VK_F22, + KeyCode::F23 => VK_F23, + KeyCode::F24 => VK_F24, + KeyCode::F25 => VIRTUAL_KEY::default(), + KeyCode::F26 => VIRTUAL_KEY::default(), + KeyCode::F27 => VIRTUAL_KEY::default(), + KeyCode::F28 => VIRTUAL_KEY::default(), + KeyCode::F29 => VIRTUAL_KEY::default(), + KeyCode::F30 => VIRTUAL_KEY::default(), + KeyCode::F31 => VIRTUAL_KEY::default(), + KeyCode::F32 => VIRTUAL_KEY::default(), + KeyCode::F33 => VIRTUAL_KEY::default(), + KeyCode::F34 => VIRTUAL_KEY::default(), + KeyCode::F35 => VIRTUAL_KEY::default(), + KeyCode::Unidentified(_) => VIRTUAL_KEY::default(), + _ => VIRTUAL_KEY::default(), } } @@ -734,228 +734,228 @@ fn keycode_to_vkey(keycode: KeyCode, hkl: u64) -> i32 { /// The result includes all non-character keys defined within `Key` plus characters from numpad keys. /// For example, backspace and tab are included. fn vkey_to_non_char_key( - vkey: i32, + vkey: VIRTUAL_KEY, native_code: NativeKeyCode, - hkl: u64, + hkl: HKL, has_alt_graph: bool, ) -> Key<'static> { // List of the Web key names and their corresponding platform-native key names: // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values - let primary_lang_id = PRIMARYLANGID(LOWORD(hkl as u32)); + let primary_lang_id = util::PRIMARYLANGID(hkl); let is_korean = primary_lang_id == LANG_KOREAN; let is_japanese = primary_lang_id == LANG_JAPANESE; match vkey { - winuser::VK_LBUTTON => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse - winuser::VK_RBUTTON => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse + win32km::VK_LBUTTON => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse + win32km::VK_RBUTTON => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse // I don't think this can be represented with a Key - winuser::VK_CANCEL => Key::Unidentified(native_code), - - winuser::VK_MBUTTON => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse - winuser::VK_XBUTTON1 => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse - winuser::VK_XBUTTON2 => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse - winuser::VK_BACK => Key::Backspace, - winuser::VK_TAB => Key::Tab, - winuser::VK_CLEAR => Key::Clear, - winuser::VK_RETURN => Key::Enter, - winuser::VK_SHIFT => Key::Shift, - winuser::VK_CONTROL => Key::Control, - winuser::VK_MENU => Key::Alt, - winuser::VK_PAUSE => Key::Pause, - winuser::VK_CAPITAL => Key::CapsLock, - - //winuser::VK_HANGEUL => Key::HangulMode, // Deprecated in favour of VK_HANGUL + win32km::VK_CANCEL => Key::Unidentified(native_code), + + win32km::VK_MBUTTON => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse + win32km::VK_XBUTTON1 => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse + win32km::VK_XBUTTON2 => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse + win32km::VK_BACK => Key::Backspace, + win32km::VK_TAB => Key::Tab, + win32km::VK_CLEAR => Key::Clear, + win32km::VK_RETURN => Key::Enter, + win32km::VK_SHIFT => Key::Shift, + win32km::VK_CONTROL => Key::Control, + win32km::VK_MENU => Key::Alt, + win32km::VK_PAUSE => Key::Pause, + win32km::VK_CAPITAL => Key::CapsLock, + + //win32km::VK_HANGEUL => Key::HangulMode, // Deprecated in favour of VK_HANGUL // VK_HANGUL and VK_KANA are defined as the same constant, therefore // we use appropriate conditions to differentate between them - winuser::VK_HANGUL if is_korean => Key::HangulMode, - winuser::VK_KANA if is_japanese => Key::KanaMode, + win32km::VK_HANGUL if is_korean => Key::HangulMode, + win32km::VK_KANA if is_japanese => Key::KanaMode, - winuser::VK_JUNJA => Key::JunjaMode, - winuser::VK_FINAL => Key::FinalMode, + win32km::VK_JUNJA => Key::JunjaMode, + win32km::VK_FINAL => Key::FinalMode, // VK_HANJA and VK_KANJI are defined as the same constant, therefore // we use appropriate conditions to differentate between them - winuser::VK_HANJA if is_korean => Key::HanjaMode, - winuser::VK_KANJI if is_japanese => Key::KanjiMode, - - winuser::VK_ESCAPE => Key::Escape, - winuser::VK_CONVERT => Key::Convert, - winuser::VK_NONCONVERT => Key::NonConvert, - winuser::VK_ACCEPT => Key::Accept, - winuser::VK_MODECHANGE => Key::ModeChange, - winuser::VK_SPACE => Key::Space, - winuser::VK_PRIOR => Key::PageUp, - winuser::VK_NEXT => Key::PageDown, - winuser::VK_END => Key::End, - winuser::VK_HOME => Key::Home, - winuser::VK_LEFT => Key::ArrowLeft, - winuser::VK_UP => Key::ArrowUp, - winuser::VK_RIGHT => Key::ArrowRight, - winuser::VK_DOWN => Key::ArrowDown, - winuser::VK_SELECT => Key::Select, - winuser::VK_PRINT => Key::Print, - winuser::VK_EXECUTE => Key::Execute, - winuser::VK_SNAPSHOT => Key::PrintScreen, - winuser::VK_INSERT => Key::Insert, - winuser::VK_DELETE => Key::Delete, - winuser::VK_HELP => Key::Help, - winuser::VK_LWIN => Key::Super, - winuser::VK_RWIN => Key::Super, - winuser::VK_APPS => Key::ContextMenu, - winuser::VK_SLEEP => Key::Standby, + win32km::VK_HANJA if is_korean => Key::HanjaMode, + win32km::VK_KANJI if is_japanese => Key::KanjiMode, + + win32km::VK_ESCAPE => Key::Escape, + win32km::VK_CONVERT => Key::Convert, + win32km::VK_NONCONVERT => Key::NonConvert, + win32km::VK_ACCEPT => Key::Accept, + win32km::VK_MODECHANGE => Key::ModeChange, + win32km::VK_SPACE => Key::Space, + win32km::VK_PRIOR => Key::PageUp, + win32km::VK_NEXT => Key::PageDown, + win32km::VK_END => Key::End, + win32km::VK_HOME => Key::Home, + win32km::VK_LEFT => Key::ArrowLeft, + win32km::VK_UP => Key::ArrowUp, + win32km::VK_RIGHT => Key::ArrowRight, + win32km::VK_DOWN => Key::ArrowDown, + win32km::VK_SELECT => Key::Select, + win32km::VK_PRINT => Key::Print, + win32km::VK_EXECUTE => Key::Execute, + win32km::VK_SNAPSHOT => Key::PrintScreen, + win32km::VK_INSERT => Key::Insert, + win32km::VK_DELETE => Key::Delete, + win32km::VK_HELP => Key::Help, + win32km::VK_LWIN => Key::Super, + win32km::VK_RWIN => Key::Super, + win32km::VK_APPS => Key::ContextMenu, + win32km::VK_SLEEP => Key::Standby, // Numpad keys produce characters - winuser::VK_NUMPAD0 => Key::Unidentified(native_code), - winuser::VK_NUMPAD1 => Key::Unidentified(native_code), - winuser::VK_NUMPAD2 => Key::Unidentified(native_code), - winuser::VK_NUMPAD3 => Key::Unidentified(native_code), - winuser::VK_NUMPAD4 => Key::Unidentified(native_code), - winuser::VK_NUMPAD5 => Key::Unidentified(native_code), - winuser::VK_NUMPAD6 => Key::Unidentified(native_code), - winuser::VK_NUMPAD7 => Key::Unidentified(native_code), - winuser::VK_NUMPAD8 => Key::Unidentified(native_code), - winuser::VK_NUMPAD9 => Key::Unidentified(native_code), - winuser::VK_MULTIPLY => Key::Unidentified(native_code), - winuser::VK_ADD => Key::Unidentified(native_code), - winuser::VK_SEPARATOR => Key::Unidentified(native_code), - winuser::VK_SUBTRACT => Key::Unidentified(native_code), - winuser::VK_DECIMAL => Key::Unidentified(native_code), - winuser::VK_DIVIDE => Key::Unidentified(native_code), - - winuser::VK_F1 => Key::F1, - winuser::VK_F2 => Key::F2, - winuser::VK_F3 => Key::F3, - winuser::VK_F4 => Key::F4, - winuser::VK_F5 => Key::F5, - winuser::VK_F6 => Key::F6, - winuser::VK_F7 => Key::F7, - winuser::VK_F8 => Key::F8, - winuser::VK_F9 => Key::F9, - winuser::VK_F10 => Key::F10, - winuser::VK_F11 => Key::F11, - winuser::VK_F12 => Key::F12, - winuser::VK_F13 => Key::F13, - winuser::VK_F14 => Key::F14, - winuser::VK_F15 => Key::F15, - winuser::VK_F16 => Key::F16, - winuser::VK_F17 => Key::F17, - winuser::VK_F18 => Key::F18, - winuser::VK_F19 => Key::F19, - winuser::VK_F20 => Key::F20, - winuser::VK_F21 => Key::F21, - winuser::VK_F22 => Key::F22, - winuser::VK_F23 => Key::F23, - winuser::VK_F24 => Key::F24, - winuser::VK_NAVIGATION_VIEW => Key::Unidentified(native_code), - winuser::VK_NAVIGATION_MENU => Key::Unidentified(native_code), - winuser::VK_NAVIGATION_UP => Key::Unidentified(native_code), - winuser::VK_NAVIGATION_DOWN => Key::Unidentified(native_code), - winuser::VK_NAVIGATION_LEFT => Key::Unidentified(native_code), - winuser::VK_NAVIGATION_RIGHT => Key::Unidentified(native_code), - winuser::VK_NAVIGATION_ACCEPT => Key::Unidentified(native_code), - winuser::VK_NAVIGATION_CANCEL => Key::Unidentified(native_code), - winuser::VK_NUMLOCK => Key::NumLock, - winuser::VK_SCROLL => Key::ScrollLock, - winuser::VK_OEM_NEC_EQUAL => Key::Unidentified(native_code), - //winuser::VK_OEM_FJ_JISHO => Key::Unidentified(native_code), // Conflicts with `VK_OEM_NEC_EQUAL` - winuser::VK_OEM_FJ_MASSHOU => Key::Unidentified(native_code), - winuser::VK_OEM_FJ_TOUROKU => Key::Unidentified(native_code), - winuser::VK_OEM_FJ_LOYA => Key::Unidentified(native_code), - winuser::VK_OEM_FJ_ROYA => Key::Unidentified(native_code), - winuser::VK_LSHIFT => Key::Shift, - winuser::VK_RSHIFT => Key::Shift, - winuser::VK_LCONTROL => Key::Control, - winuser::VK_RCONTROL => Key::Control, - winuser::VK_LMENU => Key::Alt, - winuser::VK_RMENU => { + win32km::VK_NUMPAD0 => Key::Unidentified(native_code), + win32km::VK_NUMPAD1 => Key::Unidentified(native_code), + win32km::VK_NUMPAD2 => Key::Unidentified(native_code), + win32km::VK_NUMPAD3 => Key::Unidentified(native_code), + win32km::VK_NUMPAD4 => Key::Unidentified(native_code), + win32km::VK_NUMPAD5 => Key::Unidentified(native_code), + win32km::VK_NUMPAD6 => Key::Unidentified(native_code), + win32km::VK_NUMPAD7 => Key::Unidentified(native_code), + win32km::VK_NUMPAD8 => Key::Unidentified(native_code), + win32km::VK_NUMPAD9 => Key::Unidentified(native_code), + win32km::VK_MULTIPLY => Key::Unidentified(native_code), + win32km::VK_ADD => Key::Unidentified(native_code), + win32km::VK_SEPARATOR => Key::Unidentified(native_code), + win32km::VK_SUBTRACT => Key::Unidentified(native_code), + win32km::VK_DECIMAL => Key::Unidentified(native_code), + win32km::VK_DIVIDE => Key::Unidentified(native_code), + + win32km::VK_F1 => Key::F1, + win32km::VK_F2 => Key::F2, + win32km::VK_F3 => Key::F3, + win32km::VK_F4 => Key::F4, + win32km::VK_F5 => Key::F5, + win32km::VK_F6 => Key::F6, + win32km::VK_F7 => Key::F7, + win32km::VK_F8 => Key::F8, + win32km::VK_F9 => Key::F9, + win32km::VK_F10 => Key::F10, + win32km::VK_F11 => Key::F11, + win32km::VK_F12 => Key::F12, + win32km::VK_F13 => Key::F13, + win32km::VK_F14 => Key::F14, + win32km::VK_F15 => Key::F15, + win32km::VK_F16 => Key::F16, + win32km::VK_F17 => Key::F17, + win32km::VK_F18 => Key::F18, + win32km::VK_F19 => Key::F19, + win32km::VK_F20 => Key::F20, + win32km::VK_F21 => Key::F21, + win32km::VK_F22 => Key::F22, + win32km::VK_F23 => Key::F23, + win32km::VK_F24 => Key::F24, + win32km::VK_NAVIGATION_VIEW => Key::Unidentified(native_code), + win32km::VK_NAVIGATION_MENU => Key::Unidentified(native_code), + win32km::VK_NAVIGATION_UP => Key::Unidentified(native_code), + win32km::VK_NAVIGATION_DOWN => Key::Unidentified(native_code), + win32km::VK_NAVIGATION_LEFT => Key::Unidentified(native_code), + win32km::VK_NAVIGATION_RIGHT => Key::Unidentified(native_code), + win32km::VK_NAVIGATION_ACCEPT => Key::Unidentified(native_code), + win32km::VK_NAVIGATION_CANCEL => Key::Unidentified(native_code), + win32km::VK_NUMLOCK => Key::NumLock, + win32km::VK_SCROLL => Key::ScrollLock, + win32km::VK_OEM_NEC_EQUAL => Key::Unidentified(native_code), + //win32km::VK_OEM_FJ_JISHO => Key::Unidentified(native_code), // Conflicts with `VK_OEM_NEC_EQUAL` + win32km::VK_OEM_FJ_MASSHOU => Key::Unidentified(native_code), + win32km::VK_OEM_FJ_TOUROKU => Key::Unidentified(native_code), + win32km::VK_OEM_FJ_LOYA => Key::Unidentified(native_code), + win32km::VK_OEM_FJ_ROYA => Key::Unidentified(native_code), + win32km::VK_LSHIFT => Key::Shift, + win32km::VK_RSHIFT => Key::Shift, + win32km::VK_LCONTROL => Key::Control, + win32km::VK_RCONTROL => Key::Control, + win32km::VK_LMENU => Key::Alt, + win32km::VK_RMENU => { if has_alt_graph { Key::AltGraph } else { Key::Alt } } - winuser::VK_BROWSER_BACK => Key::BrowserBack, - winuser::VK_BROWSER_FORWARD => Key::BrowserForward, - winuser::VK_BROWSER_REFRESH => Key::BrowserRefresh, - winuser::VK_BROWSER_STOP => Key::BrowserStop, - winuser::VK_BROWSER_SEARCH => Key::BrowserSearch, - winuser::VK_BROWSER_FAVORITES => Key::BrowserFavorites, - winuser::VK_BROWSER_HOME => Key::BrowserHome, - winuser::VK_VOLUME_MUTE => Key::AudioVolumeMute, - winuser::VK_VOLUME_DOWN => Key::AudioVolumeDown, - winuser::VK_VOLUME_UP => Key::AudioVolumeUp, - winuser::VK_MEDIA_NEXT_TRACK => Key::MediaTrackNext, - winuser::VK_MEDIA_PREV_TRACK => Key::MediaTrackPrevious, - winuser::VK_MEDIA_STOP => Key::MediaStop, - winuser::VK_MEDIA_PLAY_PAUSE => Key::MediaPlayPause, - winuser::VK_LAUNCH_MAIL => Key::LaunchMail, - winuser::VK_LAUNCH_MEDIA_SELECT => Key::LaunchMediaPlayer, - winuser::VK_LAUNCH_APP1 => Key::LaunchApplication1, - winuser::VK_LAUNCH_APP2 => Key::LaunchApplication2, + win32km::VK_BROWSER_BACK => Key::BrowserBack, + win32km::VK_BROWSER_FORWARD => Key::BrowserForward, + win32km::VK_BROWSER_REFRESH => Key::BrowserRefresh, + win32km::VK_BROWSER_STOP => Key::BrowserStop, + win32km::VK_BROWSER_SEARCH => Key::BrowserSearch, + win32km::VK_BROWSER_FAVORITES => Key::BrowserFavorites, + win32km::VK_BROWSER_HOME => Key::BrowserHome, + win32km::VK_VOLUME_MUTE => Key::AudioVolumeMute, + win32km::VK_VOLUME_DOWN => Key::AudioVolumeDown, + win32km::VK_VOLUME_UP => Key::AudioVolumeUp, + win32km::VK_MEDIA_NEXT_TRACK => Key::MediaTrackNext, + win32km::VK_MEDIA_PREV_TRACK => Key::MediaTrackPrevious, + win32km::VK_MEDIA_STOP => Key::MediaStop, + win32km::VK_MEDIA_PLAY_PAUSE => Key::MediaPlayPause, + win32km::VK_LAUNCH_MAIL => Key::LaunchMail, + win32km::VK_LAUNCH_MEDIA_SELECT => Key::LaunchMediaPlayer, + win32km::VK_LAUNCH_APP1 => Key::LaunchApplication1, + win32km::VK_LAUNCH_APP2 => Key::LaunchApplication2, // This function only converts "non-printable" - winuser::VK_OEM_1 => Key::Unidentified(native_code), - winuser::VK_OEM_PLUS => Key::Unidentified(native_code), - winuser::VK_OEM_COMMA => Key::Unidentified(native_code), - winuser::VK_OEM_MINUS => Key::Unidentified(native_code), - winuser::VK_OEM_PERIOD => Key::Unidentified(native_code), - winuser::VK_OEM_2 => Key::Unidentified(native_code), - winuser::VK_OEM_3 => Key::Unidentified(native_code), - - winuser::VK_GAMEPAD_A => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_B => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_X => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_Y => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_RIGHT_SHOULDER => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_LEFT_SHOULDER => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_LEFT_TRIGGER => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_RIGHT_TRIGGER => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_DPAD_UP => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_DPAD_DOWN => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_DPAD_LEFT => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_DPAD_RIGHT => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_MENU => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_VIEW => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_BUTTON => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_BUTTON => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_UP => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_DOWN => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_RIGHT => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_LEFT => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_UP => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_DOWN => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_RIGHT => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_LEFT => Key::Unidentified(native_code), + win32km::VK_OEM_1 => Key::Unidentified(native_code), + win32km::VK_OEM_PLUS => Key::Unidentified(native_code), + win32km::VK_OEM_COMMA => Key::Unidentified(native_code), + win32km::VK_OEM_MINUS => Key::Unidentified(native_code), + win32km::VK_OEM_PERIOD => Key::Unidentified(native_code), + win32km::VK_OEM_2 => Key::Unidentified(native_code), + win32km::VK_OEM_3 => Key::Unidentified(native_code), + + win32km::VK_GAMEPAD_A => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_B => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_X => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_Y => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_RIGHT_SHOULDER => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_LEFT_SHOULDER => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_LEFT_TRIGGER => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_RIGHT_TRIGGER => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_DPAD_UP => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_DPAD_DOWN => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_DPAD_LEFT => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_DPAD_RIGHT => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_MENU => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_VIEW => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_LEFT_THUMBSTICK_BUTTON => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_RIGHT_THUMBSTICK_BUTTON => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_LEFT_THUMBSTICK_UP => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_LEFT_THUMBSTICK_DOWN => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_LEFT_THUMBSTICK_RIGHT => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_LEFT_THUMBSTICK_LEFT => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_RIGHT_THUMBSTICK_UP => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_RIGHT_THUMBSTICK_DOWN => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_RIGHT_THUMBSTICK_RIGHT => Key::Unidentified(native_code), + win32km::VK_GAMEPAD_RIGHT_THUMBSTICK_LEFT => Key::Unidentified(native_code), // This function only converts "non-printable" - winuser::VK_OEM_4 => Key::Unidentified(native_code), - winuser::VK_OEM_5 => Key::Unidentified(native_code), - winuser::VK_OEM_6 => Key::Unidentified(native_code), - winuser::VK_OEM_7 => Key::Unidentified(native_code), - winuser::VK_OEM_8 => Key::Unidentified(native_code), - winuser::VK_OEM_AX => Key::Unidentified(native_code), - winuser::VK_OEM_102 => Key::Unidentified(native_code), - - winuser::VK_ICO_HELP => Key::Unidentified(native_code), - winuser::VK_ICO_00 => Key::Unidentified(native_code), - - winuser::VK_PROCESSKEY => Key::Process, - - winuser::VK_ICO_CLEAR => Key::Unidentified(native_code), - winuser::VK_PACKET => Key::Unidentified(native_code), - winuser::VK_OEM_RESET => Key::Unidentified(native_code), - winuser::VK_OEM_JUMP => Key::Unidentified(native_code), - winuser::VK_OEM_PA1 => Key::Unidentified(native_code), - winuser::VK_OEM_PA2 => Key::Unidentified(native_code), - winuser::VK_OEM_PA3 => Key::Unidentified(native_code), - winuser::VK_OEM_WSCTRL => Key::Unidentified(native_code), - winuser::VK_OEM_CUSEL => Key::Unidentified(native_code), - - winuser::VK_OEM_ATTN => Key::Attn, - winuser::VK_OEM_FINISH => { + win32km::VK_OEM_4 => Key::Unidentified(native_code), + win32km::VK_OEM_5 => Key::Unidentified(native_code), + win32km::VK_OEM_6 => Key::Unidentified(native_code), + win32km::VK_OEM_7 => Key::Unidentified(native_code), + win32km::VK_OEM_8 => Key::Unidentified(native_code), + win32km::VK_OEM_AX => Key::Unidentified(native_code), + win32km::VK_OEM_102 => Key::Unidentified(native_code), + + win32km::VK_ICO_HELP => Key::Unidentified(native_code), + win32km::VK_ICO_00 => Key::Unidentified(native_code), + + win32km::VK_PROCESSKEY => Key::Process, + + win32km::VK_ICO_CLEAR => Key::Unidentified(native_code), + win32km::VK_PACKET => Key::Unidentified(native_code), + win32km::VK_OEM_RESET => Key::Unidentified(native_code), + win32km::VK_OEM_JUMP => Key::Unidentified(native_code), + win32km::VK_OEM_PA1 => Key::Unidentified(native_code), + win32km::VK_OEM_PA2 => Key::Unidentified(native_code), + win32km::VK_OEM_PA3 => Key::Unidentified(native_code), + win32km::VK_OEM_WSCTRL => Key::Unidentified(native_code), + win32km::VK_OEM_CUSEL => Key::Unidentified(native_code), + + win32km::VK_OEM_ATTN => Key::Attn, + win32km::VK_OEM_FINISH => { if is_japanese { Key::Katakana } else { @@ -967,19 +967,19 @@ fn vkey_to_non_char_key( Key::Unidentified(native_code) } } - winuser::VK_OEM_COPY => Key::Copy, - winuser::VK_OEM_AUTO => Key::Hankaku, - winuser::VK_OEM_ENLW => Key::Zenkaku, - winuser::VK_OEM_BACKTAB => Key::Romaji, - winuser::VK_ATTN => Key::KanaMode, - winuser::VK_CRSEL => Key::CrSel, - winuser::VK_EXSEL => Key::ExSel, - winuser::VK_EREOF => Key::EraseEof, - winuser::VK_PLAY => Key::Play, - winuser::VK_ZOOM => Key::ZoomToggle, - winuser::VK_NONAME => Key::Unidentified(native_code), - winuser::VK_PA1 => Key::Unidentified(native_code), - winuser::VK_OEM_CLEAR => Key::Clear, + win32km::VK_OEM_COPY => Key::Copy, + win32km::VK_OEM_AUTO => Key::Hankaku, + win32km::VK_OEM_ENLW => Key::Zenkaku, + win32km::VK_OEM_BACKTAB => Key::Romaji, + win32km::VK_ATTN => Key::KanaMode, + win32km::VK_CRSEL => Key::CrSel, + win32km::VK_EXSEL => Key::ExSel, + win32km::VK_EREOF => Key::EraseEof, + win32km::VK_PLAY => Key::Play, + win32km::VK_ZOOM => Key::ZoomToggle, + win32km::VK_NONAME => Key::Unidentified(native_code), + win32km::VK_PA1 => Key::Unidentified(native_code), + win32km::VK_OEM_CLEAR => Key::Clear, _ => Key::Unidentified(native_code), } } diff --git a/src/platform_impl/windows/keycode.rs b/src/platform_impl/windows/keycode.rs index 495b394b29..df8855bb0c 100644 --- a/src/platform_impl/windows/keycode.rs +++ b/src/platform_impl/windows/keycode.rs @@ -1,17 +1,16 @@ -use crate::keyboard::{KeyCode, NativeKeyCode}; -use winapi::{ - shared::minwindef::LOWORD, - um::{ - winnt::{LANG_KOREAN, PRIMARYLANGID}, - winuser::GetKeyboardLayout, - }, +use crate::{ + keyboard::{KeyCode, NativeKeyCode}, + platform_impl::platform::util, +}; +use windows::Win32::{ + System::SystemServices::LANG_KOREAN, UI::Input::KeyboardAndMouse::GetKeyboardLayout, }; pub fn keycode_to_scancode(code: KeyCode) -> Option { // See `from_scancode` for more info let hkl = unsafe { GetKeyboardLayout(0) }; - let primary_lang_id = PRIMARYLANGID(LOWORD(hkl as u32)); + let primary_lang_id = util::PRIMARYLANGID(hkl); let is_korean = primary_lang_id == LANG_KOREAN; match code { diff --git a/src/platform_impl/windows/menu.rs b/src/platform_impl/windows/menu.rs index d3d0f18527..4f3213bb3e 100644 --- a/src/platform_impl/windows/menu.rs +++ b/src/platform_impl/windows/menu.rs @@ -1,12 +1,15 @@ // Copyright 2019-2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -use raw_window_handle::RawWindowHandle; -use std::{collections::HashMap, ffi::CString, fmt, sync::Mutex}; - -use winapi::{ - shared::{basetsd, minwindef, windef}, - um::{commctrl, winuser}, +use std::{collections::HashMap, fmt, sync::Mutex}; + +use windows::Win32::{ + Foundation::{HWND, LPARAM, LRESULT, PSTR, PWSTR, WPARAM}, + UI::{ + Input::KeyboardAndMouse::*, + Shell::*, + WindowsAndMessaging::{self as win32wm, *}, + }, }; use crate::{ @@ -17,10 +20,10 @@ use crate::{ window::WindowId as RootWindowId, }; -use super::{accelerator::register_accel, keyboard::key_to_vk, util::to_wstring, WindowId}; +use super::{accelerator::register_accel, keyboard::key_to_vk, util, WindowId}; #[derive(Copy, Clone)] -struct AccelWrapper(winuser::ACCEL); +struct AccelWrapper(ACCEL); impl fmt::Debug for AccelWrapper { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.pad(&format!("")) @@ -72,7 +75,7 @@ impl MenuHandler { } #[derive(Debug, Clone)] -pub struct MenuItemAttributes(pub(crate) u16, windef::HMENU); +pub struct MenuItemAttributes(pub(crate) u16, HMENU); impl MenuItemAttributes { pub fn id(&self) -> MenuId { @@ -80,37 +83,36 @@ impl MenuItemAttributes { } pub fn set_enabled(&mut self, enabled: bool) { unsafe { - winuser::EnableMenuItem( + EnableMenuItem( self.1, self.0 as u32, match enabled { - true => winuser::MF_ENABLED, - false => winuser::MF_DISABLED, + true => MF_ENABLED, + false => MF_DISABLED, }, ); } } pub fn set_title(&mut self, title: &str) { unsafe { - let mut info = winuser::MENUITEMINFOA { - cbSize: std::mem::size_of::() as _, - fMask: winuser::MIIM_STRING, + let info = MENUITEMINFOA { + cbSize: std::mem::size_of::() as _, + fMask: MIIM_STRING, + dwTypeData: PSTR(String::from(title).as_mut_ptr()), ..Default::default() }; - let c_str = CString::new(title).unwrap(); - info.dwTypeData = c_str.as_ptr() as _; - winuser::SetMenuItemInfoA(self.1, self.0 as u32, minwindef::FALSE, &info); + SetMenuItemInfoA(self.1, self.0 as u32, false, &info); } } pub fn set_selected(&mut self, selected: bool) { unsafe { - winuser::CheckMenuItem( + CheckMenuItem( self.1, self.0 as u32, match selected { - true => winuser::MF_CHECKED, - false => winuser::MF_UNCHECKED, + true => MF_CHECKED, + false => MF_UNCHECKED, }, ); } @@ -122,7 +124,7 @@ impl MenuItemAttributes { #[derive(Debug, Clone)] pub struct Menu { - hmenu: windef::HMENU, + hmenu: HMENU, accels: HashMap, } @@ -138,7 +140,7 @@ impl Default for Menu { impl Menu { pub fn new() -> Self { unsafe { - let hmenu = winuser::CreateMenu(); + let hmenu = CreateMenu(); Menu { hmenu, accels: HashMap::default(), @@ -148,7 +150,7 @@ impl Menu { pub fn new_popup_menu() -> Self { unsafe { - let hmenu = winuser::CreatePopupMenu(); + let hmenu = CreatePopupMenu(); Menu { hmenu, accels: HashMap::default(), @@ -156,12 +158,12 @@ impl Menu { } } - pub fn hmenu(&self) -> windef::HMENU { + pub fn hmenu(&self) -> HMENU { self.hmenu } // Get the accels table - pub(crate) fn accels(&self) -> Option> { + pub(crate) fn accels(&self) -> Option> { if self.accels.is_empty() { return None; } @@ -178,12 +180,12 @@ impl Menu { _menu_type: MenuType, ) -> CustomMenuItem { unsafe { - let mut flags = winuser::MF_STRING; + let mut flags = MF_STRING; if !enabled { - flags |= winuser::MF_GRAYED; + flags |= MF_GRAYED; } if selected { - flags |= winuser::MF_CHECKED; + flags |= MF_CHECKED; } let mut anno_title = title.to_string(); @@ -193,12 +195,7 @@ impl Menu { format_hotkey(accelerators, &mut anno_title); } - winuser::AppendMenuW( - self.hmenu, - flags, - menu_id.0 as _, - to_wstring(&anno_title).as_mut_ptr(), - ); + AppendMenuW(self.hmenu, flags, menu_id.0 as _, anno_title); // add our accels if let Some(accelerators) = accelerators { @@ -216,17 +213,12 @@ impl Menu { let child_accels = std::mem::take(&mut submenu.accels); self.accels.extend(child_accels); - let mut flags = winuser::MF_POPUP; + let mut flags = MF_POPUP; if !enabled { - flags |= winuser::MF_DISABLED; + flags |= MF_DISABLED; } - winuser::AppendMenuW( - self.hmenu, - flags, - submenu.hmenu() as _, - to_wstring(&title).as_mut_ptr(), - ); + AppendMenuW(self.hmenu, flags, submenu.hmenu().0 as usize, title); } } @@ -238,72 +230,32 @@ impl Menu { match item { MenuItem::Separator => { unsafe { - winuser::AppendMenuW(self.hmenu, winuser::MF_SEPARATOR, 0, std::ptr::null()); + AppendMenuW(self.hmenu, MF_SEPARATOR, 0, PWSTR::default()); }; } MenuItem::Cut => unsafe { - winuser::AppendMenuW( - self.hmenu, - winuser::MF_STRING, - CUT_ID, - to_wstring("&Cut\tCtrl+X").as_mut_ptr(), - ); + AppendMenuW(self.hmenu, MF_STRING, CUT_ID, "&Cut\tCtrl+X"); }, MenuItem::Copy => unsafe { - winuser::AppendMenuW( - self.hmenu, - winuser::MF_STRING, - COPY_ID, - to_wstring("&Copy\tCtrl+C").as_mut_ptr(), - ); + AppendMenuW(self.hmenu, MF_STRING, COPY_ID, "&Copy\tCtrl+C"); }, MenuItem::Paste => unsafe { - winuser::AppendMenuW( - self.hmenu, - winuser::MF_STRING, - PASTE_ID, - to_wstring("&Paste\tCtrl+V").as_mut_ptr(), - ); + AppendMenuW(self.hmenu, MF_STRING, PASTE_ID, "&Paste\tCtrl+V"); }, MenuItem::SelectAll => unsafe { - winuser::AppendMenuW( - self.hmenu, - winuser::MF_STRING, - SELECT_ALL_ID, - to_wstring("&Select all\tCtrl+A").as_mut_ptr(), - ); + AppendMenuW(self.hmenu, MF_STRING, SELECT_ALL_ID, "&Select all\tCtrl+A"); }, MenuItem::Hide => unsafe { - winuser::AppendMenuW( - self.hmenu, - winuser::MF_STRING, - HIDE_ID, - to_wstring("&Hide\tCtrl+H").as_mut_ptr(), - ); + AppendMenuW(self.hmenu, MF_STRING, HIDE_ID, "&Hide\tCtrl+H"); }, MenuItem::CloseWindow => unsafe { - winuser::AppendMenuW( - self.hmenu, - winuser::MF_STRING, - CLOSE_ID, - to_wstring("&Close\tAlt+F4").as_mut_ptr(), - ); + AppendMenuW(self.hmenu, MF_STRING, CLOSE_ID, "&Close\tAlt+F4"); }, MenuItem::Quit => unsafe { - winuser::AppendMenuW( - self.hmenu, - winuser::MF_STRING, - QUIT_ID, - to_wstring("&Quit").as_mut_ptr(), - ); + AppendMenuW(self.hmenu, MF_STRING, QUIT_ID, "&Quit"); }, MenuItem::Minimize => unsafe { - winuser::AppendMenuW( - self.hmenu, - winuser::MF_STRING, - MINIMIZE_ID, - to_wstring("&Minimize").as_mut_ptr(), - ); + AppendMenuW(self.hmenu, MF_STRING, MINIMIZE_ID, "&Minimize"); }, // FIXME: create all shortcuts of MenuItem if possible... // like linux? @@ -324,7 +276,7 @@ impl Menu { impl Drop for Menu { fn drop(&mut self) { unsafe { - winuser::DestroyMenu(self.hmenu); + DestroyMenu(self.hmenu); } } } @@ -332,53 +284,40 @@ impl Menu { const MENU_SUBCLASS_ID: usize = 4568; -pub fn initialize( - menu_builder: Menu, - window_handle: RawWindowHandle, - menu_handler: MenuHandler, -) -> Option { - if let RawWindowHandle::Windows(handle) = window_handle { - let sender: *mut MenuHandler = Box::into_raw(Box::new(menu_handler)); - let menu = menu_builder.clone().hmenu(); - - unsafe { - commctrl::SetWindowSubclass( - handle.hwnd as _, - Some(subclass_proc), - MENU_SUBCLASS_ID, - sender as _, - ); - winuser::SetMenu(handle.hwnd as _, menu); - } +pub fn initialize(menu_builder: Menu, window: HWND, menu_handler: MenuHandler) -> HMENU { + let sender: *mut MenuHandler = Box::into_raw(Box::new(menu_handler)); + let menu = menu_builder.hmenu(); - if let Some(accels) = menu_builder.accels() { - register_accel(handle.hwnd as _, &accels); - } + unsafe { + SetWindowSubclass(window, Some(subclass_proc), MENU_SUBCLASS_ID, sender as _); + SetMenu(window, menu); + } - Some(menu) - } else { - None + if let Some(accels) = menu_builder.accels() { + register_accel(window, &accels); } + + menu } pub(crate) unsafe extern "system" fn subclass_proc( - hwnd: windef::HWND, - msg: minwindef::UINT, - wparam: minwindef::WPARAM, - lparam: minwindef::LPARAM, - _id: basetsd::UINT_PTR, - subclass_input_ptr: basetsd::DWORD_PTR, -) -> minwindef::LRESULT { + hwnd: HWND, + msg: u32, + wparam: WPARAM, + lparam: LPARAM, + _id: usize, + subclass_input_ptr: usize, +) -> LRESULT { let subclass_input_ptr = subclass_input_ptr as *mut MenuHandler; let subclass_input = &*(subclass_input_ptr); - if msg == winuser::WM_DESTROY { + if msg == WM_DESTROY { Box::from_raw(subclass_input_ptr); } match msg { - winuser::WM_COMMAND => { - match wparam { + win32wm::WM_COMMAND => { + match wparam.0 { CUT_ID => { execute_edit_command(EditCommand::Cut); } @@ -392,30 +331,31 @@ pub(crate) unsafe extern "system" fn subclass_proc( execute_edit_command(EditCommand::SelectAll); } HIDE_ID => { - winuser::ShowWindow(hwnd, winuser::SW_HIDE); + ShowWindow(hwnd, SW_HIDE); } CLOSE_ID => { subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(hwnd)), + window_id: RootWindowId(WindowId(hwnd.0)), event: WindowEvent::CloseRequested, }); } QUIT_ID => { subclass_input.send_event(Event::LoopDestroyed); + PostQuitMessage(0); } MINIMIZE_ID => { - winuser::ShowWindow(hwnd, winuser::SW_MINIMIZE); + ShowWindow(hwnd, SW_MINIMIZE); } _ => { - let menu_id = minwindef::LOWORD(wparam as _); + let menu_id = util::LOWORD(wparam.0 as u32); if MENU_IDS.lock().unwrap().contains(&menu_id) { subclass_input.send_menu_event(menu_id); } } } - 0 + LRESULT(0) } - _ => commctrl::DefSubclassProc(hwnd, msg, wparam, lparam), + _ => DefSubclassProc(hwnd, msg, wparam, lparam), } } @@ -434,53 +374,53 @@ fn execute_edit_command(command: EditCommand) { }; unsafe { - let mut inputs: [winuser::INPUT; 4] = std::mem::zeroed(); - inputs[0].type_ = winuser::INPUT_KEYBOARD; - inputs[0].u.ki_mut().wVk = winuser::VK_CONTROL as _; + let mut inputs: [INPUT; 4] = std::mem::zeroed(); + inputs[0].r#type = INPUT_KEYBOARD; + inputs[0].Anonymous.ki.wVk = VK_CONTROL as _; - inputs[1].type_ = winuser::INPUT_KEYBOARD; - inputs[1].u.ki_mut().wVk = key; + inputs[1].r#type = INPUT_KEYBOARD; + inputs[1].Anonymous.ki.wVk = key as VIRTUAL_KEY; - inputs[2].type_ = winuser::INPUT_KEYBOARD; - inputs[2].u.ki_mut().wVk = key; - inputs[2].u.ki_mut().dwFlags = winuser::KEYEVENTF_KEYUP; + inputs[2].r#type = INPUT_KEYBOARD; + inputs[2].Anonymous.ki.wVk = key as VIRTUAL_KEY; + inputs[2].Anonymous.ki.dwFlags = KEYEVENTF_KEYUP; - inputs[3].type_ = winuser::INPUT_KEYBOARD; - inputs[3].u.ki_mut().wVk = winuser::VK_CONTROL as _; - inputs[3].u.ki_mut().dwFlags = winuser::KEYEVENTF_KEYUP; + inputs[3].r#type = INPUT_KEYBOARD; + inputs[3].Anonymous.ki.wVk = VK_CONTROL as _; + inputs[3].Anonymous.ki.dwFlags = KEYEVENTF_KEYUP; - winuser::SendInput( + SendInput( inputs.len() as _, inputs.as_mut_ptr(), - std::mem::size_of::() as _, + std::mem::size_of::() as _, ); } } // Convert a hotkey to an accelerator. -fn convert_accelerator(id: u16, key: Accelerator) -> Option { - let mut virt_key = winuser::FVIRTKEY; +fn convert_accelerator(id: u16, key: Accelerator) -> Option { + let mut virt_key = FVIRTKEY; let key_mods: ModifiersState = key.mods; if key_mods.control_key() { - virt_key |= winuser::FCONTROL; + virt_key |= FCONTROL; } if key_mods.alt_key() { - virt_key |= winuser::FALT; + virt_key |= FALT; } if key_mods.shift_key() { - virt_key |= winuser::FSHIFT; + virt_key |= FSHIFT; } let raw_key = if let Some(vk_code) = key_to_vk(&key.key) { let mod_code = vk_code >> 8; if mod_code & 0x1 != 0 { - virt_key |= winuser::FSHIFT; + virt_key |= FSHIFT; } if mod_code & 0x02 != 0 { - virt_key |= winuser::FCONTROL; + virt_key |= FCONTROL; } if mod_code & 0x04 != 0 { - virt_key |= winuser::FALT; + virt_key |= FALT; } vk_code & 0x00ff } else { @@ -488,8 +428,8 @@ fn convert_accelerator(id: u16, key: Accelerator) -> Option { return None; }; - Some(winuser::ACCEL { - fVirt: virt_key, + Some(ACCEL { + fVirt: virt_key as u8, key: raw_key as u16, cmd: id, }) @@ -547,6 +487,19 @@ fn format_hotkey(key: Accelerator, s: &mut String) { KeyCode::Digit7 => s.push('7'), KeyCode::Digit8 => s.push('8'), KeyCode::Digit9 => s.push('9'), + KeyCode::Comma => s.push(','), + KeyCode::Minus => s.push('-'), + KeyCode::Period => s.push('.'), + KeyCode::Space => s.push_str("Space"), + KeyCode::Equal => s.push('='), + KeyCode::Semicolon => s.push(';'), + KeyCode::Slash => s.push('/'), + KeyCode::Backslash => s.push('\\'), + KeyCode::Quote => s.push('\''), + KeyCode::Backquote => s.push('`'), + KeyCode::BracketLeft => s.push('['), + KeyCode::BracketRight => s.push(']'), + KeyCode::Tab => s.push_str("Tab"), KeyCode::Escape => s.push_str("Esc"), KeyCode::Delete => s.push_str("Del"), KeyCode::Insert => s.push_str("Ins"), diff --git a/src/platform_impl/windows/minimal_ime.rs b/src/platform_impl/windows/minimal_ime.rs index 10e6fef24e..648758b167 100644 --- a/src/platform_impl/windows/minimal_ime.rs +++ b/src/platform_impl/windows/minimal_ime.rs @@ -1,26 +1,23 @@ use std::mem::MaybeUninit; -use winapi::{ - shared::{ - minwindef::{LPARAM, WPARAM}, - windef::HWND, - }, - um::winuser, +use windows::Win32::{ + Foundation::{HWND, LPARAM, LRESULT, WPARAM}, + UI::WindowsAndMessaging::{self as win32wm, *}, }; use crate::platform_impl::platform::event_loop::ProcResult; pub fn is_msg_ime_related(msg_kind: u32) -> bool { - match msg_kind { - winuser::WM_IME_COMPOSITION - | winuser::WM_IME_COMPOSITIONFULL - | winuser::WM_IME_STARTCOMPOSITION - | winuser::WM_IME_ENDCOMPOSITION - | winuser::WM_IME_CHAR - | winuser::WM_CHAR - | winuser::WM_SYSCHAR => true, - _ => false, - } + matches!( + msg_kind, + win32wm::WM_IME_COMPOSITION + | win32wm::WM_IME_COMPOSITIONFULL + | win32wm::WM_IME_STARTCOMPOSITION + | win32wm::WM_IME_ENDCOMPOSITION + | win32wm::WM_IME_CHAR + | win32wm::WM_CHAR + | win32wm::WM_SYSCHAR + ) } pub struct MinimalIme { @@ -47,30 +44,30 @@ impl MinimalIme { result: &mut ProcResult, ) -> Option { match msg_kind { - winuser::WM_IME_ENDCOMPOSITION => { + win32wm::WM_IME_ENDCOMPOSITION => { self.getting_ime_text = true; } - winuser::WM_CHAR | winuser::WM_SYSCHAR => { + win32wm::WM_CHAR | win32wm::WM_SYSCHAR => { + *result = ProcResult::Value(LRESULT(0)); if self.getting_ime_text { - *result = ProcResult::Value(0); - self.utf16parts.push(wparam as u16); + self.utf16parts.push(wparam.0 as u16); let more_char_coming; unsafe { let mut next_msg = MaybeUninit::uninit(); - let has_message = winuser::PeekMessageW( + let has_message = PeekMessageW( next_msg.as_mut_ptr(), hwnd, - winuser::WM_KEYFIRST, - winuser::WM_KEYLAST, - winuser::PM_NOREMOVE, + WM_KEYFIRST, + WM_KEYLAST, + PM_NOREMOVE, ); - let has_message = has_message != 0; + let has_message = has_message.as_bool(); if !has_message { more_char_coming = false; } else { let next_msg = next_msg.assume_init().message; - if next_msg == winuser::WM_CHAR || next_msg == winuser::WM_SYSCHAR { + if next_msg == WM_CHAR || next_msg == WM_SYSCHAR { more_char_coming = true; } else { more_char_coming = false; @@ -83,6 +80,8 @@ impl MinimalIme { self.getting_ime_text = false; return result; } + } else { + return String::from_utf16(&[wparam.0 as u16]).ok(); } } _ => (), diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 233c1986f9..8890054224 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -3,9 +3,9 @@ #![cfg(target_os = "windows")] -use winapi::{ - self, - shared::windef::{HMENU, HWND}, +use windows::Win32::{ + Foundation::{HANDLE, HWND}, + UI::WindowsAndMessaging::HMENU, }; pub use self::{ @@ -70,12 +70,12 @@ unsafe impl Sync for PlatformSpecificWindowBuilderAttributes {} // Cursor name in UTF-16. Used to set cursor in `WM_SETCURSOR`. #[derive(Debug, Clone, Copy)] -pub struct Cursor(pub *const winapi::ctypes::wchar_t); +pub struct Cursor(pub *const u16); unsafe impl Send for Cursor {} unsafe impl Sync for Cursor {} #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DeviceId(u32); +pub struct DeviceId(isize); impl DeviceId { pub unsafe fn dummy() -> Self { @@ -86,7 +86,7 @@ impl DeviceId { impl DeviceId { pub fn persistent_identifier(&self) -> Option { if self.0 != 0 { - raw_input::get_raw_input_device_name(self.0 as _) + raw_input::get_raw_input_device_name(HANDLE(self.0)) } else { None } @@ -113,7 +113,7 @@ impl std::fmt::Display for OsError { // Constant device ID, to be removed when this backend is updated to report real device IDs. const DEVICE_ID: RootDeviceId = RootDeviceId(DeviceId(0)); -fn wrap_device_id(id: u32) -> RootDeviceId { +fn wrap_device_id(id: isize) -> RootDeviceId { RootDeviceId(DeviceId(id)) } @@ -124,15 +124,13 @@ pub struct KeyEventExtra { } #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct WindowId(HWND); +pub struct WindowId(isize); unsafe impl Send for WindowId {} unsafe impl Sync for WindowId {} impl WindowId { pub unsafe fn dummy() -> Self { - use std::ptr::null_mut; - - WindowId(null_mut()) + WindowId(0) } } diff --git a/src/platform_impl/windows/monitor.rs b/src/platform_impl/windows/monitor.rs index 503bb4da08..eb8a89c16c 100644 --- a/src/platform_impl/windows/monitor.rs +++ b/src/platform_impl/windows/monitor.rs @@ -1,12 +1,9 @@ // Copyright 2019-2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -use winapi::{ - shared::{ - minwindef::{BOOL, DWORD, LPARAM, TRUE, WORD}, - windef::{HDC, HMONITOR, HWND, LPRECT, POINT}, - }, - um::{wingdi, winuser}, +use windows::Win32::{ + Foundation::{BOOL, HWND, LPARAM, POINT, PWSTR, RECT}, + Graphics::Gdi::*, }; use std::{ @@ -30,7 +27,7 @@ pub struct VideoMode { pub(crate) bit_depth: u16, pub(crate) refresh_rate: u16, pub(crate) monitor: MonitorHandle, - pub(crate) native_video_mode: wingdi::DEVMODEW, + pub(crate) native_video_mode: DEVMODEW, } impl PartialEq for VideoMode { @@ -85,34 +82,27 @@ impl VideoMode { } #[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] -pub struct MonitorHandle(HMONITOR); - -// Send is not implemented for HMONITOR, we have to wrap it and implement it manually. -// For more info see: -// https://github.com/retep998/winapi-rs/issues/360 -// https://github.com/retep998/winapi-rs/issues/396 - -unsafe impl Send for MonitorHandle {} +pub struct MonitorHandle(isize); unsafe extern "system" fn monitor_enum_proc( hmonitor: HMONITOR, _hdc: HDC, - _place: LPRECT, + _place: *mut RECT, data: LPARAM, ) -> BOOL { - let monitors = data as *mut VecDeque; + let monitors = data.0 as *mut VecDeque; (*monitors).push_back(MonitorHandle::new(hmonitor)); - TRUE // continue enumeration + true.into() // continue enumeration } pub fn available_monitors() -> VecDeque { let mut monitors: VecDeque = VecDeque::new(); unsafe { - winuser::EnumDisplayMonitors( - ptr::null_mut(), + EnumDisplayMonitors( + HDC::default(), ptr::null_mut(), Some(monitor_enum_proc), - &mut monitors as *mut _ as LPARAM, + LPARAM(&mut monitors as *mut _ as _), ); } monitors @@ -120,12 +110,12 @@ pub fn available_monitors() -> VecDeque { pub fn primary_monitor() -> MonitorHandle { const ORIGIN: POINT = POINT { x: 0, y: 0 }; - let hmonitor = unsafe { winuser::MonitorFromPoint(ORIGIN, winuser::MONITOR_DEFAULTTOPRIMARY) }; + let hmonitor = unsafe { MonitorFromPoint(ORIGIN, MONITOR_DEFAULTTOPRIMARY) }; MonitorHandle::new(hmonitor) } pub fn current_monitor(hwnd: HWND) -> MonitorHandle { - let hmonitor = unsafe { winuser::MonitorFromWindow(hwnd, winuser::MONITOR_DEFAULTTONEAREST) }; + let hmonitor = unsafe { MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST) }; MonitorHandle::new(hmonitor) } @@ -140,16 +130,16 @@ impl Window { } } -pub(crate) fn get_monitor_info(hmonitor: HMONITOR) -> Result { - let mut monitor_info: winuser::MONITORINFOEXW = unsafe { mem::zeroed() }; - monitor_info.cbSize = mem::size_of::() as DWORD; +pub(crate) fn get_monitor_info(hmonitor: HMONITOR) -> Result { + let mut monitor_info = MONITORINFOEXW::default(); + monitor_info.monitorInfo.cbSize = mem::size_of::() as u32; let status = unsafe { - winuser::GetMonitorInfoW( + GetMonitorInfoW( hmonitor, - &mut monitor_info as *mut winuser::MONITORINFOEXW as *mut winuser::MONITORINFO, + &mut monitor_info as *mut MONITORINFOEXW as *mut MONITORINFO, ) }; - if status == 0 { + if !status.as_bool() { Err(io::Error::last_os_error()) } else { Ok(monitor_info) @@ -158,13 +148,15 @@ pub(crate) fn get_monitor_info(hmonitor: HMONITOR) -> Result Self { - MonitorHandle(hmonitor) + MonitorHandle(hmonitor.0) } #[inline] pub fn name(&self) -> Option { - let monitor_info = get_monitor_info(self.0).unwrap(); - Some(util::wchar_ptr_to_string(monitor_info.szDevice.as_ptr())) + let mut monitor_info = get_monitor_info(self.hmonitor()).unwrap(); + Some(util::wchar_ptr_to_string(PWSTR( + monitor_info.szDevice.as_mut_ptr(), + ))) } #[inline] @@ -174,30 +166,32 @@ impl MonitorHandle { #[inline] pub fn hmonitor(&self) -> HMONITOR { - self.0 + HMONITOR(self.0) } #[inline] pub fn size(&self) -> PhysicalSize { - let monitor_info = get_monitor_info(self.0).unwrap(); + let monitor_info = get_monitor_info(self.hmonitor()).unwrap(); PhysicalSize { - width: (monitor_info.rcMonitor.right - monitor_info.rcMonitor.left) as u32, - height: (monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top) as u32, + width: (monitor_info.monitorInfo.rcMonitor.right - monitor_info.monitorInfo.rcMonitor.left) + as u32, + height: (monitor_info.monitorInfo.rcMonitor.bottom - monitor_info.monitorInfo.rcMonitor.top) + as u32, } } #[inline] pub fn position(&self) -> PhysicalPosition { - let monitor_info = get_monitor_info(self.0).unwrap(); + let monitor_info = get_monitor_info(self.hmonitor()).unwrap(); PhysicalPosition { - x: monitor_info.rcMonitor.left, - y: monitor_info.rcMonitor.top, + x: monitor_info.monitorInfo.rcMonitor.left, + y: monitor_info.monitorInfo.rcMonitor.top, } } #[inline] pub fn scale_factor(&self) -> f64 { - dpi_to_scale_factor(get_monitor_dpi(self.0).unwrap_or(96)) + dpi_to_scale_factor(get_monitor_dpi(self.hmonitor()).unwrap_or(96)) } #[inline] @@ -210,19 +204,19 @@ impl MonitorHandle { loop { unsafe { - let monitor_info = get_monitor_info(self.0).unwrap(); - let device_name = monitor_info.szDevice.as_ptr(); - let mut mode: wingdi::DEVMODEW = mem::zeroed(); - mode.dmSize = mem::size_of_val(&mode) as WORD; - if winuser::EnumDisplaySettingsExW(device_name, i, &mut mode, 0) == 0 { + let mut monitor_info = get_monitor_info(self.hmonitor()).unwrap(); + let device_name = PWSTR(monitor_info.szDevice.as_mut_ptr()); + let mut mode: DEVMODEW = mem::zeroed(); + mode.dmSize = mem::size_of_val(&mode) as u16; + if !EnumDisplaySettingsExW(device_name, i as ENUM_DISPLAY_SETTINGS_MODE, &mut mode, 0) + .as_bool() + { break; } i += 1; - const REQUIRED_FIELDS: DWORD = wingdi::DM_BITSPERPEL - | wingdi::DM_PELSWIDTH - | wingdi::DM_PELSHEIGHT - | wingdi::DM_DISPLAYFREQUENCY; + const REQUIRED_FIELDS: u32 = + (DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY) as u32; assert!(mode.dmFields & REQUIRED_FIELDS == REQUIRED_FIELDS); modes.insert(RootVideoMode { diff --git a/src/platform_impl/windows/raw_input.rs b/src/platform_impl/windows/raw_input.rs index 7ee5249856..60dec6a3b8 100644 --- a/src/platform_impl/windows/raw_input.rs +++ b/src/platform_impl/windows/raw_input.rs @@ -6,21 +6,12 @@ use std::{ ptr, }; -use winapi::{ - ctypes::wchar_t, - shared::{ - hidusage::{HID_USAGE_GENERIC_KEYBOARD, HID_USAGE_GENERIC_MOUSE, HID_USAGE_PAGE_GENERIC}, - minwindef::{TRUE, UINT, USHORT}, - windef::HWND, - }, - um::{ - winnt::HANDLE, - winuser::{ - self, HRAWINPUT, RAWINPUT, RAWINPUTDEVICE, RAWINPUTDEVICELIST, RAWINPUTHEADER, - RIDEV_DEVNOTIFY, RIDEV_INPUTSINK, RIDI_DEVICEINFO, RIDI_DEVICENAME, RID_DEVICE_INFO, - RID_DEVICE_INFO_HID, RID_DEVICE_INFO_KEYBOARD, RID_DEVICE_INFO_MOUSE, RID_INPUT, RIM_TYPEHID, - RIM_TYPEKEYBOARD, RIM_TYPEMOUSE, - }, +use windows::Win32::{ + Devices::HumanInterfaceDevice::*, + Foundation::{HANDLE, HWND}, + UI::{ + Input::{self as win32i, *}, + WindowsAndMessaging::*, }, }; @@ -28,22 +19,21 @@ use crate::{event::ElementState, platform_impl::platform::util}; #[allow(dead_code)] pub fn get_raw_input_device_list() -> Option> { - let list_size = size_of::() as UINT; + let list_size = size_of::() as u32; let mut num_devices = 0; - let status = - unsafe { winuser::GetRawInputDeviceList(ptr::null_mut(), &mut num_devices, list_size) }; + let status = unsafe { GetRawInputDeviceList(ptr::null_mut(), &mut num_devices, list_size) }; - if status == UINT::max_value() { + if status == u32::max_value() { return None; } let mut buffer = Vec::with_capacity(num_devices as _); let num_stored = - unsafe { winuser::GetRawInputDeviceList(buffer.as_ptr() as _, &mut num_devices, list_size) }; + unsafe { GetRawInputDeviceList(buffer.as_ptr() as _, &mut num_devices, list_size) }; - if num_stored == UINT::max_value() { + if num_stored == u32::max_value() { return None; } @@ -66,9 +56,9 @@ impl From for RawDeviceInfo { fn from(info: RID_DEVICE_INFO) -> Self { unsafe { match info.dwType { - RIM_TYPEMOUSE => RawDeviceInfo::Mouse(*info.u.mouse()), - RIM_TYPEKEYBOARD => RawDeviceInfo::Keyboard(*info.u.keyboard()), - RIM_TYPEHID => RawDeviceInfo::Hid(*info.u.hid()), + win32i::RIM_TYPEMOUSE => RawDeviceInfo::Mouse(info.Anonymous.mouse), + win32i::RIM_TYPEKEYBOARD => RawDeviceInfo::Keyboard(info.Anonymous.keyboard), + win32i::RIM_TYPEHID => RawDeviceInfo::Hid(info.Anonymous.hid), _ => unreachable!(), } } @@ -78,13 +68,13 @@ impl From for RawDeviceInfo { #[allow(dead_code)] pub fn get_raw_input_device_info(handle: HANDLE) -> Option { let mut info: RID_DEVICE_INFO = unsafe { mem::zeroed() }; - let info_size = size_of::() as UINT; + let info_size = size_of::() as u32; info.cbSize = info_size; let mut minimum_size = 0; let status = unsafe { - winuser::GetRawInputDeviceInfoW( + GetRawInputDeviceInfoW( handle, RIDI_DEVICEINFO, &mut info as *mut _ as _, @@ -92,7 +82,7 @@ pub fn get_raw_input_device_info(handle: HANDLE) -> Option { ) }; - if status == UINT::max_value() || status == 0 { + if status == u32::max_value() || status == 0 { return None; } @@ -103,18 +93,17 @@ pub fn get_raw_input_device_info(handle: HANDLE) -> Option { pub fn get_raw_input_device_name(handle: HANDLE) -> Option { let mut minimum_size = 0; - let status = unsafe { - winuser::GetRawInputDeviceInfoW(handle, RIDI_DEVICENAME, ptr::null_mut(), &mut minimum_size) - }; + let status = + unsafe { GetRawInputDeviceInfoW(handle, RIDI_DEVICENAME, ptr::null_mut(), &mut minimum_size) }; if status != 0 { return None; } - let mut name: Vec = Vec::with_capacity(minimum_size as _); + let mut name: Vec = Vec::with_capacity(minimum_size as _); let status = unsafe { - winuser::GetRawInputDeviceInfoW( + GetRawInputDeviceInfoW( handle, RIDI_DEVICENAME, name.as_ptr() as _, @@ -122,7 +111,7 @@ pub fn get_raw_input_device_name(handle: HANDLE) -> Option { ) }; - if status == UINT::max_value() || status == 0 { + if status == u32::max_value() || status == 0 { return None; } @@ -134,13 +123,12 @@ pub fn get_raw_input_device_name(handle: HANDLE) -> Option { } pub fn register_raw_input_devices(devices: &[RAWINPUTDEVICE]) -> bool { - let device_size = size_of::() as UINT; + let device_size = size_of::() as u32; - let success = unsafe { - winuser::RegisterRawInputDevices(devices.as_ptr() as _, devices.len() as _, device_size) - }; + let success = + unsafe { RegisterRawInputDevices(devices.as_ptr() as _, devices.len() as _, device_size) }; - success == TRUE + success.as_bool() } pub fn register_all_mice_and_keyboards_for_raw_input(window_handle: HWND) -> bool { @@ -168,11 +156,11 @@ pub fn register_all_mice_and_keyboards_for_raw_input(window_handle: HWND) -> boo pub fn get_raw_input_data(handle: HRAWINPUT) -> Option { let mut data: RAWINPUT = unsafe { mem::zeroed() }; - let mut data_size = size_of::() as UINT; - let header_size = size_of::() as UINT; + let mut data_size = size_of::() as u32; + let header_size = size_of::() as u32; let status = unsafe { - winuser::GetRawInputData( + GetRawInputData( handle, RID_INPUT, &mut data as *mut _ as _, @@ -181,7 +169,7 @@ pub fn get_raw_input_data(handle: HRAWINPUT) -> Option { ) }; - if status == UINT::max_value() || status == 0 { + if status == u32::max_value() || status == 0 { return None; } @@ -189,36 +177,36 @@ pub fn get_raw_input_data(handle: HRAWINPUT) -> Option { } fn button_flags_to_element_state( - button_flags: USHORT, - down_flag: USHORT, - up_flag: USHORT, + button_flags: u16, + down_flag: u32, + up_flag: u32, ) -> Option { // We assume the same button won't be simultaneously pressed and released. - if util::has_flag(button_flags, down_flag) { + if util::has_flag(button_flags, down_flag as u16) { Some(ElementState::Pressed) - } else if util::has_flag(button_flags, up_flag) { + } else if util::has_flag(button_flags, up_flag as u16) { Some(ElementState::Released) } else { None } } -pub fn get_raw_mouse_button_state(button_flags: USHORT) -> [Option; 3] { +pub fn get_raw_mouse_button_state(button_flags: u16) -> [Option; 3] { [ button_flags_to_element_state( button_flags, - winuser::RI_MOUSE_LEFT_BUTTON_DOWN, - winuser::RI_MOUSE_LEFT_BUTTON_UP, + RI_MOUSE_LEFT_BUTTON_DOWN, + RI_MOUSE_LEFT_BUTTON_UP, ), button_flags_to_element_state( button_flags, - winuser::RI_MOUSE_MIDDLE_BUTTON_DOWN, - winuser::RI_MOUSE_MIDDLE_BUTTON_UP, + RI_MOUSE_MIDDLE_BUTTON_DOWN, + RI_MOUSE_MIDDLE_BUTTON_UP, ), button_flags_to_element_state( button_flags, - winuser::RI_MOUSE_RIGHT_BUTTON_DOWN, - winuser::RI_MOUSE_RIGHT_BUTTON_UP, + RI_MOUSE_RIGHT_BUTTON_DOWN, + RI_MOUSE_RIGHT_BUTTON_UP, ), ] } diff --git a/src/platform_impl/windows/system_tray.rs b/src/platform_impl/windows/system_tray.rs index 113fcc090a..bcb483b6b8 100644 --- a/src/platform_impl/windows/system_tray.rs +++ b/src/platform_impl/windows/system_tray.rs @@ -2,28 +2,23 @@ // SPDX-License-Identifier: Apache-2.0 use super::{ - dpi::{dpi_to_scale_factor, hwnd_dpi}, menu::{subclass_proc as menu_subclass_proc, Menu, MenuHandler}, util, OsError, }; use crate::{ - dpi::{LogicalPosition, LogicalSize}, + dpi::{PhysicalPosition, PhysicalSize}, error::OsError as RootOsError, event::{Event, Rectangle, TrayEvent}, event_loop::EventLoopWindowTarget, menu::MenuType, system_tray::SystemTray as RootSystemTray, }; -use winapi::{ - shared::{ - basetsd::{DWORD_PTR, UINT_PTR}, - minwindef::{LPARAM, LRESULT, UINT, WPARAM}, - windef::{HICON, HMENU, HWND, POINT, RECT}, - }, - um::{ - commctrl, libloaderapi, - shellapi::{self, NIF_ICON, NIF_MESSAGE, NIM_ADD, NIM_DELETE, NIM_MODIFY, NOTIFYICONDATAW}, - winuser::{self, CW_USEDEFAULT, WNDCLASSW, WS_OVERLAPPEDWINDOW}, +use windows::Win32::{ + Foundation::{HWND, LPARAM, LRESULT, POINT, PSTR, PWSTR, WPARAM}, + System::LibraryLoader::*, + UI::{ + Shell::*, + WindowsAndMessaging::{self as win32wm, *}, }, }; @@ -56,37 +51,37 @@ impl SystemTrayBuilder { ) -> Result { let hmenu: Option = self.tray_menu.map(|m| m.hmenu()); - let class_name = util::to_wstring("tao_system_tray_app"); + let mut class_name = util::to_wstring("tao_system_tray_app"); unsafe { - let hinstance = libloaderapi::GetModuleHandleA(std::ptr::null_mut()); + let hinstance = GetModuleHandleA(PSTR::default()); let wnd_class = WNDCLASSW { - lpfnWndProc: Some(winuser::DefWindowProcW), - lpszClassName: class_name.as_ptr(), + lpfnWndProc: Some(util::call_default_window_proc), + lpszClassName: PWSTR(class_name.as_mut_ptr()), hInstance: hinstance, ..Default::default() }; - winuser::RegisterClassW(&wnd_class); + RegisterClassW(&wnd_class); - let hwnd = winuser::CreateWindowExW( + let hwnd = CreateWindowExW( 0, - class_name.as_ptr(), - util::to_wstring("tao_system_tray_window").as_ptr(), + PWSTR(class_name.as_mut_ptr()), + "tao_system_tray_window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, - 0 as _, - 0 as _, - hinstance as _, + HWND::default(), + HMENU::default(), + hinstance, std::ptr::null_mut(), ); - if hwnd.is_null() { + if hwnd.is_invalid() { return Err(os_error!(OsError::CreationError( - "Unable to get valid mutable pointer for winuser::CreateWindowEx" + "Unable to get valid mutable pointer for CreateWindowEx" ))); } @@ -95,10 +90,10 @@ impl SystemTrayBuilder { hWnd: hwnd, uID: TRAYICON_UID, uCallbackMessage: WM_USER_TRAYICON, - ..Default::default() + ..std::mem::zeroed() }; - if shellapi::Shell_NotifyIconW(NIM_ADD, &mut nid as _) == 0 { + if !Shell_NotifyIconW(NIM_ADD, &mut nid as _).as_bool() { return Err(os_error!(OsError::CreationError( "Error with shellapi::Shell_NotifyIconW" ))); @@ -117,7 +112,7 @@ impl SystemTrayBuilder { } }), }; - commctrl::SetWindowSubclass( + SetWindowSubclass( hwnd, Some(tray_subclass_proc), TRAY_SUBCLASS_ID, @@ -135,8 +130,8 @@ impl SystemTrayBuilder { MenuType::ContextMenu, None, ); - commctrl::SetWindowSubclass( - hwnd as _, + SetWindowSubclass( + hwnd, Some(menu_subclass_proc), TRAY_MENU_SUBCLASS_ID, Box::into_raw(Box::new(menu_handler)) as _, @@ -169,9 +164,9 @@ impl SystemTray { hWnd: self.hwnd, hIcon: icon, uID: TRAYICON_UID, - ..Default::default() + ..std::mem::zeroed() }; - if shellapi::Shell_NotifyIconW(NIM_MODIFY, &mut nid as _) == 0 { + if !Shell_NotifyIconW(NIM_MODIFY, &mut nid as _).as_bool() { debug!("Error setting icon"); } } @@ -180,11 +175,11 @@ impl SystemTray { pub fn set_menu(&mut self, tray_menu: &Menu) { unsafe { // send the new menu to the subclass proc where we will update there - winuser::SendMessageW( + SendMessageW( self.hwnd, WM_USER_UPDATE_TRAYMENU, - tray_menu.hmenu() as _, - 0, + WPARAM(tray_menu.hmenu().0 as _), + LPARAM(0), ); } } @@ -198,70 +193,65 @@ impl Drop for SystemTray { uFlags: NIF_ICON, hWnd: self.hwnd, uID: TRAYICON_UID, - ..Default::default() + ..std::mem::zeroed() }; - if shellapi::Shell_NotifyIconW(NIM_DELETE, &mut nid as _) == 0 { + if !Shell_NotifyIconW(NIM_DELETE, &mut nid as _).as_bool() { debug!("Error removing system tray icon"); } // destroy the hidden window used by the tray - winuser::DestroyWindow(self.hwnd); + DestroyWindow(self.hwnd); } } } unsafe extern "system" fn tray_subclass_proc( hwnd: HWND, - msg: UINT, + msg: u32, wparam: WPARAM, lparam: LPARAM, - _id: UINT_PTR, - subclass_input_ptr: DWORD_PTR, + _id: usize, + subclass_input_ptr: usize, ) -> LRESULT { let subclass_input_ptr = subclass_input_ptr as *mut TrayLoopData; let mut subclass_input = &mut *(subclass_input_ptr); - if msg == winuser::WM_DESTROY { + if msg == WM_DESTROY { Box::from_raw(subclass_input_ptr); } if msg == WM_USER_UPDATE_TRAYMENU { - subclass_input.hmenu = Some(wparam as HMENU); + subclass_input.hmenu = Some(HMENU(wparam.0 as _)); } if msg == WM_USER_TRAYICON && matches!( - lparam as u32, - winuser::WM_LBUTTONUP | winuser::WM_RBUTTONUP | winuser::WM_LBUTTONDBLCLK + lparam.0 as u32, + WM_LBUTTONUP | WM_RBUTTONUP | WM_LBUTTONDBLCLK ) { - let mut icon_rect = RECT::default(); - let nid = shellapi::NOTIFYICONIDENTIFIER { + let nid = NOTIFYICONIDENTIFIER { hWnd: hwnd, - cbSize: std::mem::size_of::() as _, + cbSize: std::mem::size_of::() as _, uID: TRAYICON_UID, - ..Default::default() + ..std::mem::zeroed() }; - shellapi::Shell_NotifyIconGetRect(&nid, &mut icon_rect); - - let dpi = hwnd_dpi(hwnd); - let scale_factor = dpi_to_scale_factor(dpi); + let icon_rect = Shell_NotifyIconGetRect(&nid).unwrap_or_default(); let mut cursor = POINT { x: 0, y: 0 }; - winuser::GetCursorPos(&mut cursor as _); + GetCursorPos(&mut cursor as _); - let position = LogicalPosition::new(cursor.x, cursor.y).to_physical(scale_factor); + let position = PhysicalPosition::new(cursor.x as f64, cursor.y as f64); let bounds = Rectangle { - position: LogicalPosition::new(icon_rect.left, icon_rect.top).to_physical(scale_factor), - size: LogicalSize::new( - icon_rect.right - icon_rect.left, - icon_rect.bottom - icon_rect.top, - ) - .to_physical(scale_factor), + position: PhysicalPosition::new(icon_rect.left as f64, icon_rect.top as f64), + size: PhysicalSize::new( + (icon_rect.right - icon_rect.left) as f64, + (icon_rect.bottom - icon_rect.top) as f64, + ), }; - match lparam as u32 { - winuser::WM_LBUTTONUP => { + match lparam.0 as u32 { + win32wm::WM_LBUTTONUP => { (subclass_input.sender)(Event::TrayEvent { event: TrayEvent::LeftClick, position, @@ -269,7 +259,7 @@ unsafe extern "system" fn tray_subclass_proc( }); } - winuser::WM_RBUTTONUP => { + win32wm::WM_RBUTTONUP => { (subclass_input.sender)(Event::TrayEvent { event: TrayEvent::RightClick, position, @@ -281,7 +271,7 @@ unsafe extern "system" fn tray_subclass_proc( } } - winuser::WM_LBUTTONDBLCLK => { + win32wm::WM_LBUTTONDBLCLK => { (subclass_input.sender)(Event::TrayEvent { event: TrayEvent::DoubleClick, position, @@ -293,21 +283,21 @@ unsafe extern "system" fn tray_subclass_proc( } } - commctrl::DefSubclassProc(hwnd, msg, wparam, lparam) + DefSubclassProc(hwnd, msg, wparam, lparam) } unsafe fn show_tray_menu(hwnd: HWND, menu: HMENU, x: i32, y: i32) { // bring the hidden window to the foreground so the pop up menu // would automatically hide on click outside - winuser::SetForegroundWindow(hwnd); + SetForegroundWindow(hwnd); // track the click - winuser::TrackPopupMenu( + TrackPopupMenu( menu, - 0, + // align bottom / right, maybe we could expose this later.. + TPM_BOTTOMALIGN | TPM_LEFTALIGN, x, y, - // align bottom / right, maybe we could expose this later.. - (winuser::TPM_BOTTOMALIGN | winuser::TPM_LEFTALIGN) as _, + 0, hwnd, std::ptr::null_mut(), ); diff --git a/src/platform_impl/windows/util.rs b/src/platform_impl/windows/util.rs index a71c563433..85fd23ea06 100644 --- a/src/platform_impl/windows/util.rs +++ b/src/platform_impl/windows/util.rs @@ -4,24 +4,26 @@ use std::{ io, mem, ops::BitAnd, - os::{raw::c_void, windows::prelude::OsStrExt}, + os::windows::prelude::OsStrExt, ptr, slice, sync::atomic::{AtomicBool, Ordering}, }; use crate::{dpi::PhysicalSize, window::CursorIcon}; -use winapi::{ - ctypes::wchar_t, - shared::{ - minwindef::{BOOL, DWORD, TRUE, UINT}, - windef::{DPI_AWARENESS_CONTEXT, HICON, HMONITOR, HWND, LPRECT, RECT}, - }, - um::{ - libloaderapi::{GetProcAddress, LoadLibraryA}, - shellscalingapi::{MONITOR_DPI_TYPE, PROCESS_DPI_AWARENESS}, - winbase::lstrlenW, - winnt::{HRESULT, LONG, LPCSTR}, - winuser, + +use windows::{ + core::HRESULT, + Win32::{ + Foundation::{BOOL, FARPROC, HWND, LPARAM, LRESULT, POINT, PWSTR, RECT, WPARAM}, + Globalization::lstrlenW, + Graphics::Gdi::{ClientToScreen, InvalidateRgn, HMONITOR, HRGN}, + System::LibraryLoader::*, + UI::{ + HiDpi::*, + Input::KeyboardAndMouse::*, + TextServices::HKL, + WindowsAndMessaging::{self as win32wm, *}, + }, }, }; @@ -32,13 +34,13 @@ where bitset & flag == flag } -pub fn wchar_to_string(wchar: &[wchar_t]) -> String { +pub fn wchar_to_string(wchar: &[u16]) -> String { String::from_utf16_lossy(wchar) } -pub fn wchar_ptr_to_string(wchar: *const wchar_t) -> String { +pub fn wchar_ptr_to_string(wchar: PWSTR) -> String { let len = unsafe { lstrlenW(wchar) } as usize; - let wchar_slice = unsafe { slice::from_raw_parts(wchar, len) }; + let wchar_slice = unsafe { slice::from_raw_parts(wchar.0, len) }; wchar_to_string(wchar_slice) } @@ -51,7 +53,7 @@ pub fn to_wstring(str: &str) -> Vec { pub unsafe fn status_map BOOL>(mut fun: F) -> Option { let mut data: T = mem::zeroed(); - if fun(&mut data) != 0 { + if fun(&mut data).as_bool() { Some(data) } else { None @@ -59,7 +61,7 @@ pub unsafe fn status_map BOOL>(mut fun: F) -> Option { } fn win_to_err BOOL>(f: F) -> Result<(), io::Error> { - if f() != 0 { + if f().as_bool() { Ok(()) } else { Err(io::Error::last_os_error()) @@ -67,32 +69,33 @@ fn win_to_err BOOL>(f: F) -> Result<(), io::Error> { } pub fn get_window_rect(hwnd: HWND) -> Option { - unsafe { status_map(|rect| winuser::GetWindowRect(hwnd, rect)) } + unsafe { status_map(|rect| GetWindowRect(hwnd, rect)) } } pub fn get_client_rect(hwnd: HWND) -> Result { + let mut rect = RECT::default(); + let mut top_left = POINT::default(); + unsafe { - let mut rect = mem::zeroed(); - let mut top_left = mem::zeroed(); + win_to_err(|| ClientToScreen(hwnd, &mut top_left))?; + win_to_err(|| GetClientRect(hwnd, &mut rect))?; + } - win_to_err(|| winuser::ClientToScreen(hwnd, &mut top_left))?; - win_to_err(|| winuser::GetClientRect(hwnd, &mut rect))?; - rect.left += top_left.x; - rect.top += top_left.y; - rect.right += top_left.x; - rect.bottom += top_left.y; + rect.left += top_left.x; + rect.top += top_left.y; + rect.right += top_left.x; + rect.bottom += top_left.y; - Ok(rect) - } + Ok(rect) } pub fn adjust_size(hwnd: HWND, size: PhysicalSize) -> PhysicalSize { let (width, height): (u32, u32) = size.into(); let rect = RECT { left: 0, - right: width as LONG, + right: width as i32, top: 0, - bottom: height as LONG, + bottom: height as i32, }; let rect = adjust_window_rect(hwnd, rect).unwrap_or(rect); PhysicalSize::new((rect.right - rect.left) as _, (rect.bottom - rect.top) as _) @@ -105,57 +108,54 @@ pub(crate) fn set_inner_size_physical(window: HWND, x: u32, y: u32) { RECT { top: 0, left: 0, - bottom: y as LONG, - right: x as LONG, + bottom: y as i32, + right: x as i32, }, ) .expect("adjust_window_rect failed"); - let outer_x = (rect.right - rect.left).abs() as _; - let outer_y = (rect.top - rect.bottom).abs() as _; - winuser::SetWindowPos( + let outer_x = (rect.right - rect.left).abs(); + let outer_y = (rect.top - rect.bottom).abs(); + SetWindowPos( window, - ptr::null_mut(), + HWND::default(), 0, 0, outer_x, outer_y, - winuser::SWP_ASYNCWINDOWPOS - | winuser::SWP_NOZORDER - | winuser::SWP_NOREPOSITION - | winuser::SWP_NOMOVE - | winuser::SWP_NOACTIVATE, + SWP_ASYNCWINDOWPOS | SWP_NOZORDER | SWP_NOREPOSITION | SWP_NOMOVE | SWP_NOACTIVATE, ); - winuser::InvalidateRgn(window, ptr::null_mut(), 0); + InvalidateRgn(window, HRGN::default(), BOOL::default()); } } pub fn adjust_window_rect(hwnd: HWND, rect: RECT) -> Option { unsafe { - let style = winuser::GetWindowLongW(hwnd, winuser::GWL_STYLE); - let style_ex = winuser::GetWindowLongW(hwnd, winuser::GWL_EXSTYLE); - adjust_window_rect_with_styles(hwnd, style as _, style_ex as _, rect) + let style = GetWindowLongW(hwnd, GWL_STYLE) as WINDOW_STYLE; + let style_ex = GetWindowLongW(hwnd, GWL_EXSTYLE) as WINDOW_EX_STYLE; + adjust_window_rect_with_styles(hwnd, style, style_ex, rect) } } pub fn adjust_window_rect_with_styles( hwnd: HWND, - style: DWORD, - style_ex: DWORD, + style: WINDOW_STYLE, + style_ex: WINDOW_EX_STYLE, rect: RECT, ) -> Option { unsafe { status_map(|r| { *r = rect; - let b_menu = !winuser::GetMenu(hwnd).is_null() as BOOL; + let b_menu: BOOL = (!GetMenu(hwnd).is_invalid()).into(); + if let (Some(get_dpi_for_window), Some(adjust_window_rect_ex_for_dpi)) = (*GET_DPI_FOR_WINDOW, *ADJUST_WINDOW_RECT_EX_FOR_DPI) { let dpi = get_dpi_for_window(hwnd); - adjust_window_rect_ex_for_dpi(r, style as _, b_menu, style_ex as _, dpi) + adjust_window_rect_ex_for_dpi(r, style, b_menu, style_ex, dpi) } else { - winuser::AdjustWindowRectEx(r, style as _, b_menu, style_ex as _) + AdjustWindowRectEx(r, style, b_menu, style_ex) } }) } @@ -165,14 +165,14 @@ pub fn set_cursor_hidden(hidden: bool) { static HIDDEN: AtomicBool = AtomicBool::new(false); let changed = HIDDEN.swap(hidden, Ordering::SeqCst) ^ hidden; if changed { - unsafe { winuser::ShowCursor(!hidden as BOOL) }; + unsafe { ShowCursor(!hidden) }; } } pub fn get_cursor_clip() -> Result { unsafe { - let mut rect: RECT = mem::zeroed(); - win_to_err(|| winuser::GetClipCursor(&mut rect)).map(|_| rect) + let mut rect = RECT::default(); + win_to_err(|| GetClipCursor(&mut rect)).map(|_| rect) } } @@ -185,63 +185,46 @@ pub fn set_cursor_clip(rect: Option) -> Result<(), io::Error> { .as_ref() .map(|r| r as *const RECT) .unwrap_or(ptr::null()); - win_to_err(|| winuser::ClipCursor(rect_ptr)) + win_to_err(|| ClipCursor(rect_ptr)) } } pub fn get_desktop_rect() -> RECT { unsafe { - let left = winuser::GetSystemMetrics(winuser::SM_XVIRTUALSCREEN); - let top = winuser::GetSystemMetrics(winuser::SM_YVIRTUALSCREEN); + let left = GetSystemMetrics(SM_XVIRTUALSCREEN); + let top = GetSystemMetrics(SM_YVIRTUALSCREEN); RECT { left, top, - right: left + winuser::GetSystemMetrics(winuser::SM_CXVIRTUALSCREEN), - bottom: top + winuser::GetSystemMetrics(winuser::SM_CYVIRTUALSCREEN), + right: left + GetSystemMetrics(SM_CXVIRTUALSCREEN), + bottom: top + GetSystemMetrics(SM_CYVIRTUALSCREEN), } } } pub fn is_focused(window: HWND) -> bool { - window == unsafe { winuser::GetActiveWindow() } + window == unsafe { GetActiveWindow() } } pub fn is_visible(window: HWND) -> bool { - unsafe { winuser::IsWindowVisible(window) == TRUE } + unsafe { IsWindowVisible(window).as_bool() } } pub fn is_maximized(window: HWND) -> bool { + let mut placement = WINDOWPLACEMENT { + length: mem::size_of::() as u32, + ..WINDOWPLACEMENT::default() + }; unsafe { - let mut placement: winuser::WINDOWPLACEMENT = mem::zeroed(); - placement.length = mem::size_of::() as u32; - winuser::GetWindowPlacement(window, &mut placement); - placement.showCmd == winuser::SW_MAXIMIZE as u32 - } -} - -pub fn set_maximized(window: HWND, maximized: bool) { - unsafe { - if winuser::IsWindowVisible(window) != 0 { - winuser::ShowWindow( - window, - match maximized { - true => winuser::SW_MAXIMIZE, - false => winuser::SW_RESTORE, - }, - ); - } + GetWindowPlacement(window, &mut placement); } + placement.showCmd == SW_MAXIMIZE } pub fn get_hicon_from_buffer(buffer: &[u8], width: i32, height: i32) -> Option { unsafe { - match winuser::LookupIconIdFromDirectoryEx( - buffer.as_ptr() as _, - 1, - width, - height, - winuser::LR_DEFAULTCOLOR, - ) as isize + match LookupIconIdFromDirectoryEx(buffer.as_ptr() as _, true, width, height, LR_DEFAULTCOLOR) + as isize { 0 => { debug!("Unable to LookupIconIdFromDirectoryEx"); @@ -250,24 +233,26 @@ pub fn get_hicon_from_buffer(buffer: &[u8], width: i32, height: i32) -> Option { // once we got the pointer offset for the directory // lets create our resource - match winuser::CreateIconFromResourceEx( + match CreateIconFromResourceEx( buffer.as_ptr().offset(offset) as _, buffer.len() as _, - 1, + true, 0x00030000, 0, 0, - winuser::LR_DEFAULTCOLOR, - ) { + LR_DEFAULTCOLOR, + ) + .ok() + { // windows is really tough on icons // if a bad icon is provided it'll fail here or in // the LookupIconIdFromDirectoryEx if this is a bad format (example png's) // with my tests, even some ICO's were failing... - hicon if hicon.is_null() => { - debug!("Unable to CreateIconFromResourceEx"); + Err(err) => { + debug!("Unable to CreateIconFromResourceEx: {:?}", err); None } - hicon => Some(hicon), + Ok(hicon) => Some(hicon), } } } @@ -275,50 +260,45 @@ pub fn get_hicon_from_buffer(buffer: &[u8], width: i32, height: i32) -> Option *const wchar_t { + pub(crate) fn to_windows_cursor(self) -> PWSTR { match self { - CursorIcon::Arrow | CursorIcon::Default => winuser::IDC_ARROW, - CursorIcon::Hand => winuser::IDC_HAND, - CursorIcon::Crosshair => winuser::IDC_CROSS, - CursorIcon::Text | CursorIcon::VerticalText => winuser::IDC_IBEAM, - CursorIcon::NotAllowed | CursorIcon::NoDrop => winuser::IDC_NO, + CursorIcon::Arrow | CursorIcon::Default => IDC_ARROW, + CursorIcon::Hand => IDC_HAND, + CursorIcon::Crosshair => IDC_CROSS, + CursorIcon::Text | CursorIcon::VerticalText => IDC_IBEAM, + CursorIcon::NotAllowed | CursorIcon::NoDrop => IDC_NO, CursorIcon::Grab | CursorIcon::Grabbing | CursorIcon::Move | CursorIcon::AllScroll => { - winuser::IDC_SIZEALL + IDC_SIZEALL } CursorIcon::EResize | CursorIcon::WResize | CursorIcon::EwResize | CursorIcon::ColResize => { - winuser::IDC_SIZEWE + IDC_SIZEWE } CursorIcon::NResize | CursorIcon::SResize | CursorIcon::NsResize | CursorIcon::RowResize => { - winuser::IDC_SIZENS + IDC_SIZENS } - CursorIcon::NeResize | CursorIcon::SwResize | CursorIcon::NeswResize => winuser::IDC_SIZENESW, - CursorIcon::NwResize | CursorIcon::SeResize | CursorIcon::NwseResize => winuser::IDC_SIZENWSE, - CursorIcon::Wait => winuser::IDC_WAIT, - CursorIcon::Progress => winuser::IDC_APPSTARTING, - CursorIcon::Help => winuser::IDC_HELP, - _ => winuser::IDC_ARROW, // use arrow for the missing cases. + CursorIcon::NeResize | CursorIcon::SwResize | CursorIcon::NeswResize => IDC_SIZENESW, + CursorIcon::NwResize | CursorIcon::SeResize | CursorIcon::NwseResize => IDC_SIZENWSE, + CursorIcon::Wait => IDC_WAIT, + CursorIcon::Progress => IDC_APPSTARTING, + CursorIcon::Help => IDC_HELP, + _ => IDC_ARROW, // use arrow for the missing cases. } } } // Helper function to dynamically load function pointer. // `library` and `function` must be zero-terminated. -pub(super) fn get_function_impl(library: &str, function: &str) -> Option<*const c_void> { +pub(super) fn get_function_impl(library: &str, function: &str) -> FARPROC { assert_eq!(library.chars().last(), Some('\0')); assert_eq!(function.chars().last(), Some('\0')); // Library names we will use are ASCII so we can use the A version to avoid string conversion. - let module = unsafe { LoadLibraryA(library.as_ptr() as LPCSTR) }; - if module.is_null() { + let module = unsafe { LoadLibraryA(library) }; + if module.is_invalid() { return None; } - let function_ptr = unsafe { GetProcAddress(module, function.as_ptr() as LPCSTR) }; - if function_ptr.is_null() { - return None; - } - - Some(function_ptr as _) + unsafe { GetProcAddress(module, function) } } macro_rules! get_function { @@ -327,7 +307,7 @@ macro_rules! get_function { concat!($lib, '\0'), concat!(stringify!($func), '\0'), ) - .map(|f| unsafe { std::mem::transmute::<*const _, $func>(f) }) + .map(|f| unsafe { std::mem::transmute::<_, $func>(f) }) }; } @@ -336,20 +316,20 @@ pub type SetProcessDpiAwareness = unsafe extern "system" fn(value: PROCESS_DPI_AWARENESS) -> HRESULT; pub type SetProcessDpiAwarenessContext = unsafe extern "system" fn(value: DPI_AWARENESS_CONTEXT) -> BOOL; -pub type GetDpiForWindow = unsafe extern "system" fn(hwnd: HWND) -> UINT; +pub type GetDpiForWindow = unsafe extern "system" fn(hwnd: HWND) -> u32; pub type GetDpiForMonitor = unsafe extern "system" fn( hmonitor: HMONITOR, dpi_type: MONITOR_DPI_TYPE, - dpi_x: *mut UINT, - dpi_y: *mut UINT, + dpi_x: *mut u32, + dpi_y: *mut u32, ) -> HRESULT; pub type EnableNonClientDpiScaling = unsafe extern "system" fn(hwnd: HWND) -> BOOL; pub type AdjustWindowRectExForDpi = unsafe extern "system" fn( - rect: LPRECT, - dwStyle: DWORD, + rect: *mut RECT, + dwStyle: WINDOW_STYLE, bMenu: BOOL, - dwExStyle: DWORD, - dpi: UINT, + dwExStyle: WINDOW_EX_STYLE, + dpi: u32, ) -> BOOL; lazy_static! { @@ -368,3 +348,94 @@ lazy_static! { pub static ref SET_PROCESS_DPI_AWARE: Option = get_function!("user32.dll", SetProcessDPIAware); } + +#[allow(non_snake_case)] +#[cfg(target_pointer_width = "32")] +pub fn SetWindowLongPtrW(window: HWND, index: WINDOW_LONG_PTR_INDEX, value: isize) -> isize { + unsafe { win32wm::SetWindowLongW(window, index, value as _) as _ } +} + +#[allow(non_snake_case)] +#[cfg(target_pointer_width = "64")] +pub fn SetWindowLongPtrW(window: HWND, index: WINDOW_LONG_PTR_INDEX, value: isize) -> isize { + unsafe { win32wm::SetWindowLongPtrW(window, index, value) } +} + +#[allow(non_snake_case)] +#[cfg(target_pointer_width = "32")] +pub fn GetWindowLongPtrW(window: HWND, index: WINDOW_LONG_PTR_INDEX) -> isize { + unsafe { win32wm::GetWindowLongW(window, index) as _ } +} + +#[allow(non_snake_case)] +#[cfg(target_pointer_width = "64")] +pub fn GetWindowLongPtrW(window: HWND, index: WINDOW_LONG_PTR_INDEX) -> isize { + unsafe { win32wm::GetWindowLongPtrW(window, index) } +} + +/// Implementation of the `LOWORD` macro. +#[allow(non_snake_case)] +#[inline] +pub fn LOWORD(dword: u32) -> u16 { + (dword & 0xFFFF) as u16 +} + +/// Implementation of the `HIWORD` macro. +#[allow(non_snake_case)] +#[inline] +pub fn HIWORD(dword: u32) -> u16 { + ((dword & 0xFFFF_0000) >> 16) as u16 +} + +/// Implementation of the `GET_X_LPARAM` macro. +#[allow(non_snake_case)] +#[inline] +pub fn GET_X_LPARAM(lparam: LPARAM) -> i16 { + ((lparam.0 as usize) & 0xFFFF) as u16 as i16 +} + +/// Implementation of the `GET_Y_LPARAM` macro. +#[allow(non_snake_case)] +#[inline] +pub fn GET_Y_LPARAM(lparam: LPARAM) -> i16 { + (((lparam.0 as usize) & 0xFFFF_0000) >> 16) as u16 as i16 +} + +/// Implementation of the `MAKELPARAM` macro. +/// Inverse of [GET_X_LPARAM] and [GET_Y_LPARAM] to put the (`x`, `y`) signed +/// coordinates/values back into an [LPARAM]. +#[allow(non_snake_case)] +#[inline] +pub fn MAKELPARAM(x: i16, y: i16) -> LPARAM { + LPARAM(((x as u16 as u32) | ((y as u16 as u32) << 16)) as usize as _) +} + +/// Implementation of the `GET_WHEEL_DELTA_WPARAM` macro. +#[allow(non_snake_case)] +#[inline] +pub fn GET_WHEEL_DELTA_WPARAM(wparam: WPARAM) -> i16 { + ((wparam.0 & 0xFFFF_0000) >> 16) as u16 as i16 +} + +/// Implementation of the `GET_XBUTTON_WPARAM` macro. +#[allow(non_snake_case)] +#[inline] +pub fn GET_XBUTTON_WPARAM(wparam: WPARAM) -> u16 { + ((wparam.0 & 0xFFFF_0000) >> 16) as u16 +} + +/// Implementation of the `PRIMARYLANGID` macro. +#[allow(non_snake_case)] +#[inline] +pub fn PRIMARYLANGID(hkl: HKL) -> u32 { + ((hkl.0 as usize) & 0x3FF) as u32 +} + +pub unsafe extern "system" fn call_default_window_proc( + hwnd: HWND, + msg: u32, + wparam: WPARAM, + lparam: LPARAM, +) -> LRESULT { + DefWindowProcW(hwnd, msg, wparam, lparam) +} diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 7125503c28..1c0b3c4e74 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -5,31 +5,29 @@ use mem::MaybeUninit; use parking_lot::Mutex; -use raw_window_handle::{windows::WindowsHandle, RawWindowHandle}; -use std::{cell::Cell, ffi::OsStr, io, mem, os::windows::ffi::OsStrExt, ptr, sync::Arc}; +use raw_window_handle::{RawWindowHandle, Win32Handle}; +use std::{ + cell::{Cell, RefCell}, + ffi::OsStr, + io, mem, + os::windows::ffi::OsStrExt, + ptr, + sync::Arc, +}; use crossbeam_channel as channel; -use winapi::{ - ctypes::c_int, - shared::{ - basetsd::LONG_PTR, - minwindef::{HINSTANCE, LPARAM, LRESULT, UINT, WPARAM}, - windef::{self, HWND, POINT, POINTS, RECT}, +use windows::Win32::{ + Foundation::{self as win32f, HINSTANCE, HWND, LPARAM, LRESULT, POINT, PWSTR, RECT, WPARAM}, + Graphics::{ + Dwm::{DwmEnableBlurBehindWindow, DWM_BB_BLURREGION, DWM_BB_ENABLE, DWM_BLURBEHIND}, + Gdi::*, }, - um::{ - combaseapi::{self, CoCreateInstance, CLSCTX_SERVER}, - dwmapi, - imm::{CFS_POINT, COMPOSITIONFORM}, - libloaderapi, - objbase::COINIT_APARTMENTTHREADED, - ole2, - oleidl::LPDROPTARGET, - shobjidl_core::{CLSID_TaskbarList, ITaskbarList, ITaskbarList2}, - wingdi::{CreateRectRgn, DeleteObject}, - winnt::{LPCWSTR, SHORT}, - winuser, + System::{Com::*, LibraryLoader::*, Ole::*}, + UI::{ + Input::{Ime::*, KeyboardAndMouse::*, Touch::*}, + Shell::*, + WindowsAndMessaging::{self as win32wm, *}, }, - Interface, }; use crate::{ @@ -54,7 +52,7 @@ use crate::{ }, }; -struct HMenuWrapper(windef::HMENU); +struct HMenuWrapper(HMENU); unsafe impl Send for HMenuWrapper {} unsafe impl Sync for HMenuWrapper {} @@ -87,36 +85,34 @@ impl Window { let drag_and_drop = pl_attr.drag_and_drop; init(w_attr, pl_attr, event_loop).map(|win| { let file_drop_handler = if drag_and_drop { - use winapi::shared::winerror::{OLE_E_WRONGCOMPOBJ, RPC_E_CHANGED_MODE, S_OK}; - - let ole_init_result = ole2::OleInitialize(ptr::null_mut()); // It is ok if the initialize result is `S_FALSE` because it might happen that // multiple windows are created on the same thread. - if ole_init_result == OLE_E_WRONGCOMPOBJ { - panic!("OleInitialize failed! Result was: `OLE_E_WRONGCOMPOBJ`"); - } else if ole_init_result == RPC_E_CHANGED_MODE { - panic!( - "OleInitialize failed! Result was: `RPC_E_CHANGED_MODE`. \ - Make sure other crates are not using multithreaded COM library \ - on the same thread or disable drag and drop support." - ); + if let Err(error) = OleInitialize(ptr::null_mut()) { + match error.code() { + win32f::OLE_E_WRONGCOMPOBJ => { + panic!("OleInitialize failed! Result was: `OLE_E_WRONGCOMPOBJ`") + } + win32f::RPC_E_CHANGED_MODE => panic!( + "OleInitialize failed! Result was: `RPC_E_CHANGED_MODE`. \ + Make sure other crates are not using multithreaded COM library \ + on the same thread or disable drag and drop support." + ), + _ => (), + }; } let file_drop_runner = event_loop.runner_shared.clone(); - let file_drop_handler = FileDropHandler::new( + let file_drop_handler: IDropTarget = FileDropHandler::new( win.window.0, Box::new(move |event| { if let Ok(e) = event.map_nonuser_event() { file_drop_runner.send_event(e) } }), - ); - let handler_interface_ptr = &mut (*file_drop_handler.data).interface as LPDROPTARGET; + ) + .into(); - assert_eq!( - ole2::RegisterDragDrop(win.window.0, handler_interface_ptr), - S_OK - ); + assert!(RegisterDragDrop(win.window.0, file_drop_handler.clone()).is_ok()); Some(file_drop_handler) } else { None @@ -125,7 +121,7 @@ impl Window { let subclass_input = event_loop::SubclassInput { window_state: win.window_state.clone(), event_loop_runner: event_loop.runner_shared.clone(), - file_drop_handler, + _file_drop_handler: file_drop_handler, subclass_removed: Cell::new(false), recurse_depth: Cell::new(0), }; @@ -137,9 +133,8 @@ impl Window { } pub fn set_title(&self, text: &str) { - let text = util::to_wstring(text); unsafe { - winuser::SetWindowTextW(self.window.0, text.as_ptr() as LPCWSTR); + SetWindowTextW(self.window.0, text); } } @@ -148,29 +143,13 @@ impl Window { #[inline] pub fn set_visible(&self, visible: bool) { - let prev = self.is_visible(); - let skip_taskbar = self.window_state.lock().skip_taskbar; - // Hidden window also skips taskbar, we need to check if it conflicts with skip_taskbar state - // If it's moving from visible to hidden, we need to unset skip_taskbar - if prev && !visible && skip_taskbar { - self.set_skip_taskbar(false, false); - } - - // If it's still the same, there's no need to set it again - if prev != visible { - let window = self.window.clone(); - let window_state = Arc::clone(&self.window_state); - self.thread_executor.execute_in_thread(move || { - WindowState::set_window_flags(window_state.lock(), window.0, |f| { - f.set(WindowFlags::VISIBLE, visible) - }); + let window = self.window.clone(); + let window_state = Arc::clone(&self.window_state); + self.thread_executor.execute_in_thread(move || { + WindowState::set_window_flags(window_state.lock(), window.0, |f| { + f.set(WindowFlags::VISIBLE, visible) }); - } - - // If it's moving from hidden to visible, we set skip_taskbar back - if !prev && visible && skip_taskbar { - self.set_skip_taskbar(true, false); - } + }); } #[inline] @@ -180,7 +159,7 @@ impl Window { let is_visible = window_flags.contains(WindowFlags::VISIBLE); let is_minimized = window_flags.contains(WindowFlags::MINIMIZED); - let is_foreground = window.0 == unsafe { winuser::GetForegroundWindow() }; + let is_foreground = window.0 == unsafe { GetForegroundWindow() }; if is_visible && !is_minimized && !is_foreground { unsafe { force_window_active(window.0) }; @@ -190,11 +169,11 @@ impl Window { #[inline] pub fn request_redraw(&self) { unsafe { - winuser::RedrawWindow( + RedrawWindow( self.window.0, ptr::null(), - ptr::null_mut(), - winuser::RDW_INTERNALPAINT, + HRGN::default(), + RDW_INTERNALPAINT, ); } } @@ -208,8 +187,8 @@ impl Window { #[inline] pub fn inner_position(&self) -> Result, NotSupportedError> { - let mut position: POINT = unsafe { mem::zeroed() }; - if unsafe { winuser::ClientToScreen(self.window.0, &mut position) } == 0 { + let mut position = POINT::default(); + if !unsafe { ClientToScreen(self.window.0, &mut position) }.as_bool() { panic!("Unexpected ClientToScreen failure") } Ok(PhysicalPosition::new(position.x as i32, position.y as i32)) @@ -219,32 +198,32 @@ impl Window { pub fn set_outer_position(&self, position: Position) { let (x, y): (i32, i32) = position.to_physical::(self.scale_factor()).into(); + let window_state = Arc::clone(&self.window_state); let window = self.window.clone(); self.thread_executor.execute_in_thread(move || { - util::set_maximized(window.0, false); + WindowState::set_window_flags(window_state.lock(), window.0, |f| { + f.set(WindowFlags::MAXIMIZED, false) + }); }); unsafe { - winuser::SetWindowPos( + SetWindowPos( self.window.0, - ptr::null_mut(), - x as c_int, - y as c_int, + HWND::default(), + x as i32, + y as i32, 0, 0, - winuser::SWP_ASYNCWINDOWPOS - | winuser::SWP_NOZORDER - | winuser::SWP_NOSIZE - | winuser::SWP_NOACTIVATE, + SWP_ASYNCWINDOWPOS | SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE, ); - winuser::InvalidateRgn(self.window.0, ptr::null_mut(), 0); + InvalidateRgn(self.window.0, HRGN::default(), false); } } #[inline] pub fn inner_size(&self) -> PhysicalSize { - let mut rect: RECT = unsafe { mem::zeroed() }; - if unsafe { winuser::GetClientRect(self.window.0, &mut rect) } == 0 { + let mut rect = RECT::default(); + if !unsafe { GetClientRect(self.window.0, &mut rect) }.as_bool() { panic!("Unexpected GetClientRect failure") } PhysicalSize::new( @@ -270,9 +249,12 @@ impl Window { let scale_factor = self.scale_factor(); let (width, height) = size.to_physical::(scale_factor).into(); + let window_state = Arc::clone(&self.window_state); let window = self.window.clone(); self.thread_executor.execute_in_thread(move || { - util::set_maximized(window.0, false); + WindowState::set_window_flags(window_state.lock(), window.0, |f| { + f.set(WindowFlags::MAXIMIZED, false) + }); }); util::set_inner_size_physical(self.window.0, width, height); @@ -314,25 +296,23 @@ impl Window { #[inline] pub fn hinstance(&self) -> HINSTANCE { - unsafe { winuser::GetWindowLongPtrW(self.hwnd(), winuser::GWLP_HINSTANCE) as *mut _ } + HINSTANCE(util::GetWindowLongPtrW(self.hwnd(), GWLP_HINSTANCE)) } #[inline] pub fn raw_window_handle(&self) -> RawWindowHandle { - let handle = WindowsHandle { - hwnd: self.window.0 as *mut _, - hinstance: self.hinstance() as *mut _, - ..WindowsHandle::empty() - }; - RawWindowHandle::Windows(handle) + let mut handle = Win32Handle::empty(); + handle.hwnd = self.window.0 .0 as *mut _; + handle.hinstance = self.hinstance().0 as *mut _; + RawWindowHandle::Win32(handle) } #[inline] pub fn set_cursor_icon(&self, cursor: CursorIcon) { self.window_state.lock().mouse.cursor = cursor; self.thread_executor.execute_in_thread(move || unsafe { - let cursor = winuser::LoadCursorW(ptr::null_mut(), cursor.to_windows_cursor()); - winuser::SetCursor(cursor); + let cursor = LoadCursorW(HINSTANCE::default(), cursor.to_windows_cursor()); + SetCursor(cursor); }); } @@ -382,12 +362,12 @@ impl Window { let mut point = POINT { x, y }; unsafe { - if winuser::ClientToScreen(self.window.0, &mut point) == 0 { + if !ClientToScreen(self.window.0, &mut point).as_bool() { return Err(ExternalError::Os(os_error!(OsError::IoError( io::Error::last_os_error() )))); } - if winuser::SetCursorPos(point.x, point.y) == 0 { + if !SetCursorPos(point.x, point.y).as_bool() { return Err(ExternalError::Os(os_error!(OsError::IoError( io::Error::last_os_error() )))); @@ -398,22 +378,15 @@ impl Window { #[inline] pub fn drag_window(&self) -> Result<(), ExternalError> { + let mut pos = POINT::default(); unsafe { - let points = { - let mut pos = mem::zeroed(); - winuser::GetCursorPos(&mut pos); - pos - }; - let points = POINTS { - x: points.x as SHORT, - y: points.y as SHORT, - }; - winuser::ReleaseCapture(); - winuser::PostMessageW( + GetCursorPos(&mut pos); + ReleaseCapture(); + PostMessageW( self.window.0, - winuser::WM_NCLBUTTONDOWN, - winuser::HTCAPTION as WPARAM, - &points as *const _ as LPARAM, + WM_NCLBUTTONDOWN, + WPARAM(HTCAPTION as _), + util::MAKELPARAM(pos.x as i16, pos.y as i16), ); } @@ -422,7 +395,7 @@ impl Window { #[inline] pub fn id(&self) -> WindowId { - WindowId(self.window.0) + WindowId(self.window.0 .0) } #[inline] @@ -439,12 +412,20 @@ impl Window { #[inline] pub fn set_maximized(&self, maximized: bool) { - util::set_maximized(self.window.0, maximized); + let window = self.window.clone(); + let window_state = Arc::clone(&self.window_state); + + self.thread_executor.execute_in_thread(move || { + WindowState::set_window_flags(window_state.lock(), window.0, |f| { + f.set(WindowFlags::MAXIMIZED, maximized) + }); + }); } #[inline] pub fn is_maximized(&self) -> bool { - util::is_maximized(self.window.0) + let window_state = self.window_state.lock(); + window_state.window_flags.contains(WindowFlags::MAXIMIZED) } #[inline] @@ -500,41 +481,41 @@ impl Window { // string, so add it display_name.push(0); - let mut native_video_mode = video_mode.video_mode.native_video_mode; + let native_video_mode = video_mode.video_mode.native_video_mode; let res = unsafe { - winuser::ChangeDisplaySettingsExW( - display_name.as_ptr(), - &mut native_video_mode, - std::ptr::null_mut(), - winuser::CDS_FULLSCREEN, + ChangeDisplaySettingsExW( + PWSTR(display_name.as_mut_ptr()), + &native_video_mode, + HWND::default(), + CDS_FULLSCREEN, std::ptr::null_mut(), ) }; - debug_assert!(res != winuser::DISP_CHANGE_BADFLAGS); - debug_assert!(res != winuser::DISP_CHANGE_BADMODE); - debug_assert!(res != winuser::DISP_CHANGE_BADPARAM); - debug_assert!(res != winuser::DISP_CHANGE_FAILED); - assert_eq!(res, winuser::DISP_CHANGE_SUCCESSFUL); + debug_assert!(res != DISP_CHANGE_BADFLAGS); + debug_assert!(res != DISP_CHANGE_BADMODE); + debug_assert!(res != DISP_CHANGE_BADPARAM); + debug_assert!(res != DISP_CHANGE_FAILED); + assert_eq!(res, DISP_CHANGE_SUCCESSFUL); } (&Some(Fullscreen::Exclusive(_)), &None) | (&Some(Fullscreen::Exclusive(_)), &Some(Fullscreen::Borderless(_))) => { let res = unsafe { - winuser::ChangeDisplaySettingsExW( - std::ptr::null_mut(), - std::ptr::null_mut(), + ChangeDisplaySettingsExW( + PWSTR::default(), std::ptr::null_mut(), - winuser::CDS_FULLSCREEN, + HWND::default(), + CDS_FULLSCREEN, std::ptr::null_mut(), ) }; - debug_assert!(res != winuser::DISP_CHANGE_BADFLAGS); - debug_assert!(res != winuser::DISP_CHANGE_BADMODE); - debug_assert!(res != winuser::DISP_CHANGE_BADPARAM); - debug_assert!(res != winuser::DISP_CHANGE_FAILED); - assert_eq!(res, winuser::DISP_CHANGE_SUCCESSFUL); + debug_assert!(res != DISP_CHANGE_BADFLAGS); + debug_assert!(res != DISP_CHANGE_BADMODE); + debug_assert!(res != DISP_CHANGE_BADPARAM); + debug_assert!(res != DISP_CHANGE_FAILED); + assert_eq!(res, DISP_CHANGE_SUCCESSFUL); } _ => (), } @@ -548,8 +529,8 @@ impl Window { // Calling `PeekMessageW` here notifies Windows that our process is still running // fine, taking control back from the DWM and ensuring that the `SetWindowPos` call // below goes through. - let mut msg = mem::zeroed(); - winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 0); + let mut msg = MSG::default(); + PeekMessageW(&mut msg, HWND::default(), 0, 0, PM_NOREMOVE); } // Update window style @@ -569,8 +550,8 @@ impl Window { Some(fullscreen) => { // Save window bounds before entering fullscreen let placement = unsafe { - let mut placement = mem::zeroed(); - winuser::GetWindowPlacement(window.0, &mut placement); + let mut placement = WINDOWPLACEMENT::default(); + GetWindowPlacement(window.0, &mut placement); placement }; @@ -588,16 +569,16 @@ impl Window { let size: (u32, u32) = monitor.size().into(); unsafe { - winuser::SetWindowPos( + SetWindowPos( window.0, - ptr::null_mut(), + HWND::default(), position.0, position.1, size.0 as i32, size.1 as i32, - winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER, + SWP_ASYNCWINDOWPOS | SWP_NOZORDER, ); - winuser::InvalidateRgn(window.0, ptr::null_mut(), 0); + InvalidateRgn(window.0, HRGN::default(), false); } } None => { @@ -605,8 +586,8 @@ impl Window { if let Some(SavedWindow { placement }) = window_state_lock.saved_window.take() { drop(window_state_lock); unsafe { - winuser::SetWindowPlacement(window.0, &placement); - winuser::InvalidateRgn(window.0, ptr::null_mut(), 0); + SetWindowPlacement(window.0, &placement); + InvalidateRgn(window.0, HRGN::default(), false); } } } @@ -674,16 +655,16 @@ impl Window { } pub(crate) fn set_ime_position_physical(&self, x: i32, y: i32) { - if unsafe { winuser::GetSystemMetrics(winuser::SM_IMMENABLED) } != 0 { - let mut composition_form = COMPOSITIONFORM { + if unsafe { GetSystemMetrics(SM_IMMENABLED) } != 0 { + let composition_form = COMPOSITIONFORM { dwStyle: CFS_POINT, ptCurrentPos: POINT { x, y }, - rcArea: unsafe { mem::zeroed() }, + rcArea: RECT::default(), }; unsafe { - let himc = winapi::um::imm::ImmGetContext(self.window.0); - winapi::um::imm::ImmSetCompositionWindow(himc, &mut composition_form); - winapi::um::imm::ImmReleaseContext(self.window.0, himc); + let himc = ImmGetContext(self.window.0); + ImmSetCompositionWindow(himc, &composition_form); + ImmReleaseContext(self.window.0, himc); } } } @@ -697,7 +678,7 @@ impl Window { #[inline] pub fn request_user_attention(&self, request_type: Option) { let window = self.window.clone(); - let active_window_handle = unsafe { winuser::GetActiveWindow() }; + let active_window_handle = unsafe { GetActiveWindow() }; if window.0 == active_window_handle { return; } @@ -705,21 +686,19 @@ impl Window { self.thread_executor.execute_in_thread(move || unsafe { let (flags, count) = request_type .map(|ty| match ty { - UserAttentionType::Critical => { - (winuser::FLASHW_ALL | winuser::FLASHW_TIMERNOFG, u32::MAX) - } - UserAttentionType::Informational => (winuser::FLASHW_TRAY | winuser::FLASHW_TIMERNOFG, 0), + UserAttentionType::Critical => (FLASHW_ALL | FLASHW_TIMERNOFG, u32::MAX), + UserAttentionType::Informational => (FLASHW_TRAY | FLASHW_TIMERNOFG, 0), }) - .unwrap_or((winuser::FLASHW_STOP, 0)); + .unwrap_or((FLASHW_STOP, 0)); - let mut flash_info = winuser::FLASHWINFO { - cbSize: mem::size_of::() as UINT, + let flash_info = FLASHWINFO { + cbSize: mem::size_of::() as u32, hwnd: window.0, dwFlags: flags, uCount: count, dwTimeout: 0, }; - winuser::FlashWindowEx(&mut flash_info); + FlashWindowEx(&flash_info); }); } @@ -731,7 +710,7 @@ impl Window { #[inline] pub fn hide_menu(&self) { unsafe { - winuser::SetMenu(self.hwnd(), ptr::null::() as _); + SetMenu(self.hwnd(), HMENU::default()); } } @@ -739,14 +718,14 @@ impl Window { pub fn show_menu(&self) { if let Some(menu) = &self.menu { unsafe { - winuser::SetMenu(self.hwnd(), menu.0); + SetMenu(self.hwnd(), menu.0); } } } #[inline] pub fn is_menu_visible(&self) -> bool { - unsafe { !winuser::GetMenu(self.hwnd()).is_null() } + unsafe { !GetMenu(self.hwnd()).is_invalid() } } #[inline] @@ -754,15 +733,15 @@ impl Window { // `ToUnicode` consumes the dead-key by default, so we are constructing a fake (but valid) // key input which we can call `ToUnicode` with. unsafe { - let vk = winuser::VK_SPACE as u32; - let scancode = winuser::MapVirtualKeyW(vk, winuser::MAPVK_VK_TO_VSC); + let vk = u32::from(VK_SPACE); + let scancode = MapVirtualKeyW(vk, MAPVK_VK_TO_VSC); let kbd_state = [0; 256]; let mut char_buff = [MaybeUninit::uninit(); 8]; - winuser::ToUnicode( + ToUnicode( vk, scancode, kbd_state.as_ptr(), - char_buff[0].as_mut_ptr(), + PWSTR(char_buff[0].as_mut_ptr()), char_buff.len() as i32, 0, ); @@ -770,29 +749,28 @@ impl Window { } #[inline] - pub(crate) fn set_skip_taskbar(&self, skip: bool, state: bool) { - // Update self skip_taskbar state if true - if state { - let mut window_state = self.window_state.lock(); - window_state.skip_taskbar = skip; + pub fn begin_resize_drag(&self, edge: isize, button: u32, x: i32, y: i32) { + unsafe { + let w_param = WPARAM(edge as _); + let l_param = util::MAKELPARAM(x as i16, y as i16); + + ReleaseCapture(); + PostMessageW(self.hwnd(), button, w_param, l_param); } + } - if self.is_visible() { - unsafe { - let mut taskbar_list: *mut ITaskbarList = std::mem::zeroed(); - CoCreateInstance( - &CLSID_TaskbarList, - std::ptr::null_mut(), - CLSCTX_SERVER, - &ITaskbarList::uuidof(), - &mut taskbar_list as *mut _ as *mut _, - ); - if skip { - (*taskbar_list).DeleteTab(self.hwnd() as _); - } else { - (*taskbar_list).AddTab(self.hwnd() as _); - } - (*taskbar_list).Release(); + #[inline] + pub(crate) fn set_skip_taskbar(&self, skip: bool) { + unsafe { + com_initialized(); + let taskbar_list: ITaskbarList = + CoCreateInstance(&TaskbarList, None, CLSCTX_SERVER).expect("failed to create TaskBarList"); + if skip { + taskbar_list + .DeleteTab(self.hwnd()) + .expect("DeleteTab failed"); + } else { + taskbar_list.AddTab(self.hwnd()).expect("AddTab failed"); } } } @@ -804,7 +782,7 @@ impl Drop for Window { unsafe { // The window must be destroyed from the same thread that created it, so we send a // custom message to be handled by our callback to do the actual work. - winuser::PostMessageW(self.window.0, *DESTROY_MSG_ID, 0, 0); + PostMessageW(self.window.0, *DESTROY_MSG_ID, WPARAM(0), LPARAM(0)); } } } @@ -826,10 +804,8 @@ unsafe fn init( pl_attribs: PlatformSpecificWindowBuilderAttributes, event_loop: &EventLoopWindowTarget, ) -> Result { - let title = util::to_wstring(&attributes.title); - // registering the window class - let class_name = register_window_class(&attributes.window_icon, &pl_attribs.taskbar_icon); + let mut class_name = register_window_class(&attributes.window_icon, &pl_attribs.taskbar_icon); let mut window_flags = WindowFlags::empty(); window_flags.set(WindowFlags::DECORATIONS, attributes.decorations); @@ -863,22 +839,22 @@ unsafe fn init( // creating the real window this time, by using the functions in `extra_functions` let real_window = { let (style, ex_style) = window_flags.to_window_styles(); - let handle = winuser::CreateWindowExW( + let handle = CreateWindowExW( ex_style, - class_name.as_ptr(), - title.as_ptr() as LPCWSTR, + PWSTR(class_name.as_mut_ptr()), + attributes.title.as_str(), style, - winuser::CW_USEDEFAULT, - winuser::CW_USEDEFAULT, - winuser::CW_USEDEFAULT, - winuser::CW_USEDEFAULT, - parent.unwrap_or(ptr::null_mut()), - pl_attribs.menu.unwrap_or(ptr::null_mut()), - libloaderapi::GetModuleHandleW(ptr::null()), - Box::into_raw(Box::new(!attributes.decorations)) as _, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + parent.unwrap_or_default(), + pl_attribs.menu.unwrap_or_default(), + GetModuleHandleW(PWSTR::default()), + Box::into_raw(Box::new(window_flags)) as _, ); - if handle.is_null() { + if handle.is_invalid() { return Err(os_error!(OsError::IoError(io::Error::last_os_error()))); } @@ -887,9 +863,9 @@ unsafe fn init( // Register for touch events if applicable { - let digitizer = winuser::GetSystemMetrics(winuser::SM_DIGITIZER) as u32; - if digitizer & winuser::NID_READY != 0 { - winuser::RegisterTouchWindow(real_window.0, winuser::TWF_WANTPALM); + let digitizer = GetSystemMetrics(SM_DIGITIZER) as u32; + if digitizer & NID_READY != 0 { + RegisterTouchWindow(real_window.0, TWF_WANTPALM); } } @@ -901,15 +877,15 @@ unsafe fn init( // Empty region for the blur effect, so the window is fully transparent let region = CreateRectRgn(0, 0, -1, -1); - let bb = dwmapi::DWM_BLURBEHIND { - dwFlags: dwmapi::DWM_BB_ENABLE | dwmapi::DWM_BB_BLURREGION, - fEnable: 1, + let bb = DWM_BLURBEHIND { + dwFlags: DWM_BB_ENABLE | DWM_BB_BLURREGION, + fEnable: true.into(), hRgnBlur: region, - fTransitionOnMaximized: 0, + fTransitionOnMaximized: false.into(), }; - dwmapi::DwmEnableBlurBehindWindow(real_window.0, &bb); - DeleteObject(region as _); + let _ = DwmEnableBlurBehindWindow(real_window.0, &bb); + DeleteObject(region); } // If the system theme is dark, we need to set the window theme now @@ -924,7 +900,6 @@ unsafe fn init( scale_factor, current_theme, pl_attribs.preferred_theme, - pl_attribs.skip_taskbar, ); let window_state = Arc::new(Mutex::new(window_state)); WindowState::set_window_flags(window_state.lock(), real_window.0, |f| *f = window_flags); @@ -938,7 +913,7 @@ unsafe fn init( menu: None, }; - win.set_skip_taskbar(pl_attribs.skip_taskbar, false); + win.set_skip_taskbar(pl_attribs.skip_taskbar); let dimensions = attributes .inner_size @@ -962,7 +937,6 @@ unsafe fn init( if let Some(window_menu) = attributes.window_menu { let event_loop_runner = event_loop.runner_shared.clone(); - let window_handle = win.raw_window_handle(); let window_id = RootWindowId(win.id()); let menu_handler = menu::MenuHandler::new( Box::new(move |event| { @@ -974,7 +948,11 @@ unsafe fn init( Some(window_id), ); - win.menu = menu::initialize(window_menu, window_handle, menu_handler).map(HMenuWrapper); + win.menu = Some(HMenuWrapper(menu::initialize( + window_menu, + win.hwnd(), + menu_handler, + ))); } Ok(win) @@ -984,29 +962,29 @@ unsafe fn register_window_class( window_icon: &Option, taskbar_icon: &Option, ) -> Vec { - let class_name = util::to_wstring("Window Class"); + let mut class_name = util::to_wstring("Window Class"); let h_icon = taskbar_icon .as_ref() .map(|icon| icon.inner.as_raw_handle()) - .unwrap_or(ptr::null_mut()); + .unwrap_or_default(); let h_icon_small = window_icon .as_ref() .map(|icon| icon.inner.as_raw_handle()) - .unwrap_or(ptr::null_mut()); + .unwrap_or_default(); - let class = winuser::WNDCLASSEXW { - cbSize: mem::size_of::() as UINT, - style: winuser::CS_HREDRAW | winuser::CS_VREDRAW | winuser::CS_OWNDC, + let class = WNDCLASSEXW { + cbSize: mem::size_of::() as u32, + style: CS_HREDRAW | CS_VREDRAW | CS_OWNDC, lpfnWndProc: Some(window_proc), cbClsExtra: 0, cbWndExtra: 0, - hInstance: libloaderapi::GetModuleHandleW(ptr::null()), + hInstance: GetModuleHandleW(PWSTR::default()), hIcon: h_icon, - hCursor: ptr::null_mut(), // must be null in order for cursor state to work properly - hbrBackground: ptr::null_mut(), - lpszMenuName: ptr::null(), - lpszClassName: class_name.as_ptr(), + hCursor: HCURSOR::default(), // must be null in order for cursor state to work properly + hbrBackground: HBRUSH::default(), + lpszMenuName: PWSTR::default(), + lpszClassName: PWSTR(class_name.as_mut_ptr()), hIconSm: h_icon_small, }; @@ -1014,65 +992,73 @@ unsafe fn register_window_class( // an error, and because errors here are detected during CreateWindowEx anyway. // Also since there is no weird element in the struct, there is no reason for this // call to fail. - winuser::RegisterClassExW(&class); + RegisterClassExW(&class); class_name } unsafe extern "system" fn window_proc( window: HWND, - msg: UINT, + msg: u32, wparam: WPARAM, lparam: LPARAM, ) -> LRESULT { - let mut userdata = winuser::GetWindowLongPtrW(window, winuser::GWL_USERDATA); - + // This window procedure is only needed until the subclass procedure is attached. + // we need this because we need to respond to WM_NCCALCSIZE as soon as possible + // in order to make the window borderless if needed. match msg { - winuser::WM_NCCALCSIZE => { - // Check if userdata is set and if the value of it is true (window wants to be borderless) - if userdata != 0 && *(userdata as *mut bool) == true { - // adjust the maximized borderless window so it doesn't cover the taskbar - if util::is_maximized(window) { - let monitor = monitor::current_monitor(window); - if let Ok(monitor_info) = monitor::get_monitor_info(monitor.hmonitor()) { - let params = &mut *(lparam as *mut winuser::NCCALCSIZE_PARAMS); - params.rgrc[0] = monitor_info.rcWork; + win32wm::WM_NCCALCSIZE => { + let userdata = util::GetWindowLongPtrW(window, GWL_USERDATA); + if userdata != 0 { + let win_flags = WindowFlags::from_bits_unchecked(userdata as _); + if !win_flags.contains(WindowFlags::DECORATIONS) { + // adjust the maximized borderless window so it doesn't cover the taskbar + if util::is_maximized(window) { + let monitor = monitor::current_monitor(window); + if let Ok(monitor_info) = monitor::get_monitor_info(monitor.hmonitor()) { + let params = &mut *(lparam.0 as *mut NCCALCSIZE_PARAMS); + params.rgrc[0] = monitor_info.monitorInfo.rcWork; + } } + return LRESULT(0); // return 0 here to make the window borderless } - 0 // return 0 here to make the windowo borderless - } else { - winuser::DefWindowProcW(window, msg, wparam, lparam) } + DefWindowProcW(window, msg, wparam, lparam) } - winuser::WM_NCCREATE => { - // Set userdata to the value of lparam. This will be cleared on event loop subclassing. + win32wm::WM_NCCREATE => { + let userdata = util::GetWindowLongPtrW(window, GWL_USERDATA); if userdata == 0 { - let createstruct = &*(lparam as *const winuser::CREATESTRUCTW); - userdata = createstruct.lpCreateParams as LONG_PTR; - winuser::SetWindowLongPtrW(window, winuser::GWL_USERDATA, userdata); + let createstruct = &*(lparam.0 as *const CREATESTRUCTW); + let userdata = createstruct.lpCreateParams; + let window_flags = Box::from_raw(userdata as *mut WindowFlags); + util::SetWindowLongPtrW(window, GWL_USERDATA, window_flags.bits() as _); } - winuser::DefWindowProcW(window, msg, wparam, lparam) + DefWindowProcW(window, msg, wparam, lparam) } - _ => winuser::DefWindowProcW(window, msg, wparam, lparam), + _ => DefWindowProcW(window, msg, wparam, lparam), } } -struct ComInitialized(*mut ()); +struct ComInitialized(Option<()>); impl Drop for ComInitialized { fn drop(&mut self) { - unsafe { combaseapi::CoUninitialize() }; + if let Some(()) = self.0.take() { + unsafe { CoUninitialize() }; + } } } thread_local! { static COM_INITIALIZED: ComInitialized = { unsafe { - combaseapi::CoInitializeEx(ptr::null_mut(), COINIT_APARTMENTTHREADED); - ComInitialized(ptr::null_mut()) + ComInitialized(match CoInitializeEx(ptr::null_mut(), COINIT_APARTMENTTHREADED) { + Ok(()) => Some(()), + Err(_) => None, + }) } }; - static TASKBAR_LIST: Cell<*mut ITaskbarList2> = Cell::new(ptr::null_mut()); + static TASKBAR_LIST: RefCell> = RefCell::new(None); } pub fn com_initialized() { @@ -1091,28 +1077,27 @@ unsafe fn taskbar_mark_fullscreen(handle: HWND, fullscreen: bool) { com_initialized(); TASKBAR_LIST.with(|task_bar_list_ptr| { - let mut task_bar_list = task_bar_list_ptr.get(); - - if task_bar_list.is_null() { - use winapi::shared::winerror::S_OK; - - let hr = combaseapi::CoCreateInstance( - &CLSID_TaskbarList, - ptr::null_mut(), - combaseapi::CLSCTX_ALL, - &ITaskbarList2::uuidof(), - &mut task_bar_list as *mut _ as *mut _, - ); + let mut task_bar_list = task_bar_list_ptr.borrow().clone(); + + if task_bar_list.is_none() { + let result: windows::core::Result = + CoCreateInstance(&TaskbarList, None, CLSCTX_ALL); + if let Ok(created) = result { + if let Ok(()) = created.HrInit() { + task_bar_list = Some(created); + } + } - if hr != S_OK || (*task_bar_list).HrInit() != S_OK { - // In some old windows, the taskbar object could not be created, we just ignore it + if task_bar_list.is_none() { return; } - task_bar_list_ptr.set(task_bar_list) + + *task_bar_list_ptr.borrow_mut() = task_bar_list.clone(); } - task_bar_list = task_bar_list_ptr.get(); - (*task_bar_list).MarkFullscreenWindow(handle, if fullscreen { 1 } else { 0 }); + let _ = task_bar_list + .unwrap() + .MarkFullscreenWindow(handle, fullscreen); }) } @@ -1121,48 +1106,42 @@ unsafe fn force_window_active(handle: HWND) { // This is a little hack which can "steal" the foreground window permission // We only call this function in the window creation, so it should be fine. // See : https://stackoverflow.com/questions/10740346/setforegroundwindow-only-working-while-visual-studio-is-open - let alt_sc = winuser::MapVirtualKeyW(winuser::VK_MENU as _, winuser::MAPVK_VK_TO_VSC); + let alt_sc = MapVirtualKeyW(u32::from(VK_MENU), MAPVK_VK_TO_VSC); - let mut inputs: [winuser::INPUT; 2] = mem::zeroed(); - inputs[0].type_ = winuser::INPUT_KEYBOARD; - inputs[0].u.ki_mut().wVk = winuser::VK_LMENU as _; - inputs[0].u.ki_mut().wScan = alt_sc as _; - inputs[0].u.ki_mut().dwFlags = winuser::KEYEVENTF_EXTENDEDKEY; + let mut inputs: [INPUT; 2] = mem::zeroed(); + inputs[0].r#type = INPUT_KEYBOARD; + inputs[0].Anonymous.ki.wVk = VK_LMENU as _; + inputs[0].Anonymous.ki.wScan = alt_sc as _; + inputs[0].Anonymous.ki.dwFlags = KEYEVENTF_EXTENDEDKEY; - inputs[1].type_ = winuser::INPUT_KEYBOARD; - inputs[1].u.ki_mut().wVk = winuser::VK_LMENU as _; - inputs[1].u.ki_mut().wScan = alt_sc as _; - inputs[1].u.ki_mut().dwFlags = winuser::KEYEVENTF_EXTENDEDKEY | winuser::KEYEVENTF_KEYUP; + inputs[1].r#type = INPUT_KEYBOARD; + inputs[1].Anonymous.ki.wVk = VK_LMENU as _; + inputs[1].Anonymous.ki.wScan = alt_sc as _; + inputs[1].Anonymous.ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP; // Simulate a key press and release - winuser::SendInput( + SendInput( inputs.len() as _, inputs.as_mut_ptr(), - mem::size_of::() as _, + mem::size_of::() as _, ); - winuser::SetForegroundWindow(handle); + SetForegroundWindow(handle); } pub fn hit_test(hwnd: HWND, cx: i32, cy: i32) -> LRESULT { - use winapi::shared::minwindef::TRUE; - use winuser::{ - GetWindowRect, HTBOTTOM, HTBOTTOMLEFT, HTBOTTOMRIGHT, HTCLIENT, HTLEFT, HTNOWHERE, HTRIGHT, - HTTOP, HTTOPLEFT, HTTOPRIGHT, - }; - + let mut window_rect = RECT::default(); unsafe { - let mut window_rect: RECT = mem::zeroed(); - if GetWindowRect(hwnd, <*mut _>::cast(&mut window_rect)) == TRUE { - const CLIENT: i32 = 0b0000; - const LEFT: i32 = 0b0001; - const RIGHT: i32 = 0b0010; - const TOP: i32 = 0b0100; - const BOTTOM: i32 = 0b1000; - const TOPLEFT: i32 = TOP | LEFT; - const TOPRIGHT: i32 = TOP | RIGHT; - const BOTTOMLEFT: i32 = BOTTOM | LEFT; - const BOTTOMRIGHT: i32 = BOTTOM | RIGHT; + if GetWindowRect(hwnd, <*mut _>::cast(&mut window_rect)).as_bool() { + const CLIENT: isize = 0b0000; + const LEFT: isize = 0b0001; + const RIGHT: isize = 0b0010; + const TOP: isize = 0b0100; + const BOTTOM: isize = 0b1000; + const TOPLEFT: isize = TOP | LEFT; + const TOPRIGHT: isize = TOP | RIGHT; + const BOTTOMLEFT: isize = BOTTOM | LEFT; + const BOTTOMRIGHT: isize = BOTTOM | RIGHT; let RECT { left, @@ -1177,7 +1156,7 @@ pub fn hit_test(hwnd: HWND, cx: i32, cy: i32) -> LRESULT { | (TOP * (if cy < (top + BORDERLESS_RESIZE_INSET) { 1 } else { 0 })) | (BOTTOM * (if cy >= (bottom - BORDERLESS_RESIZE_INSET) { 1 } else { 0 })); - match result { + LRESULT(match result { CLIENT => HTCLIENT, LEFT => HTLEFT, RIGHT => HTRIGHT, @@ -1188,9 +1167,9 @@ pub fn hit_test(hwnd: HWND, cx: i32, cy: i32) -> LRESULT { BOTTOMLEFT => HTBOTTOMLEFT, BOTTOMRIGHT => HTBOTTOMRIGHT, _ => HTNOWHERE, - } + } as _) } else { - HTNOWHERE + LRESULT(HTNOWHERE as _) } } } diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index b095fcc39f..fdc6f8cee0 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -9,13 +9,11 @@ use crate::{ window::{CursorIcon, Fullscreen, Theme, WindowAttributes}, }; use parking_lot::MutexGuard; -use std::{io, ptr}; -use winapi::{ - shared::{ - minwindef::DWORD, - windef::{HWND, RECT}, - }, - um::winuser, +use std::io; +use windows::Win32::{ + Foundation::{HWND, LPARAM, RECT, WPARAM}, + Graphics::Gdi::{InvalidateRgn, HRGN}, + UI::WindowsAndMessaging::*, }; /// Contains information about states and the window that the callback is going to use. @@ -42,13 +40,11 @@ pub struct WindowState { pub ime_handler: MinimalIme, pub window_flags: WindowFlags, - - pub skip_taskbar: bool, } #[derive(Clone)] pub struct SavedWindow { - pub placement: winuser::WINDOWPLACEMENT, + pub placement: WINDOWPLACEMENT, } #[derive(Clone)] @@ -96,6 +92,7 @@ bitflags! { const MINIMIZED = 1 << 12; const EXCLUSIVE_FULLSCREEN_OR_MASK = WindowFlags::ALWAYS_ON_TOP.bits; + const INVISIBLE_AND_MASK = !WindowFlags::MAXIMIZED.bits; } } @@ -106,7 +103,6 @@ impl WindowState { scale_factor: f64, current_theme: Theme, preferred_theme: Option, - skip_taskbar: bool, ) -> WindowState { WindowState { mouse: MouseProperties { @@ -133,8 +129,6 @@ impl WindowState { key_event_builder: KeyEventBuilder::default(), ime_handler: MinimalIme::default(), window_flags: WindowFlags::empty(), - - skip_taskbar, } } @@ -191,12 +185,14 @@ impl WindowFlags { self |= WindowFlags::EXCLUSIVE_FULLSCREEN_OR_MASK; } + if !self.contains(WindowFlags::VISIBLE) { + self &= WindowFlags::INVISIBLE_AND_MASK; + } + self } - pub fn to_window_styles(self) -> (DWORD, DWORD) { - use winapi::um::winuser::*; - + pub fn to_window_styles(self) -> (WINDOW_STYLE, WINDOW_EX_STYLE) { let (mut style, mut style_ex) = (0, 0); style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX; style_ex |= WS_EX_ACCEPTFILES; @@ -253,44 +249,53 @@ impl WindowFlags { if diff.contains(WindowFlags::VISIBLE) { unsafe { - winuser::ShowWindow( + ShowWindow( window, match new.contains(WindowFlags::VISIBLE) { - true => winuser::SW_SHOW, - false => winuser::SW_HIDE, + true => SW_SHOW, + false => SW_HIDE, }, ); } } if diff.contains(WindowFlags::ALWAYS_ON_TOP) { unsafe { - winuser::SetWindowPos( + SetWindowPos( window, match new.contains(WindowFlags::ALWAYS_ON_TOP) { - true => winuser::HWND_TOPMOST, - false => winuser::HWND_NOTOPMOST, + true => HWND_TOPMOST, + false => HWND_NOTOPMOST, }, 0, 0, 0, 0, - winuser::SWP_ASYNCWINDOWPOS - | winuser::SWP_NOMOVE - | winuser::SWP_NOSIZE - | winuser::SWP_NOACTIVATE, + SWP_ASYNCWINDOWPOS | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE, ); - winuser::InvalidateRgn(window, ptr::null_mut(), 0); + InvalidateRgn(window, HRGN::default(), false); } } // Minimize operations should execute after maximize for proper window animations if diff.contains(WindowFlags::MINIMIZED) { unsafe { - winuser::ShowWindow( + ShowWindow( window, match new.contains(WindowFlags::MINIMIZED) { - true => winuser::SW_MINIMIZE, - false => winuser::SW_RESTORE, + true => SW_MINIMIZE, + false => SW_RESTORE, + }, + ); + } + } + + if diff.contains(WindowFlags::MAXIMIZED) || new.contains(WindowFlags::MAXIMIZED) { + unsafe { + ShowWindow( + window, + match new.contains(WindowFlags::MAXIMIZED) { + true => SW_MAXIMIZE, + false => SW_RESTORE, }, ); } @@ -300,18 +305,20 @@ impl WindowFlags { let (style, style_ex) = new.to_window_styles(); unsafe { - winuser::SendMessageW(window, *event_loop::SET_RETAIN_STATE_ON_SIZE_MSG_ID, 1, 0); + SendMessageW( + window, + *event_loop::SET_RETAIN_STATE_ON_SIZE_MSG_ID, + WPARAM(1), + LPARAM(0), + ); // This condition is necessary to avoid having an unrestorable window if !new.contains(WindowFlags::MINIMIZED) { - winuser::SetWindowLongW(window, winuser::GWL_STYLE, style as _); - winuser::SetWindowLongW(window, winuser::GWL_EXSTYLE, style_ex as _); + SetWindowLongW(window, GWL_STYLE, style as i32); + SetWindowLongW(window, GWL_EXSTYLE, style_ex as i32); } - let mut flags = winuser::SWP_NOZORDER - | winuser::SWP_NOMOVE - | winuser::SWP_NOSIZE - | winuser::SWP_FRAMECHANGED; + let mut flags = SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED; // We generally don't want style changes here to affect window // focus, but for fullscreen windows they must be activated @@ -319,12 +326,17 @@ impl WindowFlags { if !new.contains(WindowFlags::MARKER_EXCLUSIVE_FULLSCREEN) && !new.contains(WindowFlags::MARKER_BORDERLESS_FULLSCREEN) { - flags |= winuser::SWP_NOACTIVATE; + flags |= SWP_NOACTIVATE; } // Refresh the window frame - winuser::SetWindowPos(window, ptr::null_mut(), 0, 0, 0, 0, flags); - winuser::SendMessageW(window, *event_loop::SET_RETAIN_STATE_ON_SIZE_MSG_ID, 0, 0); + SetWindowPos(window, HWND::default(), 0, 0, 0, 0, flags); + SendMessageW( + window, + *event_loop::SET_RETAIN_STATE_ON_SIZE_MSG_ID, + WPARAM(0), + LPARAM(0), + ); } } } diff --git a/src/system_tray.rs b/src/system_tray.rs index 7314849359..95cc21d58a 100644 --- a/src/system_tray.rs +++ b/src/system_tray.rs @@ -18,11 +18,6 @@ //! .unwrap(); //! ``` //! -//! # Windows -//! The icon is not removed automatically. -//! -//! Use `SystemTrayExtWindows` and use the `remove()` function when your application is closing. -//! //! # Linux //! A menu is required or the tray return an error containing `assertion 'G_IS_DBUS_CONNECTION (connection)'`. //! diff --git a/src/window.rs b/src/window.rs index 222d178780..a58c8dc251 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1060,4 +1060,4 @@ impl Default for UserAttentionType { } /// A constant used to determine how much inside the window, the resize handler should appear (only used in Linux(gtk) and Windows). -pub const BORDERLESS_RESIZE_INSET: i32 = 3; +pub const BORDERLESS_RESIZE_INSET: i32 = 5;