9.0
TODO: distributed clipboard example
-TODO: use correct %ENV for perl exec'ed processes.
- setuid/setgid operation is now _encouraged_: security has been
improved by moving privileged operations into a separate process
and permanently dropping privileges within the terminal. This
- made some fixes to xpm offset and scaling code.
- perl-overlays and refresh hooks were not applied in correct order.
- changed coordinate-system of view_start/nsaved to be top to bottom.
+ - iom extended to support listening for child exits.
7.0 Fri Jan 13 14:02:18 CET 2006
- added sections for DISTRIBUTION MAINTAINERS and about
def (DESTROY)
def (RESET)
def (START)
+ def (CHILD_START)
+ def (CHILD_EXIT)
def (SEL_BEGIN)
def (SEL_EXTEND)
#endif
pty->close_tty ();
+
+ child_ev.start (cmd_pid);
+
+ HOOK_INVOKE ((this, HOOK_CHILD_START, DT_INT, cmd_pid, DT_END));
break;
}
}
#include <cstdio>
#include <cstdlib>
#include <cerrno>
+#include <cassert>
+#include <sys/types.h>
#include <sys/time.h>
-#include <assert.h>
-
#if 1 // older unices need these includes for select (2)
# include <unistd.h>
-# include <sys/types.h>
# include <time.h>
#endif
-// for IOM_SIG
+#if IOM_CHILD
+# include <sys/wait.h>
+#endif
+
#if IOM_SIG
# include <csignal>
# include <fcntl.h>
};
static vector<sig_vec *> sw;
#endif
+#if IOM_CHILD
+static io_manager_vec<child_watcher> pw;
+#endif
// this is a dummy time watcher to ensure that the first
// time watcher is _always_ valid, this gets rid of a lot
// of null-pointer-checks
// (must come _before_ iom is being defined)
static struct tw0 : time_watcher
+{
+ void cb (time_watcher &w)
{
- void cb (time_watcher &w)
- {
- // should never get called
- // reached end-of-time, or tstamp has a bogus definition,
- // or compiler initialisation order broken, or something else :)
- abort ();
- }
+ // should never get called
+ // reached end-of-time, or tstamp has a bogus definition,
+ // or compiler initialisation order broken, or something else :)
+ abort ();
+ }
- tw0 ()
- : time_watcher (this, &tw0::cb)
- { }
- } tw0;
+ tw0 ()
+ : time_watcher (this, &tw0::cb)
+ { }
+} tw0;
tstamp NOW;
+#if IOM_CHILD
+// sig_watcher for child signal(s)
+static struct sw0 : sig_watcher
+{
+ void cb (sig_watcher &w)
+ {
+ // SIGCHLD, call corresponding watchera
+ pid_t pid;
+ int status;
+
+ while ((pid = waitpid (-1, &status, WNOHANG)) > 0)
+ for (int i = pw.size (); i--; )
+ {
+ child_watcher *w = pw[i];
+
+ if (!w)
+ pw.erase_unordered (i);
+ else if (w->pid == pid)
+ {
+ w->stop ();
+ w->call (*w, status);
+ }
+ }
+
+ }
+
+ sw0 ()
+ : sig_watcher (this, &sw0::cb)
+ { }
+} sw0;
+#endif
+
#if IOM_TIME
tstamp io_manager::now ()
{
static struct init {
init ()
{
+ iom_valid = true;
+
#if IOM_SIG
sigemptyset (&sigs);
fcntl (sigpipe[1], F_SETFL, O_NONBLOCK); fcntl (sigpipe[1], F_SETFD, FD_CLOEXEC);
#endif
- iom_valid = true;
+#if IOM_CHILD
+ sw0.start (SIGCHLD);
+#endif
#if IOM_TIME
io_manager::set_now ();
}
#endif
+#if IOM_CHILD
+void io_manager::reg (child_watcher &w) { io_manager::reg (w, pw); }
+void io_manager::unreg (child_watcher &w) { io_manager::unreg (w, pw); }
+#endif
+
void io_manager::loop ()
{
init::required ();
// edit iom_conf.h as appropriate.
#include "iom_conf.h"
-#include "callback.h"
-
-#ifndef IOM_IO
-# define IOM_IO 0
-#endif
-#ifndef IOM_TIME
-# define IOM_TIME 0
-#endif
-#ifndef IOM_CHECK
-# define IOM_CHECK 0
-#endif
-#ifndef IOM_IDLE
-# define IOM_IDLE 0
-#endif
-#ifndef IOM_SIG
-# define IOM_SIG 0
+#if IOM_CHILD
+# undef IOM_SIG
+# define IOM_SIG 1
#endif
+#include "callback.h"
+
typedef double tstamp;
extern tstamp NOW;
#if IOM_SIG
struct sig_watcher;
#endif
+#if IOM_CHILD
+struct child_watcher;
+#endif
template<class watcher>
struct io_manager_vec : vector<watcher *> {
#if IOM_SIG
static void reg (sig_watcher &w); static void unreg (sig_watcher &w);
#endif
+#if IOM_CHILD
+ static void reg (child_watcher &w); static void unreg (child_watcher &w);
+#endif
static void loop ();
};
template<class O1, class O2>
io_watcher (O1 *object, void (O2::*method) (io_watcher &, short))
- : callback2<void, io_watcher &, short> (object,method)
+ : callback2<void, io_watcher &, short> (object, method)
{ }
~io_watcher () { stop (); }
};
template<class O1, class O2>
time_watcher (O1 *object, void (O2::*method) (time_watcher &))
- : callback1<void, time_watcher &> (object,method), at (0)
+ : callback1<void, time_watcher &> (object, method), at (0)
{ }
~time_watcher () { stop (); }
};
template<class O1, class O2>
check_watcher (O1 *object, void (O2::*method) (check_watcher &))
- : callback1<void, check_watcher &> (object,method)
+ : callback1<void, check_watcher &> (object, method)
{ }
~check_watcher () { stop (); }
};
template<class O1, class O2>
idle_watcher (O1 *object, void (O2::*method) (idle_watcher &))
- : callback1<void, idle_watcher &> (object,method)
+ : callback1<void, idle_watcher &> (object, method)
{ }
~idle_watcher () { stop (); }
};
template<class O1, class O2>
sig_watcher (O1 *object, void (O2::*method) (sig_watcher &))
- : callback1<void, sig_watcher &> (object,method), signum (-1)
+ : callback1<void, sig_watcher &> (object, method), signum (0)
{ }
~sig_watcher () { stop (); }
};
#endif
+#if IOM_CHILD
+struct child_watcher : watcher, callback2<void, child_watcher &, int> {
+ int /*pid_t*/ pid;
+
+ void start (int pid) { this->pid = pid; io_manager::reg (*this); }
+ void stop () { io_manager::unreg (*this); }
+
+ template<class O1, class O2>
+ child_watcher (O1 *object, void (O2::*method) (child_watcher &, int status))
+ : callback2<void, child_watcher &, int> (object, method), pid (0)
+ { }
+ ~child_watcher () { stop (); }
+};
+#endif
+
#endif
#include <rxvtutil.h>
-#define IOM_IO 1
-#define IOM_TIME 1
+#define IOM_IO 1
+#define IOM_TIME 1
#define IOM_CHECK 1
-#define IOM_SIG 1
+#define IOM_SIG 1
+#define IOM_CHILD 1
#endif
#endif
termwin_ev (this, &rxvt_term::x_cb),
vt_ev (this, &rxvt_term::x_cb),
+ child_ev (this, &rxvt_term::child_cb),
check_ev (this, &rxvt_term::check_cb),
flush_ev (this, &rxvt_term::flush_cb),
destroy_ev (this, &rxvt_term::destroy_cb),
#endif
}
+// child has exited, usually destroys
void
-rxvt_term::child_exit ()
+rxvt_term::child_cb (child_watcher &w, int status)
{
+ HOOK_INVOKE ((this, HOOK_CHILD_EXIT, DT_INT, status, DT_END));
+
cmd_pid = 0;
if (!OPTION (Opt_hold))
static struct sig_handlers
{
- sig_watcher sw_chld, sw_term, sw_int;
+ sig_watcher sw_term, sw_int;
- void sig_chld (sig_watcher &w)
- {
- // we are being called for every SIGCHLD, find the corresponding term
- int pid;
-
- while ((pid = waitpid (-1, NULL, WNOHANG)) > 0)
- for (rxvt_term **t = rxvt_term::termlist.begin (); t < rxvt_term::termlist.end (); t++)
- if (pid == (*t)->cmd_pid)
- {
- (*t)->child_exit ();
- break;
- }
- }
-
/*
* Catch a fatal signal and tidy up before quitting
*/
}
sig_handlers ()
- : sw_chld (this, &sig_handlers::sig_chld),
- sw_term (this, &sig_handlers::sig_term),
+ : sw_term (this, &sig_handlers::sig_term),
sw_int (this, &sig_handlers::sig_term)
{
}
signal (SIGHUP, SIG_IGN);
signal (SIGPIPE, SIG_IGN);
- sig_handlers.sw_chld.start (SIGCHLD);
sig_handlers.sw_term.start (SIGTERM);
sig_handlers.sw_int.start (SIGINT);
if ($event->{button} == 2 && ($event->{state} & $mask) == 0) {
while ($text =~ /$url/g) {
if ($-[1] <= $col && $+[1] >= $col) {
- urxvt::exec_async $self->{browser}, $1;
+ $self->exec_async ($self->{browser}, $1);
return 1;
}
}
for (my $idx = 0; defined (my $res = $self->x_resource ("selection.pattern-$idx")); $idx++) {
no re 'eval'; # just to be sure
- $res = utf8::encode $self->locale_decode ($res);
+ $res = $self->locale_decode ($res);
+ utf8::encode $res;
push @{ $self->{patterns} }, qr/$res/;
}
and $add_button->("shell quote" => sub { $_ = "\Q$_" });
/^(http|ftp|telnet|irc|news):\//
- and $add_button->("run $self->{browser}" => sub { urxvt::exec_async $self->{browser}, $_ });
+ and $add_button->("run $self->{browser}" => sub { $self->exec_async ($self->{browser}, $_) });
for my $hook (@hook) {
if (my ($title, $cb) = $hook->($popup)) {
xevent_watcher scrollbar_ev;
#endif
+ void child_cb (child_watcher &w, int status); child_watcher child_ev;
void check_cb (check_watcher &w); check_watcher check_ev;
void destroy_cb (time_watcher &w); time_watcher destroy_ev;
void flush_cb (time_watcher &w); time_watcher flush_ev;
rxvt_term ();
~rxvt_term ();
- void child_exit (); // child has exited, usually destroys
void destroy ();
void emergency_cleanup ();
And this example matches the same,but replaces it with vi-commands you can
paste directly into your (vi :) editor:
- URxvt.selection-autotransform.0: s/^([^:[:space:]]+(\\d+):?$/\\x1b:e \\Q$1\\E\\x0d:$2\\x0d/
+ URxvt.selection-autotransform.0: s/^([^:[:space:]]+(\\d+):?$/:e \\Q$1\\E\\x0d:$2\\x0d/
Of course, this can be modified to suit your needs and your editor :)
To expand the example above to typical perl error messages ("XXX at
FILENAME line YYY."), you need a slightly more elaborate solution:
- URxvt.selection.pattern-0: ( at .*? line \\d+\\.)
- URxvt.selection-autotransform.0: s/^ at (.*?) line (\\d+)\\.$/\x1b:e \\Q$1\E\\x0d:$2\\x0d/
+ URxvt.selection.pattern-0: ( at .*? line \\d+[,.])
+ URxvt.selection-autotransform.0: s/^ at (.*?) line (\\d+)[,.]$/:e \\Q$1\E\\x0d:$2\\x0d/
The first line tells the selection code to treat the unchanging part of
every error message as a selection pattern, and the second line transforms
Called at the very end of initialisation of a new terminal, just before
returning to the mainloop.
+=item on_child_start $term, $pid
+
+Called just after the child process has been C<fork>ed.
+
+=item on_child_exit $term, $status
+
+Called just after the child process has exited. C<$status> is the status
+from C<waitpid>.
+
=item on_sel_make $term, $eventtime
Called whenever a selection has been made by the user, but before the
$retval
}
-sub exec_async(@) {
- my $pid = fork;
-
- return
- if !defined $pid or $pid;
-
- %ENV = %{ $TERM->env };
-
- exec @_;
- _exit 255;
-}
-
# urxvt::term::extension
package urxvt::term::extension;
etc.). Please note that @@RXVT_NAME@@ will not exit as long as any event
watchers (timers, io watchers) are still active.
+=item $term->exec_async ($cmd[, @args])
+
+Works like the combination of the C<fork>/C<exec> builtins, which executes
+("starts") programs in the background. This function takes care of setting
+the user environment before exec'ing the command (e.g. C<PATH>) and should
+be preferred over explicit calls to C<exec> or C<system>.
+
+Returns the pid of the subprocess or C<undef> on error.
+
+=cut
+
+sub exec_async {
+ my $self = shift;
+
+ my $pid = fork;
+
+ return $pid
+ if !defined $pid or $pid;
+
+ %ENV = %{ $self->env };
+
+ exec @_;
+ urxvt::_exit 255;
+}
+
=item $isset = $term->option ($optval[, $set])
Returns true if the option specified by C<$optval> is enabled, and