Skip to content

Commit b83e95d

Browse files
committed
Checkpoint
1 parent fc6be77 commit b83e95d

File tree

6 files changed

+254
-42
lines changed

6 files changed

+254
-42
lines changed

Package.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,16 @@ let package = Package(
235235
name: "WinUIBackend",
236236
dependencies: [
237237
"SwiftCrossUI",
238+
"WinUIInterop",
238239
.product(name: "WinUI", package: "swift-winui"),
239240
.product(name: "WinAppSDK", package: "swift-windowsappsdk"),
240241
.product(name: "WindowsFoundation", package: "swift-windowsfoundation"),
241242
]
242243
),
244+
.target(
245+
name: "WinUIInterop",
246+
dependencies: []
247+
),
243248
// .target(
244249
// name: "CursesBackend",
245250
// dependencies: ["SwiftCrossUI", "TermKit"]
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import CWinRT
2+
import WinAppSDK
3+
import WinSDK
4+
import WinUI
5+
import WindowsFoundation
6+
7+
public func getWindowIDFromWindow(_ hWnd: HWND?) -> WinAppSDK.WindowId {
8+
HWNDInterop.shared.getWindowIDFromWindow(hWnd)
9+
}
10+
11+
public func getWindowFromWindowId(_ windowID: WinAppSDK.WindowId) -> HWND? {
12+
HWNDInterop.shared.getWindowFromWindowId(windowID)
13+
}
14+
15+
extension WinAppSDK.AppWindow {
16+
/// Returns the window handle for the app window.
17+
public func getHWND() -> HWND? {
18+
HWNDInterop.shared.getWindowFromWindowId(id)
19+
}
20+
}
21+
22+
extension WinUI.Window {
23+
/// Returns the window handle for the window.
24+
///
25+
/// - Note: This is a relatively expensive operation, particularly due to its use
26+
/// of the `appWindow` getter. If an `AppWindow` is already available, prefer to
27+
/// use `getHWND()` on that instead; better yet, if the window handle will be used
28+
/// frequently, assign it to a stored property, as it will not change during the
29+
/// lifetime of the window.
30+
public func getHWND() -> HWND? {
31+
// The appWindow can become nil when a Window is closed.
32+
guard let appWindow else { return nil }
33+
return appWindow.getHWND()
34+
}
35+
}
36+
37+
private struct HWNDInterop {
38+
private typealias pfnGetWindowIdFromWindow = @convention(c) (
39+
HWND?, UnsafeMutablePointer<__x_ABI_CMicrosoft_CUI_CWindowId>?
40+
) -> HRESULT
41+
private typealias pfnGetWindowFromWindowId = @convention(c) (
42+
__x_ABI_CMicrosoft_CUI_CWindowId, UnsafeMutablePointer<HWND?>?
43+
) -> HRESULT
44+
private var hModule: HMODULE!
45+
private var getWindowIDFromWindow_impl: pfnGetWindowIdFromWindow!
46+
private var getWindowFromWindowID_impl: pfnGetWindowFromWindowId!
47+
48+
static let shared = HWNDInterop()
49+
50+
init() {
51+
"Microsoft.Internal.FrameworkUdk.dll".withCString(encodedAs: UTF16.self) {
52+
hModule = GetModuleHandleW($0)
53+
if hModule == nil {
54+
hModule = LoadLibraryW($0)
55+
}
56+
}
57+
58+
if let pfn = GetProcAddress(hModule, "Windowing_GetWindowIdFromWindow") {
59+
getWindowIDFromWindow_impl = unsafeBitCast(pfn, to: pfnGetWindowIdFromWindow.self)
60+
}
61+
62+
if let pfn = GetProcAddress(hModule, "Windowing_GetWindowFromWindowId") {
63+
getWindowFromWindowID_impl = unsafeBitCast(pfn, to: pfnGetWindowFromWindowId.self)
64+
}
65+
}
66+
67+
fileprivate func getWindowIDFromWindow(_ hWnd: HWND?) -> WinAppSDK.WindowId {
68+
var windowID = __x_ABI_CMicrosoft_CUI_CWindowId()
69+
let hr: HRESULT = getWindowIDFromWindow_impl(hWnd, &windowID)
70+
if hr != S_OK {
71+
fatalError("Unable to get window ID")
72+
}
73+
return .init(value: windowID.Value)
74+
}
75+
76+
fileprivate func getWindowFromWindowId(_ windowID: WinAppSDK.WindowId) -> HWND? {
77+
var hWnd: HWND?
78+
let hr: HRESULT = getWindowFromWindowID_impl(.from(swift: windowID), &hWnd)
79+
if hr != S_OK {
80+
fatalError("Unable to get window from window ID")
81+
}
82+
return hWnd
83+
}
84+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import CWinRT
2+
import WinUIInterop
3+
4+
private var IID_IInitializeWithWindow: WindowsFoundation.IID {
5+
// 3E68D4BD-7135-4D10-8018-9FB6D9F33FA1
6+
.init(
7+
Data1: 0x3E68_D4BD,
8+
Data2: 0x7135,
9+
Data3: 0x4D10,
10+
Data4: (0x80, 0x18, 0x9F, 0xB6, 0xD9, 0xF3, 0x3F, 0xA1)
11+
)
12+
}
13+
14+
public protocol IInitializeWithWindow: WinRTInterface {
15+
func initialize(_ hwnd: HWND) throws
16+
}
17+
18+
typealias AnyIInitializeWithWindow = any IInitialieWithWindow
19+
20+
enum Interop {
21+
typealias IInitializeWithWindowWrapper = InterfaceWrapperBase<IInitializeWithWindowBridge>
22+
23+
public enum IInitializeWithWindowBridge: AbiInterfaceBridge {
24+
public typealias CABI = ABI_IInitializeWithWindow
25+
public typealias SwiftABI = IInitializeWithWindow
26+
public typealias SwiftProjection = AnyIInitializeWithWindow
27+
public static func from(abi: ComPtr<CABI>?) -> SwiftProjection? {
28+
guard let abi = abi else { return nil }
29+
return IInitializeWithWindowImpl(abi)
30+
}
31+
32+
public static func makeAbi() -> CABI {
33+
let vtblPtr = withUnsafeMutablePointer(
34+
to: &IInitializeWithWindowVTable
35+
) { $0 }
36+
return .init(lpVtbl: vtblPtr)
37+
}
38+
}
39+
40+
static var IInitializeWithWindowVTable: ABI_IInitializeWithWindowVtbl = .init(
41+
QueryInterface: {
42+
IInitializeWithWindowWrapper.queryInterface($0, $1, $2)
43+
},
44+
AddRef: { IInitializeWithWindowWrapper.addRef($0) },
45+
Release: { IInitializeWithWindowWrapper.release($0) },
46+
Initialize: {
47+
guard
48+
let __unwrapped__instance =
49+
IInitializeWithWindowWrapper.tryUnwrapFrom(raw: $0)
50+
else { return E_INVALIDARG }
51+
__unwrapped__instance.initialize($1)
52+
return S_OK
53+
}
54+
)
55+
56+
fileprivate class IItemContainerMappingImpl: IInitializeWithWindow, WinRTAbiImpl {
57+
fileprivate typealias Bridge = IInitializeWithWWindow
58+
fileprivate let _default: Bridge.SwiftABI
59+
fileprivate var thisPtr: WindowsFoundation.IInspectable { _default }
60+
fileprivate init(_ fromAbi: ComPtr<Bridge.CABI>) {
61+
_default = Bridge.SwiftABI(fromAbi)
62+
}
63+
64+
fileprivate func initialize(_ hwnd: HWND) throws {
65+
try _default.InitializeImpl(hwnd)
66+
}
67+
}
68+
69+
public class IInitializeWithWindow: WindowsFoundation.IInspectable {
70+
override public class var IID: WindowsFoundation.IID {
71+
IID_InitializeWithWindow
72+
}
73+
74+
open func InitializeImpl(_ hwnd: HWND) throws -> WinUI.DependencyObject? {
75+
_ = try perform(
76+
as: ABI_IInitializeWithWindow.self
77+
) { pThis in
78+
try CHECKED(
79+
pThis.pointee.lpVtbl.pointee.Initialize(pThis, hwnd)
80+
)
81+
}
82+
}
83+
}
84+
}

Sources/WinUIBackend/WinUIBackend.swift

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -954,49 +954,49 @@ public final class WinUIBackend: AppBackend {
954954
}
955955
}
956956

