summaryrefslogtreecommitdiffstats
path: root/src/perl/readline
blob: b22677f7400fda8933d0752109df1a1261c71197 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#! perl

=head1 NAME

readline - improve readline editing (enabled by default)

=head1 DESCRIPTION

A support package that tries to make editing with readline easier. At
the moment, it reacts to clicking shift-left mouse button by trying to
move the text cursor to this position. It does so by generating as many
cursor-left or cursor-right keypresses as required (this only works
for programs that correctly support wide characters).

To avoid too many false positives, this is only done when:

=over 4

=item - the tty is in ICANON state.

=item - the text cursor is visible.

=item - the primary screen is currently being displayed.

=item - the mouse is on the same (multi-row-) line as the text cursor.

=back

The normal selection mechanism isn't disabled, so quick successive clicks
might interfere with selection creation in harmless ways.

=cut

use POSIX ();

my $termios = new POSIX::Termios;

sub on_init {
   my ($self) = @_;

   $self->{enabled} = 1;

   push @{ $self->{term}{option_popup_hook} }, sub {
      ("readline" => $self->{enabled}, sub { $self->{enabled} = shift })
   };

   ()
}

sub on_button_press {
   my ($self, $event) = @_;

   $self->current_screen || $self->hidden_cursor || !$self->{enabled}
      and return;

   my $mask = $self->ModLevel3Mask | $self->ModMetaMask
            | urxvt::ShiftMask | urxvt::ControlMask;

   ($event->{state} & $mask) == urxvt::ShiftMask
      or return;

   $termios->getattr ($self->pty_fd)
      or return;

   $termios->getlflag & &POSIX::ICANON
      and return;

   my ($row, $col) = $self->screen_cur;
   my $line = $self->line ($row);
   my $cur = $line->offset_of ($row, $col);
   my $ofs = $line->offset_of ($event->{row}, $event->{col});

   $ofs >= 0 && $ofs < $line->l
      or return;

   my $diff = $ofs - $cur;
   my $move;

   if ($diff < 0) {
      ($ofs, $cur) = ($cur, $ofs);
      $move = "\x1b[D";
   } else {
      $move = "\x1b[C";
   }

   my $skipped = substr $line->t, $cur, $ofs - $cur;
   $skipped =~ s/\x{ffff}//g;

   $self->tt_write ($move x length $skipped);

   1
}