xsel

Program for manipulating the X clipboard
git clone https://git.sinitax.com/kfish/xsel
Log | Files | Refs | README | LICENSE | sfeed.txt

commit 969e755e1b757e7d922fa43a0aae55d6219e397d
parent ba8656dc7c7e771c802fc957ce3dd128d4b6e3ae
Author: Conrad Parker <conrad@metadecks.org>
Date:   Wed, 15 Oct 2014 20:56:22 +1100

Merge pull request #5 from zevweiss/master

Fixes and new functionality for '-t'; also a small build fix
Diffstat:
Mconfigure.ac | 5+----
Mxsel.1x | 3++-
Mxsel.c | 129+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
3 files changed, 106 insertions(+), 31 deletions(-)

diff --git a/configure.ac b/configure.ac @@ -18,10 +18,7 @@ AC_SUBST(X_LDFLAGS) AC_SUBST(X_LIBS) AC_SUBST(X_EXTRA_LIBS) -AC_CHECK_LIB([X11], [XOpenDisplay], HAVE_X11="yes", HAVE_X11="no") -if test "x$HAVE_X11" = "xno" ; then - AC_MSG_ERROR([libX11 is required]) -fi +AC_SEARCH_LIBS([XOpenDisplay], [X11], [], [AC_MSG_ERROR([libX11 is required])]) # Error out on compile warnings dnl Add some useful warnings if we have gcc. diff --git a/xsel.1x b/xsel.1x @@ -87,7 +87,8 @@ specify the server to use; see \fIX(1)\fP. .TP \fB\-t\fR \fIms\fR, \fB\-\-selectionTimeout\fR \fIms\fR Specify the timeout in milliseconds within which the selection must be -retrieved. A value of \fB0\fR (zero) specifies no timeout (default). +retrieved. In \fB\-\-input\fR mode, the background process exits after this +amount of time. A value of \fB0\fR (zero) specifies no timeout (default). .PP \fBMiscellaneous options\fR diff --git a/xsel.c b/xsel.c @@ -92,6 +92,8 @@ static int current_alloc = 0; static long timeout = 0; static struct itimerval timer; +#define USEC_PER_SEC 1000000 + static int saved_argc; static char ** saved_argv; @@ -373,6 +375,78 @@ gotpw: } /* + * The set of terminal signals we block while handling SelectionRequests. + * + * If we exit in the middle of handling a SelectionRequest, we might leave the + * requesting client hanging, so we try to be nice and finish handling + * requests before terminating. Hence we block SIG{ALRM,INT,TERM} while + * handling requests and unblock them only while waiting in XNextEvent(). + */ +static sigset_t exit_sigs; + +static void block_exit_sigs(void) +{ + sigprocmask (SIG_BLOCK, &exit_sigs, NULL); +} + +static void unblock_exit_sigs(void) +{ + sigprocmask (SIG_UNBLOCK, &exit_sigs, NULL); +} + +/* The jmp_buf to longjmp out of the signal handler */ +static sigjmp_buf env_alrm; + +/* + * alarm_handler (sig) + * + * Signal handler for catching SIGALRM. + */ +static void +alarm_handler (int sig) +{ + siglongjmp (env_alrm, 1); +} + +/* + * set_timer_timeout () + * + * Set timer parameters according to specified timeout. + */ +static void +set_timer_timeout (void) +{ + timer.it_interval.tv_sec = timeout / USEC_PER_SEC; + timer.it_interval.tv_usec = timeout % USEC_PER_SEC; + timer.it_value.tv_sec = timeout / USEC_PER_SEC; + timer.it_value.tv_usec = timeout % USEC_PER_SEC; +} + +/* + * set_daemon_timeout () + * + * Set up a timer to cause the daemon to exit after the desired + * amount of time. + */ +static void +set_daemon_timeout (void) +{ + if (signal (SIGALRM, alarm_handler) == SIG_ERR) { + exit_err ("error setting timeout handler"); + } + + set_timer_timeout (); + + if (sigsetjmp (env_alrm, 0) == 0) { + setitimer (ITIMER_REAL, &timer, (struct itimerval *)0); + } else { + print_debug (D_INFO, "daemon exiting after %d ms", timeout / 1000); + exit (0); + } +} + + +/* * become_daemon () * * Perform the required procedure to become a daemon process, as @@ -387,7 +461,12 @@ become_daemon (void) int null_r_fd, null_w_fd, log_fd; char * homedir; - if (no_daemon) return; + if (no_daemon) { + /* If the user has specified a timeout, enforce it even if we don't + * actually daemonize */ + set_daemon_timeout (); + return; + } homedir = get_homedir (); @@ -455,6 +534,8 @@ become_daemon (void) if (dup2 (log_fd, 2) == -1) { exit_err ("error duplicating logfile %s on stderr", logfile); } + + set_daemon_timeout (); } /* @@ -496,25 +577,11 @@ get_timestamp (void) * * The selection retrieval can time out if no response is received within * a user-specified time limit. In order to ensure we time the entire - * selection retrieval, we use an interval timer and catch SIGVTALRM. + * selection retrieval, we use an interval timer and catch SIGALRM. * [Calling select() on the XConnectionNumber would only provide a timeout * to the first XEvent.] */ -/* The jmp_buf to longjmp out of the signal handler */ -static sigjmp_buf env_alrm; - -/* - * alarm_handler (sig) - * - * Signal handler for catching SIGVTALRM. - */ -static void -alarm_handler (int sig) -{ - siglongjmp (env_alrm, 1); -} - /* * get_append_property () * @@ -698,7 +765,7 @@ wait_selection (Atom selection, Atom request_target) /* Now that we've received the SelectionNotify event, clear any * remaining timeout. */ if (timeout > 0) { - setitimer (ITIMER_VIRTUAL, (struct itimerval *)0, (struct itimerval *)0); + setitimer (ITIMER_REAL, (struct itimerval *)0, (struct itimerval *)0); } return retval; @@ -725,17 +792,14 @@ get_selection (Atom selection, Atom request_target) XSync (display, False); if (timeout > 0) { - if (signal (SIGVTALRM, alarm_handler) == SIG_ERR) { + if (signal (SIGALRM, alarm_handler) == SIG_ERR) { exit_err ("error setting timeout handler"); } - timer.it_interval.tv_sec = 0; - timer.it_interval.tv_usec = timeout; - timer.it_value.tv_sec = 0; - timer.it_value.tv_usec = timeout; + set_timer_timeout (); if (sigsetjmp (env_alrm, 0) == 0) { - setitimer (ITIMER_VIRTUAL, &timer, (struct itimerval *)0); + setitimer (ITIMER_REAL, &timer, (struct itimerval *)0); retval = wait_selection (selection, request_target); } else { print_debug (D_WARN, "selection timed out"); @@ -1669,12 +1733,16 @@ set_selection (Atom selection, unsigned char * sel) { XEvent event; IncrTrack * it; - + if (own_selection (selection) == False) return; for (;;) { + /* Flush before unblocking signals so we send replies before exiting */ + XFlush (display); + unblock_exit_sigs (); XNextEvent (display, &event); - + block_exit_sigs (); + switch (event.type) { case SelectionClear: if (event.xselectionclear.selection == selection) return; @@ -1755,8 +1823,12 @@ set_selection_pair (unsigned char * sel_p, unsigned char * sel_s) } for (;;) { + /* Flush before unblocking signals so we send replies before exiting */ + XFlush (display); + unblock_exit_sigs (); XNextEvent (display, &event); - + block_exit_sigs (); + switch (event.type) { case SelectionClear: if (event.xselectionclear.selection == XA_PRIMARY) { @@ -2170,6 +2242,11 @@ main(int argc, char *argv[]) */ compound_text_atom = XInternAtom (display, "COMPOUND_TEXT", False); + sigemptyset (&exit_sigs); + sigaddset (&exit_sigs, SIGALRM); + sigaddset (&exit_sigs, SIGINT); + sigaddset (&exit_sigs, SIGTERM); + /* handle selection keeping and exit if so */ if (do_keep) { keep_selections ();