957-
// public func showOpenDialog(
958-
// fileDialogOptions: FileDialogOptions,
959-
// openDialogOptions: OpenDialogOptions,
960-
// window: Window?,
961-
// resultHandler handleResult: @escaping (DialogResult<[URL]>) -> Void
962-
// ) {
963-
// let picker = FileOpenPicker()
964-
// // TODO: Associate the picker with a window. Requires some janky WinUI
965-
// // Win32 interop kinda stuff I believe.
966-
// if openDialogOptions.allowMultipleSelections {
967-
// let promise = try! picker.pickMultipleFilesAsync()!
968-
// promise.completed = { operation, status in
969-
// guard
970-
// status == .completed,
971-
// let operation,
972-
// let result = try? operation.getResults()
973-
// else {
974-
// return
975-
// }
976-
// print(result)
977-
// }
978-
// } else {
979-
// let promise = try! picker.pickSingleFileAsync()!
980-
// promise.completed = { operation, status in
981-
// guard
982-
// status == .completed,
983-
// let operation,
984-
// let result = try? operation.getResults()
985-
// else {
986-
// return
987-
// }
988-
// print(result)
989-
// }
990-
// }
991-
// }
957+
public func showOpenDialog(
958+
fileDialogOptions: FileDialogOptions,
959+
openDialogOptions: OpenDialogOptions,
960+
window: Window?,
961+
resultHandler handleResult: @escaping (DialogResult<[URL]>) -> Void
962+
) {
963+
let picker = FileOpenPicker()
964+
// TODO: Associate the picker with a window. Requires some janky WinUI
965+
// Win32 interop kinda stuff I believe.
966+
if openDialogOptions.allowMultipleSelections {
967+
let promise = try! picker.pickMultipleFilesAsync()!
968+
promise.completed = { operation, status in
969+
guard
970+
status == .completed,
971+
let operation,
972+
let result = try? operation.getResults()
973+
else {
974+
return
975+
}
976+
print(result)
977+
}
978+
} else {
979+
let promise = try! picker.pickSingleFileAsync()!
980+
promise.completed = { operation, status in
981+
guard
982+
status == .completed,
983+
let operation,
984+
let result = try? operation.getResults()
985+
else {
986+
return
987+
}
988+
print(result)
989+
}
990+
}
991+
}
992992

