|
| 1 | +#include <fcntl.h> |
| 2 | +#include <unistd.h> |
1 | 3 | #include <cstring> |
2 | 4 | #include <iostream> |
3 | 5 | #include <mutex> |
@@ -342,16 +344,25 @@ WindowManager::WindowManager() : pimpl_(std::make_unique<Impl>(this)) { |
342 | 344 | // In headless environments, this may fail, which is acceptable |
343 | 345 | if (!gdk_display_get_default()) { |
344 | 346 | // 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 | + } |
348 | 357 |
|
349 | | - gboolean gtk_result = gtk_init_check(nullptr, nullptr); |
| 358 | + gtk_init_check(nullptr, nullptr); |
350 | 359 |
|
351 | 360 | // Restore stderr |
352 | 361 | 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 | + } |
355 | 366 |
|
356 | 367 | // gtk_init_check returns FALSE if initialization failed (e.g., no display) |
357 | 368 | // This is acceptable for headless environments |
@@ -514,34 +525,36 @@ std::shared_ptr<Window> WindowManager::GetCurrent() { |
514 | 525 | return nullptr; |
515 | 526 | } |
516 | 527 |
|
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; |
531 | 533 | GList* toplevels = gtk_window_list_toplevels(); |
532 | 534 | for (GList* l = toplevels; l != nullptr; l = l->next) { |
533 | 535 | 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; |
541 | 550 | } |
542 | 551 | } |
543 | 552 | g_list_free(toplevels); |
544 | 553 |
|
| 554 | + if (first_visible) { |
| 555 | + return Get(GetOrCreateWindowId(first_visible)); |
| 556 | + } |
| 557 | + |
545 | 558 | return nullptr; |
546 | 559 | } |
547 | 560 |
|
|
0 commit comments