}
}
- if (HOOK_INVOKE ((this, HOOK_KEY_PRESS, DT_XEVENT, &ev, DT_STR_LEN, kbuf, len, DT_END)))
+ if (HOOK_INVOKE ((this, HOOK_KEY_PRESS, DT_XEVENT, &ev, DT_INT, keysym, DT_STR_LEN, kbuf, len, DT_END)))
return;
if (len <= 0)
case KeyRelease:
{
-#if (MOUSE_WHEEL && MOUSE_SLIP_WHEELING) || ISO_14755
- KeySym ks;
+#if (MOUSE_WHEEL && MOUSE_SLIP_WHEELING) || ISO_14755 || ENABLE_PERL
+ KeySym keysym;
- ks = XLookupKeysym (&ev.xkey, ev.xkey.state & ShiftMask ? 1 : 0); // sorry, only shift supported :/
+ keysym = XLookupKeysym (&ev.xkey, ev.xkey.state & ShiftMask ? 1 : 0); // sorry, only shift supported :/
#endif
#if ENABLE_FRILLS || ISO_14755
// iso14755 part 5.2 handling: release time
// first: controls
if ((ev.xkey.state & ControlMask)
- && ((ks >= 0x40 && ks <= 0x5f)
- || (ks >= 0x61 && ks <= 0x7f)))
+ && ((keysym >= 0x40 && keysym <= 0x5f)
+ || (keysym >= 0x61 && keysym <= 0x7f)))
{
- iso14755buf = ISO_14755_51 | 0x2400 | (ks & 0x1f);
+ iso14755buf = ISO_14755_51 | 0x2400 | (keysym & 0x1f);
commit_iso14755 ();
goto skip_switch;
}
for (unsigned short *i = iso14755_symtab; i[0]; i+= 2)
- if (i[0] == ks)
+ if (i[0] == keysym)
{
iso14755buf = ISO_14755_51 | i[1];
commit_iso14755 ();
#endif
if (ev.xany.window == vt
- && HOOK_INVOKE ((this, HOOK_KEY_RELEASE, DT_XEVENT, &ev, DT_END)))
+ && HOOK_INVOKE ((this, HOOK_KEY_RELEASE, DT_XEVENT, &ev, DT_INT, keysym, DT_END)))
break;
#if defined(MOUSE_WHEEL) && defined(MOUSE_SLIP_WHEELING)
if (!(ev.xkey.state & ControlMask))
slip_wheel_ev.stop ();
- else if (ks == XK_Control_L || ks == XK_Control_R)
+ else if (keysym == XK_Control_L || keysym == XK_Control_R)
mouse_slip_wheel_speed = 0;
#endif
break;
/*
* Enable the keysym resource which allows you to define strings associated
* with various KeySyms (0xFF00 - 0xFFFF).
- * Only works with the default hand-rolled resources.
+ * Required by perl.
*/
-#if !NO_RESOURCES && ENABLE_FRILLS
+#if (!NO_RESOURCES && ENABLE_FRILLS) || ENABLE_PERL
# define KEYSYM_RESOURCE
#endif
};
#if IOM_IO
-enum { EVENT_READ = 1, EVENT_WRITE = 2 };
+enum { EVENT_UNDEF = -1, EVENT_NONE = 0, EVENT_READ = 1, EVENT_WRITE = 2 };
struct io_watcher : watcher, callback2<void, io_watcher &, short> {
int fd;
keysym_t keyboard_manager::stock_keymap[] = {
/* examples */
/* keysym, state, range, handler, str */
-//{XK_ISO_Left_Tab, 0, 1, keysym_t::NORMAL, "\033[Z"},
+//{XK_ISO_Left_Tab, 0, 1, keysym_t::STRING, "\033[Z"},
//{ 'a', 0, 26, keysym_t::RANGE_META8, "a" "%c"},
//{ 'a', ControlMask, 26, keysym_t::RANGE_META8, "\ 1" "%c"},
//{ XK_Left, 0, 4, keysym_t::LIST, ".\033[.DACB."},
//{ XK_Left, ShiftMask, 4, keysym_t::LIST, ".\033[.dacb."},
//{ XK_Left, ControlMask, 4, keysym_t::LIST, ".\033O.dacb."},
-//{ XK_Tab, ControlMask, 1, keysym_t::NORMAL, "\033<C-Tab>"},
-//{ XK_apostrophe, ControlMask, 1, keysym_t::NORMAL, "\033<C-'>"},
-//{ XK_slash, ControlMask, 1, keysym_t::NORMAL, "\033<C-/>"},
-//{ XK_semicolon, ControlMask, 1, keysym_t::NORMAL, "\033<C-;>"},
-//{ XK_grave, ControlMask, 1, keysym_t::NORMAL, "\033<C-`>"},
-//{ XK_comma, ControlMask, 1, keysym_t::NORMAL, "\033<C-\054>"},
-//{ XK_Return, ControlMask, 1, keysym_t::NORMAL, "\033<C-Return>"},
-//{ XK_Return, ShiftMask, 1, keysym_t::NORMAL, "\033<S-Return>"},
-//{ ' ', ShiftMask, 1, keysym_t::NORMAL, "\033<S-Space>"},
-//{ '.', ControlMask, 1, keysym_t::NORMAL, "\033<C-.>"},
+//{ XK_Tab, ControlMask, 1, keysym_t::STRING, "\033<C-Tab>"},
+//{ XK_apostrophe, ControlMask, 1, keysym_t::STRING, "\033<C-'>"},
+//{ XK_slash, ControlMask, 1, keysym_t::STRING, "\033<C-/>"},
+//{ XK_semicolon, ControlMask, 1, keysym_t::STRING, "\033<C-;>"},
+//{ XK_grave, ControlMask, 1, keysym_t::STRING, "\033<C-`>"},
+//{ XK_comma, ControlMask, 1, keysym_t::STRING, "\033<C-\054>"},
+//{ XK_Return, ControlMask, 1, keysym_t::STRING, "\033<C-Return>"},
+//{ XK_Return, ShiftMask, 1, keysym_t::STRING, "\033<S-Return>"},
+//{ ' ', ShiftMask, 1, keysym_t::STRING, "\033<S-Space>"},
+//{ '.', ControlMask, 1, keysym_t::STRING, "\033<C-.>"},
//{ '0', ControlMask, 10, keysym_t::RANGE, "0" "\033<C-%c>"},
//{ '0', MetaMask|ControlMask, 10, keysym_t::RANGE, "0" "\033<M-C-%c>"},
//{ 'a', MetaMask|ControlMask, 26, keysym_t::RANGE, "a" "\033<M-C-%c>"},
key->state = state;
key->range = 1;
key->str = translation;
- key->type = keysym_t::NORMAL;
+ key->type = keysym_t::STRING;
if (strncmp (translation, "list", 4) == 0 && translation [4])
{
switch (key.type)
{
- case keysym_t::NORMAL:
+ case keysym_t::STRING:
output_string (term, str);
break;
struct keysym_t
{
enum keysym_type {
- NORMAL, RANGE, RANGE_META8, LIST, BUILTIN,
+ STRING, RANGE, RANGE_META8, LIST, BUILTIN,
};
KeySym keysym;
--- /dev/null
+#! perl
+
+# this extension implements scrollback buffer search
+
+sub on_init {
+ my ($self) = @_;
+
+ my $hotkey = $self->{argv}[0] || "M-s";
+
+ $self->parse_keysym ($hotkey, "perl:searchable-scrollback:enter")
+ or warn "unable to register '$hotkey' as scrollback search enter hotkey\n";
+
+ ()
+}
+
+sub on_keyboard_command {
+ my ($self, $cmd) = @_;
+
+ if ($cmd eq "searchable-scrollback:enter") {
+ $self->enter;
+ }
+
+ ()
+}
+
+sub msg {
+ my ($self, $msg) = @_;
+
+ $self->{overlay} = $self->overlay (0, -1, $self->ncol, 1, urxvt::OVERLAY_RSTYLE, 0);
+ $self->{overlay}->set (0, 0, $self->special_encode ($msg));
+}
+
+sub enter {
+ my ($self) = @_;
+
+ return if $self->{overlay};
+
+ $self->{pty_ev_events} = $self->pty_ev_events (urxvt::EVENT_NONE);
+ $self->{view_start} = $self->view_start;
+
+ $self->enable (
+ refresh_begin => sub {
+ warn "beg(@_)\n";
+ },
+ refresh_end => sub {
+ warn "end(@_)\n";
+ },
+ );
+
+ $self->idle;
+}
+
+sub leave {
+ my ($self) = @_;
+
+ delete $self->{overlay};
+
+ $self->disable ("refresh_begin", "refresh_end");
+
+ $self->pty_ev_events ($self->{pty_ev_events});
+ $self->want_refresh;
+}
+
+sub idle {
+ my ($self) = @_;
+
+ $self->msg ("scrollback search, escape=exit, enter=accept, /=start search, n=next, p=previous");
+
+ delete $self->{in_search};
+}
+
+sub search {
+ my ($self) = @_;
+
+ my $row = -$self->view_start;
+
+ delete $self->{found};
+
+ no re 'eval'; # just to be sure
+ my $re = qr/$self->{search}/;
+
+ while ($row > -$self->nsaved) {
+ my $line = $self->line ($row)
+ or last;
+
+ my $text = $line->t;
+ if ($text =~ /$re/g) {
+ do {
+ push @{ $self->{found} }, [$line->coord_of ($-[0]), $line->coord_of ($+[1])];
+ } while $text =~ /$re/g;
+
+ $self->view_start (-$row + ($self->nrow >> 1));
+ last;
+ }
+
+ $row = $line->beg - 1;
+ }
+
+ $self->msg ("enter/type/backspace: /$self->{search}_"
+ . ($self->{found} ? " (not found)" : ""));
+ $self->scr_bell unless $self->{found};
+}
+
+sub on_key_press {
+ my ($self, $event, $keysym, $string) = @_;
+
+ $self->{overlay}
+ or return;
+
+ if (exists $self->{in_search}) {
+ if ($keysym == 0xff0d || $keysym == 0xff8d) {
+ delete $self->{in_search};
+ } elsif ($keysym == 0xff1b) {
+ $self->view_start (delete $self->{in_search});
+ } elsif ($keysym == 0xff08) {
+ substr $self->{search}, -1, 1, "";
+ } elsif ($string ne "") {
+ $self->{search} .= $string;
+ }
+
+ $self->search;
+ } else {
+ if ($keysym == 0xff0d || $keysym == 0xff8d) {
+ # OK
+ $self->leave;
+ } elsif ($keysym == 0xff1b) {
+ $self->view_start ($self->{view_start});
+ $self->leave;
+ } elsif ($keysym == 0xff52) {
+ $self->view_start ($self->view_start + 1);
+ } elsif ($keysym == 0xff54) {
+ $self->view_start ($self->view_start - 1);
+ } elsif ($string eq "/") {
+ $self->{in_search} = $self->view_start;
+ $self->search;
+ } elsif ($string eq "n") {
+ } elsif ($string eq "p") {
+ } elsif ($string ne "") {
+ $self->scr_bell;
+ }
+ }
+
+ 1
+}
+
+
#include <cstddef>
#include <cstdarg>
-#include "rxvt.h"
#include "iom.h"
+#include "rxvt.h"
+#include "keyboard.h"
#include "rxvtutil.h"
#include "rxvtperl.h"
{
// handled later
}
- else if (htype == HOOK_REFRESH_BEGIN || htype == HOOK_REFRESH_END)
+ else
{
- HV *hv = (HV *)SvRV (*hv_fetch ((HV *)SvRV ((SV *)term->perl.self), "_overlay", 8, 0));
-
- if (HvKEYS (hv))
+ if (htype == HOOK_REFRESH_BEGIN || htype == HOOK_REFRESH_END)
{
- hv_iterinit (hv);
+ HV *hv = (HV *)SvRV (*hv_fetch ((HV *)SvRV ((SV *)term->perl.self), "_overlay", 8, 0));
+
+ if (HvKEYS (hv))
+ {
+ hv_iterinit (hv);
+
+ while (HE *he = hv_iternext (hv))
+ ((overlay *)SvIV (hv_iterval (hv, he)))->swap ();
+ }
- while (HE *he = hv_iternext (hv))
- ((overlay *)SvIV (hv_iterval (hv, he)))->swap ();
}
+
+ if (!should_invoke [htype])
+ return false;
}
- else if (!should_invoke [htype])
- return false;
dSP;
va_list ap;
export_const_iv (Button4Mask);
export_const_iv (Button5Mask);
export_const_iv (AnyModifier);
+
+ export_const_iv (EVENT_NONE);
+ export_const_iv (EVENT_READ);
+ export_const_iv (EVENT_WRITE);
}
SV *
OUTPUT:
RETVAL
+int
+rxvt_term::pty_ev_events (int events = EVENT_UNDEF)
+ CODE:
+ RETVAL = THIS->pty_ev.events;
+ if (events != EVENT_UNDEF)
+ THIS->pty_ev.set (events);
+ OUTPUT:
+ RETVAL
+
U32
rxvt_term::parent ()
CODE:
OUTPUT:
RETVAL
+bool
+rxvt_term::parse_keysym (char *keysym, char *str)
+ CODE:
+ RETVAL = 0 < THIS->parse_keysym (keysym, str);
+ THIS->keyboard->register_done ();
+ OUTPUT:
+ RETVAL
+
void
rxvt_term::screen_cur (...)
PROTOTYPE: $;$$
}
void
+rxvt_term::scr_bell ()
+
+void
rxvt_term::scr_add_lines (SV *string)
CODE:
{
if (OPTION (Opt_visualBell))
{
scr_rvideo_mode (!rvideo); /* refresh also done */
+ display->flush ();
rxvt_usleep (VISUAL_BELL_DURATION);
scr_rvideo_mode (!rvideo); /* refresh also done */
}
Binds a popup menu to Ctrl-Button3 that lets you convert the selection
text into various other formats/action.
+=item searchable-scrollback (enabled by default)
+
+Adds regex search functionality to the scrollback buffer, triggered by a
+hotkey (default: C<M-s>). When in search mode, terminal input/output is
+suspended, C</> starts an incremental regex search, C<n> searches further,
+C<p> jumps to the previous match. C<enter> leaves search mode at the
+current position and C<escape> returns to the original position.
+
=item digital-clock
Displays a digital clock using the built-in overlay.
=back
+=head2 Extension Objects
+
+Very perl extension is a perl class. A separate perl object is created
+for each terminal and each extension and passed as the first parameter to
+hooks. So extensions can use their C<$self> object without having to think
+about other extensions, with the exception of methods and members that
+begin with an underscore character C<_>: these are reserved for internal
+use.
+
+Although it isn't a C<urxvt::term> object, you can call all methods of the
+C<urxvt::term> class on this object.
+
+It has the following methods and data members:
+
+=over 4
+
+=item $urxvt_term = $self->{term}
+
+Returns the C<urxvt::term> object associated with this instance of the
+extension. This member I<must not> be changed in any way.
+
+=item $self->enable ($hook_name => $cb, [$hook_name => $cb..])
+
+Dynamically enable the given hooks (named without the C<on_> prefix) for
+this extension, replacing any previous hook. This is useful when you want
+to overwrite time-critical hooks only temporarily.
+
+=item $self->disable ($hook_name[, $hook_name..])
+
+Dynamically disable the given hooks.
+
+=back
+
=head2 Hooks
The following subroutines can be declared in extension files, and will be
called whenever the relevant event happens.
-The first argument passed to them is an object private to each terminal
-and extension package. You can call all C<urxvt::term> methods on it, but
-its not a real C<urxvt::term> object. Instead, the real C<urxvt::term>
-object that is shared between all packages is stored in the C<term>
-member. It is, however, blessed intot he package of the extension script,
-so for all practical purposes you can treat an extension script as a class.
+The first argument passed to them is an extension oject as described in
+the in the C<Extension Objects> section.
-All of them must return a boolean value. If it is true, then the event
-counts as being I<consumed>, and the invocation of other hooks is skipped,
-and the relevant action might not be carried out by the C++ code.
+B<All> of these hooks must return a boolean value. If it is true, then the
+event counts as being I<consumed>, and the invocation of other hooks is
+skipped, and the relevant action might not be carried out by the C++ code.
-When in doubt, return a false value (preferably C<()>).
+I<< When in doubt, return a false value (preferably C<()>). >>
=over 4
Called wheneever the window loses keyboard focus, before rxvt-unicode does
focus out processing.
-=item on_key_press $term, $event, $octets
+=item on_key_press $term, $event, $keysym, $octets
-=item on_key_release $term, $event
+=item on_key_release $term, $event, $keysym
=item on_button_press $term, $event
use utf8;
use strict;
+use Carp ();
use Scalar::Util ();
use List::Util ();
our $VERSION = 1;
our $TERM;
our @HOOKNAME;
+our %HOOKTYPE = map +($HOOKNAME[$_] => $_), 0..$#HOOKNAME;
our %OPTION;
our $LIBDIR;
open my $fh, "<:raw", $path
or die "$path: $!";
- my $source = untaint "package $pkg; use strict; use utf8;\n"
- . "use base urxvt::term::proxy::;\n"
- . "#line 1 \"$path\"\n{\n"
- . (do { local $/; <$fh> })
- . "\n};\n1";
+ my $source = untaint
+ "package $pkg; use strict; use utf8;\n"
+ . "use base urxvt::term::proxy::;\n"
+ . "#line 1 \"$path\"\n{\n"
+ . (do { local $/; <$fh> })
+ . "\n};\n1";
- eval $source or die "$path: $@";
+ eval $source
+ or die "$path: $@";
$pkg
}
for (map { split /,/, $TERM->resource ("perl_ext_$_") } 1, 2) {
if ($_ eq "default") {
- $ext_arg{$_} ||= [] for qw(selection option-popup selection-popup);
+ $ext_arg{$_} ||= [] for qw(selection option-popup selection-popup searchable-scrollback);
} elsif (/^-(.*)$/) {
delete $ext_arg{$1};
} elsif (/^([^<]+)<(.*)>$/) {
}
if ($htype == 1) { # DESTROY
- # remove hooks if unused
- if (my $hook = $TERM->{_hook}) {
+ if (my $hook = delete $TERM->{_hook}) {
for my $htype (0..$#$hook) {
$hook_count[$htype] -= scalar keys %{ $hook->[$htype] || {} }
or set_should_invoke $htype, 0;
# urxvt::term::proxy
-sub urxvt::term::proxy::AUTOLOAD {
- $urxvt::term::proxy::AUTOLOAD =~ /:([^:]+)$/
- or die "FATAL: \$AUTOLOAD '$urxvt::term::proxy::AUTOLOAD' unparsable";
+package urxvt::term::proxy;
+
+sub enable {
+ my ($self, %hook) = @_;
+ my $pkg = $self->{_pkg};
+
+ while (my ($name, $cb) = each %hook) {
+ my $htype = $HOOKTYPE{uc $name};
+ defined $htype
+ or Carp::croak "unsupported hook type '$name'";
+
+ unless (exists $self->{term}{_hook}[$htype]{$pkg}) {
+ $hook_count[$htype]++
+ or urxvt::set_should_invoke $htype, 1;
+ }
+
+ $self->{term}{_hook}[$htype]{$pkg} = $cb;
+ }
+}
+
+sub disable {
+ my ($self, @hook) = @_;
+ my $pkg = $self->{_pkg};
+
+ for my $name (@hook) {
+ my $htype = $HOOKTYPE{uc $name};
+ defined $htype
+ or Carp::croak "unsupported hook type '$name'";
+
+ if (delete $self->{term}{_hook}[$htype]{$pkg}) {
+ --$hook_count[$htype]
+ or urxvt::set_should_invoke $htype, 0;
+ }
+ }
+}
+
+our $AUTOLOAD;
+
+sub AUTOLOAD {
+ $AUTOLOAD =~ /:([^:]+)$/
+ or die "FATAL: \$AUTOLOAD '$AUTOLOAD' unparsable";
eval qq{
- sub $urxvt::term::proxy::AUTOLOAD {
+ sub $AUTOLOAD {
my \$proxy = shift;
\$proxy->{term}->$1 (\@_)
}
1
} or die "FATAL: unable to compile method forwarder: $@";
- goto &$urxvt::term::proxy::AUTOLOAD;
+ goto &$AUTOLOAD;
}
-sub urxvt::term::proxy::DESTROY {
+sub DESTROY {
# nop
}
sub urxvt::anyevent::condvar::wait {
unless (${$_[0]}) {
- require Carp;
- Carp::croak ("AnyEvent->condvar blocking wait unsupported in urxvt, use a non-blocking API");
+ Carp::croak "AnyEvent->condvar blocking wait unsupported in urxvt, use a non-blocking API";
}
}
sub register_package {
my ($self, $pkg, $argv) = @_;
- my $proxy = bless { argv => $argv }, $pkg;
- Scalar::Util::weaken ($proxy->{term} = $TERM);
+ my $proxy = bless {
+ _pkg => $pkg,
+ argv => $argv,
+ }, $pkg;
+ Scalar::Util::weaken ($proxy->{term} = $self);
$self->{_pkg}{$pkg} = $proxy;
- for my $htype (0.. $#HOOKNAME) {
- my $name = $HOOKNAME[$htype];
-
- my $ref = $pkg->can ("on_" . lc $name)
- or next;
-
- $self->{_hook}[$htype]{$pkg} = $ref;
- $hook_count[$htype]++
- or urxvt::set_should_invoke $htype, 1;
+ for my $name (@HOOKNAME) {
+ if (my $ref = $pkg->can ("on_" . lc $name)) {
+ $proxy->enable ($name => $ref);
+ }
}
}
&urxvt::term::_resource
}
+=item $success = $term->parse_keysym ($keysym_spec, $command_string)
+
+Adds a keymap translation exactly as specified via a resource. See the
+C<keysym> resource in the @@RXVT_NAME@@(1) manpage.
+
=item $rend = $term->rstyle ([$new_rstyle])
Return and optionally change the current rendition. Text that is output by
Return the current selection text and optionally replace it by C<$newtext>.
-#=item $term->overlay ($x, $y, $text)
-#
-#Create a simple multi-line overlay box. See the next method for details.
-#
-#=cut
-#
-#sub urxvt::term::scr_overlay {
-# my ($self, $x, $y, $text) = @_;
-#
-# my @lines = split /\n/, $text;
-#
-# my $w = 0;
-# for (map $self->strwidth ($_), @lines) {
-# $w = $_ if $w < $_;
-# }
-#
-# $self->scr_overlay_new ($x, $y, $w, scalar @lines);
-# $self->scr_overlay_set (0, $_, $lines[$_]) for 0.. $#lines;
-#}
+=item $term->overlay_simple ($x, $y, $text)
+
+Create a simple multi-line overlay box. See the next method for details.
+
+=cut
+
+sub overlay_simple {
+ my ($self, $x, $y, $text) = @_;
+
+ my @lines = split /\n/, $text;
+
+ my $w = List::Util::max map $self->strwidth ($_), @lines;
+
+ my $overlay = $self->overlay ($x, $y, $w, scalar @lines);
+ $overlay->set (0, $_, $lines[$_]) for 0.. $#lines;
+
+ $overlay
+}
=item $term->overlay ($x, $y, $width, $height[, $rstyle[, $border]])
Convert the given locale-encoded octets into a perl string.
+=item $term->scr_bell
+
+Ring the bell!
+
=item $term->scr_add_lines ($string)
Write the given text string to the screen, as if output by the application
pass characters instead of octets, you should convert your strings first
to the locale-specific encoding using C<< $term->locale_encode >>.
+=item $old_events = $term->pty_ev_events ([$new_events])
+
+Replaces the event mask of the pty watcher by the given event mask. Can
+be used to suppress input and output handling to the pty/tty. See the
+description of C<< urxvt::timer->events >>. Make sure to always restore
+the previous value.
+
=item $windowid = $term->parent
Return the window id of the toplevel window.
$term->{iow} = urxvt::iow
->new
->fd (fileno $term->{socket})
- ->events (1) # wait for read data
+ ->events (urxvt::EVENT_READ)
->start
->cb (sub {
my ($iow, $revents) = @_;
=item $iow = $iow->events ($eventmask)
-Set the event mask to watch. Bit #0 (value C<1>) enables watching for read
-data, Bit #1 (value C<2>) enables watching for write data.
+Set the event mask to watch. The only allowed values are
+C<urxvt::EVENT_READ> and C<urxvt::EVENT_WRITE>, which might be ORed
+together, or C<urxvt::EVENT_NONE>.
=item $iow = $iow->start
return 0;
str += n; /* skip `keysym.' */
- if ((pmodend = strchr (str, ':')) < str)
+ if (!(pmodend = strchr (str, ':')))
return -1;
}
else