Use XCB and SelectSelectionInput instead of GTK+
As it happens, there is no real need to constantly poll for changes, since XFixes can inform us of updates as they happen. With GTK+ gone we've got dependencies and error handling under control. XCB is a truly awful thing to learn, though. Our method will never work on Wayland or Windows, so we don't miss out on anything by abandoning the huge toolkit.
This commit is contained in:
parent
b0d3b2dcb5
commit
942bda7db4
|
@ -74,19 +74,18 @@ else (USE_SYSTEM_TERMO)
|
|||
set (Termo_LIBRARIES termo-static)
|
||||
endif (USE_SYSTEM_TERMO)
|
||||
|
||||
# We actually don't care about the specific version
|
||||
pkg_search_module (gtk gtk+-3.0 gtk+-2.0)
|
||||
option (WITH_GTK "Compile with GTK+ support" ${gtk_FOUND})
|
||||
pkg_check_modules (xcb xcb xcb-xfixes)
|
||||
option (WITH_X11 "Compile with X11 selection support using XCB" ${xcb_FOUND})
|
||||
|
||||
if (WITH_GTK)
|
||||
if (NOT gtk_FOUND)
|
||||
message (FATAL_ERROR "GTK+ library not found")
|
||||
endif (NOT gtk_FOUND)
|
||||
if (WITH_X11)
|
||||
if (NOT xcb_FOUND)
|
||||
message (FATAL_ERROR "XCB not found")
|
||||
endif (NOT xcb_FOUND)
|
||||
|
||||
list (APPEND dependencies_INCLUDE_DIRS ${gtk_INCLUDE_DIRS})
|
||||
list (APPEND dependencies_LIBRARY_DIRS ${gtk_LIBRARY_DIRS})
|
||||
list (APPEND dependencies_LIBRARIES ${gtk_LIBRARIES})
|
||||
endif (WITH_GTK)
|
||||
list (APPEND dependencies_INCLUDE_DIRS ${xcb_INCLUDE_DIRS})
|
||||
list (APPEND dependencies_LIBRARY_DIRS ${xcb_LIBRARY_DIRS})
|
||||
list (APPEND dependencies_LIBRARIES ${xcb_LIBRARIES})
|
||||
endif (WITH_X11)
|
||||
|
||||
link_directories (${dependencies_LIBRARY_DIRS})
|
||||
include_directories (${ZLIB_INCLUDE_DIRS} ${icu_INCLUDE_DIRS}
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2013 - 2016, Přemysl Janouch <p@janouch.name>
|
||||
Copyright (c) 2013 - 2018, Přemysl Janouch <p@janouch.name>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
|
|
@ -7,7 +7,7 @@ dictionary software of this kind, GUI or not, and thus decided to write my own.
|
|||
|
||||
The project is covered by a permissive license, unlike vast majority of other
|
||||
similar projects, and can serve as a base for implementing other dictionary
|
||||
software. I wasn't able to reuse _anything_.
|
||||
software. I wasn't able to reuse _anything_ for StarDict.
|
||||
|
||||
Further Development
|
||||
-------------------
|
||||
|
@ -32,12 +32,12 @@ Building and Running
|
|||
--------------------
|
||||
Build dependencies: CMake, pkg-config, xsltproc, docbook-xsl +
|
||||
Runtime dependencies: ncursesw, zlib, ICU, termo (included),
|
||||
glib-2.0, pango, gtk+ (optional, any version)
|
||||
glib-2.0, pango, xcb and xcb-xfixes (optional)
|
||||
|
||||
$ git clone --recursive https://git.janouch.name/p/sdtui.git
|
||||
$ mkdir sdtui/build
|
||||
$ cd sdtui/build
|
||||
$ cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug -DWITH_GTK=ON
|
||||
$ cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug -DWITH_X11=ON
|
||||
$ make
|
||||
|
||||
To install the application, you can do either the usual:
|
||||
|
@ -85,7 +85,7 @@ odd = 16 255
|
|||
|
||||
The `watch-selection` option makes the application watch the X11 primary
|
||||
selection for changes and automatically search for selected text.
|
||||
This feature requires GTK+ and it will never work on Wayland by its design.
|
||||
This feature requires XCB and it will never work on Wayland by its design.
|
||||
|
||||
You can also set up some dictionaries to be loaded at startup automatically:
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#define GETTEXT_PACKAGE PROJECT_NAME
|
||||
#define GETTEXT_DIRNAME "${CMAKE_INSTALL_PREFIX}/share/locale"
|
||||
|
||||
#cmakedefine WITH_GTK
|
||||
#cmakedefine WITH_X11
|
||||
#cmakedefine HAVE_RESIZETERM
|
||||
|
||||
#endif // ! CONFIG_H
|
||||
|
|
322
src/sdtui.c
322
src/sdtui.c
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* StarDict terminal UI
|
||||
*
|
||||
* Copyright (c) 2013 - 2016, Přemysl Janouch <p@janouch.name>
|
||||
* Copyright (c) 2013 - 2018, Přemysl Janouch <p@janouch.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted.
|
||||
|
@ -43,10 +43,6 @@
|
|||
#include "stardict.h"
|
||||
#include "utils.h"
|
||||
|
||||
#ifdef WITH_GTK
|
||||
#include <gtk/gtk.h>
|
||||
#endif // WITH_GTK
|
||||
|
||||
#define CTRL_KEY(x) ((x) - 'A' + 1)
|
||||
|
||||
#define TOP_BAR_CUTOFF 2 ///< How many lines are reserved on top
|
||||
|
@ -213,6 +209,7 @@ struct application
|
|||
guint center_search : 1; ///< Whether to center the search
|
||||
guint underline_last : 1; ///< Underline the last definition
|
||||
guint hl_prefix : 1; ///< Highlight the common prefix
|
||||
guint watch_x11_sel : 1; ///< Requested X11 selection watcher
|
||||
|
||||
guint32 top_position; ///< Index of the topmost dict. entry
|
||||
guint top_offset; ///< Offset into the top entry
|
||||
|
@ -226,10 +223,6 @@ struct application
|
|||
|
||||
gfloat division; ///< Position of the division column
|
||||
|
||||
guint selection_timer; ///< Selection watcher timeout timer
|
||||
gint selection_interval; ///< Selection watcher timer interval
|
||||
gchar * selection_contents; ///< Selection contents
|
||||
|
||||
struct attrs attrs[ATTRIBUTE_COUNT];
|
||||
};
|
||||
|
||||
|
@ -398,18 +391,6 @@ app_reload_view (Application *self)
|
|||
g_object_unref (iterator);
|
||||
}
|
||||
|
||||
#ifdef WITH_GTK
|
||||
static gboolean on_selection_timer (gpointer data);
|
||||
|
||||
static void
|
||||
rearm_selection_watcher (Application *self)
|
||||
{
|
||||
if (self->selection_interval > 0)
|
||||
self->selection_timer = g_timeout_add
|
||||
(self->selection_interval, on_selection_timer, self);
|
||||
}
|
||||
#endif // WITH_GTK
|
||||
|
||||
/// Load configuration for a color using a subset of git config colors.
|
||||
static void
|
||||
app_load_color (Application *self, GKeyFile *kf, const gchar *name, int id)
|
||||
|
@ -468,14 +449,8 @@ app_load_config_values (Application *self, GKeyFile *kf)
|
|||
app_load_bool (kf, "underline-last", self->underline_last);
|
||||
self->hl_prefix =
|
||||
app_load_bool (kf, "hl-common-prefix", self->hl_prefix);
|
||||
|
||||
guint64 timer;
|
||||
const gchar *watch_selection = "watch-selection";
|
||||
if (app_load_bool (kf, watch_selection, FALSE))
|
||||
self->selection_interval = 500;
|
||||
else if ((timer = g_key_file_get_uint64
|
||||
(kf, "Settings", watch_selection, NULL)) && timer <= G_MAXINT)
|
||||
self->selection_interval = timer;
|
||||
self->watch_x11_sel =
|
||||
app_load_bool (kf, "watch-selection", self->watch_x11_sel);
|
||||
|
||||
#define XX(name, config, fg_, bg_, attrs_) \
|
||||
app_load_color (self, kf, config, ATTRIBUTE_ ## name);
|
||||
|
@ -613,9 +588,6 @@ static void
|
|||
app_init (Application *self, char **filenames)
|
||||
{
|
||||
self->loop = NULL;
|
||||
self->selection_interval = -1;
|
||||
self->selection_timer = 0;
|
||||
self->selection_contents = NULL;
|
||||
|
||||
self->tk = NULL;
|
||||
self->tk_timer = 0;
|
||||
|
@ -624,6 +596,7 @@ app_init (Application *self, char **filenames)
|
|||
self->center_search = TRUE;
|
||||
self->underline_last = TRUE;
|
||||
self->hl_prefix = TRUE;
|
||||
self->watch_x11_sel = FALSE;
|
||||
|
||||
self->top_position = 0;
|
||||
self->top_offset = 0;
|
||||
|
@ -660,17 +633,6 @@ app_init (Application *self, char **filenames)
|
|||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Now we have settings for the clipboard watcher, we can arm the timer
|
||||
#ifdef WITH_GTK
|
||||
if (gtk_init_check (0, NULL))
|
||||
{
|
||||
// So that we set the input only when it actually changes
|
||||
GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
|
||||
self->selection_contents = gtk_clipboard_wait_for_text (clipboard);
|
||||
rearm_selection_watcher (self);
|
||||
}
|
||||
else
|
||||
#endif // WITH_GTK
|
||||
self->loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
// Dictionaries given on the command line override the configuration
|
||||
|
@ -744,10 +706,6 @@ app_destroy (Application *self)
|
|||
if (self->tk_timer)
|
||||
g_source_remove (self->tk_timer);
|
||||
|
||||
if (self->selection_timer)
|
||||
g_source_remove (self->selection_timer);
|
||||
g_free (self->selection_contents);
|
||||
|
||||
g_ptr_array_free (self->entries, TRUE);
|
||||
g_free (self->search_label);
|
||||
g_array_free (self->input, TRUE);
|
||||
|
@ -760,24 +718,14 @@ app_destroy (Application *self)
|
|||
static void
|
||||
app_run (Application *self)
|
||||
{
|
||||
if (self->loop)
|
||||
g_main_loop_run (self->loop);
|
||||
#ifdef WITH_GTK
|
||||
else
|
||||
gtk_main ();
|
||||
#endif // WITH_GTK
|
||||
}
|
||||
|
||||
/// Quit the main event dispatch loop.
|
||||
static void
|
||||
app_quit (Application *self)
|
||||
{
|
||||
if (self->loop)
|
||||
g_main_loop_quit (self->loop);
|
||||
#ifdef WITH_GTK
|
||||
else
|
||||
gtk_main_quit ();
|
||||
#endif // WITH_GTK
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -1023,7 +971,7 @@ app_show_help (Application *self)
|
|||
{
|
||||
PROJECT_NAME " " PROJECT_VERSION,
|
||||
_("Terminal UI for StarDict dictionaries"),
|
||||
"Copyright (c) 2013 - 2016, Přemysl Janouch",
|
||||
"Copyright (c) 2013 - 2018, Přemysl Janouch",
|
||||
"",
|
||||
_("Type to search")
|
||||
};
|
||||
|
@ -1835,6 +1783,215 @@ install_winch_handler (void)
|
|||
sigaction (SIGWINCH, &act, &oldact);
|
||||
}
|
||||
|
||||
// --- X11 selection watcher ---------------------------------------------------
|
||||
|
||||
#ifdef WITH_X11
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xfixes.h>
|
||||
|
||||
/// Data relating to one entry within the dictionary.
|
||||
typedef struct selection_watch SelectionWatch;
|
||||
|
||||
struct selection_watch
|
||||
{
|
||||
Application *app;
|
||||
xcb_connection_t *X;
|
||||
const xcb_query_extension_reply_t *xfixes;
|
||||
|
||||
guint watch; ///< X11 connection watcher
|
||||
xcb_window_t wid; ///< Withdrawn communications window
|
||||
xcb_atom_t atom_utf8_string; ///< UTF8_STRING
|
||||
xcb_timestamp_t in_progress; ///< Timestamp of last processed event
|
||||
};
|
||||
|
||||
static gboolean
|
||||
is_xcb_ok (xcb_connection_t *X)
|
||||
{
|
||||
int xcb_error = xcb_connection_has_error (X);
|
||||
if (xcb_error)
|
||||
{
|
||||
g_warning (_("X11 connection failed (error code %d)"), xcb_error);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static xcb_atom_t
|
||||
resolve_atom (xcb_connection_t *X, const char *atom)
|
||||
{
|
||||
xcb_intern_atom_reply_t *iar = xcb_intern_atom_reply (X,
|
||||
xcb_intern_atom (X, false, strlen (atom), atom), NULL);
|
||||
xcb_atom_t result = iar ? iar->atom : XCB_NONE;
|
||||
free (iar);
|
||||
return result;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
static void
|
||||
app_set_input (Application *self, const gchar *text, gsize text_len);
|
||||
|
||||
static void
|
||||
on_selection_text_received (SelectionWatch *self, const gchar *text);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
static gboolean
|
||||
read_utf8_property (SelectionWatch *self, xcb_window_t wid, xcb_atom_t property,
|
||||
GString *buf)
|
||||
{
|
||||
guint32 offset = 0;
|
||||
gboolean loop = TRUE, ok = TRUE;
|
||||
while (ok && loop)
|
||||
{
|
||||
xcb_get_property_reply_t *gpr = xcb_get_property_reply (self->X,
|
||||
xcb_get_property (self->X, FALSE /* delete */, wid,
|
||||
property, XCB_GET_PROPERTY_TYPE_ANY, offset, 0x7fff), NULL);
|
||||
|
||||
if (!gpr || gpr->type != self->atom_utf8_string || gpr->format != 8)
|
||||
ok = FALSE;
|
||||
else
|
||||
{
|
||||
int len = xcb_get_property_value_length (gpr);
|
||||
g_string_append_len (buf, xcb_get_property_value (gpr), len);
|
||||
offset += len >> 2;
|
||||
loop = gpr->bytes_after > 0;
|
||||
}
|
||||
|
||||
free (gpr);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void
|
||||
process_x11_event (SelectionWatch *self, xcb_generic_event_t *event)
|
||||
{
|
||||
xcb_generic_error_t *err = NULL;
|
||||
int event_code = event->response_type & 0x7f;
|
||||
if (event_code == 0)
|
||||
{
|
||||
err = (xcb_generic_error_t *) event;
|
||||
g_warning (_("X11 request error (%d, major %d, minor %d)"),
|
||||
err->error_code, err->major_code, err->minor_code);
|
||||
}
|
||||
else if (event_code ==
|
||||
self->xfixes->first_event + XCB_XFIXES_SELECTION_NOTIFY)
|
||||
{
|
||||
xcb_xfixes_selection_notify_event_t *e =
|
||||
(xcb_xfixes_selection_notify_event_t *) event;
|
||||
|
||||
// Not checking whether we should give up when this interrupts our
|
||||
// current retrieval attempt--the timeout solves this
|
||||
if (e->owner == XCB_NONE)
|
||||
return;
|
||||
|
||||
// Don't try to process two things at once. Each request gets a few
|
||||
// seconds to finish, then we move on, hoping that a property race
|
||||
// doesn't commence. Ideally we'd set up a separate queue for these
|
||||
// skipped requests and process them later.
|
||||
if (self->in_progress != 0 && e->timestamp - self->in_progress < 5000)
|
||||
return;
|
||||
|
||||
// ICCCM says we should ensure the named property doesn't exist
|
||||
(void) xcb_delete_property (self->X, self->wid, XCB_ATOM_PRIMARY);
|
||||
|
||||
(void) xcb_convert_selection (self->X, self->wid, e->selection,
|
||||
self->atom_utf8_string, XCB_ATOM_PRIMARY, e->timestamp);
|
||||
self->in_progress = e->timestamp;
|
||||
}
|
||||
else if (event_code == XCB_SELECTION_NOTIFY)
|
||||
{
|
||||
xcb_selection_notify_event_t *e =
|
||||
(xcb_selection_notify_event_t *) event;
|
||||
if (e->time != self->in_progress)
|
||||
return;
|
||||
|
||||
self->in_progress = 0;
|
||||
if (e->property == XCB_ATOM_NONE)
|
||||
return;
|
||||
|
||||
GString *buf = g_string_new (NULL);
|
||||
if (read_utf8_property (self, e->requestor, e->property, buf))
|
||||
on_selection_text_received (self, buf->str);
|
||||
g_string_free (buf, TRUE);
|
||||
|
||||
(void) xcb_delete_property (self->X, self->wid, e->property);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
process_x11 (G_GNUC_UNUSED GIOChannel *source,
|
||||
G_GNUC_UNUSED GIOCondition condition, gpointer data)
|
||||
{
|
||||
SelectionWatch *self = data;
|
||||
|
||||
xcb_generic_event_t *event;
|
||||
while ((event = xcb_poll_for_event (self->X)))
|
||||
{
|
||||
process_x11_event (self, event);
|
||||
free (event);
|
||||
}
|
||||
(void) xcb_flush (self->X);
|
||||
return is_xcb_ok (self->X);
|
||||
}
|
||||
|
||||
static void
|
||||
selection_watch_init (SelectionWatch *self, Application *app)
|
||||
{
|
||||
memset (self, 0, sizeof *self);
|
||||
if (!app->watch_x11_sel)
|
||||
return;
|
||||
self->app = app;
|
||||
|
||||
int which_screen = -1;
|
||||
self->X = xcb_connect (NULL, &which_screen);
|
||||
if (!is_xcb_ok (self->X))
|
||||
return;
|
||||
|
||||
// Most modern applications support this, though an XCB_ATOM_STRING
|
||||
// fallback might be good to add (COMPOUND_TEXT is complex)
|
||||
g_return_if_fail
|
||||
((self->atom_utf8_string = resolve_atom (self->X, "UTF8_STRING")));
|
||||
|
||||
self->xfixes = xcb_get_extension_data (self->X, &xcb_xfixes_id);
|
||||
g_return_if_fail (self->xfixes->present);
|
||||
|
||||
(void) xcb_xfixes_query_version_unchecked (self->X,
|
||||
XCB_XFIXES_MAJOR_VERSION, XCB_XFIXES_MINOR_VERSION);
|
||||
|
||||
const xcb_setup_t *setup = xcb_get_setup (self->X);
|
||||
xcb_screen_iterator_t setup_iter = xcb_setup_roots_iterator (setup);
|
||||
while (which_screen--)
|
||||
xcb_screen_next (&setup_iter);
|
||||
|
||||
xcb_screen_t *screen = setup_iter.data;
|
||||
self->wid = xcb_generate_id (self->X);
|
||||
(void) xcb_create_window (self->X, screen->root_depth, self->wid,
|
||||
screen->root, 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
|
||||
screen->root_visual, 0, NULL);
|
||||
|
||||
(void) xcb_xfixes_select_selection_input (self->X, self->wid,
|
||||
XCB_ATOM_PRIMARY, XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
|
||||
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
|
||||
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE);
|
||||
|
||||
(void) xcb_flush (self->X);
|
||||
self->watch = g_io_add_watch (g_io_channel_unix_new
|
||||
(xcb_get_file_descriptor (self->X)), G_IO_IN, process_x11, self);
|
||||
}
|
||||
|
||||
static void
|
||||
selection_watch_destroy (SelectionWatch *self)
|
||||
{
|
||||
if (self->X)
|
||||
xcb_disconnect (self->X);
|
||||
if (self->watch)
|
||||
g_source_remove (self->watch);
|
||||
}
|
||||
|
||||
#endif // WITH_X11
|
||||
|
||||
// --- Initialisation, event handling ------------------------------------------
|
||||
|
||||
static gboolean on_stdin_input_timeout (gpointer data);
|
||||
|
@ -1903,7 +2060,7 @@ on_terminated (gpointer user_data)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
#ifdef WITH_GTK
|
||||
#ifdef WITH_X11
|
||||
static void
|
||||
app_set_input (Application *self, const gchar *text, gsize text_len)
|
||||
{
|
||||
|
@ -1935,14 +2092,8 @@ app_set_input (Application *self, const gchar *text, gsize text_len)
|
|||
}
|
||||
|
||||
static void
|
||||
on_selection_text_received (G_GNUC_UNUSED GtkClipboard *clipboard,
|
||||
const gchar *text, gpointer data)
|
||||
on_selection_text_received (SelectionWatch *self, const gchar *text)
|
||||
{
|
||||
Application *app = data;
|
||||
rearm_selection_watcher (app);
|
||||
|
||||
if (text)
|
||||
{
|
||||
// Strip ASCII whitespace: this is compatible with UTF-8
|
||||
while (g_ascii_isspace (*text))
|
||||
text++;
|
||||
|
@ -1950,32 +2101,10 @@ on_selection_text_received (G_GNUC_UNUSED GtkClipboard *clipboard,
|
|||
while (text_len && g_ascii_isspace (text[text_len - 1]))
|
||||
text_len--;
|
||||
|
||||
if (app->selection_contents &&
|
||||
!strncmp (app->selection_contents, text, text_len))
|
||||
return;
|
||||
|
||||
g_free (app->selection_contents);
|
||||
app->selection_contents = g_strndup (text, text_len);
|
||||
app_set_input (app, text, text_len);
|
||||
}
|
||||
else if (app->selection_contents)
|
||||
{
|
||||
g_free (app->selection_contents);
|
||||
app->selection_contents = NULL;
|
||||
}
|
||||
if (text_len)
|
||||
app_set_input (self->app, text, text_len);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
on_selection_timer (gpointer data)
|
||||
{
|
||||
Application *app = data;
|
||||
GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
|
||||
gtk_clipboard_request_text (clipboard, on_selection_text_received, app);
|
||||
|
||||
app->selection_timer = 0;
|
||||
return FALSE;
|
||||
}
|
||||
#endif // WITH_GTK
|
||||
#endif // WITH_X11
|
||||
|
||||
static void
|
||||
log_handler_curses (Application *self, const gchar *message)
|
||||
|
@ -2091,7 +2220,7 @@ G_GNUC_END_IGNORE_DEPRECATIONS
|
|||
// g_unix_signal_add() cannot handle SIGWINCH
|
||||
install_winch_handler ();
|
||||
|
||||
// GtkClipboard can internally issue some rather disruptive warnings
|
||||
// Avoid disruptive warnings
|
||||
g_log_set_default_handler (log_handler, &app);
|
||||
|
||||
// Message loop
|
||||
|
@ -2102,8 +2231,17 @@ G_GNUC_END_IGNORE_DEPRECATIONS
|
|||
guint watch_winch = g_io_add_watch (g_io_channel_unix_new (g_winch_pipe[0]),
|
||||
G_IO_IN, process_winch_input, &app);
|
||||
|
||||
#ifdef WITH_X11
|
||||
SelectionWatch sw;
|
||||
selection_watch_init (&sw, &app);
|
||||
#endif // WITH_X11
|
||||
|
||||
app_run (&app);
|
||||
|
||||
#ifdef WITH_X11
|
||||
selection_watch_destroy (&sw);
|
||||
#endif // WITH_X11
|
||||
|
||||
g_source_remove (watch_term);
|
||||
g_source_remove (watch_int);
|
||||
g_source_remove (watch_stdin);
|
||||
|
|
Loading…
Reference in New Issue