diff --git a/.gitignore b/.gitignore index ea8c4bf..dadf223 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ /target +*.user +bin/ +obj/ +.vs/ \ No newline at end of file diff --git a/AppxManifest.xml b/AppxManifest.xml new file mode 100644 index 0000000..1b11d22 --- /dev/null +++ b/AppxManifest.xml @@ -0,0 +1,27 @@ + + + + + + Minesweeper-rs + robmi + assets\StoreLogo.png + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 1102616..8d762c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,11 +5,13 @@ authors = ["Robert Mikhayelyan "] edition = "2018" [dependencies] -winit = "0.22.0" -raw-window-handle = "0.3.3" winrt = { git = "https://github.com/microsoft/winrt-rs" } rand = "0.7.3" bindings = { path = "bindings" } +[target.'cfg(target_vendor = "pc")'.dependencies] +winit = "0.22.2" +raw-window-handle = "0.3.3" + [features] show-mines = [] diff --git a/ClickOnce/Minesweeper.csproj b/ClickOnce/Minesweeper.csproj new file mode 100644 index 0000000..8c67a2c --- /dev/null +++ b/ClickOnce/Minesweeper.csproj @@ -0,0 +1,15 @@ + + + WinExe + net5.0-windows + true + Release + ClickOnceProfile + Square44x44Logo.ico + + + + Always + + + \ No newline at end of file diff --git a/ClickOnce/Program.cs b/ClickOnce/Program.cs new file mode 100644 index 0000000..8ba6fbc --- /dev/null +++ b/ClickOnce/Program.cs @@ -0,0 +1,21 @@ +using System; +using System.Diagnostics; +using System.Windows.Forms; + +namespace WindowsFormsApp1 +{ + static class Program + { + static void Main() + { + try + { + Process.Start(new ProcessStartInfo { CreateNoWindow = true, FileName = "minesweeper-rs.exe" }); + } + catch (Exception x) + { + MessageBox.Show(x.Message, "Error Starting Minesweeper", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + } +} diff --git a/ClickOnce/Properties/PublishProfiles/ClickOnceProfile.pubxml b/ClickOnce/Properties/PublishProfiles/ClickOnceProfile.pubxml new file mode 100644 index 0000000..a281a5a --- /dev/null +++ b/ClickOnce/Properties/PublishProfiles/ClickOnceProfile.pubxml @@ -0,0 +1,34 @@ + + + 0 + 0.1.0.* + True + Release + True + True + true + Web + True + True + true + false + Any CPU + bin\publish\ + bin\publish\ + ClickOnce + False + False + False + sha256RSA + True + net5.0-windows + True + Foreground + + + + true + .NET Desktop Runtime 5.0.1 (x64) + + + diff --git a/ClickOnce/Properties/PublishProfiles/ClickOnceProfile.pubxml.user.template b/ClickOnce/Properties/PublishProfiles/ClickOnceProfile.pubxml.user.template new file mode 100644 index 0000000..74cb83e --- /dev/null +++ b/ClickOnce/Properties/PublishProfiles/ClickOnceProfile.pubxml.user.template @@ -0,0 +1,6 @@ + + + https://ctaggart.github.io/minesweeper-rs/ + 1A92BF20220B301077205787F406B1BCEE6DA97E + + diff --git a/ClickOnce/Square44x44Logo.ico b/ClickOnce/Square44x44Logo.ico new file mode 100644 index 0000000..ffdfbf6 Binary files /dev/null and b/ClickOnce/Square44x44Logo.ico differ diff --git a/ClickOnce/build.ps1 b/ClickOnce/build.ps1 new file mode 100644 index 0000000..49b2f5b --- /dev/null +++ b/ClickOnce/build.ps1 @@ -0,0 +1 @@ +msbuild $PSScriptRoot /t:Restore,Publish /v:m diff --git a/README.md b/README.md index bc9764a..fb77ee8 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,25 @@ A port of [robmikh/Minesweeper](https://github.com/robmikh/Minesweeper) using [winrt-rs](https://github.com/microsoft/winrt-rs). ## Running -Running this sample requires at least Windows build 1803 (v10.0.17134.0). To compile and run (after setting up), use: +Running this sample requires at least Windows build 1803 (v10.0.17134.0). Instructions are a little different between desktop and UWP: + +### Desktop +Running Minesweeper as a normal desktop application can be done as follows: ``` cargo run --release ``` +### UWP + +Running Minesweeper as a UWP application can be done by building for a `*-uwp-windows-msvc` target and then registering the app. More information can be found [here](UWP.md). + +``` +cargo +nightly build -Z build-std=std,panic_abort --target x86_64-uwp-windows-msvc +(powershell.exe) Add-AppxPackage -Register AppxManifest.xml +``` +*NOTE: AppManifest.xml currently assumes the `x86_64-uwp-windows-msvc` target but can be updated.* + +Then launch minesweeper-rs from the Start Menu. + ![minesweeper-opt2](https://user-images.githubusercontent.com/7089228/80656536-45ac2c80-8a36-11ea-8521-ab40fc922ce1.gif) diff --git a/UWP.md b/UWP.md new file mode 100644 index 0000000..14a7a57 --- /dev/null +++ b/UWP.md @@ -0,0 +1,26 @@ +# Building for `*-uwp-windows-msvc` targets + +## Required tools +First, you'll need to install the nightly toolchain: + +``` +rustup toolchain install nightly +rustup component add rust-src +``` + +I'm using version `1.50.0-nightly (1c389ffef 2020-11-24)`. If you already have a nightly toolchain installed and you're seeing an error about `SetThreadStackGuarantee`, update your nightly toolchain. + +## Building Minesweeper +From the appropriate VS command prompt (e.g. "x64 Native Tools Command Prompt for VS 2019" when building for x86_64), run cargo but target a uwp target: + +``` +cargo +nightly build -Z build-std=std,panic_abort --target x86_64-uwp-windows-msvc +``` + +After that, you should be able to register your application: + +``` +(powershell.exe) Add-AppxPackage -Register AppxManifest.xml +``` + +Special thanks to [bdbai](https://github.com/bdbai) for the [firstuwp-rs](https://github.com/bdbai/firstuwp-rs) project. Without that, I wouldn't have known about the [build-std](https://doc.rust-lang.org/cargo/reference/unstable.html#build-std) cargo feature. \ No newline at end of file diff --git a/assets/LockScreenLogo.png b/assets/LockScreenLogo.png new file mode 100644 index 0000000..99e3933 Binary files /dev/null and b/assets/LockScreenLogo.png differ diff --git a/assets/SplashScreen.png b/assets/SplashScreen.png new file mode 100644 index 0000000..1605ae2 Binary files /dev/null and b/assets/SplashScreen.png differ diff --git a/assets/Square150x150Logo.png b/assets/Square150x150Logo.png new file mode 100644 index 0000000..66b5fd9 Binary files /dev/null and b/assets/Square150x150Logo.png differ diff --git a/assets/Square44x44Logo.png b/assets/Square44x44Logo.png new file mode 100644 index 0000000..4aa5c0e Binary files /dev/null and b/assets/Square44x44Logo.png differ diff --git a/assets/StoreLogo.png b/assets/StoreLogo.png new file mode 100644 index 0000000..d8275a0 Binary files /dev/null and b/assets/StoreLogo.png differ diff --git a/assets/Wide310x150Logo.png b/assets/Wide310x150Logo.png new file mode 100644 index 0000000..16712d7 Binary files /dev/null and b/assets/Wide310x150Logo.png differ diff --git a/bindings/build.rs b/bindings/build.rs index 2735865..06a59e7 100644 --- a/bindings/build.rs +++ b/bindings/build.rs @@ -1,5 +1,11 @@ fn main() { winrt::build!( + windows::application_model::core::{ + CoreApplication, + CoreApplicationView, + IFrameworkViewSource, + IFrameworkView, + } windows::foundation::numerics::{Vector2, Vector3} windows::foundation::TimeSpan windows::graphics::SizeInt32 @@ -18,5 +24,12 @@ fn main() { } windows::ui::composition::desktop::DesktopWindowTarget windows::ui::Colors + windows::ui::core::{ + CoreDispatcher, + CoreWindow, + CoreProcessEventsOption, + WindowSizeChangedEventArgs, + PointerEventArgs, + } ); } diff --git a/src/desktop/interop.rs b/src/desktop/interop.rs new file mode 100644 index 0000000..746571c --- /dev/null +++ b/src/desktop/interop.rs @@ -0,0 +1,95 @@ +use bindings::windows::{ + system::DispatcherQueueController, ui::composition::desktop::DesktopWindowTarget, +}; + +// Note: This COM ABI code will be generated for you when this issue is completed: +// https://github.com/microsoft/winrt-rs/issues/81 + +#[repr(C)] +pub struct ICompositorDesktopInterop_vtable( + usize, + usize, + usize, + extern "system" fn( + winrt::RawPtr, + winrt::RawPtr, + bool, + &mut Option, + ) -> winrt::ErrorCode, +); + +unsafe impl winrt::Interface for ICompositorDesktopInterop { + type Vtable = ICompositorDesktopInterop_vtable; + + const IID: winrt::Guid = + winrt::Guid::from_values(702976506, 17767, 19914, [179, 25, 208, 242, 7, 235, 104, 7]); +} + +#[repr(transparent)] +#[derive(Clone, PartialEq)] +pub struct ICompositorDesktopInterop(winrt::IUnknown); + +impl ICompositorDesktopInterop { + pub fn create_desktop_window_target( + &self, + hwnd: winrt::RawPtr, + is_topmost: bool, + ) -> winrt::Result { + use winrt::{Abi, Interface}; + let mut result = None; + unsafe { (self.vtable().3)(self.abi(), hwnd, is_topmost, &mut result).and_some(result) } + } +} + +#[link(name = "coremessaging")] +extern "stdcall" { + fn CreateDispatcherQueueController( + options: DispatcherQueueOptions, + dispatcherQueueController: &mut Option, + ) -> winrt::ErrorCode; +} + +#[repr(C)] +struct DispatcherQueueOptions { + size: u32, + thread_type: DispatcherQueueThreadType, + apartment_type: DispatcherQueueThreadApartmentType, +} + +#[allow(dead_code)] +#[repr(i32)] +pub enum DispatcherQueueThreadType { + Dedicated = 1, + Current = 2, +} + +#[allow(dead_code)] +#[repr(i32)] +pub enum DispatcherQueueThreadApartmentType { + None = 0, + ASTA = 1, + STA = 2, +} + +pub fn create_dispatcher_queue_controller( + thread_type: DispatcherQueueThreadType, + apartment_type: DispatcherQueueThreadApartmentType, +) -> winrt::Result { + let options = DispatcherQueueOptions { + size: std::mem::size_of::() as u32, + thread_type, + apartment_type, + }; + unsafe { + let mut result = None; + CreateDispatcherQueueController(options, &mut result).and_some(result) + } +} + +pub fn create_dispatcher_queue_controller_for_current_thread( +) -> winrt::Result { + create_dispatcher_queue_controller( + DispatcherQueueThreadType::Current, + DispatcherQueueThreadApartmentType::None, + ) +} diff --git a/src/desktop/mod.rs b/src/desktop/mod.rs new file mode 100644 index 0000000..41837be --- /dev/null +++ b/src/desktop/mod.rs @@ -0,0 +1,5 @@ +mod interop; +mod run; +mod window_target; + +pub use run::run; diff --git a/src/desktop/run.rs b/src/desktop/run.rs new file mode 100644 index 0000000..c7a08ee --- /dev/null +++ b/src/desktop/run.rs @@ -0,0 +1,74 @@ +use crate::desktop::interop::create_dispatcher_queue_controller_for_current_thread; +use crate::desktop::window_target::CompositionDesktopWindowTargetSource; +use crate::interop::{ro_initialize, RoInitType}; +use crate::minesweeper::Minesweeper; +use winit::{ + event::{ElementState, Event, MouseButton, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, +}; + +use bindings::windows::{foundation::numerics::Vector2, ui::composition::Compositor}; + +pub fn run() -> winrt::Result<()> { + ro_initialize(RoInitType::MultiThreaded)?; + let _controller = create_dispatcher_queue_controller_for_current_thread()?; + + let event_loop = EventLoop::new(); + let window = WindowBuilder::new().build(&event_loop).unwrap(); + window.set_title("Minesweeper"); + + let compositor = Compositor::new()?; + let target = window.create_window_target(&compositor, false)?; + + let root = compositor.create_container_visual()?; + root.set_relative_size_adjustment(Vector2 { x: 1.0, y: 1.0 })?; + target.set_root(&root)?; + + let window_size = window.inner_size(); + let window_size = Vector2 { + x: window_size.width as f32, + y: window_size.height as f32, + }; + let mut game = Minesweeper::new(&root, &window_size)?; + + event_loop.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Wait; + match event { + Event::WindowEvent { + event: WindowEvent::CloseRequested, + window_id, + } if window_id == window.id() => *control_flow = ControlFlow::Exit, + Event::WindowEvent { + event: WindowEvent::Resized(size), + .. + } => { + let size = Vector2 { + x: size.width as f32, + y: size.height as f32, + }; + game.on_parent_size_changed(&size).unwrap(); + } + Event::WindowEvent { + event: WindowEvent::CursorMoved { position, .. }, + .. + } => { + let point = Vector2 { + x: position.x as f32, + y: position.y as f32, + }; + game.on_pointer_moved(&point).unwrap(); + } + Event::WindowEvent { + event: WindowEvent::MouseInput { state, button, .. }, + .. + } => { + if state == ElementState::Pressed { + game.on_pointer_pressed(button == MouseButton::Right, false) + .unwrap(); + } + } + _ => (), + } + }); +} diff --git a/src/window_target.rs b/src/desktop/window_target.rs similarity index 94% rename from src/window_target.rs rename to src/desktop/window_target.rs index dc82fb4..a4b3fd4 100644 --- a/src/window_target.rs +++ b/src/desktop/window_target.rs @@ -1,4 +1,4 @@ -use crate::interop::ICompositorDesktopInterop; +use crate::desktop::interop::ICompositorDesktopInterop; use bindings::windows::ui::composition::{desktop::DesktopWindowTarget, Compositor}; use raw_window_handle::HasRawWindowHandle; use winrt::Interface; diff --git a/src/interop.rs b/src/interop.rs index d0c752e..61f7b39 100644 --- a/src/interop.rs +++ b/src/interop.rs @@ -1,46 +1,3 @@ -use bindings::windows::{ - system::DispatcherQueueController, ui::composition::desktop::DesktopWindowTarget, -}; - -// Note: This COM ABI code will be generated for you when this issue is completed: -// https://github.com/microsoft/winrt-rs/issues/81 - -#[repr(C)] -pub struct ICompositorDesktopInterop_vtable( - usize, - usize, - usize, - extern "system" fn( - winrt::RawPtr, - winrt::RawPtr, - bool, - &mut Option, - ) -> winrt::ErrorCode, -); - -unsafe impl winrt::Interface for ICompositorDesktopInterop { - type Vtable = ICompositorDesktopInterop_vtable; - - const IID: winrt::Guid = - winrt::Guid::from_values(702976506, 17767, 19914, [179, 25, 208, 242, 7, 235, 104, 7]); -} - -#[repr(transparent)] -#[derive(Clone, PartialEq)] -pub struct ICompositorDesktopInterop(winrt::IUnknown); - -impl ICompositorDesktopInterop { - pub fn create_desktop_window_target( - &self, - hwnd: winrt::RawPtr, - is_topmost: bool, - ) -> winrt::Result { - use winrt::{Abi, Interface}; - let mut result = None; - unsafe { (self.vtable().3)(self.abi(), hwnd, is_topmost, &mut result).and_some(result) } - } -} - #[link(name = "windowsapp")] extern "stdcall" { fn RoInitialize(init_type: RoInitType) -> winrt::ErrorCode; @@ -53,59 +10,7 @@ pub enum RoInitType { SingleThreaded = 1, } +#[allow(dead_code)] pub fn ro_initialize(init_type: RoInitType) -> winrt::Result<()> { unsafe { RoInitialize(init_type).ok() } } - -#[link(name = "coremessaging")] -extern "stdcall" { - fn CreateDispatcherQueueController( - options: DispatcherQueueOptions, - dispatcherQueueController: &mut Option, - ) -> winrt::ErrorCode; -} - -#[repr(C)] -struct DispatcherQueueOptions { - size: u32, - thread_type: DispatcherQueueThreadType, - apartment_type: DispatcherQueueThreadApartmentType, -} - -#[allow(dead_code)] -#[repr(i32)] -pub enum DispatcherQueueThreadType { - Dedicated = 1, - Current = 2, -} - -#[allow(dead_code)] -#[repr(i32)] -pub enum DispatcherQueueThreadApartmentType { - None = 0, - ASTA = 1, - STA = 2, -} - -pub fn create_dispatcher_queue_controller( - thread_type: DispatcherQueueThreadType, - apartment_type: DispatcherQueueThreadApartmentType, -) -> winrt::Result { - let options = DispatcherQueueOptions { - size: std::mem::size_of::() as u32, - thread_type, - apartment_type, - }; - unsafe { - let mut result = None; - CreateDispatcherQueueController(options, &mut result).and_some(result) - } -} - -pub fn create_dispatcher_queue_controller_for_current_thread( -) -> winrt::Result { - create_dispatcher_queue_controller( - DispatcherQueueThreadType::Current, - DispatcherQueueThreadApartmentType::None, - ) -} diff --git a/src/main.rs b/src/main.rs index 42c6081..ce7e997 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,84 +1,21 @@ +#![windows_subsystem = "windows"] + mod comp_assets; mod comp_ui; mod interop; mod minesweeper; mod numerics; mod visual_grid; -mod window_target; - -use interop::{create_dispatcher_queue_controller_for_current_thread, ro_initialize, RoInitType}; -use minesweeper::Minesweeper; -use window_target::CompositionDesktopWindowTargetSource; -use winit::{ - event::{ElementState, Event, MouseButton, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, -}; - -use bindings::windows::{foundation::numerics::Vector2, ui::composition::Compositor}; - -fn run() -> winrt::Result<()> { - ro_initialize(RoInitType::MultiThreaded)?; - let _controller = create_dispatcher_queue_controller_for_current_thread()?; - let event_loop = EventLoop::new(); - let window = WindowBuilder::new().build(&event_loop).unwrap(); - window.set_title("Minesweeper"); +#[cfg(target_vendor = "pc")] +mod desktop; +#[cfg(target_vendor = "uwp")] +mod uwp; - let compositor = Compositor::new()?; - let target = window.create_window_target(&compositor, false)?; - - let root = compositor.create_container_visual()?; - root.set_relative_size_adjustment(Vector2 { x: 1.0, y: 1.0 })?; - target.set_root(&root)?; - - let window_size = window.inner_size(); - let window_size = Vector2 { - x: window_size.width as f32, - y: window_size.height as f32, - }; - let mut game = Minesweeper::new(&root, &window_size)?; - - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; - match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - window_id, - } if window_id == window.id() => *control_flow = ControlFlow::Exit, - Event::WindowEvent { - event: WindowEvent::Resized(size), - .. - } => { - let size = Vector2 { - x: size.width as f32, - y: size.height as f32, - }; - game.on_parent_size_changed(&size).unwrap(); - } - Event::WindowEvent { - event: WindowEvent::CursorMoved { position, .. }, - .. - } => { - let point = Vector2 { - x: position.x as f32, - y: position.y as f32, - }; - game.on_pointer_moved(&point).unwrap(); - } - Event::WindowEvent { - event: WindowEvent::MouseInput { state, button, .. }, - .. - } => { - if state == ElementState::Pressed { - game.on_pointer_pressed(button == MouseButton::Right, false) - .unwrap(); - } - } - _ => (), - } - }); -} +#[cfg(target_vendor = "pc")] +use desktop::run; +#[cfg(target_vendor = "uwp")] +use uwp::run; fn main() { let result = run(); diff --git a/src/uwp/app.rs b/src/uwp/app.rs new file mode 100644 index 0000000..22bc56d --- /dev/null +++ b/src/uwp/app.rs @@ -0,0 +1,152 @@ +use crate::minesweeper::Minesweeper; +use bindings::windows::{ + application_model::core::{CoreApplicationView, IFrameworkView}, + foundation::numerics::Vector2, + foundation::TypedEventHandler, + ui::composition::{CompositionTarget, Compositor}, + ui::core::{CoreProcessEventsOption, CoreWindow, PointerEventArgs, WindowSizeChangedEventArgs}, +}; +use bindings::*; +use std::sync::{Arc, Mutex}; + +struct AppState { + _window: CoreWindow, + _compositor: Compositor, + _target: CompositionTarget, + + game: Minesweeper, +} + +#[winrt::implement(windows::application_model::core::IFrameworkViewSource)] +pub struct MinesweeperAppSource {} + +impl MinesweeperAppSource { + fn create_view(&mut self) -> winrt::Result { + let app = MinesweeperApp { + state: Arc::new(Mutex::new(None)), + }; + let view: IFrameworkView = app.into(); + Ok(view) + } +} + +// TOOD: A way to do this without the arc/mutex? +#[winrt::implement(windows::application_model::core::IFrameworkView)] +pub struct MinesweeperApp { + state: Arc>>, +} + +impl MinesweeperApp { + fn initialize(&mut self, _window: &Option) -> winrt::Result<()> { + Ok(()) + } + + fn set_window(&mut self, _window: &Option) -> winrt::Result<()> { + Ok(()) + } + + fn load(&mut self, _entry_point: &winrt::HString) -> winrt::Result<()> { + Ok(()) + } + + fn run(&mut self) -> winrt::Result<()> { + let window = CoreWindow::get_for_current_thread()?; + + // Init Composition + let compositor = Compositor::new()?; + let root = compositor.create_container_visual()?; + root.set_relative_size_adjustment(Vector2 { x: 1.0, y: 1.0 })?; + let target = compositor.create_target_for_current_view()?; + target.set_root(&root)?; + + // Init minesweeper + let window_size = get_window_size(&window)?; + let game = Minesweeper::new(&root, &window_size)?; + + // Initialize our internal state + let state = AppState { + _window: window.clone(), + _compositor: compositor, + _target: target, + + game, + }; + self.state.lock().unwrap().replace(state); + + // Hook events + type SizeChangedHandler = TypedEventHandler; + type PointerMovedHandler = TypedEventHandler; + type PointerPressedHandler = TypedEventHandler; + + let size_changed_handler = SizeChangedHandler::new({ + let state = self.state.clone(); + move |_sender, args| { + let args = args.as_ref().unwrap(); + let size = args.size()?; + let size = Vector2 { + x: size.width as f32, + y: size.height as f32, + }; + let mut state = state.lock().unwrap(); + let state = state.as_mut().unwrap(); + let game = &mut state.game; + game.on_parent_size_changed(&size)?; + Ok(()) + } + }); + let pointer_moved_handler = PointerMovedHandler::new({ + let state = self.state.clone(); + move |_sender, args| { + let args = args.as_ref().unwrap(); + let point = args.current_point()?.position()?; + let point = Vector2 { + x: point.x as f32, + y: point.y as f32, + }; + let mut state = state.lock().unwrap(); + let state = state.as_mut().unwrap(); + let game = &mut state.game; + game.on_pointer_moved(&point)?; + Ok(()) + } + }); + let pointer_pressed_handler = PointerPressedHandler::new({ + let state = self.state.clone(); + move |_sender, args| { + let args = args.as_ref().unwrap(); + let properties = args.current_point()?.properties()?; + let is_right = properties.is_right_button_pressed()?; + let is_eraser = properties.is_eraser()?; + let mut state = state.lock().unwrap(); + let state = state.as_mut().unwrap(); + let game = &mut state.game; + game.on_pointer_pressed(is_right, is_eraser)?; + Ok(()) + } + }); + + window.size_changed(size_changed_handler)?; + window.pointer_moved(pointer_moved_handler)?; + window.pointer_pressed(pointer_pressed_handler)?; + + // Activate the window and start running the dispatcher + window.activate()?; + + let dispatcher = window.dispatcher()?; + dispatcher.process_events(CoreProcessEventsOption::ProcessUntilQuit)?; + + Ok(()) + } + + fn uninitialize(&mut self) -> winrt::Result<()> { + Ok(()) + } +} + +fn get_window_size(window: &CoreWindow) -> winrt::Result { + let bounds = window.bounds()?; + Ok(Vector2 { + x: bounds.width as f32, + y: bounds.height as f32, + }) +} diff --git a/src/uwp/mod.rs b/src/uwp/mod.rs new file mode 100644 index 0000000..648d42f --- /dev/null +++ b/src/uwp/mod.rs @@ -0,0 +1,4 @@ +mod app; +mod run; + +pub use run::run; diff --git a/src/uwp/run.rs b/src/uwp/run.rs new file mode 100644 index 0000000..ee75e4b --- /dev/null +++ b/src/uwp/run.rs @@ -0,0 +1,9 @@ +use crate::uwp::app::MinesweeperAppSource; +use bindings::windows::application_model::core::{CoreApplication, IFrameworkViewSource}; + +pub fn run() -> winrt::Result<()> { + let app_source = MinesweeperAppSource {}; + let view_source: IFrameworkViewSource = app_source.into(); + CoreApplication::run(&view_source)?; + Ok(()) +}