fix: handle X11 connection loss gracefully

Use XSetIOErrorExitHandler on X11 to detect connection loss and
disable GTK features (tray, clipboard, notifications).

Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Paul Fertser <fercerpav@gmail.com>
Co-authored-by: Paul Fertser <fercerpav@gmail.com>
This commit is contained in:
Michael Vetter
2026-03-31 13:06:09 +02:00
parent 9b2f96fb45
commit 8eac31e5e1
6 changed files with 76 additions and 1 deletions

View File

@@ -188,9 +188,18 @@ if get_option('notifications').enabled()
endif
# GTK (for icons and clipboard)
x11_extra_dep = []
if get_option('icons-and-clipboard').enabled()
gtk_dep = dependency('gtk+-3.0', version: '>= 3.24.0', required: true)
conf_data.set('HAVE_GTK', 1)
x11_dep = dependency('x11', required: false)
if x11_dep.found()
if cc.has_function('XSetIOErrorExitHandler', dependencies: x11_dep)
conf_data.set('HAVE_XEXITHANDLER', 1)
x11_extra_dep = [x11_dep]
endif
endif
endif
# XScreenSaver
@@ -330,6 +339,10 @@ if xscrnsaver_dep.length() > 0
profanity_deps += xscrnsaver_dep
endif
if x11_extra_dep.length() > 0
profanity_deps += x11_extra_dep
endif
if python_dep.found()
profanity_deps += python_dep
endif

View File

@@ -6477,6 +6477,10 @@ gboolean
cmd_tray(ProfWin* window, const char* const command, gchar** args)
{
#ifdef HAVE_GTK
if (!tray_gtk_ready()) {
log_warning("GTK is not ready. Tray functionality is disabled.");
return TRUE;
}
if (g_strcmp0(args[0], "timer") == 0) {
if (args[1] == NULL) {
cons_bad_cmd_usage(command);
@@ -8975,6 +8979,10 @@ gboolean
cmd_paste(ProfWin* window, const char* const command, gchar** args)
{
#ifdef HAVE_GTK
if (!tray_gtk_ready()) {
log_warning("GTK is not ready. Paste from clipboard is disabled.");
return TRUE;
}
auto_char char* clipboard_buffer = clipboard_get();
if (clipboard_buffer) {

View File

@@ -15,6 +15,7 @@
#include <stdlib.h>
#include "log.h"
#include "ui/tray.h"
/*
For now we rely on tray_init(void)
@@ -27,6 +28,9 @@ void clipboard_init(int argc, char **argv) {
char*
clipboard_get(void)
{
if (!tray_gtk_ready()) {
return NULL;
}
gchar* clip;
GtkClipboard* cl = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);

View File

@@ -26,6 +26,9 @@
#include "config/preferences.h"
#include "ui/ui.h"
#include "ui/window_list.h"
#ifdef HAVE_GTK
#include "ui/tray.h"
#endif
#include "xmpp/xmpp.h"
#include "xmpp/muc.h"
@@ -54,6 +57,11 @@ _notifier_uninit(void)
static void
_notify(const char* const message, int timeout, const char* const category)
{
#ifdef HAVE_GTK
if (!tray_gtk_ready()) {
return;
}
#endif
log_debug("Attempting notification: %s", message);
if (notify_is_initted()) {
log_debug("Reinitialising libnotify");

View File

@@ -21,6 +21,11 @@
#include "ui/tray.h"
#include "ui/window_list.h"
#if defined(GDK_WINDOWING_X11) && defined(HAVE_XEXITHANDLER)
#include <gdk/gdkx.h>
#include <X11/Xlib.h>
#endif
static gboolean gtk_ready = FALSE;
static GtkStatusIcon* prof_tray = NULL;
static GString* icon_filename = NULL;
@@ -29,6 +34,21 @@ static gint unread_messages;
static gboolean shutting_down;
static guint timer;
#if defined(GDK_WINDOWING_X11) && defined(HAVE_XEXITHANDLER)
static void
_x_io_error_handler(Display* display, void* user_data)
{
log_warning("Error: X Server connection lost.");
gtk_ready = FALSE;
}
static int
_x_io_handler(Display* display)
{
return 0;
}
#endif
/*
* Get icons from installation share folder or (if defined) .locale user's folder
*
@@ -98,7 +118,7 @@ _get_icons(void)
gboolean
_tray_change_icon(gpointer data)
{
if (shutting_down) {
if (shutting_down || !gtk_ready) {
return FALSE;
}
@@ -151,6 +171,14 @@ tray_init(void)
return;
}
#if defined(GDK_WINDOWING_X11) && defined(HAVE_XEXITHANDLER)
GdkDisplay* display = gdk_display_get_default();
if (GDK_IS_X11_DISPLAY(display)) {
XSetIOErrorExitHandler(GDK_DISPLAY_XDISPLAY(display), _x_io_error_handler, NULL);
XSetIOErrorHandler(_x_io_handler);
}
#endif
if (prefs_get_boolean(PREF_TRAY)) {
log_debug("Building GTK icon");
tray_enable();
@@ -167,9 +195,18 @@ tray_update(void)
}
}
gboolean
tray_gtk_ready(void)
{
return gtk_ready;
}
void
tray_set_timer(int interval)
{
if (!gtk_ready) {
return;
}
if (timer) {
g_source_remove(timer);
}
@@ -186,6 +223,9 @@ tray_set_timer(int interval)
void
tray_enable(void)
{
if (!gtk_ready) {
return;
}
prof_tray = gtk_status_icon_new_from_file(icon_filename->str);
shutting_down = FALSE;
_tray_change_icon(NULL);

View File

@@ -11,8 +11,10 @@
#define UI_TRAY_H
#ifdef HAVE_GTK
#include <glib.h>
void tray_init(void);
void tray_update(void);
gboolean tray_gtk_ready(void);
void tray_enable(void);
void tray_disable(void);