993-
// public func showSaveDialog(
994-
// fileDialogOptions: FileDialogOptions,
995-
// saveDialogOptions: SaveDialogOptions,
996-
// window: Window?,
997-
// resultHandler handleResult: @escaping (DialogResult<URL>) -> Void
998-
// ) {
999-
// }
993+
public func showSaveDialog(
994+
fileDialogOptions: FileDialogOptions,
995+
saveDialogOptions: SaveDialogOptions,
996+
window: Window?,
997+
resultHandler handleResult: @escaping (DialogResult<URL>) -> Void
998+
) {
999+
}
10001000

10011001
public func createClickTarget(wrapping child: Widget) -> Widget {
10021002
let clickTarget = ClickTarget()
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#include <inspectable.h>
2+
#include <EventToken.h>
3+
#include <windowscontracts.h>
4+
#include "Windows.Foundation.h"
5+
6+
#if !defined(__ABI_IInitializeWithWindow_INTERFACE_DEFINED__)
7+
#define __ABI_IInitializeWithWindow_INTERFACE_DEFINED__
8+
9+
typedef struct ABI_IInitializeWithWindowVtbl
10+
{
11+
BEGIN_INTERFACE
12+
13+
HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
14+
IInitializeWithWindow * This,
15+
/* [in] */ REFIID riid,
16+
/* [annotation][iid_is][out] */
17+
void **ppvObject);
18+
19+
ULONG ( STDMETHODCALLTYPE *AddRef )(
20+
IInitializeWithWindow * This);
21+
22+
ULONG ( STDMETHODCALLTYPE *Release )(
23+
IInitializeWithWindow * This);
24+
25+
HRESULT ( STDMETHODCALLTYPE *Initialize )(
26+
IInitializeWithWindow * This,
27+
/* [in] */ HWND hwnd);
28+
29+
END_INTERFACE
30+
} IInitializeWithWindowVtbl;
31+
32+
interface ABI_IInitializeWithWindow
33+
{
34+
CONST_VTBL struct IInitializeWithWindowVtbl *lpVtbl;
35+
};
36+
37+
EXTERN_C const IID IID_IInitializeWithWindow;
38+
#endif /* !defined(__ABI_IInitializeWithWindow_INTERFACE_DEFINED__) */
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#include "InitializeWithWindow.h"

0 commit comments

Comments
 (0)