Skip to content

Commit cd2d21f

Browse files
committed
feat: update WindowProc delegate plugin with new callback management and UI controls
1 parent ceb1946 commit cd2d21f

File tree

11 files changed

+149
-74
lines changed

11 files changed

+149
-74
lines changed

.gitignore

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Miscellaneous
2+
*.class
3+
*.log
4+
*.pyc
5+
*.swp
6+
.DS_Store
7+
.atom/
8+
.build/
9+
.buildlog/
10+
.history
11+
.svn/
12+
.swiftpm/
13+
migrate_working_dir/
14+
15+
# IntelliJ related
16+
*.iml
17+
*.ipr
18+
*.iws
19+
.idea/
20+
21+

packages/window_proc_delegate/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.0.2
2+
* Removed engine-specific message restriction - messages are now delivered to all registered delegates
3+
* Fixed crash issue when unregistering delegates
4+
15
## 0.0.1
26

37
* Initial release with WindowProc delegate support for Windows

packages/window_proc_delegate/example/lib/main.dart

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class _MyAppState extends State<MyApp> {
2020
@override
2121
void initState() {
2222
super.initState();
23-
_registerDelegate();
23+
// Removed auto-registration - use buttons to register/unregister
2424
}
2525

2626
@override
@@ -33,7 +33,12 @@ class _MyAppState extends State<MyApp> {
3333

3434
// Register a WindowProc delegate to intercept messages
3535
void _registerDelegate() {
36-
_delegateId = registerWindowProcDelegate((hwnd, message, wParam, lParam) {
36+
if (_delegateId != null) {
37+
// Already registered
38+
return;
39+
}
40+
41+
final id = registerWindowProcDelegate((hwnd, message, wParam, lParam) {
3742
// Example: Log WM_ACTIVATEAPP (0x001C) messages
3843
if (message == 0x001C) {
3944
setState(() {
@@ -55,6 +60,28 @@ class _MyAppState extends State<MyApp> {
5560
// Return null to let other handlers process the message
5661
return null;
5762
});
63+
64+
setState(() {
65+
_delegateId = id;
66+
_messages.insert(0, 'Delegate registered with ID: $id');
67+
if (_messages.length > 10) {
68+
_messages.removeLast();
69+
}
70+
});
71+
}
72+
73+
// Unregister the WindowProc delegate
74+
void _unregisterDelegate() {
75+
if (_delegateId != null) {
76+
unregisterWindowProcDelegate(_delegateId!);
77+
setState(() {
78+
_messages.insert(0, 'Delegate unregistered (ID: $_delegateId)');
79+
_delegateId = null;
80+
if (_messages.length > 10) {
81+
_messages.removeLast();
82+
}
83+
});
84+
}
5885
}
5986

6087
@override
@@ -67,6 +94,29 @@ class _MyAppState extends State<MyApp> {
6794
child: Column(
6895
crossAxisAlignment: CrossAxisAlignment.start,
6996
children: [
97+
// Control buttons
98+
Row(
99+
children: [
100+
ElevatedButton(
101+
onPressed: _delegateId == null ? _registerDelegate : null,
102+
child: const Text('Register Delegate'),
103+
),
104+
const SizedBox(width: 8),
105+
ElevatedButton(
106+
onPressed: _delegateId != null ? _unregisterDelegate : null,
107+
child: const Text('Unregister Delegate'),
108+
),
109+
],
110+
),
111+
const SizedBox(height: 8),
112+
Text(
113+
'Status: ${_delegateId != null ? "Registered (ID: $_delegateId)" : "Not Registered"}',
114+
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
115+
color: _delegateId != null ? Colors.green : Colors.red,
116+
fontWeight: FontWeight.bold,
117+
),
118+
),
119+
const SizedBox(height: 16),
70120
Text(
71121
'WindowProc Messages:',
72122
style: Theme.of(context).textTheme.titleMedium,
@@ -76,7 +126,7 @@ class _MyAppState extends State<MyApp> {
76126
child: _messages.isEmpty
77127
? const Center(
78128
child: Text(
79-
'No messages intercepted yet.\nTry switching to another window and back.',
129+
'No messages intercepted yet.\nRegister the delegate and try switching to another window and back.',
80130
textAlign: TextAlign.center,
81131
),
82132
)

packages/window_proc_delegate/example/pubspec.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,5 +271,5 @@ packages:
271271
source: path
272272
version: "0.0.1"
273273
sdks:
274-
dart: ">=3.11.0-264.0.dev <4.0.0"
274+
dart: ">=3.10.0 <4.0.0"
275275
flutter: ">=3.18.0-18.0.pre.54"

packages/window_proc_delegate/lib/src/window_proc_delegate_internal.dart

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,11 @@ external void setCallback(
3232
callback,
3333
);
3434

35-
final MethodChannel channel = const MethodChannel('window_proc_delegate');
36-
37-
ffi.NativeCallable<NativeWindowProcCallback>? nativeCallable;
38-
bool initialized = false;
39-
bool dartApiInitialized = false;
35+
bool _initialized = false;
36+
bool _dartApiInitialized = false;
4037

4138
void ensureNativeLibraryInitialized() {
42-
if (dartApiInitialized) return;
39+
if (_dartApiInitialized) return;
4340

4441
if (!Platform.isWindows) return;
4542

@@ -48,7 +45,7 @@ void ensureNativeLibraryInitialized() {
4845
if (initResult != 0) {
4946
debugPrint('Failed to initialize Dart API DL: $initResult');
5047
} else {
51-
dartApiInitialized = true;
48+
_dartApiInitialized = true;
5249
}
5350
} catch (e) {
5451
debugPrint('Failed to initialize Dart API: $e');
@@ -59,40 +56,24 @@ void initialize(
5956
List<WindowProcDelegateCallback?> delegates,
6057
void Function(ffi.Pointer<WindowsMessage>) handleWindowProc,
6158
) {
62-
if (initialized) return;
59+
if (_initialized) return;
6360

6461
if (!Platform.isWindows) return;
6562

6663
ensureNativeLibraryInitialized();
6764

6865
// Create native callable that dispatches to all delegates
69-
nativeCallable = ffi.NativeCallable<NativeWindowProcCallback>.isolateLocal(
70-
handleWindowProc,
71-
);
66+
final nativeCallable =
67+
ffi.NativeCallable<NativeWindowProcCallback>.isolateLocal(
68+
handleWindowProc,
69+
);
7270

7371
// Get the engine ID and register the native callback
7472
try {
7573
final engineId = PlatformDispatcher.instance.engineId!;
7674
setCallback(engineId, nativeCallable!.nativeFunction);
77-
// Also notify the plugin instance via method channel
78-
channel.invokeMethod('setEngineId', {'engineId': engineId});
7975
} catch (e) {
8076
debugPrint('Failed to set callback: $e');
8177
}
82-
initialized = true;
83-
}
84-
85-
void cleanup() {
86-
if (nativeCallable != null) {
87-
try {
88-
final engineId = PlatformDispatcher.instance.implicitView?.viewId ?? 0;
89-
setCallback(engineId, ffi.nullptr);
90-
channel.invokeMethod('setEngineId', {'engineId': 0});
91-
} catch (e) {
92-
debugPrint('Failed to clear callback: $e');
93-
}
94-
nativeCallable?.close();
95-
nativeCallable = null;
96-
initialized = false;
97-
}
78+
_initialized = true;
9879
}

packages/window_proc_delegate/lib/window_proc_delegate.dart

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@ final List<WindowProcDelegateCallback?> _delegates = [];
2222
/// The delegate will be called for each WindowProc message.
2323
/// Returns an ID that can be used to unregister the delegate.
2424
int registerWindowProcDelegate(WindowProcDelegateCallback delegate) {
25-
if (!internal.initialized) {
26-
internal.initialize(_delegates, _handleWindowProc);
27-
}
25+
internal.initialize(_delegates, _handleWindowProc);
2826

2927
_delegates.add(delegate);
3028
return _delegates.length - 1;
@@ -38,7 +36,7 @@ void unregisterWindowProcDelegate(int id) {
3836

3937
// If all delegates are removed, clear the native callback
4038
if (_delegates.every((d) => d == null)) {
41-
_cleanup();
39+
_delegates.clear();
4240
}
4341
}
4442

@@ -63,8 +61,3 @@ void _handleWindowProc(ffi.Pointer<WindowsMessage> message) {
6361
}
6462
}
6563
}
66-
67-
void _cleanup() {
68-
internal.cleanup();
69-
_delegates.clear();
70-
}

packages/window_proc_delegate/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: window_proc_delegate
22
description: A Flutter plugin that allows you to hook into Windows WindowProc messages from Dart code.
3-
version: 0.0.1
3+
version: 0.0.2
44
homepage: https://github.com/boyan01/packages
55

66
environment:

packages/window_proc_delegate/windows/include/window_proc_delegate/window_proc_delegate_plugin_c_api.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ typedef std::function<void(WindowsMessage*)> DartWindowProcCallback;
4848
window_proc_delegate::DartWindowProcCallback GetCallbackForEngine(
4949
int64_t engineId);
5050

51+
// Get all registered callbacks
52+
std::vector<window_proc_delegate::DartWindowProcCallback> GetAllCallbacks();
53+
5154
#endif
5255

5356
#endif // FLUTTER_PLUGIN_WINDOW_PROC_DELEGATE_PLUGIN_C_API_H_

packages/window_proc_delegate/windows/window_proc_delegate_plugin.cpp

Lines changed: 17 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -34,25 +34,27 @@ void WindowProcDelegatePlugin::RegisterWithRegistrar(
3434

3535
WindowProcDelegatePlugin::WindowProcDelegatePlugin(
3636
flutter::PluginRegistrarWindows* registrar)
37-
: registrar_(registrar), engine_id_(0) {
37+
: registrar_(registrar) {
3838
window_proc_delegate_id_ = registrar->RegisterTopLevelWindowProcDelegate(
3939
[this](HWND hwnd, UINT message, WPARAM wparam,
4040
LPARAM lparam) -> std::optional<LRESULT> {
41-
// Get the callback for this engine's ID
42-
auto callback = GetCallbackForEngine(engine_id_);
43-
if (callback) {
44-
WindowsMessage msg = {};
45-
msg.windowHandle = reinterpret_cast<intptr_t>(hwnd);
46-
msg.message = static_cast<int32_t>(message);
47-
msg.wParam = static_cast<int64_t>(wparam);
48-
msg.lParam = static_cast<int64_t>(lparam);
49-
msg.lResult = 0;
50-
msg.handled = false;
41+
// Get all registered callbacks and invoke them
42+
auto callbacks = GetAllCallbacks();
43+
for (const auto& callback : callbacks) {
44+
if (callback) {
45+
WindowsMessage msg = {};
46+
msg.windowHandle = reinterpret_cast<intptr_t>(hwnd);
47+
msg.message = static_cast<int32_t>(message);
48+
msg.wParam = static_cast<int64_t>(wparam);
49+
msg.lParam = static_cast<int64_t>(lparam);
50+
msg.lResult = 0;
51+
msg.handled = false;
5152

52-
callback(&msg);
53+
callback(&msg);
5354

54-
if (msg.handled) {
55-
return static_cast<LRESULT>(msg.lResult);
55+
if (msg.handled) {
56+
return static_cast<LRESULT>(msg.lResult);
57+
}
5658
}
5759
}
5860
return std::nullopt;
@@ -66,22 +68,7 @@ WindowProcDelegatePlugin::~WindowProcDelegatePlugin() {
6668
void WindowProcDelegatePlugin::HandleMethodCall(
6769
const flutter::MethodCall<flutter::EncodableValue>& method_call,
6870
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
69-
if (method_call.method_name().compare("setEngineId") == 0) {
70-
const auto* arguments =
71-
std::get_if<flutter::EncodableMap>(method_call.arguments());
72-
if (arguments) {
73-
auto engine_id_it = arguments->find(flutter::EncodableValue("engineId"));
74-
if (engine_id_it != arguments->end()) {
75-
engine_id_ = std::get<int64_t>(engine_id_it->second);
76-
result->Success();
77-
return;
78-
}
79-
}
80-
result->Error("INVALID_ARGUMENTS",
81-
"Missing or invalid 'engineId' argument");
82-
} else {
83-
result->NotImplemented();
84-
}
71+
result->NotImplemented();
8572
}
8673

8774
} // namespace window_proc_delegate

packages/window_proc_delegate/windows/window_proc_delegate_plugin.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ class WindowProcDelegatePlugin : public flutter::Plugin {
3939
private:
4040
flutter::PluginRegistrarWindows* registrar_;
4141
int window_proc_delegate_id_;
42-
int64_t engine_id_;
4342
};
4443

4544
} // namespace window_proc_delegate

0 commit comments

Comments
 (0)