Skip to content

Commit 09ca5f5

Browse files
committed
Improve Linux window ID management and event hooks
WindowId is now attached directly to GtkWidget and GdkWindow objects using g_object_set_data, ensuring stable and consistent ID retrieval. WindowManager event hooks are renamed and extended with HasWillShowHook, HasWillHideHook, CallOriginalShow, and CallOriginalHide methods. Window visibility and focus methods now prefer GtkWidget operations, improving integration with GTK. These changes enhance reliability and maintainability of window management on Linux.
1 parent 326a789 commit 09ca5f5

File tree

2 files changed

+170
-63
lines changed

2 files changed

+170
-63
lines changed

src/platform/linux/window_linux.cpp

Lines changed: 110 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@
1212

1313
namespace nativeapi {
1414

15+
// Key to store/retrieve WindowId on GObjects
16+
static const char* kWindowIdKey = "NativeAPIWindowId";
17+
1518
// Private implementation class
1619
class Window::Impl {
1720
public:
18-
Impl(GdkWindow* window) : gdk_window_(window) {}
21+
Impl(GtkWidget* widget, GdkWindow* gdk_window) : widget_(widget), gdk_window_(gdk_window) {}
22+
GtkWidget* widget_;
1923
GdkWindow* gdk_window_;
2024
};
2125

@@ -24,75 +28,87 @@ Window::Window() {
2428
GdkDisplay* display = gdk_display_get_default();
2529
if (!display) {
2630
std::cerr << "No display available for window creation" << std::endl;
27-
pimpl_ = std::make_unique<Impl>(nullptr);
31+
pimpl_ = std::make_unique<Impl>(nullptr, nullptr);
2832
return;
2933
}
3034

31-
// Create a new GTK window
32-
GtkWidget* gtk_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
33-
if (!gtk_window) {
35+
// Create a new GTK toplevel window
36+
GtkWidget* widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
37+
if (!widget) {
3438
std::cerr << "Failed to create GTK window" << std::endl;
35-
pimpl_ = std::make_unique<Impl>(nullptr);
39+
pimpl_ = std::make_unique<Impl>(nullptr, nullptr);
3640
return;
3741
}
3842

3943
// Realize to ensure GdkWindow exists
40-
if (!gtk_widget_get_realized(gtk_window)) {
41-
gtk_widget_realize(gtk_window);
44+
if (!gtk_widget_get_realized(widget)) {
45+
gtk_widget_realize(widget);
4246
}
4347

4448
// Obtain GdkWindow
45-
GdkWindow* gdk_window = gtk_widget_get_window(gtk_window);
49+
GdkWindow* gdk_window = gtk_widget_get_window(widget);
4650
if (!gdk_window) {
4751
std::cerr << "Failed to get GdkWindow from GTK widget" << std::endl;
48-
gtk_widget_destroy(gtk_window);
49-
pimpl_ = std::make_unique<Impl>(nullptr);
52+
gtk_widget_destroy(widget);
53+
pimpl_ = std::make_unique<Impl>(nullptr, nullptr);
5054
return;
5155
}
5256

57+
// Allocate and attach a stable WindowId to the native objects
58+
WindowId id = IdAllocator::Allocate<Window>();
59+
if (id != IdAllocator::kInvalidId) {
60+
g_object_set_data(G_OBJECT(widget), kWindowIdKey, reinterpret_cast<gpointer>(static_cast<uintptr_t>(id)));
61+
g_object_set_data(G_OBJECT(gdk_window), kWindowIdKey, reinterpret_cast<gpointer>(static_cast<uintptr_t>(id)));
62+
}
63+
5364
// Only create the instance, don't show the window
54-
pimpl_ = std::make_unique<Impl>(gdk_window);
65+
pimpl_ = std::make_unique<Impl>(widget, gdk_window);
5566
}
5667

57-
Window::Window(void* native_window) : pimpl_(std::make_unique<Impl>((GdkWindow*)native_window)) {}
58-
59-
Window::~Window() {}
68+
Window::Window(void* native_window) {
69+
// Wrap existing GdkWindow or GtkWidget
70+
GtkWidget* widget = nullptr;
71+
GdkWindow* gdk_window = nullptr;
6072

61-
WindowId Window::GetId() const {
62-
// Use IdAllocator to generate unique IDs instead of casting pointers
63-
if (!pimpl_->gdk_window_) {
64-
return IdAllocator::kInvalidId;
73+
// Heuristic: if this looks like a GtkWidget*, use it; otherwise treat as GdkWindow*
74+
// In our codebase, native Linux window handles should be GtkWidget* (GtkWindow)
75+
widget = static_cast<GtkWidget*>(native_window);
76+
if (widget && GTK_IS_WIDGET(widget)) {
77+
if (!gtk_widget_get_realized(widget)) {
78+
gtk_widget_realize(widget);
79+
}
80+
gdk_window = gtk_widget_get_window(widget);
81+
} else {
82+
// Fallback: assume GdkWindow*
83+
gdk_window = static_cast<GdkWindow*>(native_window);
6584
}
6685

67-
// Store the allocated ID in a static map to ensure consistency
68-
static std::unordered_map<GdkWindow*, WindowId> window_id_map;
69-
static std::mutex map_mutex;
70-
71-
std::lock_guard<std::mutex> lock(map_mutex);
72-
auto it = window_id_map.find(pimpl_->gdk_window_);
73-
if (it != window_id_map.end()) {
74-
return it->second;
75-
}
86+
pimpl_ = std::make_unique<Impl>(widget, gdk_window);
87+
}
7688

77-
// Allocate new ID using the IdAllocator
78-
WindowId new_id = IdAllocator::Allocate<Window>();
79-
if (new_id != IdAllocator::kInvalidId) {
80-
window_id_map[pimpl_->gdk_window_] = new_id;
89+
Window::~Window() {}
8190

82-
// Register window in registry (delayed registration)
83-
// This requires the Window to be managed by shared_ptr
84-
try {
85-
WindowRegistry::GetInstance().Add(new_id, const_cast<Window*>(this)->shared_from_this());
86-
} catch (const std::bad_weak_ptr&) {
87-
// Window not yet managed by shared_ptr, skip registration
88-
// Registration will happen when window is properly managed by shared_ptr
91+
WindowId Window::GetId() const {
92+
// Prefer reading ID stored on the native objects
93+
if (pimpl_->gdk_window_) {
94+
gpointer data = g_object_get_data(G_OBJECT(pimpl_->gdk_window_), kWindowIdKey);
95+
if (data) {
96+
return static_cast<WindowId>(reinterpret_cast<uintptr_t>(data));
97+
}
98+
}
99+
if (pimpl_->widget_) {
100+
gpointer data = g_object_get_data(G_OBJECT(pimpl_->widget_), kWindowIdKey);
101+
if (data) {
102+
return static_cast<WindowId>(reinterpret_cast<uintptr_t>(data));
89103
}
90104
}
91-
return new_id;
105+
return IdAllocator::kInvalidId;
92106
}
93107

94108
void Window::Focus() {
95-
if (pimpl_->gdk_window_) {
109+
if (pimpl_->widget_) {
110+
gtk_window_present(GTK_WINDOW(pimpl_->widget_));
111+
} else if (pimpl_->gdk_window_) {
96112
gdk_window_focus(pimpl_->gdk_window_, GDK_CURRENT_TIME);
97113
}
98114
}
@@ -120,27 +136,37 @@ bool Window::IsFocused() const {
120136
}
121137

122138
void Window::Show() {
123-
if (pimpl_->gdk_window_) {
139+
if (pimpl_->widget_) {
140+
gtk_widget_show(pimpl_->widget_);
141+
} else if (pimpl_->gdk_window_) {
124142
gdk_window_show(pimpl_->gdk_window_);
125143
}
126144
}
127145

128146
void Window::ShowInactive() {
129-
if (pimpl_->gdk_window_) {
147+
if (pimpl_->widget_) {
148+
gtk_widget_show(pimpl_->widget_);
149+
} else if (pimpl_->gdk_window_) {
130150
gdk_window_show_unraised(pimpl_->gdk_window_);
131151
}
132152
}
133153

134154
void Window::Hide() {
135-
if (pimpl_->gdk_window_) {
155+
if (pimpl_->widget_) {
156+
gtk_widget_hide(pimpl_->widget_);
157+
} else if (pimpl_->gdk_window_) {
136158
gdk_window_hide(pimpl_->gdk_window_);
137159
}
138160
}
139161

140162
bool Window::IsVisible() const {
141-
if (!pimpl_->gdk_window_)
142-
return false;
143-
return gdk_window_is_visible(pimpl_->gdk_window_);
163+
if (pimpl_->widget_) {
164+
return gtk_widget_get_visible(pimpl_->widget_);
165+
}
166+
if (pimpl_->gdk_window_) {
167+
return gdk_window_is_visible(pimpl_->gdk_window_);
168+
}
169+
return false;
144170
}
145171

146172
void Window::Maximize() {
@@ -388,13 +414,44 @@ void Window::Center() {
388414
}
389415

390416
void Window::SetTitle(std::string title) {
391-
// GDK windows don't have titles directly - this would be set on the GTK
392-
// widget For now, provide stub implementation
417+
// Prefer setting title via GtkWindow if available
418+
if (pimpl_->widget_ && GTK_IS_WINDOW(pimpl_->widget_)) {
419+
gtk_window_set_title(GTK_WINDOW(pimpl_->widget_), title.c_str());
420+
return;
421+
}
422+
423+
// If only GdkWindow is available, try to get associated GtkWindow
424+
if (pimpl_->gdk_window_) {
425+
gpointer user_data = nullptr;
426+
gdk_window_get_user_data(pimpl_->gdk_window_, &user_data);
427+
if (user_data && GTK_IS_WINDOW(user_data)) {
428+
gtk_window_set_title(GTK_WINDOW(user_data), title.c_str());
429+
return;
430+
}
431+
432+
// Fallback: set title via GDK for toplevel windows
433+
gdk_window_set_title(pimpl_->gdk_window_, title.c_str());
434+
}
393435
}
394436

395437
std::string Window::GetTitle() const {
396-
// GDK windows don't have titles directly - this would come from the GTK
397-
// widget
438+
// Prefer reading title via GtkWindow if available
439+
if (pimpl_->widget_ && GTK_IS_WINDOW(pimpl_->widget_)) {
440+
const gchar* t = gtk_window_get_title(GTK_WINDOW(pimpl_->widget_));
441+
return t ? std::string(t) : std::string();
442+
}
443+
444+
// If only GdkWindow is available, try to get associated GtkWindow
445+
if (pimpl_->gdk_window_) {
446+
gpointer user_data = nullptr;
447+
gdk_window_get_user_data(pimpl_->gdk_window_, &user_data);
448+
if (user_data && GTK_IS_WINDOW(user_data)) {
449+
const gchar* t = gtk_window_get_title(GTK_WINDOW(user_data));
450+
return t ? std::string(t) : std::string();
451+
}
452+
}
453+
454+
// No reliable way to get title directly from GdkWindow
398455
return std::string();
399456
}
400457

@@ -460,7 +517,8 @@ void Window::StartResizing() {
460517
}
461518

462519
void* Window::GetNativeObjectInternal() const {
463-
return pimpl_ ? pimpl_->gdk_window_ : nullptr;
520+
// Return the GtkWidget* (GtkWindow) as the native handle on Linux
521+
return pimpl_ ? static_cast<void*>(pimpl_->widget_ ? pimpl_->widget_ : nullptr) : nullptr;
464522
}
465523

466524
} // namespace nativeapi

src/platform/linux/window_manager_linux.cpp

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515

1616
namespace nativeapi {
1717

18+
// Key to store/retrieve WindowId on GObjects (must match window_linux.cpp)
19+
static const char* kWindowIdKey = "NativeAPIWindowId";
20+
1821
// Shared static variables for window ID mapping
1922
static std::unordered_map<GdkWindow*, WindowId> g_window_id_map;
2023
static std::mutex g_map_mutex;
@@ -32,15 +35,31 @@ static WindowId GetOrCreateWindowId(GdkWindow* gdk_window) {
3235
return IdAllocator::kInvalidId;
3336
}
3437

35-
std::lock_guard<std::mutex> lock(g_map_mutex);
36-
auto it = g_window_id_map.find(gdk_window);
37-
if (it != g_window_id_map.end()) {
38-
return it->second;
38+
// First, try to read ID attached to the GObject
39+
gpointer data = g_object_get_data(G_OBJECT(gdk_window), kWindowIdKey);
40+
if (data) {
41+
WindowId id = static_cast<WindowId>(reinterpret_cast<uintptr_t>(data));
42+
// Cache it in the map for faster lookup next time
43+
std::lock_guard<std::mutex> lock(g_map_mutex);
44+
g_window_id_map[gdk_window] = id;
45+
return id;
46+
}
47+
48+
// Fallback to cached map
49+
{
50+
std::lock_guard<std::mutex> lock(g_map_mutex);
51+
auto it = g_window_id_map.find(gdk_window);
52+
if (it != g_window_id_map.end()) {
53+
return it->second;
54+
}
3955
}
4056

41-
// Allocate new ID using the IdAllocator
57+
// Allocate new ID and attach to the GObject for consistency
4258
WindowId new_id = IdAllocator::Allocate<Window>();
4359
if (new_id != IdAllocator::kInvalidId) {
60+
g_object_set_data(G_OBJECT(gdk_window), kWindowIdKey,
61+
reinterpret_cast<gpointer>(static_cast<uintptr_t>(new_id)));
62+
std::lock_guard<std::mutex> lock(g_map_mutex);
4463
g_window_id_map[gdk_window] = new_id;
4564
}
4665
return new_id;
@@ -75,7 +94,7 @@ static gboolean on_show_emission_hook(GSignalInvocationHint* ihint,
7594
GdkWindow* gdk_window = gtk_widget_get_window(widget);
7695
if (gdk_window) {
7796
WindowId id = GetOrCreateWindowId(gdk_window);
78-
WindowManager::GetInstance().InvokeWillShowHook(id);
97+
WindowManager::GetInstance().HandleWillShow(id);
7998
}
8099
}
81100

@@ -96,7 +115,7 @@ static gboolean on_hide_emission_hook(GSignalInvocationHint* ihint,
96115
GdkWindow* gdk_window = gtk_widget_get_window(widget);
97116
if (gdk_window) {
98117
WindowId id = GetOrCreateWindowId(gdk_window);
99-
WindowManager::GetInstance().InvokeWillHideHook(id);
118+
WindowManager::GetInstance().HandleWillHide(id);
100119
}
101120
}
102121

@@ -113,7 +132,7 @@ static gboolean OnGtkMapEvent(GtkWidget* widget, GdkEvent* event, gpointer user_
113132
GdkWindow* gdk_window = gtk_widget_get_window(widget);
114133
if (gdk_window) {
115134
WindowId id = GetOrCreateWindowId(gdk_window);
116-
manager.InvokeWillShowHook(id);
135+
manager.HandleWillShow(id);
117136
}
118137
}
119138
// Return FALSE to propagate event further
@@ -129,7 +148,7 @@ static gboolean OnGtkUnmapEvent(GtkWidget* widget, GdkEvent* event, gpointer use
129148
GdkWindow* gdk_window = gtk_widget_get_window(widget);
130149
if (gdk_window) {
131150
WindowId id = GetOrCreateWindowId(gdk_window);
132-
manager.InvokeWillHideHook(id);
151+
manager.HandleWillHide(id);
133152
}
134153
}
135154
// Return FALSE to propagate event further
@@ -353,18 +372,48 @@ void WindowManager::SetWillHideHook(std::optional<WindowWillHideHook> hook) {
353372
}
354373
}
355374

356-
void WindowManager::InvokeWillShowHook(WindowId id) {
375+
bool WindowManager::HasWillShowHook() const {
376+
return pimpl_->will_show_hook_.has_value();
377+
}
378+
379+
bool WindowManager::HasWillHideHook() const {
380+
return pimpl_->will_hide_hook_.has_value();
381+
}
382+
383+
void WindowManager::HandleWillShow(WindowId id) {
357384
if (pimpl_->will_show_hook_) {
358385
(*pimpl_->will_show_hook_)(id);
359386
}
360387
}
361388

362-
void WindowManager::InvokeWillHideHook(WindowId id) {
389+
void WindowManager::HandleWillHide(WindowId id) {
363390
if (pimpl_->will_hide_hook_) {
364391
(*pimpl_->will_hide_hook_)(id);
365392
}
366393
}
367394

395+
bool WindowManager::CallOriginalShow(WindowId id) {
396+
GdkWindow* gdk_window = FindGdkWindowById(id);
397+
if (!gdk_window) {
398+
return false;
399+
}
400+
401+
// Call the original GDK show function directly
402+
gdk_window_show(gdk_window);
403+
return true;
404+
}
405+
406+
bool WindowManager::CallOriginalHide(WindowId id) {
407+
GdkWindow* gdk_window = FindGdkWindowById(id);
408+
if (!gdk_window) {
409+
return false;
410+
}
411+
412+
// Call the original GDK hide function directly
413+
gdk_window_hide(gdk_window);
414+
return true;
415+
}
416+
368417
void WindowManager::StartEventListening() {
369418
pimpl_->StartEventListening();
370419
}

0 commit comments

Comments
 (0)