Skip to content

Commit 522df16

Browse files
committed
Fix WindowManager::GetCurrent() and the stderr it left behind on Linux
GetCurrent() asked the seat's keyboard for the window at its position, but that call is about pointer position and GDK rejects keyboard devices, so the focused window was never found and GDK logged an assertion failure instead (libnativeapi/nativeapi-flutter#6). Walk the toplevels and prefer the active one, which works on both X11 and Wayland. The constructor also silenced GTK's headless warnings by reopening stderr onto /dev/null and restoring it from /dev/tty. freopen() reuses the stream, so the saved FILE* pointed at /dev/null too, and a process without a controlling terminal — an SSH session, a systemd service, an app started from the desktop — lost stderr for good. Swap the file descriptor instead and restore it from a dup.
1 parent a2906b3 commit 522df16

1 file changed

Lines changed: 40 additions & 27 deletions

File tree

src/platform/linux/window_manager_linux.cpp

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#include <fcntl.h>
2+
#include <unistd.h>
13
#include <cstring>
24
#include <iostream>
35
#include <mutex>
@@ -342,16 +344,25 @@ WindowManager::WindowManager() : pimpl_(std::make_unique<Impl>(this)) {
342344
// In headless environments, this may fail, which is acceptable
343345
if (!gdk_display_get_default()) {
344346
// Temporarily redirect stderr to suppress GTK warnings in headless
345-
// environments
346-
FILE* original_stderr = stderr;
347-
freopen("/dev/null", "w", stderr);
347+
// environments. This swaps the file descriptor rather than reopening the
348+
// stderr stream: freopen() would leave the caller without a usable stderr
349+
// whenever the process has no controlling terminal to restore from.
350+
fflush(stderr);
351+
int saved_stderr = dup(STDERR_FILENO);
352+
int devnull = open("/dev/null", O_WRONLY | O_CLOEXEC);
353+
if (devnull != -1) {
354+
dup2(devnull, STDERR_FILENO);
355+
close(devnull);
356+
}
348357

349-
gboolean gtk_result = gtk_init_check(nullptr, nullptr);
358+
gtk_init_check(nullptr, nullptr);
350359

351360
// Restore stderr
352361
fflush(stderr);
353-
freopen("/dev/tty", "w", stderr);
354-
stderr = original_stderr;
362+
if (saved_stderr != -1) {
363+
dup2(saved_stderr, STDERR_FILENO);
364+
close(saved_stderr);
365+
}
355366

356367
// gtk_init_check returns FALSE if initialization failed (e.g., no display)
357368
// This is acceptable for headless environments
@@ -514,34 +525,36 @@ std::shared_ptr<Window> WindowManager::GetCurrent() {
514525
return nullptr;
515526
}
516527

517-
// Try to get the focused window
518-
GdkSeat* seat = gdk_display_get_default_seat(display);
519-
if (seat) {
520-
GdkDevice* keyboard = gdk_seat_get_keyboard(seat);
521-
if (keyboard) {
522-
GdkWindow* focused_window = gdk_device_get_window_at_position(keyboard, nullptr, nullptr);
523-
if (focused_window) {
524-
WindowId window_id = GetOrCreateWindowId(focused_window);
525-
return Get(window_id);
526-
}
527-
}
528-
}
529-
530-
// Fallback: get the first visible window
528+
// The focused window, falling back to the first visible one. Asking the seat's
529+
// keyboard for the window at its position is not an option: that call is about
530+
// pointer position and GDK rejects keyboard devices outright, so on every
531+
// backend it only logs an assertion failure.
532+
GdkWindow* first_visible = nullptr;
531533
GList* toplevels = gtk_window_list_toplevels();
532534
for (GList* l = toplevels; l != nullptr; l = l->next) {
533535
GtkWindow* gtk_window = GTK_WINDOW(l->data);
534-
if (gtk_widget_get_visible(GTK_WIDGET(gtk_window))) {
535-
GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(gtk_window));
536-
if (gdk_window) {
537-
WindowId window_id = GetOrCreateWindowId(gdk_window);
538-
g_list_free(toplevels);
539-
return Get(window_id);
540-
}
536+
if (!gtk_widget_get_visible(GTK_WIDGET(gtk_window))) {
537+
continue;
538+
}
539+
GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(gtk_window));
540+
if (!gdk_window) {
541+
continue;
542+
}
543+
if (gtk_window_is_active(gtk_window)) {
544+
WindowId window_id = GetOrCreateWindowId(gdk_window);
545+
g_list_free(toplevels);
546+
return Get(window_id);
547+
}
548+
if (!first_visible) {
549+
first_visible = gdk_window;
541550
}
542551
}
543552
g_list_free(toplevels);
544553

554+
if (first_visible) {
555+
return Get(GetOrCreateWindowId(first_visible));
556+
}
557+
545558
return nullptr;
546559
}
547560

0 commit comments

Comments
 (0)