diff options
author | Matthew Lemon <lemon@x220.lan> | 2020-09-06 16:48:05 +0100 |
---|---|---|
committer | Matthew Lemon <lemon@x220.lan> | 2020-09-06 16:48:05 +0100 |
commit | e4640e28c8449eea66392052bfab57ce0cdd27f9 (patch) | |
tree | 2be81b55bc37da60599f4e6d434e74a57694962e /urxvt |
first commit
Diffstat (limited to 'urxvt')
-rw-r--r-- | urxvt/ext/clipboard | 117 | ||||
-rw-r--r-- | urxvt/ext/font-size | 471 | ||||
-rw-r--r-- | urxvt/ext/fullscreen | 33 | ||||
-rw-r--r-- | urxvt/ext/keyboard-select | 598 | ||||
-rw-r--r-- | urxvt/ext/url-select | 410 | ||||
-rw-r--r-- | urxvt/ext/vtwheel | 49 |
6 files changed, 1678 insertions, 0 deletions
diff --git a/urxvt/ext/clipboard b/urxvt/ext/clipboard new file mode 100644 index 0000000..e5d2322 --- /dev/null +++ b/urxvt/ext/clipboard @@ -0,0 +1,117 @@ +#!/usr/bin/perl +# Author: Bert Muennich +# Website: http://www.github.com/muennich/urxvt-perls +# License: GPLv2 + +# Use keyboard shortcuts to copy the selection to the clipboard and to paste +# the clipboard contents (optionally escaping all special characters). +# Requires xsel to be installed! + +# Usage: put the following lines in your .Xdefaults/.Xresources: +# URxvt.perl-ext-common: ...,clipboard +# URxvt.keysym.M-c: perl:clipboard:copy +# URxvt.keysym.M-v: perl:clipboard:paste +# URxvt.keysym.M-C-v: perl:clipboard:paste_escaped + +# Options: +# URxvt.clipboard.autocopy: If true, PRIMARY overwrites clipboard + +# You can also overwrite the system commands to use for copying/pasting. +# The default ones are: +# URxvt.clipboard.copycmd: xsel -ib +# URxvt.clipboard.pastecmd: xsel -ob +# If you prefer xclip, then put these lines in your .Xdefaults/.Xresources: +# URxvt.clipboard.copycmd: xclip -i -selection clipboard +# URxvt.clipboard.pastecmd: xclip -o -selection clipboard +# On Mac OS X, put these lines in your .Xdefaults/.Xresources: +# URxvt.clipboard.copycmd: pbcopy +# URxvt.clipboard.pastecmd: pbpaste + +# The use of the functions should be self-explanatory! + + +use strict; +use warnings; + +sub on_start { + my ($self) = @_; + + $self->{copy_cmd} = $self->x_resource('clipboard.copycmd') || 'xsel -ib'; + $self->{paste_cmd} = $self->x_resource('clipboard.pastecmd') || 'xsel -ob'; + + if ($self->x_resource('clipboard.autocopy') eq 'true') { + $self->enable(sel_grab => \&sel_grab); + } + + () +} + +sub copy { + my ($self) = @_; + + if (open(CLIPBOARD, "| $self->{copy_cmd}")) { + my $sel = $self->selection(); + utf8::encode($sel); + print CLIPBOARD $sel; + close(CLIPBOARD); + } else { + print STDERR "error running '$self->{copy_cmd}': $!\n"; + } + + () +} + +sub paste { + my ($self) = @_; + + my $str = `$self->{paste_cmd}`; + if ($? == 0) { + $self->tt_paste($str); + } else { + print STDERR "error running '$self->{paste_cmd}': $!\n"; + } + + () +} + +sub paste_escaped { + my ($self) = @_; + + my $str = `$self->{paste_cmd}`; + if ($? == 0) { + $str =~ s/([!#\$%&\*\(\) ='"\\\|\[\]`~,<>\?])/\\$1/g; + $self->tt_paste($str); + } else { + print STDERR "error running '$self->{paste_cmd}': $!\n"; + } + + () +} + +sub on_action { + my ($self, $action) = @_; + + on_user_command($self, "clipboard:" . $action); +} + +sub on_user_command { + my ($self, $cmd) = @_; + + if ($cmd eq "clipboard:copy") { + $self->copy; + } elsif ($cmd eq "clipboard:paste") { + $self->paste; + } elsif ($cmd eq "clipboard:paste_escaped") { + $self->paste_escaped; + } + + () +} + +sub sel_grab { + my ($self) = @_; + + $self->copy; + + () +} diff --git a/urxvt/ext/font-size b/urxvt/ext/font-size new file mode 100644 index 0000000..f6a33bb --- /dev/null +++ b/urxvt/ext/font-size @@ -0,0 +1,471 @@ +#!/usr/bin/perl +# +# On-the-fly adjusting of the font size in urxvt +# +# Copyright (c) 2008 David O'Neill +# 2012 Noah K. Tilton <noahktilton@gmail.com> +# 2009-2012 Simon Lundström <simmel@soy.se> +# 2012-2016 Jan Larres <jan@majutsushi.net> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# URL: https://github.com/majutsushi/urxvt-font-size +# +# Based on: +# https://github.com/dave0/urxvt-font-size +# https://github.com/noah/urxvt-font +# https://github.com/simmel/urxvt-resize-font +# + +#:META:X_RESOURCE:%.step:interger:font size increase/decrease step + +=head1 NAME + +font-size - interactive font size setter + +=head1 USAGE + +Put the font-size script into $HOME/.urxvt/ext/ and add it to the list +of enabled perl-extensions in ~/.Xresources: + + URxvt.perl-ext-common: ...,font-size + +Add some keybindings: + + URxvt.keysym.C-Up: font-size:increase + URxvt.keysym.C-Down: font-size:decrease + URxvt.keysym.C-S-Up: font-size:incglobal + URxvt.keysym.C-S-Down: font-size:decglobal + URxvt.keysym.C-equal: font-size:reset + URxvt.keysym.C-slash: font-size:show + +Note that for urxvt versions older than 9.21 the resources have to look like this: + + URxvt.keysym.C-Up: perl:font-size:increase + URxvt.keysym.C-Down: perl:font-size:decrease + URxvt.keysym.C-S-Up: perl:font-size:incglobal + URxvt.keysym.C-S-Down: perl:font-size:decglobal + URxvt.keysym.C-equal: perl:font-size:reset + URxvt.keysym.C-slash: perl:font-size:show + +Supported functions: + +=over 2 + +=item * increase/decrease: + + increase or decrease the font size of the current terminal. + +=item * incglobal/decglobal: + + same as above and also adjust the X server values so all newly + started terminals will use the same fontsize. + +=item * incsave/decsave: + + same as incglobal/decglobal and also modify the ~/.Xresources + file so the changed font sizes will persist over a restart of + the X server or a reboot. + +=item * reset: + + reset the font size to the value of the resource when starting + the terminal. + +=item * show + + show the current value of the 'font' resource in a popup. + +=back + +You can also change the step size that the script will use to increase +the font size: + + URxvt.font-size.step: 4 + +The default step size is 1. This means that with this setting a +size change sequence would be for example 8->12->16->20 instead of +8->9->10->11->12 etc. Please note that many X11 fonts are only +available in specific sizes, though, and odd sizes are often not +available, resulting in an effective step size of 2 instead of 1 +in that case. +=cut + +use strict; +use warnings; + +my %escapecodes = ( + "font" => 710, + "boldFont" => 711, + "italicFont" => 712, + "boldItalicFont" => 713 +); + +sub on_start +{ + my ($self) = @_; + + $self->{step} = $self->x_resource("%.step") || 1; + + foreach my $type (qw(font boldFont italicFont boldItalicFont)) { + $self->{$type} = $self->x_resource($type) || "undef"; + } +} + +# Needed for backwards compatibility with < 9.21 +sub on_user_command +{ + my ($self, $cmd) = @_; + + my $step = $self->{step}; + + if ($cmd eq "font-size:increase") { + fonts_change_size($self, $step, 0); + } elsif ($cmd eq "font-size:decrease") { + fonts_change_size($self, -$step, 0); + } elsif ($cmd eq "font-size:incglobal") { + fonts_change_size($self, $step, 1); + } elsif ($cmd eq "font-size:decglobal") { + fonts_change_size($self, -$step, 1); + } elsif ($cmd eq "font-size:incsave") { + fonts_change_size($self, $step, 2); + } elsif ($cmd eq "font-size:decsave") { + fonts_change_size($self, -$step, 2); + } elsif ($cmd eq "font-size:reset") { + fonts_reset($self); + } elsif ($cmd eq "font-size:show") { + fonts_show($self); + } +} + +sub on_action +{ + my ($self, $action) = @_; + + my $step = $self->{step}; + + if ($action eq "increase") { + fonts_change_size($self, $step, 0); + } elsif ($action eq "decrease") { + fonts_change_size($self, -$step, 0); + } elsif ($action eq "incglobal") { + fonts_change_size($self, $step, 1); + } elsif ($action eq "decglobal") { + fonts_change_size($self, -$step, 1); + } elsif ($action eq "incsave") { + fonts_change_size($self, $step, 2); + } elsif ($action eq "decsave") { + fonts_change_size($self, -$step, 2); + } elsif ($action eq "reset") { + fonts_reset($self); + } elsif ($action eq "show") { + fonts_show($self); + } +} + +sub fonts_change_size +{ + my ($term, $delta, $save) = @_; + + my @newfonts = (); + + my $curres = $term->resource('font'); + if (!$curres) { + $term->scr_add_lines("\r\nWarning: No font configured, trying a default.\r\nPlease set a font with the 'URxvt.font' resource."); + $curres = "fixed"; + } + my @curfonts = split(/\s*,\s*/, $curres); + + my $basefont = shift(@curfonts); + my ($newbasefont, $newbasedelta, $newbasesize) = handle_font($term, $basefont, $delta, 0, 0); + push @newfonts, $newbasefont; + + # Only adjust other fonts if base font changed + if ($newbasefont ne $basefont) { + foreach my $font (@curfonts) { + my ($newfont, $newdelta, $newsize) = handle_font($term, $font, $delta, $newbasedelta, $newbasesize); + push @newfonts, $newfont; + } + my $newres = join(",", @newfonts); + font_apply_new($term, $newres, "font", $save); + + handle_type($term, "boldFont", $delta, $newbasedelta, $newbasesize, $save); + handle_type($term, "italicFont", $delta, $newbasedelta, $newbasesize, $save); + handle_type($term, "boldItalicFont", $delta, $newbasedelta, $newbasesize, $save); + } + + if ($save > 1) { + # write the new values back to the file + my $xresources = readlink $ENV{"HOME"} . "/.Xresources"; + system("xrdb -edit " . $xresources); + } +} + +sub fonts_reset +{ + my ($term) = @_; + + foreach my $type (qw(font boldFont italicFont boldItalicFont)) { + my $initial = $term->{$type}; + if ($initial ne "undef") { + font_apply_new($term, $initial, $type, 0); + } + } +} + +sub fonts_show +{ + my ($term) = @_; + + my $out = $term->resource('font'); + $out =~ s/\s*,\s*/\n/g; + + $term->{'font-size'}{'overlay'} = { + overlay => $term->overlay_simple(0, -1, $out), + timer => urxvt::timer->new->start(urxvt::NOW + 5)->cb( + sub { + delete $term->{'font-size'}{'overlay'}; + } + ), + }; +} + +sub handle_type +{ + my ($term, $type, $delta, $basedelta, $basesize, $save) = @_; + + my $curres = $term->resource($type); + if (!$curres) { + return; + } + my @curfonts = split(/\s*,\s*/, $curres); + my @newfonts = (); + + foreach my $font (@curfonts) { + my ($newfont, $newdelta, $newsize) = handle_font($term, $font, $delta, $basedelta, $basesize); + push @newfonts, $newfont; + } + + my $newres = join(",", @newfonts); + font_apply_new($term, $newres, $type, $save); +} + +sub handle_font +{ + my ($term, $font, $delta, $basedelta, $basesize) = @_; + + my $newfont; + my $newdelta; + my $newsize; + my $prefix = 0; + + if ($font =~ /^\s*x:/) { + $font =~ s/^\s*x://; + $prefix = 1; + } + if ($font =~ /^\s*(\[.*\])?xft:/) { + ($newfont, $newdelta, $newsize) = font_change_size_xft($term, $font, $delta, $basedelta, $basesize); + } elsif ($font =~ /^\s*-/) { + ($newfont, $newdelta, $newsize) = font_change_size_xlfd($term, $font, $delta, $basedelta, $basesize); + } else { + # check whether the font is a valid alias and if yes resolve it to the + # actual font + my $lsfinfo = `xlsfonts -l $font 2>/dev/null`; + + if ($lsfinfo eq "") { + # not a valid alias, ring the bell if it is the base font and just + # return the current font + if ($basesize == 0) { + $term->scr_bell; + } + return ($font, $basedelta, $basesize); + } + + my $fontinfo = (split(/\n/, $lsfinfo))[-1]; + my ($fontfull) = ($fontinfo =~ /\s+([-a-z0-9]+$)/); + ($newfont, $newdelta, $newsize) = font_change_size_xlfd($term, $fontfull, $delta, $basedelta, $basesize); + } + + # $term->scr_add_lines("\r\nNew font is $newfont\n"); + if ($prefix) { + $newfont = "x:$newfont"; + } + return ($newfont, $newdelta, $newsize); +} + +sub font_change_size_xft +{ + my ($term, $fontstring, $delta, $basedelta, $basesize) = @_; + + my @pieces = split(/:/, $fontstring); + my @resized = (); + my $size = 0; + my $new_size = 0; + + foreach my $piece (@pieces) { + if ($piece =~ /^(?:(?:pixel)?size=|[^=-]+-)(\d+(\.\d*)?)$/) { + $size = $1; + + if ($basedelta != 0) { + $new_size = $size + $basedelta; + } else { + $new_size = $size + $delta; + } + + $piece =~ s/(=|-)$size/$1$new_size/; + } + push @resized, $piece; + } + + my $resized_str = join(":", @resized); + + # don't make fonts too small + if ($new_size >= 6) { + return ($resized_str, $new_size - $size, $new_size); + } else { + if ($basesize == 0) { + $term->scr_bell; + } + return ($fontstring, 0, $size); + } +} + +sub font_change_size_xlfd +{ + my ($term, $fontstring, $delta, $basedelta, $basesize) = @_; + + #-xos4-terminus-medium-r-normal-*-12-*-*-*-*-*-*-1 + + my @fields = qw(foundry family weight slant setwidth style pixelSize pointSize Xresolution Yresolution spacing averageWidth registry encoding); + + my %font; + $fontstring =~ s/^-//; # Strip leading - before split + @font{@fields} = split(/-/, $fontstring); + + if ($font{pixelSize} eq '*') { + $term->scr_add_lines("\r\nWarning: Font size undefined, assuming 12.\r\nPlease set the 'URxvt.font' resource to a font with a concrete size."); + $font{pixelSize} = '12' + } + if ($font{registry} eq '*') { + $font{registry} ='iso8859'; + } + + # Blank out the size for the pattern + my %pattern = %font; + $pattern{foundry} = '*'; + $pattern{setwidth} = '*'; + $pattern{pixelSize} = '*'; + $pattern{pointSize} = '*'; + # if ($basesize != 0) { + # $pattern{Xresolution} = '*'; + # $pattern{Yresolution} = '*'; + # } + $pattern{averageWidth} = '*'; + # make sure there are no empty fields + foreach my $field (@fields) { + $pattern{$field} = '*' unless defined($pattern{$field}); + } + my $new_fontstring = '-' . join('-', @pattern{@fields}); + + my @candidates; + # $term->scr_add_lines("\r\nPattern is $new_fontstring\n"); + open(FOO, "xlsfonts -fn '$new_fontstring' | sort -u |") or die $!; + while (<FOO>) { + chomp; + s/^-//; # Strip leading '-' before split + my @fontdata = split(/-/, $_); + + push @candidates, [$fontdata[6], "-$_"]; + # $term->scr_add_lines("\r\npossibly $fontdata[6] $_\n"); + } + close(FOO); + + if (!@candidates) { + die "No possible fonts!"; + } + + if ($basesize != 0) { + # sort by font size, descending + @candidates = sort {$b->[0] <=> $a->[0]} @candidates; + + # font is not the base font, so find the largest font that is at most + # as large as the base font. If the largest possible font is smaller + # than the base font bail and hope that a 0-size font can be found at + # the end of the function + if ($candidates[0]->[0] > $basesize) { + foreach my $candidate (@candidates) { + if ($candidate->[0] <= $basesize) { + return ($candidate->[1], $candidate->[0] - $font{pixelSize}, $candidate->[0]); + } + } + } + } elsif ($delta > 0) { + # sort by font size, ascending + @candidates = sort {$a->[0] <=> $b->[0]} @candidates; + + foreach my $candidate (@candidates) { + if ($candidate->[0] >= $font{pixelSize} + $delta) { + return ($candidate->[1], $candidate->[0] - $font{pixelSize}, $candidate->[0]); + } + } + } elsif ($delta < 0) { + # sort by font size, descending + @candidates = sort {$b->[0] <=> $a->[0]} @candidates; + + foreach my $candidate (@candidates) { + if ($candidate->[0] <= $font{pixelSize} + $delta && $candidate->[0] != 0) { + return ($candidate->[1], $candidate->[0] - $font{pixelSize}, $candidate->[0]); + } + } + } + + # no fitting font available, check whether a 0-size font can be used to + # fit the size of the base font + @candidates = sort {$a->[0] <=> $b->[0]} @candidates; + if ($basesize != 0 && $candidates[0]->[0] == 0) { + return ($candidates[0]->[1], $basedelta, $basesize); + } else { + # if there is absolutely no smaller/larger font that can be used + # return the current one, and beep if this is the base font + if ($basesize == 0) { + $term->scr_bell; + } + return ("-$fontstring", 0, $font{pixelSize}); + } +} + +sub font_apply_new +{ + my ($term, $newfont, $type, $save) = @_; + + # $term->scr_add_lines("\r\nnew font is $newfont\n"); + + $term->cmd_parse("\033]" . $escapecodes{$type} . ";" . $newfont . "\033\\"); + + # load the xrdb db + # system("xrdb -load " . X_RESOURCES); + + if ($save > 0) { + # merge the new values + open(XRDB_MERGE, "| xrdb -merge") || die "can't fork: $!"; + local $SIG{PIPE} = sub { die "xrdb pipe broken" }; + print XRDB_MERGE "URxvt." . $type . ": " . $newfont; + close(XRDB_MERGE) || die "bad xrdb: $! $?"; + } +} diff --git a/urxvt/ext/fullscreen b/urxvt/ext/fullscreen new file mode 100644 index 0000000..086c2ea --- /dev/null +++ b/urxvt/ext/fullscreen @@ -0,0 +1,33 @@ +#!/usr/bin/perl +# +# Toggle fullscreen +# +# URL: https://aur.archlinux.org/packages/urxvt-fullscreen +# +# Copyright (C) 2010-2011 Christopher Luna +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +use strict; +use warnings; + +sub on_user_command +{ + my ($self, $cmd) = @_; + + if ($cmd eq "fullscreen:switch") { + my $dummy = `wmctrl -r :ACTIVE: -b toggle,fullscreen,above`; + } +} diff --git a/urxvt/ext/keyboard-select b/urxvt/ext/keyboard-select new file mode 100644 index 0000000..a6f89af --- /dev/null +++ b/urxvt/ext/keyboard-select @@ -0,0 +1,598 @@ +#!/usr/bin/perl +# Author: Bert Muennich +# Website: http://www.github.com/muennich/urxvt-perls +# License: GPLv2 + +# Use keyboard shortcuts to select and copy text. + +# Usage: put the following lines in your .Xdefaults/.Xresources: +# URxvt.perl-ext-common: ...,keyboard-select +# URxvt.keysym.M-Escape: perl:keyboard-select:activate +# The following line overwrites the default Meta-s binding and allows to +# activate keyboard-select directly in backward search mode: +# URxvt.keysym.M-s: perl:keyboard-select:search + +# Use Meta-Escape to activate selection mode, then use the following keys: +# h/j/k/l: Move cursor left/down/up/right (also with arrow keys) +# g/G/0/^/$/H/M/L/f/F/;/,/w/W/b/B/e/E: More vi-like cursor movement keys +# '/'/?: Start forward/backward search +# n/N: Repeat last search, N: in reverse direction +# Ctrl-f/b: Scroll down/up one screen +# Ctrl-d/u: Scroll down/up half a screen +# v/V/Ctrl-v: Toggle normal/linewise/blockwise selection +# y/Return: Copy selection to primary buffer, Return: quit afterwards +# Y: Copy selected lines to primary buffer or cursor line and quit +# q/Escape: Quit keyboard selection mode + + +use strict; +use warnings; + +sub on_start{ + my ($self) = @_; + + $self->{patterns}{'w'} = qr/\w[^\w\s]|\W\w|\s\S/; + $self->{patterns}{'W'} = qr/\s\S/; + $self->{patterns}{'b'} = qr/.*(?:\w[^\w\s]|\W\w|\s\S)/; + $self->{patterns}{'B'} = qr/.*\s\S/; + $self->{patterns}{'e'} = qr/[^\w\s](?=\w)|\w(?=\W)|\S(?=\s|$)/; + $self->{patterns}{'E'} = qr/\S(?=\s|$)/; + + () +} + + +sub on_action { + my ($self, $action) = @_; + + on_user_command($self, "keyboard-select:" . $action); +} + + +sub on_user_command { + my ($self, $cmd) = @_; + + if (not $self->{active}) { + if ($cmd eq 'keyboard-select:activate') { + activate($self); + } elsif ($cmd eq 'keyboard-select:search') { + activate($self, 1); + } + } + + () +} + + +sub key_press { + my ($self, $event, $keysym, $char) = @_; + my $key = chr($keysym); + + if (lc($key) eq 'c' && $event->{state} & urxvt::ControlMask) { + deactivate($self); + } elsif ($self->{search}) { + if ($keysym == 0xff1b) { + if ($self->{search_mode}) { + deactivate($self); + } else { + $self->{search} = ''; + status_area($self); + } + } elsif ($keysym == 0xff08) { + $self->{search} = substr($self->{search}, 0, -1); + if (not $self->{search} and $self->{search_mode}) { + deactivate($self); + } else { + status_area($self); + } + } elsif ($keysym == 0xff0d || + (lc($key) eq 'm' && $event->{state} & urxvt::ControlMask)) { + my $txt = substr($self->{search}, 1); + if ($txt) { + $self->{pattern} = ($txt =~ m/[[:upper:]]/) ? qr/\Q$txt\E/ : + qr/\Q$txt\E/i; + } elsif ($self->{pattern}) { + delete $self->{pattern}; + } + $self->{search} = ''; + $self->screen_cur($self->{srhcr}, $self->{srhcc}); + if (not find_next($self)) { + if ($self->{search_mode}) { + deactivate($self); + } else { + status_area($self); + } + } + } elsif (length($char) > 0) { + $self->{search} .= $self->locale_decode($char); + my $txt = substr($self->{search}, 1); + if ($txt) { + $self->{pattern} = ($txt =~ m/[[:upper:]]/) ? qr/\Q$txt\E/ : + qr/\Q$txt\E/i; + } elsif ($self->{pattern}) { + delete $self->{pattern}; + } + $self->screen_cur($self->{srhcr}, $self->{srhcc}); + find_next($self); + status_area($self); + } + } elsif ($self->{move_to}) { + if ($keysym == 0xff1b) { + $self->{move_to} = 0; + status_area($self); + } elsif (length($char) > 0) { + $self->{move_to} = 0; + $self->{patterns}{'f-1'} = qr/^.*\Q$key\E/; + $self->{patterns}{'f+1'} = qr/^.+?\Q$key\E/; + move_to($self, ';'); + status_area($self); + } + } elsif ($keysym == 0xff1b || lc($key) eq 'q') { + deactivate($self); + } elsif (lc($key) eq 'y' || $keysym == 0xff0d || + (lc($key) eq 'm' && $event->{state} & urxvt::ControlMask)) { + my $quit = 0; + if ($key eq 'Y' && $self->{select} ne 'l') { + $quit = !$self->{select}; + toggle_select($self, 'l'); + } + if ($self->{select}) { + my ($br, $bc, $er, $ec) = calc_span($self); + $ec = $self->line($er)->l if $self->{select} eq 'l'; + $self->selection_beg($br, $bc); + $self->selection_end($er, $ec); + $self->selection_make($event->{time}, $self->{select} eq 'b'); + if (lc($key) eq 'y') { + $self->selection_beg(1, 0); + $self->selection_end(1, 0); + $self->{select} = ''; + status_area($self); + $self->want_refresh(); + } else { + $quit = 1; + } + } + if ($quit) { + deactivate($self); + } + } elsif ($key eq 'V') { + toggle_select($self, 'l'); + } elsif ($key eq 'v') { + if ($event->{state} & urxvt::ControlMask) { + toggle_select($self, 'b'); + } else { + toggle_select($self, 'n'); + } + } elsif ($key eq 'k' || $keysym == 0xff52) { + move_cursor($self, 'k'); + } elsif ($key eq 'j' || $keysym == 0xff54) { + move_cursor($self, 'j'); + } elsif ($key eq 'h' || $keysym == 0xff51) { + move_cursor($self, 'h'); + } elsif ($key eq 'l' || $keysym == 0xff53) { + move_cursor($self, 'l'); + } elsif ($keysym == 0xff57) { + move_cursor($self, '$'); + } elsif ($keysym == 0xff50) { + move_cursor($self, '^'); + } elsif ('gG0^$HML' =~ m/\Q$key\E/ || + ('fbdu' =~ m/\Q$key\E/ && $event->{state} & urxvt::ControlMask)) { + move_cursor($self, $key); + } elsif (lc($key) eq 'f') { + $self->{move_to} = 1; + $self->{move_dir} = $key eq 'F' ? -1 : 1; + status_area($self, $key); + } elsif (';,wWbBeE' =~ m/\Q$key\E/) { + move_to($self, $key); + } elsif ($key eq '/' || $key eq '?') { + $self->{search} = $key; + $self->{search_dir} = $key eq '?' ? -1 : 1; + ($self->{srhcr}, $self->{srhcc}) = $self->screen_cur(); + status_area($self); + } elsif (lc($key) eq 'n') { + find_next($self, $self->{search_dir} * ($key eq 'N' ? -1 : 1)); + } + + return 1; +} + + +sub move_cursor { + my ($self, $key) = @_; + my ($cr, $cc) = $self->screen_cur(); + my $line = $self->line($cr); + + if ($key eq 'k' && $line->beg > $self->top_row) { + $cr = $line->beg - 1; + } elsif ($key eq 'j' && $line->end < $self->nrow - 1) { + $cr = $line->end + 1; + } elsif ($key eq 'h' && $self->{offset} > 0) { + $self->{offset} = $line->offset_of($cr, $cc) - 1; + $self->{dollar} = 0; + } elsif ($key eq 'l' && $self->{offset} < $line->l - 1) { + ++$self->{offset}; + } elsif ($key eq 'f' || $key eq 'd') { + my $vs = $self->view_start() + + ($key eq 'd' ? $self->nrow / 2 : $self->nrow - 1); + $vs = 0 if $vs > 0; + $cr += $vs - $self->view_start($vs); + } elsif ($key eq 'b' || $key eq 'u') { + my $vs = $self->view_start() - + ($key eq 'u' ? $self->nrow / 2 : $self->nrow - 1); + $vs = $self->top_row if $vs < $self->top_row; + $cr += $vs - $self->view_start($vs); + } elsif ($key eq 'g') { + ($cr, $self->{offset}) = ($self->top_row, 0); + $self->{dollar} = 0; + } elsif ($key eq 'G') { + ($cr, $self->{offset}) = ($self->nrow - 1, 0); + $self->{dollar} = 0; + } elsif ($key eq '0') { + $self->{offset} = 0; + $self->{dollar} = 0; + } elsif ($key eq '^') { + my $ltxt = $self->special_decode($line->t); + while ($ltxt =~ s/^( *)\t/$1 . " " x (8 - length($1) % 8)/e) {} + $self->{offset} = $ltxt =~ m/^ +/ ? $+[0] : 0; + $self->{dollar} = 0; + } elsif ($key eq '$') { + my $co = $line->offset_of($cr, $cc); + $self->{dollar} = $co + 1; + $self->{offset} = $line->l - 1; + } elsif ($key eq 'H') { + $cr = $self->view_start(); + } elsif ($key eq 'M') { + $cr = $self->view_start() + $self->nrow / 2; + } elsif ($key eq 'L') { + $cr = $self->view_start() + $self->nrow - 1; + } + + $line = $self->line($cr); + $cc = $self->{dollar} || $self->{offset} >= $line->l ? $line->l - 1 : + $self->{offset}; + $self->screen_cur($line->coord_of($cc)); + + status_area($self); + $self->want_refresh(); + + () +} + + +sub move_to { + my ($self, $key) = @_; + my ($cr, $cc) = $self->screen_cur(); + my $line = $self->line($cr); + my $offset = $self->{offset}; + my ($dir, $pattern); + my ($wrap, $found) = (0, 0); + + if ($key eq ';' || $key eq ',') { + $dir = $self->{move_dir} * ($key eq ',' ? -1 : 1); + $pattern = $self->{patterns}{sprintf('f%+d', $dir)}; + return if not $pattern; + } else { + if (lc($key) eq 'b') { + $dir = -1; + } else { + $dir = 1; + ++$offset if lc($key) eq 'e'; + } + $pattern = $self->{patterns}{$key}; + $wrap = 1; + } + + if ($dir > 0) { + NEXTDOWN: my $text = substr($line->t, $offset); + if ($text =~ m/$pattern/) { + $offset += $+[0] - 1; + $found = 1; + } elsif ($wrap && $line->end + 1 < $self->nrow) { + $cr = $line->end + 1; + $line = $self->line($cr); + $offset = 0; + if (lc($key) eq 'e') { + goto NEXTDOWN; + } else { + $found = 1; + } + } + } elsif ($dir < 0) { + NEXTUP: my $text = substr($line->t, 0, $offset); + if ($text =~ m/$pattern/) { + $offset += $+[0] - length($text) - 1; + $found = 1; + } elsif ($wrap) { + if ($offset > 0) { + $offset = 0; + $found = 1; + } elsif ($line->beg > $self->top_row) { + $cr = $line->beg - 1; + $line = $self->line($cr); + $offset = $line->l; + goto NEXTUP; + } + } + } + + if ($found) { + $self->{dollar} = 0; + $self->{offset} = $offset; + $self->screen_cur($line->coord_of($offset)); + $self->want_refresh(); + } + + () +} + + +sub find_next { + my ($self, $dir) = @_; + + return if not $self->{pattern}; + $dir = $self->{search_dir} if not $dir; + + my ($cr, $cc) = $self->screen_cur(); + my $line = $self->line($cr); + my $offset = $line->offset_of($cr, $cc); + my $text; + my $found = 0; + + ++$offset if $dir > 0; + + while (not $found) { + if ($dir > 0) { + $text = substr($line->t, $offset); + if ($text =~ m/$self->{pattern}/) { + $found = 1; + $offset += $-[0]; + } else { + last if $line->end >= $self->nrow; + $line = $self->line($line->end + 1); + $offset = 0; + } + } else { + $text = substr($line->t, 0, $offset); + if ($text =~ m/$self->{pattern}/) { + $found = 1; + $offset = $-[0] while $text =~ m/$self->{pattern}/g; + } else { + last if $line->beg <= $self->top_row; + $line = $self->line($line->beg - 1); + $offset = $line->l; + } + } + } + + if ($found) { + $self->{dollar} = 0; + $self->{offset} = $offset; + $self->screen_cur($line->coord_of($offset)); + status_area($self); + $self->want_refresh(); + } + + return $found; +} + + +sub tt_write { + return 1; +} + + +sub refresh { + my ($self) = @_; + my ($cr, $cc) = $self->screen_cur(); + + # scroll the current cursor position into visible area + if ($cr < $self->view_start()) { + $self->view_start($cr); + } elsif ($cr >= $self->view_start() + $self->nrow) { + $self->view_start($cr - $self->nrow + 1); + } + + if ($self->{select}) { + my ($hl, $reverse_cursor); + my ($br, $bc, $er, $ec) = calc_span($self); + + if ($self->x_resource('highlightColor')) { + $hl = urxvt::RS_Sel; + $reverse_cursor = 0; + } else { + $hl = urxvt::RS_RVid; + $reverse_cursor = $self->{select} ne 'l'; + } + if ($self->{select} eq 'b') { + my $co = $self->line($cr)->offset_of($cr, $cc); + my $dollar = $self->{dollar} && $co >= $self->{dollar} - 1; + + my $r = $br; + while ($r <= $er) { + my $line = $self->line($r); + if ($bc < $line->l) { + $ec = $line->l if $dollar; + my ($br, $bc) = $line->coord_of($bc); + my ($er, $ec) = $line->coord_of($ec <= $line->l ? $ec : $line->l); + $self->scr_xor_span($br, $bc, $er, $ec, $hl); + } elsif ($r == $cr) { + $reverse_cursor = 0; + } + $r = $line->end + 1; + } + } else { + $self->scr_xor_span($br, $bc, $er, $ec, $hl); + } + + if ($reverse_cursor) { + # make the cursor visible again + $self->scr_xor_span($cr, $cc, $cr, $cc + 1, $hl); + } + } + + () +} + + +sub activate { + my ($self, $search) = @_; + + $self->{active} = 1; + + $self->{select} = ''; + $self->{dollar} = 0; + $self->{move_to} = 0; + + if ($search) { + $self->{search} = '?'; + $self->{search_dir} = -1; + $self->{search_mode} = 1; + } else { + $self->{search} = ''; + $self->{search_mode} = 0; + } + + ($self->{oldcr}, $self->{oldcc}) = $self->screen_cur(); + ($self->{srhcr}, $self->{srhcc}) = $self->screen_cur(); + $self->{old_view_start} = $self->view_start(); + $self->{old_pty_ev_events} = $self->pty_ev_events(urxvt::EV_NONE); + + my $line = $self->line($self->{oldcr}); + $self->{offset} = $line->offset_of($self->{oldcr}, $self->{oldcc}); + + $self->selection_beg(1, 0); + $self->selection_end(1, 0); + + $self->enable( + key_press => \&key_press, + refresh_begin => \&refresh, + refresh_end => \&refresh, + tt_write => \&tt_write, + ); + + if ($self->{offset} >= $line->l) { + $self->{offset} = $line->l > 0 ? $line->l - 1 : 0; + $self->screen_cur($line->coord_of($self->{offset})); + $self->want_refresh(); + } + + $self->{overlay_len} = 0; + status_area($self); + + () +} + + +sub deactivate { + my ($self) = @_; + + $self->selection_beg(1, 0); + $self->selection_end(1, 0); + + delete $self->{overlay} if $self->{overlay}; + + $self->disable("key_press", "refresh_begin", "refresh_end", "tt_write"); + $self->screen_cur($self->{oldcr}, $self->{oldcc}); + $self->view_start($self->{old_view_start}); + $self->pty_ev_events($self->{old_pty_ev_events}); + + $self->want_refresh(); + + $self->{active} = 0; + + () +} + + +sub status_area { + my ($self, $extra) = @_; + my ($stat, $stat_len); + + if ($self->{search}) { + $stat_len = $self->ncol; + $stat = $self->{search} . ' ' x ($stat_len - length($self->{search})); + } else { + if ($self->{select}) { + $stat = "-V" . ($self->{select} ne 'n' ? uc($self->{select}) : "") . "- "; + } + + if ($self->top_row == 0) { + $stat .= "All"; + } elsif ($self->view_start() == $self->top_row) { + $stat .= "Top"; + } elsif ($self->view_start() == 0) { + $stat .= "Bot"; + } else { + $stat .= sprintf("%2d%%", + ($self->top_row - $self->view_start) * 100 / $self->top_row); + } + + $stat = "$extra $stat" if $extra; + $stat_len = length($stat); + } + + if (!$self->{overlay} || $self->{overlay_len} != $stat_len) { + delete $self->{overlay} if $self->{overlay}; + $self->{overlay} = $self->overlay(-1, -1, $stat_len, 1, + urxvt::OVERLAY_RSTYLE, 0); + $self->{overlay_len} = $stat_len; + } + + $self->{overlay}->set(0, 0, $self->special_encode($stat)); + $self->{overlay}->show(); + + () +} + + +sub toggle_select { + my ($self, $mode) = @_; + + if ($self->{select} eq $mode) { + $self->{select} = ''; + } else { + if (not $self->{select}) { + ($self->{ar}, $self->{ac}) = $self->screen_cur(); + } + $self->{select} = $mode; + } + + status_area($self); + $self->want_refresh(); + + () +} + + +sub calc_span { + my ($self) = @_; + my ($cr, $cc) = $self->screen_cur(); + my ($br, $bc, $er, $ec); + + if ($self->{select} eq 'b') { + $br = $self->line($cr)->beg; + $bc = $self->line($cr)->offset_of($cr, $cc); + $er = $self->line($self->{ar})->beg; + $ec = $self->line($self->{ar})->offset_of($self->{ar}, $self->{ac}); + ($br, $er) = ($er, $br) if $br > $er; + ($bc, $ec) = ($ec, $bc) if $bc > $ec; + } else { + if ($cr < $self->{ar}) { + ($br, $bc, $er, $ec) = ($cr, $cc, $self->{ar}, $self->{ac}); + } elsif ($cr > $self->{ar}) { + ($br, $bc, $er, $ec) = ($self->{ar}, $self->{ac}, $cr, $cc); + } else { + ($br, $er) = ($cr, $cr); + ($bc, $ec) = $cc < $self->{ac} ? ($cc, $self->{ac}) : ($self->{ac}, $cc); + } + } + + if ($self->{select} eq 'l') { + ($br, $er) = ($self->line($br)->beg, $self->line($er)->end); + ($bc, $ec) = (0, $self->ncol); + } else { + ++$ec; + } + + return ($br, $bc, $er, $ec); +} diff --git a/urxvt/ext/url-select b/urxvt/ext/url-select new file mode 100644 index 0000000..1afbd70 --- /dev/null +++ b/urxvt/ext/url-select @@ -0,0 +1,410 @@ +#!/usr/bin/perl +# Author: Bert Muennich +# Website: http://www.github.com/muennich/urxvt-perls +# Based on: http://www.jukie.net/~bart/blog/urxvt-url-yank +# License: GPLv2 + +# Use keyboard shortcuts to select URLs. +# This should be used as a replacement for the default matcher extension, +# it also makes URLs clickable with the middle mouse button. + +# Usage: put the following lines in your .Xdefaults/.Xresources: +# URxvt.perl-ext-common: ...,url-select +# URxvt.keysym.M-u: perl:url-select:select_next + +# Use Meta-u to activate URL selection mode, then use the following keys: +# j/k: Select next downward/upward URL (also with arrow keys) +# g/G: Select first/last URL (also with home/end key) +# o/Return: Open selected URL in browser, Return: deactivate afterwards +# y: Copy (yank) selected URL and deactivate selection mode +# q/Escape: Deactivate URL selection mode + +# Options: +# URxvt.url-select.autocopy: If true, selected URLs are copied to PRIMARY +# URvxt.url-select.button: Mouse button to click-open URLs (default: 2) +# URxvt.url-select.launcher: Browser/command to open selected URL with +# URxvt.url-select.underline: If set to true, all URLs get underlined + + +use strict; +use warnings; + +# The custom rendition bit to use for marking the cell as being underlined +# by us so we can unset it again after a line has changed. +use constant UNDERLINED => 1<<3; # arbitrarily chosen in hope of no collision + +sub on_start { + my ($self) = @_; + + # read resource settings + if ($self->x_resource('url-select.launcher')) { + @{$self->{browser}} = split /\s+/, $self->x_resource('url-select.launcher'); + } else { + @{$self->{browser}} = ('x-www-browser'); + } + if ($self->x_resource('url-select.underline') eq 'true') { + $self->enable(line_update => \&line_update); + } + if ($self->x_resource('url-select.autocopy') eq 'true') { + $self->{autocopy} = 1; + } + + $self->{state} = 0; + + for my $mod (split '', $self->x_resource("url-select.button") || + $self->x_resource("matcher.button") || 2) { + if ($mod =~ /^\d+$/) { + $self->{button} = $mod; + } elsif ($mod eq "C") { + $self->{state} |= urxvt::ControlMask; + } elsif ($mod eq "S") { + $self->{state} |= urxvt::ShiftMask; + } elsif ($mod eq "M") { + $self->{state} |= $self->ModMetaMask; + } elsif ($mod ne "-" && $mod ne " ") { + warn("invalid button/modifier in $self->{_name}<$self->{argv}[0]>: $mod\n"); + } + } + + if ($self->x_resource('matcher.pattern')) { + @{$self->{pattern}} = ($self->x_resource('matcher.pattern')); + } elsif ($self->x_resource('matcher.pattern.0')) { + my $current = 0; + + while (defined (my $res = $self->x_resource("matcher.pattern.$current"))) { + $res = $self->locale_decode($res); + utf8::encode $res; + push @{$self->{pattern}}, qr($res)x; + $current++; + } + } else { + @{$self->{pattern}} = qr{ + (?:https?://|ftp://|news://|mailto:|file://|\bwww\.) + [\w\-\@;\/?:&=%\$.+!*\x27,~#]* + ( + \([\w\-\@;\/?:&=%\$.+!*\x27,~#]*\) # Allow a pair of matched parentheses + | # + [\w\-\@;\/?:&=%\$+*~] # exclude some trailing characters (heuristic) + )+ + }x; + } + + () +} + + +sub line_update { + my ($self, $row) = @_; + + my $line = $self->line($row); + my $text = $line->t; + my $rend = $line->r; + + # clear all underlines that were set by us + for (@$rend) { + if (urxvt::GET_CUSTOM($_) & UNDERLINED) { + $_ = urxvt::SET_CUSTOM($_, urxvt::GET_CUSTOM($_) & ~UNDERLINED) & + ~urxvt::RS_Uline; + } + } + + for my $pattern (@{$self->{pattern}}) { + while ($text =~ /$pattern/g) { + my $url = $&; + my ($beg, $end) = ($-[0], $+[0] - 1); + + for (@{$rend}[$beg .. $end]) { + unless ($_ & urxvt::RS_Uline) { + $_ = urxvt::SET_CUSTOM($_, urxvt::GET_CUSTOM($_) | UNDERLINED); + $_ |= urxvt::RS_Uline; + } + } + } + } + + $line->r($rend); + + () +} + +sub on_action { + my ($self, $action) = @_; + + on_user_command($self, "url-select:" . $action); +} + + +sub on_user_command { + my ($self, $cmd) = @_; + + if ($cmd eq 'url-select:select_next') { + if (not $self->{active}) { + activate($self); + } + select_next($self, -1); + } + + () +} + + +sub key_press { + my ($self, $event, $keysym) = @_; + my $char = chr($keysym); + + if ($keysym == 0xff1b || lc($char) eq 'q' || + (lc($char) eq 'c' && $event->{state} & urxvt::ControlMask)) { + deactivate($self); + } elsif ($keysym == 0xff0d || $char eq 'o' || + (lc($char) eq 'm' && $event->{state} & urxvt::ControlMask)) { + $self->exec_async(@{$self->{browser}}, ${$self->{found}[$self->{n}]}[4]); + deactivate($self) unless $char eq 'o'; + } elsif ($char eq 'y') { + my $found = $self->{found}[$self->{n}]; + $self->selection_beg(${$found}[0], ${$found}[1]); + $self->selection_end(${$found}[2], ${$found}[3]); + $self->selection_make($event->{time}); + $self->selection_beg(1, 0); + $self->selection_end(1, 0); + deactivate($self); + } elsif ($char eq 'k' || $keysym == 0xff52 || $keysym == 0xff51) { + select_next($self, -1, $event); + } elsif ($char eq 'j' || $keysym == 0xff54 || $keysym == 0xff53) { + select_next($self, 1, $event); + } elsif ($char eq 'g' || $keysym == 0xff50) { + $self->{row} = $self->top_row - 1; + delete $self->{found}; + select_next($self, 1, $event); + } elsif ($char eq 'G' || $keysym == 0xff57) { + $self->{row} = $self->nrow; + delete $self->{found}; + select_next($self, -1, $event); + } + + return 1; +} + + +sub on_button_press { + my ($self, $event) = @_; + + my $mask = $self->ModLevel3Mask | $self->ModMetaMask | + urxvt::ShiftMask | urxvt::ControlMask; + + if ($event->{button} == $self->{button} && ($event->{state} & $mask) == $self->{state}) { + my $col = $event->{col}; + my $row = $event->{row}; + my $line = $self->line($row); + my $text = $line->t; + + for my $pattern (@{$self->{pattern}}) { + while ($text =~ /$pattern/g) { + my ($url, $beg, $end) = ($&, $-[0], $+[0]); + --$end if $url =~ s/["')]$//; + + if ($col >= $beg && $col <= $end) { + $self->{button_pressed} = 1; + $self->{button_col} = $col; + $self->{button_row} = $row; + $self->{button_url} = $url; + return 1; + } + } + } + } + + () +} + +sub on_button_release { + my ($self, $event) = @_; + + if ($self->{button_pressed} && $event->{button} == $self->{button}) { + my $col = $event->{col}; + my $row = $event->{row}; + + $self->{button_pressed} = 0; + + if ($col == $self->{button_col} && $row == $self->{button_row}) { + $self->exec_async(@{$self->{browser}}, $self->{button_url}); + return 1; + } + } + + () +} + + +sub select_next { + # $dir < 0: up, > 0: down + my ($self, $dir, $event) = @_; + my $row = $self->{row}; + + if (($dir < 0 && $self->{n} > 0) || + ($dir > 0 && $self->{n} < $#{ $self->{found} })) { + # another url on current line + $self->{n} += $dir; + hilight($self); + if ($self->{autocopy}) { + my $found = $self->{found}[$self->{n}]; + $self->selection_beg(${$found}[0], ${$found}[1]); + $self->selection_end(${$found}[2], ${$found}[3]); + $self->selection_make($event->{time}); + $self->selection_beg(1, 0); + $self->selection_end(1, 0); + } + return; + } + + while (($dir < 0 && $row > $self->top_row) || + ($dir > 0 && $row < $self->nrow - 1)) { + my $line = $self->line($row); + $row = ($dir < 0 ? $line->beg : $line->end) + $dir; + $line = $self->line($row); + my $text = $line->t; + + for my $pattern (@{$self->{pattern}}) { + if ($text =~ /$pattern/g) { + delete $self->{found}; + + do { + my ($beg, $end) = ($-[0], $+[0]); + push @{$self->{found}}, [$line->coord_of($beg), + $line->coord_of($end), substr($text, $beg, $end - $beg)]; + } while ($text =~ /$pattern/g); + + $self->{row} = $row; + $self->{n} = $dir < 0 ? $#{$self->{found}} : 0; + hilight($self); + if ($self->{autocopy}) { + my $found = $self->{found}[$self->{n}]; + $self->selection_beg(${$found}[0], ${$found}[1]); + $self->selection_end(${$found}[2], ${$found}[3]); + $self->selection_make($event->{time}); + $self->selection_beg(1, 0); + $self->selection_end(1, 0); + } + return; + } + } + } + + deactivate($self) unless $self->{found}; + + () +} + + +sub hilight { + my ($self) = @_; + + if ($self->{found}) { + if ($self->{row} < $self->view_start() || + $self->{row} >= $self->view_start() + $self->nrow) { + # scroll selected url into visible area + my $top = $self->{row} - ($self->nrow >> 1); + $self->view_start($top < 0 ? $top : 0); + } + + status_area($self); + $self->want_refresh(); + } + + () +} + + +sub refresh { + my ($self) = @_; + + if ($self->{found}) { + if ($self->x_resource('highlightColor')) { + $self->scr_xor_span(@{$self->{found}[$self->{n}]}[0 .. 3], urxvt::RS_Sel); + } else { + $self->scr_xor_span(@{$self->{found}[$self->{n}]}[0 .. 3], urxvt::RS_RVid); + } + } + + () +} + + +sub status_area { + my ($self) = @_; + + my $row = $self->{row} < 0 ? + $self->{row} - $self->top_row : abs($self->top_row) + $self->{row}; + my $text = sprintf("%d,%d ", $row + 1, $self->{n} + 1); + + if ($self->top_row == 0) { + $text .= "All"; + } elsif ($self->view_start() == $self->top_row) { + $text .= "Top"; + } elsif ($self->view_start() == 0) { + $text .= "Bot"; + } else { + $text .= sprintf("%2d%", + ($self->top_row - $self->view_start) * 100 / $self->top_row); + } + + my $text_len = length($text); + + if ($self->{overlay_len} != $text_len) { + delete $self->{overlay} if $self->{overlay}; + $self->{overlay} = $self->overlay(-1, -1, $text_len, 1, + urxvt::OVERLAY_RSTYLE, 0); + $self->{overlay_len} = $text_len; + } + + $self->{overlay}->set(0, 0, $self->special_encode($text)); + $self->{overlay}->show(); + + () +} + + +sub tt_write { + return 1; +} + + +sub activate { + my ($self) = @_; + + $self->{active} = 1; + + $self->{row} = $self->view_start() + $self->nrow; + $self->{n} = 0; + $self->{overlay_len} = 0; + $self->{button_pressed} = 0; + + $self->{view_start} = $self->view_start(); + $self->{pty_ev_events} = $self->pty_ev_events(urxvt::EV_NONE); + + $self->enable( + key_press => \&key_press, + refresh_begin => \&refresh, + refresh_end => \&refresh, + tt_write => \&tt_write, + ); + + () +} + + +sub deactivate { + my ($self) = @_; + + $self->disable("key_press", "refresh_begin", "refresh_end", "tt_write"); + $self->view_start($self->{view_start}); + $self->pty_ev_events($self->{pty_ev_events}); + + delete $self->{overlay} if $self->{overlay}; + delete $self->{found} if $self->{found}; + + $self->want_refresh(); + + $self->{active} = 0; + + () +} diff --git a/urxvt/ext/vtwheel b/urxvt/ext/vtwheel new file mode 100644 index 0000000..7f51226 --- /dev/null +++ b/urxvt/ext/vtwheel @@ -0,0 +1,49 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +# Implements a scrollwheel just like in good old vt100's mices + +sub simulate_keypress { + my ($self, $type) = @_; #type: 0:up, 1:down + + my $keycode_up = 111; + my $keycode_down = 116; + + my $numlines = 3; + + my $keycode = 0; + if ($type eq 0) { + $keycode = $keycode_up; + } elsif ($type eq 1) { + $keycode = $keycode_down; + } else { + return; + } + + for (my $i = 0 ; $i ne $numlines ; $i++) { + $self->key_press(0,$keycode); + $self->key_release(0,$keycode); + } +} + +sub on_button_release { + my ($self, $event) = @_; + + #my $res_ss = $self->resource("secondaryScroll"); + #warn("ressource ss is <$res_ss>"); + + !$self->current_screen and return (); + + #warn("foo, event: <$event->{button}>\n"); + if ($event->{button} eq "4") { # scroll up + $self->simulate_keypress(0); + return 1; + } elsif ($event->{button} eq "5") { # scroll down + $self->simulate_keypress(1); + return 1; + } + + return (); +} |