Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
/target
*.user
bin/
obj/
.vs/
27 changes: 27 additions & 0 deletions AppxManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" IgnorableNamespaces="uap mp build" xmlns:build="http://schemas.microsoft.com/developer/appx/2015/build">
<Identity Name="748880FC-7B08-4BC5-B0C1-35FA863C53C8" Publisher="CN=25C90434-4343-4A2A-BB16-CF3209256BD3" Version="0.0.1.0" ProcessorArchitecture="x64" />
<mp:PhoneIdentity PhoneProductId="748880FC-7B08-4BC5-B0C1-35FA863C53C8" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
<Properties>
<DisplayName>Minesweeper-rs</DisplayName>
<PublisherDisplayName>robmi</PublisherDisplayName>
<Logo>assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17134.0" MaxVersionTested="10.0.18362.0" />
<PackageDependency Name="Microsoft.VCLibs.140.00" MinVersion="14.0.27810.0" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
</Dependencies>
<Resources>
<Resource Language="EN-US" />
</Resources>
<Applications>
<Application Id="App" Executable="target\x86_64-uwp-windows-msvc\debug\minesweeper-rs.exe" EntryPoint="Minesweeper.App">
<uap:VisualElements DisplayName="minesweeper-rs" Description="A port of robmikh/Minesweeper using winrt-rs." Square44x44Logo="assets\Square44x44Logo.png" Square150x150Logo="assets\Square150x150Logo.png" BackgroundColor="transparent">
<uap:DefaultTile Wide310x150Logo="assets\Wide310x150Logo.png"></uap:DefaultTile>
<uap:SplashScreen Image="assets\SplashScreen.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
</Capabilities>
</Package>
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ authors = ["Robert Mikhayelyan <[email protected]>"]
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 = []
15 changes: 15 additions & 0 deletions ClickOnce/Minesweeper.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net5.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<Configuration>Release</Configuration>
<PublishProfile>ClickOnceProfile</PublishProfile>
<ApplicationIcon>Square44x44Logo.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Content Include="..\target\release\minesweeper-rs.exe">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
21 changes: 21 additions & 0 deletions ClickOnce/Program.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
}
34 changes: 34 additions & 0 deletions ClickOnce/Properties/PublishProfiles/ClickOnceProfile.pubxml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<Project>
<PropertyGroup>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>0.1.0.*</ApplicationVersion>
<BootstrapperEnabled>True</BootstrapperEnabled>
<Configuration>Release</Configuration>
<CreateDesktopShortcut>True</CreateDesktopShortcut>
<GenerateManifests>True</GenerateManifests>
<Install>true</Install>
<InstallFrom>Web</InstallFrom>
<IsRevisionIncremented>True</IsRevisionIncremented>
<IsWebBootstrapper>True</IsWebBootstrapper>
<MapFileExtensions>true</MapFileExtensions>
<OpenBrowserOnPublish>false</OpenBrowserOnPublish>
<Platform>Any CPU</Platform>
<PublishDir>bin\publish\</PublishDir>
<PublishUrl>bin\publish\</PublishUrl>
<PublishProtocol>ClickOnce</PublishProtocol>
<PublishReadyToRun>False</PublishReadyToRun>
<PublishSingleFile>False</PublishSingleFile>
<SelfContained>False</SelfContained>
<SignatureAlgorithm>sha256RSA</SignatureAlgorithm>
<SignManifests>True</SignManifests>
<TargetFramework>net5.0-windows</TargetFramework>
<UpdateEnabled>True</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
</PropertyGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.NetCore.DesktopRuntime.5.0.x64">
<Install>true</Install>
<ProductName>.NET Desktop Runtime 5.0.1 (x64)</ProductName>
</BootstrapperPackage>
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Project>
<PropertyGroup>
<InstallUrl>https://ctaggart.github.io/minesweeper-rs/</InstallUrl>
<ManifestCertificateThumbprint>1A92BF20220B301077205787F406B1BCEE6DA97E</ManifestCertificateThumbprint>
</PropertyGroup>
</Project>
Binary file added ClickOnce/Square44x44Logo.ico
Binary file not shown.
1 change: 1 addition & 0 deletions ClickOnce/build.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
msbuild $PSScriptRoot /t:Restore,Publish /v:m
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
26 changes: 26 additions & 0 deletions UWP.md
Original file line number Diff line number Diff line change
@@ -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.
Binary file added assets/LockScreenLogo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/SplashScreen.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/Square150x150Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/Square44x44Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/StoreLogo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/Wide310x150Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions bindings/build.rs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -18,5 +24,12 @@ fn main() {
}
windows::ui::composition::desktop::DesktopWindowTarget
windows::ui::Colors
windows::ui::core::{
CoreDispatcher,
CoreWindow,
CoreProcessEventsOption,
WindowSizeChangedEventArgs,
PointerEventArgs,
}
);
}
95 changes: 95 additions & 0 deletions src/desktop/interop.rs
Original file line number Diff line number Diff line change
@@ -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<DesktopWindowTarget>,
) -> 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<DesktopWindowTarget> {
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<DispatcherQueueController>,
) -> 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<DispatcherQueueController> {
let options = DispatcherQueueOptions {
size: std::mem::size_of::<DispatcherQueueOptions>() 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<DispatcherQueueController> {
create_dispatcher_queue_controller(
DispatcherQueueThreadType::Current,
DispatcherQueueThreadApartmentType::None,
)
}
5 changes: 5 additions & 0 deletions src/desktop/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mod interop;
mod run;
mod window_target;

pub use run::run;
74 changes: 74 additions & 0 deletions src/desktop/run.rs
Original file line number Diff line number Diff line change
@@ -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();
}
}
_ => (),
}
});
}
2 changes: 1 addition & 1 deletion src/window_target.rs → src/desktop/window_target.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Loading