33#include < mutex>
44#include < string>
55#include < unordered_map>
6+ #include < set>
67
78#include " ../../window.h"
89#include " ../../window_manager.h"
@@ -17,6 +18,13 @@ namespace nativeapi {
1718static std::unordered_map<GdkWindow*, WindowId> g_window_id_map;
1819static std::mutex g_map_mutex;
1920
21+ // Track widgets that have been hooked to avoid duplicate connections
22+ static std::set<GtkWidget*> g_hooked_widgets;
23+ static std::mutex g_hook_mutex;
24+
25+ // Flag to indicate if global swizzling has been installed
26+ static bool g_swizzle_installed = false ;
27+
2028// Helper function to manage mapping between GdkWindow pointers and WindowIds
2129static WindowId GetOrCreateWindowId (GdkWindow* gdk_window) {
2230 if (!gdk_window) {
@@ -48,15 +56,64 @@ static GdkWindow* FindGdkWindowById(WindowId id) {
4856 return nullptr ;
4957}
5058
51- // GTK signal callbacks to invoke hooks
59+ // Forward declarations for swizzling functions
60+ static void InstallShowHideHooks (GtkWidget* widget);
61+ static void InstallGlobalSwizzling ();
62+
63+ // Signal emission hook for show signal
64+ static gboolean on_show_emission_hook (GSignalInvocationHint* ihint,
65+ guint n_param_values,
66+ const GValue* param_values,
67+ gpointer data) {
68+ (void )ihint;
69+ (void )n_param_values;
70+ (void )data;
71+
72+ GtkWidget* widget = GTK_WIDGET (g_value_get_object (¶m_values[0 ]));
73+ if (widget && GTK_IS_WINDOW (widget)) {
74+ GdkWindow* gdk_window = gtk_widget_get_window (widget);
75+ if (gdk_window) {
76+ WindowId id = GetOrCreateWindowId (gdk_window);
77+ WindowManager::GetInstance ().InvokeWillShowHook (id);
78+ }
79+ }
80+
81+ return TRUE ; // Continue emission
82+ }
83+
84+ // Signal emission hook for hide signal
85+ static gboolean on_hide_emission_hook (GSignalInvocationHint* ihint,
86+ guint n_param_values,
87+ const GValue* param_values,
88+ gpointer data) {
89+ (void )ihint;
90+ (void )n_param_values;
91+ (void )data;
92+
93+ GtkWidget* widget = GTK_WIDGET (g_value_get_object (¶m_values[0 ]));
94+ if (widget && GTK_IS_WINDOW (widget)) {
95+ GdkWindow* gdk_window = gtk_widget_get_window (widget);
96+ if (gdk_window) {
97+ WindowId id = GetOrCreateWindowId (gdk_window);
98+ WindowManager::GetInstance ().InvokeWillHideHook (id);
99+ }
100+ }
101+
102+ return TRUE ; // Continue emission
103+ }
104+
105+ // GTK signal callbacks to invoke hooks (used as fallback)
52106static gboolean OnGtkMapEvent (GtkWidget* widget, GdkEvent* event, gpointer user_data) {
53107 (void )event;
54108 (void )user_data;
55- auto & manager = WindowManager::GetInstance ();
56- GdkWindow* gdk_window = gtk_widget_get_window (widget);
57- if (gdk_window) {
58- WindowId id = GetOrCreateWindowId (gdk_window);
59- manager.InvokeWillShowHook (id);
109+
110+ if (GTK_IS_WINDOW (widget)) {
111+ auto & manager = WindowManager::GetInstance ();
112+ GdkWindow* gdk_window = gtk_widget_get_window (widget);
113+ if (gdk_window) {
114+ WindowId id = GetOrCreateWindowId (gdk_window);
115+ manager.InvokeWillShowHook (id);
116+ }
60117 }
61118 // Return FALSE to propagate event further
62119 return FALSE ;
@@ -65,28 +122,88 @@ static gboolean OnGtkMapEvent(GtkWidget* widget, GdkEvent* event, gpointer user_
65122static gboolean OnGtkUnmapEvent (GtkWidget* widget, GdkEvent* event, gpointer user_data) {
66123 (void )event;
67124 (void )user_data;
68- auto & manager = WindowManager::GetInstance ();
69- GdkWindow* gdk_window = gtk_widget_get_window (widget);
70- if (gdk_window) {
71- WindowId id = GetOrCreateWindowId (gdk_window);
72- manager.InvokeWillHideHook (id);
125+
126+ if (GTK_IS_WINDOW (widget)) {
127+ auto & manager = WindowManager::GetInstance ();
128+ GdkWindow* gdk_window = gtk_widget_get_window (widget);
129+ if (gdk_window) {
130+ WindowId id = GetOrCreateWindowId (gdk_window);
131+ manager.InvokeWillHideHook (id);
132+ }
73133 }
74134 // Return FALSE to propagate event further
75135 return FALSE ;
76136}
77137
78- // Private implementation for Linux (stub for now)
138+ // Install hooks for a specific widget
139+ static void InstallShowHideHooks (GtkWidget* widget) {
140+ if (!widget || !GTK_IS_WINDOW (widget)) {
141+ return ;
142+ }
143+
144+ std::lock_guard<std::mutex> lock (g_hook_mutex);
145+
146+ // Check if already hooked
147+ if (g_hooked_widgets.find (widget) != g_hooked_widgets.end ()) {
148+ return ;
149+ }
150+
151+ // Connect map/unmap events as fallback
152+ g_signal_connect (G_OBJECT (widget), " map-event" , G_CALLBACK (OnGtkMapEvent), nullptr );
153+ g_signal_connect (G_OBJECT (widget), " unmap-event" , G_CALLBACK (OnGtkUnmapEvent), nullptr );
154+
155+ g_hooked_widgets.insert (widget);
156+ }
157+
158+ // Install global swizzling using signal emission hooks
159+ static void InstallGlobalSwizzling () {
160+ if (g_swizzle_installed) {
161+ return ;
162+ }
163+
164+ // Get the show and hide signal IDs for GtkWidget
165+ guint show_signal_id = g_signal_lookup (" show" , GTK_TYPE_WIDGET);
166+ guint hide_signal_id = g_signal_lookup (" hide" , GTK_TYPE_WIDGET);
167+
168+ if (show_signal_id != 0 ) {
169+ // Add emission hook for show signal
170+ g_signal_add_emission_hook (show_signal_id, 0 , on_show_emission_hook, nullptr , nullptr );
171+ }
172+
173+ if (hide_signal_id != 0 ) {
174+ // Add emission hook for hide signal
175+ g_signal_add_emission_hook (hide_signal_id, 0 , on_hide_emission_hook, nullptr , nullptr );
176+ }
177+
178+ g_swizzle_installed = true ;
179+ }
180+
181+ // Private implementation for Linux
79182class WindowManager ::Impl {
80183 public:
81184 Impl (WindowManager* manager) : manager_(manager) {}
82185 ~Impl () {}
83186
84187 void SetupEventMonitoring () {
85- // TODO: Implement Linux-specific event monitoring using GTK signals
188+ // Install global swizzling for show/hide interception
189+ InstallGlobalSwizzling ();
190+
191+ // Monitor all existing windows
192+ GdkDisplay* display = gdk_display_get_default ();
193+ if (display) {
194+ GList* toplevels = gtk_window_list_toplevels ();
195+ for (GList* l = toplevels; l != nullptr ; l = l->next ) {
196+ GtkWindow* gtk_window = GTK_WINDOW (l->data );
197+ InstallShowHideHooks (GTK_WIDGET (gtk_window));
198+ }
199+ g_list_free (toplevels);
200+ }
86201 }
87202
88203 void CleanupEventMonitoring () {
89- // TODO: Implement Linux-specific cleanup
204+ // Clear hooked widgets set
205+ std::lock_guard<std::mutex> lock (g_hook_mutex);
206+ g_hooked_widgets.clear ();
90207 }
91208
92209 private:
@@ -250,9 +367,8 @@ std::shared_ptr<Window> WindowManager::Create(const WindowOptions& options) {
250367 return nullptr ;
251368 }
252369
253- // Connect map/unmap events to invoke hooks
254- g_signal_connect (G_OBJECT (gtk_window), " map-event" , G_CALLBACK (OnGtkMapEvent), nullptr );
255- g_signal_connect (G_OBJECT (gtk_window), " unmap-event" , G_CALLBACK (OnGtkUnmapEvent), nullptr );
370+ // Install show/hide hooks for this window
371+ InstallShowHideHooks (gtk_window);
256372
257373 // Set window properties from options
258374 if (!options.title .empty ()) {
@@ -326,10 +442,18 @@ bool WindowManager::Destroy(WindowId id) {
326442
327443void WindowManager::SetWillShowHook (std::optional<WindowWillShowHook> hook) {
328444 pimpl_->will_show_hook_ = std::move (hook);
445+ if (pimpl_->will_show_hook_ ) {
446+ // Ensure global swizzling is installed when hook is set
447+ InstallGlobalSwizzling ();
448+ }
329449}
330450
331451void WindowManager::SetWillHideHook (std::optional<WindowWillHideHook> hook) {
332452 pimpl_->will_hide_hook_ = std::move (hook);
453+ if (pimpl_->will_hide_hook_ ) {
454+ // Ensure global swizzling is installed when hook is set
455+ InstallGlobalSwizzling ();
456+ }
333457}
334458
335459void WindowManager::InvokeWillShowHook (WindowId id) {
0 commit comments