1 /**
2  * The master window procedure and the HWND → Widget registry.
3  *
4  * Every Deft window class is registered with the single `wndProc` below. It
5  * finds the `Widget` that owns the target `HWND` and forwards the message to
6  * `Widget.processMessage`, where per-widget handling lives. Lookup is by
7  * `GWLP_USERDATA` (set when the widget's handle is created), with the registry
8  * associative array as a fallback.
9  */
10 module deft.platform.win32.wndproc;
11 
12 version (Windows):
13 
14 import core.sys.windows.windows;
15 
16 import deft.widget : Widget;
17 
18 /// HWND → Widget map. Accessed only from the single UI thread.
19 private __gshared Widget[HWND] g_widgets;
20 
21 /// Register a widget under its HWND.
22 void registerWidget(HWND h, Widget w)
23 {
24 	g_widgets[h] = w;
25 }
26 
27 /// Remove a widget's HWND from the registry.
28 void unregisterWidget(HWND h)
29 {
30 	g_widgets.remove(h);
31 }
32 
33 /// Look up the widget that owns an HWND, or null.
34 Widget lookupWidget(HWND h)
35 {
36 	if (auto p = h in g_widgets)
37 		return *p;
38 	return null;
39 }
40 
41 /**
42  * The single window procedure shared by all Deft window classes.
43  *
44  * `extern(Windows)` callbacks must not let a D exception escape into the OS, so
45  * the dispatch is wrapped: any `Throwable` is swallowed and the message falls
46  * through to `DefWindowProcW`.
47  */
48 extern (Windows) LRESULT wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) nothrow
49 {
50 	try
51 	{
52 		Widget widget;
53 
54 		auto userData = GetWindowLongPtrW(hwnd, GWLP_USERDATA);
55 		if (userData != 0)
56 			widget = cast(Widget) cast(void*) userData;
57 		else
58 			widget = lookupWidget(hwnd);
59 
60 		if (widget !is null)
61 			return widget.processMessage(msg, wParam, lParam);
62 	}
63 	catch (Throwable)
64 	{
65 		// Swallow: never propagate a D throwable through the Win32 dispatcher.
66 	}
67 
68 	return DefWindowProcW(hwnd, msg, wParam, lParam);
69 }