Skip to content

Commit

Permalink
Replace windows with windows-sys (#118)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jake-Shadle authored Apr 26, 2023
1 parent 89174df commit 6168ea6
Show file tree
Hide file tree
Showing 9 changed files with 565 additions and 230 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ jobs:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: "[Ubuntu] install dependencies"
if: matrix.name == 'Ubuntu GTK'
run: sudo apt update && sudo apt install libgtk-3-dev
- name: "[WASM] rustup"
if: matrix.name == 'WASM32'
run: rustup target add wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v1
- uses: Swatinem/rust-cache@v2
- name: Build
run: cargo build --target ${{ matrix.target }} ${{ matrix.flags }}
- name: Test
Expand Down
5 changes: 2 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ default = ["gtk3"]
file-handle-inner = []
gtk3 = ["gtk-sys", "glib-sys", "gobject-sys"]
xdg-portal = ["ashpd", "urlencoding", "pollster"]
common-controls-v6 = ["windows/Win32_UI_Controls"]
common-controls-v6 = ["windows-sys/Win32_UI_Controls"]

[dev-dependencies]
futures = "0.3.12"
Expand All @@ -32,7 +32,7 @@ block = "0.1.6"
objc-foundation = "0.1.1"

[target.'cfg(target_os = "windows")'.dependencies]
windows = { version = "0.44", features = [
windows-sys = { version = "0.48", features = [
"Win32_Foundation",
"Win32_System_Com",
"Win32_UI_Shell_Common",
Expand Down Expand Up @@ -76,4 +76,3 @@ name = "async"

[package.metadata.docs.rs]
features = ["file-handle-inner"]

23 changes: 14 additions & 9 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
fn main() {
let target = std::env::var("TARGET").unwrap();
let target_os = std::env::var("CARGO_CFG_TARGET_OS").expect("target OS not detected");

if target.contains("darwin") {
println!("cargo:rustc-link-lib=framework=AppKit");
}

#[cfg(all(feature = "gtk3", feature = "xdg-portal"))]
compile_error!("You can't enable both GTK3 & XDG Portal features at once");
match target_os.as_str() {
"macos" => println!("cargo:rustc-link-lib=framework=AppKit"),
"windows" => {}
_ => {
let gtk = std::env::var_os("CARGO_FEATURE_GTK3").is_some();
let xdg = std::env::var_os("CARGO_FEATURE_XDG_PORTAL").is_some();

#[cfg(not(any(feature = "gtk3", feature = "xdg-portal")))]
compile_error!("You need to choose at least one backend: `gtk3` or `xdg-portal` features");
if gtk && xdg {
panic!("You can't enable both `gtk3` and `xdg-portal` features at once");
} else if !gtk && !xdg {
panic!("You need to choose at least one backend: `gtk3` or `xdg-portal` features");
}
}
}
}
5 changes: 2 additions & 3 deletions src/backend/win_cid/file_dialog.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
mod com;
pub mod dialog_ffi;
mod dialog_future;

use dialog_ffi::IDialog;
use dialog_ffi::{IDialog, Result};
use dialog_future::{multiple_return_future, single_return_future};

use crate::backend::DialogFutureType;
Expand All @@ -10,8 +11,6 @@ use crate::FileHandle;

use std::path::PathBuf;

use windows::core::Result;

use super::utils::init_com;

//
Expand Down
268 changes: 268 additions & 0 deletions src/backend/win_cid/file_dialog/com.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
#![allow(non_snake_case)]

use std::ffi::c_void;
use windows_sys::core::{HRESULT, PCWSTR, PWSTR};
pub use windows_sys::{
core::GUID,
Win32::{
Foundation::HWND,
UI::Shell::{Common::COMDLG_FILTERSPEC, FILEOPENDIALOGOPTIONS, SIGDN, SIGDN_FILESYSPATH},
},
};

pub(crate) type Result<T> = std::result::Result<T, HRESULT>;

#[inline]
pub(super) fn wrap_err(hresult: HRESULT) -> Result<()> {
if hresult >= 0 {
Ok(())
} else {
Err(hresult)
}
}

#[inline]
unsafe fn read_to_string(ptr: *const u16) -> String {
let mut cursor = ptr;

while *cursor != 0 {
cursor = cursor.add(1);
}

let slice = std::slice::from_raw_parts(ptr, cursor.offset_from(ptr) as usize);
String::from_utf16(slice).unwrap()
}

#[repr(C)]
pub(super) struct Interface<T> {
vtable: *mut T,
}

impl<T> Interface<T> {
#[inline]
pub(super) fn vtbl(&self) -> &T {
unsafe { &*self.vtable }
}
}

#[repr(C)]
pub(super) struct IUnknownV {
__query_interface: usize,
__add_ref: usize,
pub(super) release: unsafe extern "system" fn(this: *mut c_void) -> u32,
}

pub(super) type IUnknown = Interface<IUnknownV>;

#[inline]
fn drop_impl(ptr: *mut c_void) {
unsafe {
((*ptr.cast::<IUnknown>()).vtbl().release)(ptr);
}
}

#[repr(C)]
pub(super) struct IShellItemV {
base: IUnknownV,
BindToHandler: unsafe extern "system" fn(
this: *mut c_void,
pbc: *mut c_void,
bhid: *const GUID,
riid: *const GUID,
ppv: *mut *mut c_void,
) -> HRESULT,
GetParent: unsafe extern "system" fn(this: *mut c_void, ppsi: *mut *mut c_void) -> HRESULT,
GetDisplayName: unsafe extern "system" fn(
this: *mut c_void,
sigdnname: SIGDN,
ppszname: *mut PWSTR,
) -> HRESULT,
GetAttributes: usize,
Compare: unsafe extern "system" fn(
this: *mut c_void,
psi: *mut c_void,
hint: u32,
piorder: *mut i32,
) -> HRESULT,
}

#[repr(transparent)]
pub(super) struct IShellItem(pub(super) *mut Interface<IShellItemV>);

impl IShellItem {
pub(super) fn get_path(&self) -> Result<std::path::PathBuf> {
let filename = unsafe {
let mut dname = std::mem::MaybeUninit::uninit();
wrap_err(((*self.0).vtbl().GetDisplayName)(
self.0.cast(),
SIGDN_FILESYSPATH,
dname.as_mut_ptr(),
))?;

let dname = dname.assume_init();
let fname = read_to_string(dname);
windows_sys::Win32::System::Com::CoTaskMemFree(dname.cast());
fname
};

Ok(filename.into())
}
}

impl Drop for IShellItem {
fn drop(&mut self) {
drop_impl(self.0.cast());
}
}

#[repr(C)]
struct IShellItemArrayV {
base: IUnknownV,
BindToHandler: unsafe extern "system" fn(
this: *mut c_void,
pbc: *mut c_void,
bhid: *const GUID,
riid: *const GUID,
ppvout: *mut *mut c_void,
) -> HRESULT,
GetPropertyStore: usize,
GetPropertyDescriptionList: usize,
GetAttributes: usize,
GetCount: unsafe extern "system" fn(this: *mut c_void, pdwnumitems: *mut u32) -> HRESULT,
GetItemAt: unsafe extern "system" fn(
this: *mut c_void,
dwindex: u32,
ppsi: *mut IShellItem,
) -> HRESULT,
EnumItems:
unsafe extern "system" fn(this: *mut c_void, ppenumshellitems: *mut *mut c_void) -> HRESULT,
}

#[repr(transparent)]
pub(super) struct IShellItemArray(*mut Interface<IShellItemArrayV>);

impl IShellItemArray {
#[inline]
pub(super) fn get_count(&self) -> Result<u32> {
let mut count = 0;
unsafe {
wrap_err(((*self.0).vtbl().GetCount)(self.0.cast(), &mut count))?;
}
Ok(count)
}

#[inline]
pub(super) fn get_item_at(&self, index: u32) -> Result<IShellItem> {
let mut item = std::mem::MaybeUninit::uninit();
unsafe {
wrap_err(((*self.0).vtbl().GetItemAt)(
self.0.cast(),
index,
item.as_mut_ptr(),
))?;
Ok(item.assume_init())
}
}
}

impl Drop for IShellItemArray {
fn drop(&mut self) {
drop_impl(self.0.cast());
}
}

#[repr(C)]
pub(super) struct IModalWindowV {
base: IUnknownV,
pub(super) Show: unsafe extern "system" fn(this: *mut c_void, owner: HWND) -> HRESULT,
}

/// <https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ifiledialog>
#[repr(C)]
pub(super) struct IFileDialogV {
pub(super) base: IModalWindowV,
pub(super) SetFileTypes: unsafe extern "system" fn(
this: *mut c_void,
cfiletypes: u32,
rgfilterspec: *const COMDLG_FILTERSPEC,
) -> HRESULT,
SetFileTypeIndex: unsafe extern "system" fn(this: *mut c_void, ifiletype: u32) -> HRESULT,
GetFileTypeIndex: unsafe extern "system" fn(this: *mut c_void, pifiletype: *mut u32) -> HRESULT,
Advise: unsafe extern "system" fn(
this: *mut c_void,
pfde: *mut c_void,
pdwcookie: *mut u32,
) -> HRESULT,
Unadvise: unsafe extern "system" fn(this: *mut c_void, dwcookie: u32) -> HRESULT,
pub(super) SetOptions:
unsafe extern "system" fn(this: *mut c_void, fos: FILEOPENDIALOGOPTIONS) -> HRESULT,
GetOptions:
unsafe extern "system" fn(this: *mut c_void, pfos: *mut FILEOPENDIALOGOPTIONS) -> HRESULT,
SetDefaultFolder: unsafe extern "system" fn(this: *mut c_void, psi: *mut c_void) -> HRESULT,
pub(super) SetFolder: unsafe extern "system" fn(this: *mut c_void, psi: *mut c_void) -> HRESULT,
GetFolder: unsafe extern "system" fn(this: *mut c_void, ppsi: *mut *mut c_void) -> HRESULT,
GetCurrentSelection:
unsafe extern "system" fn(this: *mut c_void, ppsi: *mut *mut c_void) -> HRESULT,
pub(super) SetFileName:
unsafe extern "system" fn(this: *mut c_void, pszname: PCWSTR) -> HRESULT,
GetFileName: unsafe extern "system" fn(this: *mut c_void, pszname: *mut PWSTR) -> HRESULT,
pub(super) SetTitle: unsafe extern "system" fn(this: *mut c_void, psztitle: PCWSTR) -> HRESULT,
SetOkButtonLabel: unsafe extern "system" fn(this: *mut c_void, psztext: PCWSTR) -> HRESULT,
SetFileNameLabel: unsafe extern "system" fn(this: *mut c_void, pszlabel: PCWSTR) -> HRESULT,
pub(super) GetResult:
unsafe extern "system" fn(this: *mut c_void, ppsi: *mut IShellItem) -> HRESULT,
AddPlace: unsafe extern "system" fn(
this: *mut c_void,
psi: *mut c_void,
fdap: windows_sys::Win32::UI::Shell::FDAP,
) -> HRESULT,
pub(super) SetDefaultExtension:
unsafe extern "system" fn(this: *mut c_void, pszdefaultextension: PCWSTR) -> HRESULT,
Close: unsafe extern "system" fn(this: *mut c_void, hr: HRESULT) -> HRESULT,
SetClientGuid: unsafe extern "system" fn(this: *mut c_void, guid: *const GUID) -> HRESULT,
ClearClientData: unsafe extern "system" fn(this: *mut c_void) -> HRESULT,
SetFilter: unsafe extern "system" fn(this: *mut c_void, pfilter: *mut c_void) -> HRESULT,
}

#[repr(transparent)]
pub(super) struct IFileDialog(pub(super) *mut Interface<IFileDialogV>);

impl Drop for IFileDialog {
fn drop(&mut self) {
drop_impl(self.0.cast());
}
}

/// <https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ifileopendialog>
#[repr(C)]
pub(super) struct IFileOpenDialogV {
pub(super) base: IFileDialogV,
/// <https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ifileopendialog-getresults>
GetResults:
unsafe extern "system" fn(this: *mut c_void, ppenum: *mut IShellItemArray) -> HRESULT,
GetSelectedItems:
unsafe extern "system" fn(this: *mut c_void, ppsai: *mut *mut c_void) -> HRESULT,
}

#[repr(transparent)]
pub(super) struct IFileOpenDialog(pub(super) *mut Interface<IFileOpenDialogV>);

impl IFileOpenDialog {
#[inline]
pub(super) fn get_results(&self) -> Result<IShellItemArray> {
let mut res = std::mem::MaybeUninit::uninit();
unsafe {
wrap_err((((*self.0).vtbl()).GetResults)(
self.0.cast(),
res.as_mut_ptr(),
))?;
Ok(res.assume_init())
}
}
}

impl Drop for IFileOpenDialog {
fn drop(&mut self) {
drop_impl(self.0.cast());
}
}
Loading

0 comments on commit 6168ea6

Please sign in to comment.