From b9794a105ce1e4f834839d1fb289d007668f2828 Mon Sep 17 00:00:00 2001 From: diego castillo salazar Date: Thu, 10 Nov 2022 05:07:32 -0500 Subject: [PATCH] uploading urxvt config --- home/.Xdefaults | 70 ++++ home/.urxvt/ext/font-size | 462 +++++++++++++++++++++ home/.urxvt/ext/fullscreen | 8 + home/.urxvt/ext/tabbedalt | 674 ++++++++++++++++++++++++++++++ home/.urxvt/ext/tabbedex | 678 +++++++++++++++++++++++++++++++ home/.urxvt/ext/url-select-plus | 433 ++++++++++++++++++++ home/.urxvt/ext/vtwheel | 44 ++ home/.urxvt/urxvt/ext/font-size | 462 +++++++++++++++++++++ home/.urxvt/urxvt/ext/fullscreen | 8 + home/.urxvt/urxvt/ext/tabbedalt | 674 ++++++++++++++++++++++++++++++ 10 files changed, 3513 insertions(+) create mode 100644 home/.Xdefaults create mode 100644 home/.urxvt/ext/font-size create mode 100644 home/.urxvt/ext/fullscreen create mode 100755 home/.urxvt/ext/tabbedalt create mode 100755 home/.urxvt/ext/tabbedex create mode 100644 home/.urxvt/ext/url-select-plus create mode 100644 home/.urxvt/ext/vtwheel create mode 100644 home/.urxvt/urxvt/ext/font-size create mode 100644 home/.urxvt/urxvt/ext/fullscreen create mode 100755 home/.urxvt/urxvt/ext/tabbedalt diff --git a/home/.Xdefaults b/home/.Xdefaults new file mode 100644 index 0000000..7667670 --- /dev/null +++ b/home/.Xdefaults @@ -0,0 +1,70 @@ +! Base16-grayscale-dark-theme +! Scheme: Alexandre Gavioli +! Fork to urxvt: d13g0x +*.foreground: #999999 +*.background: #101010 +*.cursorColor: #999999 +*.color0: #101010 +*.color8: #ababab +*.color1: #252525 +*.color9: #252525 +*.color2: #101010 +*.color10: #686868 +*.color3: #999999 +*.color11: #252525 +*.color4: #a0a0a0 +*.color12: #f7f7f7 +*.color5: #747474 +*.color13: #f7f7f7 +*.color6: #686868 +*.color14: #5e5e5e +*.color7: #7c7c7c +*.color15: #ababab + +! General config +URxvt*termName: rxvt-unicode-256color +URxvt.cursorBlink: true +URxvt.internalBorder: 8 +URxvt.cursorUnderline: true +URxvt.font: xft:Hack Nerd Font:style=Regular:size=8 +URxvt.boldFont: xft:Hack Nerd Font:style=Bold:size=8 +URxvt.italicFont: xft:Hack Nerd Font:style=Italic:size=8 +URxvt.boldItalicFont: xft:Hack Nerd Font:style=Bold Italic:size=8 +URxvt.letterSpace: 0 +URxvt.geometry: 92x24 +URxvt.saveline: 2048 +URxvt.scrollBar: false +URxvt.scrollBar_right: false +URxvt.scrollBar_floating: false +URxvt.scrollstyle: rxvt + +! Tabbed extension & perl extension config +URxvt.perl-ext: tabbedalt +URxvt.tabbedalt.new-button: false +URxvt.tabbedalt.tabbar-fg: 3 +URxvt.tabbedalt.tabbar-bg: 0 +URxvt.tabbedalt.tab-fg: 3 +URxvt.tabbedalt.tab-bg: 0 +URxvt.tabbedalt.active-fg: 3 +URxvt.tabbedalt.actives-fg: 0 +URxvt.tabbedalt.autohide: false +URxvt.tabbedalt.tab-numbers: false + + +! Copy Paste & Other Extensions +URxvt.perl-ext-common: default,selection-to-clipboard,matcher,keyboard-select,font-size +URxvt.keysym.M-u: perl:url-select:select_next +URxvt.url-launcher: /bin/qutebrowser +URxvt.underlineURLs: True +URxvt.matcher.button: 1 +URxvt.keysym.M-Escape: perl:keyboard-select:activate +URxvt.keysym.Shift-Control-V: eval:paste_clipboard +URxvt.keysym.Shift-Control-C: eval:selection_to_clipboard +URxvt.clipboard.autocopy: true +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 + diff --git a/home/.urxvt/ext/font-size b/home/.urxvt/ext/font-size new file mode 100644 index 0000000..4d10830 --- /dev/null +++ b/home/.urxvt/ext/font-size @@ -0,0 +1,462 @@ +#!/usr/bin/env perl +# +# On-the-fly adjusting of the font size in urxvt +# +# Copyright (c) 2008 David O'Neill +# 2012 Noah K. Tilton +# 2009-2012 Simon Lundström +# 2012-2016 Jan Larres +# +# 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 +# +# X11 fonts background: +# http://keithp.com/~keithp/talks/xtc2001/paper/ + +#: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 +The extension automatically binds Ctrl++ to the 'increase' function, +Ctrl+- to 'decrease', and Ctrl+0 to 'reset'. To use the other available functions +or change the keys, add some keybindings of your own: + 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_init { + my ($self) = @_; + + $self->bind_action ("C-plus", "%:increase") + or warn "unable to register 'C-plus' as font-size increase hotkey\n"; + $self->bind_action ("C-minus", "%:decrease") + or warn "unable to register 'C-minus' as font-size decrease hotkey\n"; + $self->bind_action ("C-0", "%:reset") + or warn "unable to register 'C-0' as font-size reset hotkey\n"; +} + +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 () { + 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/home/.urxvt/ext/fullscreen b/home/.urxvt/ext/fullscreen new file mode 100644 index 0000000..8e49228 --- /dev/null +++ b/home/.urxvt/ext/fullscreen @@ -0,0 +1,8 @@ +#! perl + +sub on_user_command { + my ($self, $cmd) = @_; + if ($cmd eq "fullscreen:switch") { + my $dummy = `wmctrl -r :ACTIVE: -b toggle,fullscreen,above` ; + } +} diff --git a/home/.urxvt/ext/tabbedalt b/home/.urxvt/ext/tabbedalt new file mode 100755 index 0000000..de5e310 --- /dev/null +++ b/home/.urxvt/ext/tabbedalt @@ -0,0 +1,674 @@ + +#! perl +# Tabbed perl extension for rxvt-unicode terminal emulator. +# Modified by Roman Dobosz +# +# 2008-08-22 18:01:55 +# - Modified shortcuts for tab navigation - now it uses shift + left/right +# arrow to navigate, also creating new shell is changed to CTRL+Shift+n. +# - Added shortcuts to move tab between others witch CTRL left/right arrow +# - Added some predefined actions - CTRL+Shift+r for "su -" command and +# CTRL+Shift+m for "mc" and other like named ssh sessions. +# - Added labels for custom shells (like "root", "mc" and so on) +# +# Please note, I don't know Perl! +# +# 2009-11-23 11:11:19 +# - Added shortcuts for apps with Mod4 key (mutt as an example) +# +# 2009-11-23 13:25:13 +# - Merged activity indicator from +# http://mina86.com/2009/05/16/tabbed-urxvt-extension/#more but without +# changes on tabs (like adding term title just behind all tabs). New +# resources can be use to change defaults (as in original solution): +# - tabbed-timeouts with format: +# ( ":" ":")* ":" ":" +# default '16:.:8:::4:+'. Asterisk is always present as a first indicator +# character, just like in original tabbed extension. +# - new-button, default to 'true'. Used to disable [NEW] button. +# +# 2009-11-24 23:34:51 +# - Added possibility to quick switch between first ten tabs with predefined +# combination of CTRL+1..0 keys, which will activate proper tab. +# - Added possibility to remove numbers from tab names by setting resource +# tab-numbers to false. +# +# 2009-11-25 21:40:30 +# - Added colors for tabs, which have activity on them. First is to be set +# when first activity (active-fg, defaults to red) appear on inactive tab. +# Last one (actived-fg, blue by default) is set when there is no more +# possible timeouts. Third one (actives-fg, purple) is set on all in between +# of these two. +# +# 2010-07-25 13:49:01 +# - Integrated renaming ability for tabs from stepb +# (http://github.com/stepb/urxvt-tabbedex) +# +# 2010-08-12 20:54:46 +# - Added functionality to create definitions of custom shells as a X +# resource, under common tabcmds name. This functionality also deprecates +# feature called here as a predefined actions. Without any configuration +# only simple shell is available under CTRL+SHIFT+N shortcut. After creating +# first custom shell this default is not available. +# +# Let's assume, that one want to mimic previous configuration, that means +# three kind of custom shells: simple one (default shell in the system), +# midnight commander and root (namely - su command). Three resources should +# be created: +# +# URxvt.tabbedalt.tabcmds.1: N|shell +# URxvt.tabbedalt.tabcmds.2: R|root|su - +# URxvt.tabbedalt.tabcmds.3: M|mc|mc +# +# URxvt.tabbedalt.tabcmds.[number] is a ordinal number, started from 1. There +# shouldn't be gaps between numbers, otherwise custom shells defined after a +# gap will not work. +# +# Resource values are two or three pipe separated values, which are in order: +# - shortcut key, which will be used for invoking custom shell together with +# CTRL+SHIFT keys. Mod4 (aka Super or Windows key) are not supported, and +# most probably will be removed from script soon, as lots of window +# managers out there make a big use of those keys. +# Note: There is limitation for characters used as a shortcut. Because some +# of them are used for control terminal itself (i.e. CTRL+SHIFT+D may not +# work), and also other characters (digits, some special characters etc.). +# Letters are case insensitive. +# - name of the tab, it could be anything but the pipe. +# - optional command. If omitted, simple shell will be launched. +# +# 2010-08-28 10:17:02 +# - Removed tab_property_notify hook, because in certain circumstances it +# provides memory consumption. It is especially well seen by running +# mocp[1] and play internet radio station (i.e digitalgunfire.com, but +# there can be others). Observe memory taken by urxvt with top or ps. Also, +# original tabbed extension is affected. +# +# This change will affect i.e. dynamic font change - it will not expand +# window to reflect size of a font. Switching to next tab and back will +# rearrange content of a tab to current window size. +# +# If anyone have a better idea how to fix memory consumption which is taking +# place in copy_properties(), please step forward :) +# +# [1] http://moc.daper.net +# +# 2011-07-12 21:05:26 +# - Fixed defaults for not defined tabcommands - now it is possible to use +# tabbed just as described. +# - Added some sort of primitive session ability, defined via resource +# session, which should contain pipe separated shortcuts defined in tabcmds +# resource. If there is no shortcuts (or wrong was defined), plain shell tab +# will appear. +# +# 2013-11-12 09:23:49 +# - Restored tab_property_notify hook. Whatever was the cause of the memory +# consumption is gone or doesn't have anything to do with that function. +# +# 2013-11-26 19:31:55 +# - Added parentheses for hook, should work on Debian now. +# +# 2019-06-05 10:55:37 +# - fixed couple of bugs regarding session +# - changed default colors to more sane values +# +# 2019-09-13 15:15:18 +# - Added shortcut for creating new shell like in original tabbed +# (SHIFT+Down). It can be disabled by an option "disable-shift-down". More +# information in README. +# - Cleaned up a bit the code and comments. + +sub tab_activity_mark ($$) { + my ($self, $tab) = @_; + return ' ' unless defined $tab->{lastActivity}; + return ' ' if $tab == $self->{cur}; + if (defined $self->{timeouts}) { + my $diff = int urxvt::NOW - $tab->{lastActivity}; + for my $spec (@{ $self->{timeouts} }) { + return $spec->[1] if $diff > $spec->[0]; + } + } + + '*'; +} + +sub refresh { + my ($self) = @_; + + my $ncol = $self->ncol; + + my $text = " " x $ncol; + my $rend = [($self->{rs_tabbar}) x $ncol]; + + my ($ofs, $idx, @ofs) = (0, 0); + + if ($self->{new_button}) { + substr $text, 0, 7, "[NEW] |"; + @$rend[0 .. 5] = ($self->{rs_tab}) x 6; + push @ofs, [0, 6, sub { $_[0]->new_tab("shell") }]; + $ofs = 7; + } + + for my $tab (@{ $self->{tabs} }) { + $idx++; + my $act = $self->tab_activity_mark($tab); + my $txt; + + if ($self->{tab_numbers}){ + $txt = sprintf "%d-%s", $idx, $tab->{name}; + }else{ + $txt = sprintf "%s", $tab->{name}; + } + + $txt = sprintf "%s%s%s", $act, $txt, $act; + + my $len = length $txt; + + # fill offset in $text with $txt + "|" + substr $text, $ofs, $len + 1, "$txt|"; + + # find and fill with proper colors + + + if ($tab == $self->{cur}) { + @$rend[$ofs .. $ofs + $len - 1] = ($self->{rs_tab}) x $len; + } else { + if ($act eq "*") { + @$rend[$ofs .. $ofs + $len - 1] = ($self->{rs_tab_act}) x $len; + } elsif ($act eq $self->{timeouts}[0][1]) { + @$rend[$ofs .. $ofs + $len - 1] = ($self->{rs_tab_acd}) x $len; + } elsif ($act ne " ") { + @$rend[$ofs .. $ofs + $len - 1] = ($self->{rs_tab_acs}) x $len; + } + } + + # sub with make current will activate events with mouse buttons + push @ofs, [ $ofs, $ofs + $len, sub { $_[0]->make_current ($tab) } ]; + $ofs += $len + 1; + } + + $self->{tabofs} = \@ofs; + + $self->ROW_t (0, $text, 0, 0, $ncol); + $self->ROW_r (0, $rend, 0, 0, $ncol); + + $self->want_refresh; +} + +sub new_tab { + my ($self, @argv) = @_; + + my $tab_name = shift @argv; + + # save a backlink to us, make sure tabbed is inactive + push @urxvt::TERM_INIT, sub { + my ($term) = @_; + $term->{parent} = $self; + + for (0 .. urxvt::NUM_RESOURCES - 1) { + my $value = $self->{resource}[$_]; + + $term->resource ("+$_" => $value) + if defined $value; + } + + $term->resource (perl_ext_2 => $term->resource ("perl_ext_2") . ",-tabbedalt"); + }; + + push @urxvt::TERM_EXT, urxvt::ext::tabbedalt::tab::; + + my $term = new urxvt::term + $self->env, $urxvt::RXVTNAME, + -embed => $self->parent, + @argv, + ; + # add name to new created tab. + $self->{tabs}[-1]->{name} = $tab_name; +} + +sub configure { + my ($self) = @_; + + my $tab = $self->{cur}; + + # this is an extremely dirty way to force a configurenotify, but who cares + $tab->XMoveResizeWindow ( + $tab->parent, + 0, $self->{tabheight} + 1, + $self->width, $self->height - $self->{tabheight} + ); + $tab->XMoveResizeWindow ( + $tab->parent, + 0, $self->{tabheight}, + $self->width, $self->height - $self->{tabheight} + ); +} + +# this is needed just to properly resize terminal to fill available space +# without it Window Maker will make window smaller then required, therefore +# we'll get ugly border. +sub on_resize_all_windows { + my ($self, $width, $height) = @_; + + 1 +} + +sub copy_properties { + my ($self) = @_; + my $tab = $self->{cur}; + + my $wm_normal_hints = $self->XInternAtom ("WM_NORMAL_HINTS"); + + my $current = delete $self->{current_properties}; + + # pass 1: copy over properties different or nonexisting + for my $atom ($tab->XListProperties ($tab->parent)) { + my ($type, $format, $items) = $self->XGetWindowProperty ($tab->parent, $atom); + + # fix up size hints + if ($atom == $wm_normal_hints) { + my (@hints) = unpack "l!*", $items; + + $hints[$_] += $self->{tabheight} for (0, 1, 4, 6, 16); + + $items = pack "l!*", @hints; + } + + my $cur = delete $current->{$atom}; + + # update if changed, we assume empty items and zero type and format will not happen + $self->XChangeProperty ($self->parent, $atom, $type, $format, $items) + if $cur->[0] != $type or $cur->[1] != $format or $cur->[2] ne $items; + + $self->{current_properties}{$atom} = [$type, $format, $items]; + } + + # pass 2, delete all extraneous properties + $self->XDeleteProperty ($self->parent, $_) for keys %$current; +} + +sub make_current { + my ($self, $tab) = @_; + + if (my $cur = $self->{cur}) { + delete $cur->{lastActivity}; + $cur->XUnmapWindow ($cur->parent) if $cur->mapped; + $cur->focus_out; + } + + $self->{cur} = $tab; + + $self->configure; + $self->copy_properties; + + $tab->focus_out; # just in case, should be a nop + $tab->focus_in if $self->focus; + + $tab->XMapWindow ($tab->parent); + delete $tab->{lastActivity}; + $self->refresh; + + () +} + +sub on_focus_in { + my ($self, $event) = @_; + + $self->{cur}->focus_in; + + () +} + +sub on_focus_out { + my ($self, $event) = @_; + + $self->{cur}->focus_out; + + () +} + +sub on_tt_write { + my ($self, $octets) = @_; + + $self->{cur}->tt_write ($octets); + + 1 +} + +sub on_key_press { + my ($self, $event) = @_; + + $self->{cur}->key_press ($event->{state}, $event->{keycode}, $event->{time}); + + 1 +} + +sub on_key_release { + my ($self, $event) = @_; + + $self->{cur}->key_release ($event->{state}, $event->{keycode}, $event->{time}); + + 1 +} + +sub on_button_press { + 1 +} + +sub on_button_release { + my ($self, $event) = @_; + + if ($event->{row} == 0) { + for my $button (@{ $self->{tabofs} }) { + $button->[2]->($self, $event) + if $event->{col} >= $button->[0] + && $event->{col} < $button->[1]; + } + } + + 1 +} + +sub on_motion_notify { + 1 +} + +sub on_init { + my ($self) = @_; + + $self->{resource} = [map $self->resource ("+$_"), 0 .. urxvt::NUM_RESOURCES - 1]; + + $self->resource (int_bwidth => 0); + $self->resource (name => "URxvt.tabbedalt"); + $self->resource (pty_fd => -1); + + $self->option ($urxvt::OPTION{scrollBar}, 0); + + my $fg = $self->x_resource ("tabbar-fg"); + my $bg = $self->x_resource ("tabbar-bg"); + my $tabfg = $self->x_resource ("tab-fg"); + my $tabbg = $self->x_resource ("tab-bg"); + my $active = $self->x_resource ("active-fg"); + my $actives = $self->x_resource ("actives-fg"); + my $actived = $self->x_resource ("actived-fg"); + + defined $fg or $fg = 8; + defined $bg or $bg = 0; + defined $tabfg or $tabfg = 15; + defined $tabbg or $tabbg = 8; + defined $active or $active = 1; + defined $actives or $actives = 5; + defined $actived or $actived = 4; + + $self->{rs_tabbar} = urxvt::SET_COLOR (urxvt::DEFAULT_RSTYLE, $fg + 2, $bg + 2); + $self->{rs_tab} = urxvt::SET_COLOR (urxvt::DEFAULT_RSTYLE, $tabfg + 2, $tabbg + 2); + $self->{rs_tab_act} = urxvt::SET_COLOR (urxvt::DEFAULT_RSTYLE, $active + 2, $bg + 2); + $self->{rs_tab_acs} = urxvt::SET_COLOR (urxvt::DEFAULT_RSTYLE, $actives + 2, $bg + 2); + $self->{rs_tab_acd} = urxvt::SET_COLOR (urxvt::DEFAULT_RSTYLE, $actived + 2, $bg + 2); + + my $timeouts = $self->x_resource ("tabbar-timeouts"); + $timeouts = '16:.:8:::4:+' unless defined $timeouts; + + if ($timeouts ne '') { + my @timeouts; + while ($timeouts =~ /^(\d+):(.)(?::(.*))?$/) { + push @timeouts, [ int $1, $2 ]; + $timeouts = defined $3 ? $3 : ''; + } + if (@timeouts) { + $self->{timeouts} = [ sort { $b->[0] <=> $a-> [0] } @timeouts ]; + } + } + + $self->{new_button} = + ($self->x_resource ('new-button') or 'true') !~ /^(?:false|0|no)/i; + + $self->{tab_numbers} = + ($self->x_resource ('tab-numbers') or 'true') !~ /^(?:false|0|no)/i; + + $self->{disable_shift_down} = + ($self->x_resource ('disable-shift-down') + or 'false') =~ /^(?:true|1|yes)/i; + + %{$self->{tabcmds}} = (); + for (my $idx = 1; defined (my $res = $self->x_resource("tabcmds.$idx")); $idx++) { + + chomp($res); + (my @args) = split('\|', $res); + my $key = uc(shift(@args)); + + if ($#args == 0) { + $self->{tabcmds}{$key} = [ $args[0] ]; + } else { + # split command, insert '-e' before it, re-add tab name at the + # beginning + (my @new_args) = ('-e'); + push @new_args, split / /, $args[1]; + unshift @new_args, $args[0]; + $self->{tabcmds}{$key} = [ @new_args ]; + } + } + + @{$self->{session}} = split('\|', $self->x_resource("session")) or (); + + (); + +} + +sub on_start { + my ($self) = @_; + + $self->{tabheight} = $self->int_bwidth + $self->fheight + $self->lineSpace; + + $self->cmd_parse ("\033[?25l"); + + my @argv = $self->argv; + + do { + shift @argv; + } while @argv && $argv[0] ne "-e"; + + # Ugly as hell ``session'' implementation + if (!(@argv) && (qx(ps x|grep "[ ]urxvt\$"|wc -l) < 2) && scalar(@{$self->{session}})){ + + my $count = 0; + my @command; + for my $item (@{$self->{session}}){ + if (exists($self->{tabcmds}{uc($item)})) { + $self->new_tab(@{$self->{tabcmds}{uc($item)}}); + $count++; + } + } + if ($count == 0) { + # no keys was valid, failsafe shell. + $self->new_tab ("shell", @argv); + } + + } else { + $self->new_tab ("shell", @argv); + } + + if (defined $self->{timeouts}) { + my $interval = ($self->{timeouts}[@{ $self->{timeouts} } - 1]->[0]); + $interval = int($interval / 4); + $self->{timer} = urxvt::timer->new + ->interval($interval < 1 ? 1 : $interval) + ->cb ( sub { $self->refresh; } ); + } + + () +} + +sub on_configure_notify { + my ($self, $event) = @_; + + $self->configure; + $self->refresh; + + () +} + +sub on_wm_delete_window { + my ($self) = @_; + + $_->destroy for @{ $self->{tabs} }; + + 1 +} + +sub tab_start { + my ($self, $tab) = @_; + + $tab->XChangeInput ($tab->parent, urxvt::PropertyChangeMask); + + push @{ $self->{tabs} }, $tab; + +# $tab->{name} ||= scalar @{ $self->{tabs} }; + $self->make_current ($tab); + + () +} + +sub tab_destroy { + my ($self, $tab) = @_; + + $self->{tabs} = [ grep $_ != $tab, @{ $self->{tabs} } ]; + + if (@{ $self->{tabs} }) { + if ($self->{cur} == $tab) { + delete $self->{cur}; + $self->make_current ($self->{tabs}[-1]); + } else { + $self->refresh; + } + } else { + # delay destruction a tiny bit + $self->{destroy} = urxvt::iw->new->start->cb (sub { $self->destroy }); + } + + () +} + +sub tab_key_press { + my ($self, $tab, $event, $keysym, $str) = @_; + + # defaults + if ($tab->{is_inputting_name}) { + if ($keysym == 0xff0d || $keysym == 0xff8d) { # enter + $tab->{name} = $tab->{new_name}; + $tab->{is_inputting_name} = 0; + } elsif ($keysym == 0xff1b) { # escape + $tab->{name} = $tab->{old_name}; + $tab->{is_inputting_name} = 0; + } elsif ($keysym == 0xff08) { # backspace + substr $tab->{new_name}, -1, 1, ""; + $tab->{name} = "$tab->{new_name}█"; + } elsif ($str !~ /[\x00-\x1f\x80-\xaf]/) { + $tab->{new_name} .= $str; + $tab->{name} = "$tab->{new_name}█"; + } + $self->refresh; + return 1; + } + + if ($event->{state} & urxvt::ShiftMask) { + if ($event->{state} & urxvt::ControlMask) { + if (exists($self->{tabcmds}{chr($keysym)})) { + # Execute user defined classes of shell programs. + $self->new_tab(@{$self->{tabcmds}{chr($keysym)}}); + return 1; + } elsif ($self->{disable_shift_down} and $keysym == 0x4e) { + # As a failsafe watch under CTRL+SHIFT+N for shell class (if + # SHIFT+DOWN is disabled). + $self->new_tab("shell"); + return 1; + } + } elsif ($keysym == 0xff51 || $keysym == 0xff53) { + my ($idx) = grep $self->{tabs}[$_] == $tab, 0 .. $#{ $self->{tabs} }; + + --$idx if $keysym == 0xff51; + ++$idx if $keysym == 0xff53; + + $self->make_current ($self->{tabs}[$idx % @{ $self->{tabs}}]); + + return 1; + } elsif ($keysym == 0xff52) { + $tab->{is_inputting_name} = 1; + $tab->{old_name} = $tab->{name} ? $tab->{name} : ""; + $tab->{new_name} = ""; + $tab->{name} = "█"; + $self->refresh; + return 1; + } elsif (not $self->{disable_shift_down} and $keysym == 0xff54) { + # Run shell on SHIFT+DOWN, if enabled. + $self->new_tab("shell"); + return 1; + } + } + elsif ($event->{state} & urxvt::ControlMask) { + if ($keysym == 0xff51 || $keysym == 0xff53) { + # tab movement + my ($idx1) = grep $self->{tabs}[$_] == $tab, 0 .. $#{ $self->{tabs} }; + my $idx2 = ($idx1 + ($keysym == 0xff51 ? -1 : +1)) % @{ $self->{tabs} }; + + ($self->{tabs}[$idx1], $self->{tabs}[$idx2]) = + ($self->{tabs}[$idx2], $self->{tabs}[$idx1]); + + $self->make_current ($self->{tabs}[$idx2]); + + return 1; + } elsif ($keysym > 0x2f and $keysym < 0x40) { + # make ctrl+1...0 switch to proper tab + my $num = $keysym - 0x30; + if ($num == 0) { + $num = 10; + } + $num--; + + if ($#{$self->{tabs}} >= $num){ + $self->make_current ($self->{tabs}[$num]); + } + + return 1; + } + } + + () +} + + +sub tab_add_lines { + my ($self, $tab) = @_; + my $mark = $self->tab_activity_mark($tab); + $tab->{lastActivity} = int urxvt::NOW; + $self->refresh if $mark ne $self->tab_activity_mark($tab); + (); +} + +sub tab_property_notify { + my ($self, $tab, $event) = @_; + + $self->copy_properties + if $event->{window} == $tab->parent; + + () +} + +package urxvt::ext::tabbedalt::tab; + +# helper extension implementing the subwindows of a tabbed terminal. +# simply proxies all interesting calls back to the tabbed class. + +{ + for my $hook (qw(start destroy key_press add_lines property_notify)) { + eval qq{ + sub on_$hook { + my \$parent = \$_[0]{term}{parent} + or return; + \$parent->tab_$hook (\@_) + } + }; + die if $@; + } +} + +# vim: tabstop=3 softtabstop=3 shiftwidth=3 expandtab diff --git a/home/.urxvt/ext/tabbedex b/home/.urxvt/ext/tabbedex new file mode 100755 index 0000000..ada0e2f --- /dev/null +++ b/home/.urxvt/ext/tabbedex @@ -0,0 +1,678 @@ +#! perl +# 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 . +# +## +## Tabbed plugin for rxvt-unicode +## Modified by Michal Nazarewicz (mina86/AT/mina86.com), StephenB +## (mail4stb/AT/gmail.com), Steven Merrill +## , Mark Pustjens +## and more... +## +## The following has been added: +## +## 1. Depending on time of last activity, activity character differs. +## By default, after 4 seconds an asterisk becomes a plus sing, +## after next 4 it becomes a colon, and finally, after another 8 +## seconds it becomes a dot. This can be configured via +## tabbar-timeouts resource. It's format is: +## +## ( ":" ":" )* ":" ":" +## +## where is timeout in seconds and is +## a single activity character. +## +## 2. The "[NEW]" button can be disabled (who on Earth uses mouse to +## create new tab anyways?) by setting new-button resource to yes. +## +## 3. If title resource is true, tab's title is displayed after last +## button. This is handy if you have terminal with no window +## decorations. Colours can be configured via title-fg and +## title-bg. +## +## 4. Incorporated Alexey Semenko patch adding +## autohide resource. If it's true tab bar is hidden if there is +## no more then one tab opened. +## +## 5. Tabs are indexed in starting with zero hex. :] If you're such +## a geek to use urxvt it shouldn't be a problem for you and it +## saves few character when many tabs are opened. +## +## 6. As a minor modification: Final pipe character is removed (unless +## title is displayed). This make tab bar look nicer. +## +## Added by StephenB: +## +## 7. Tabs can be named with Shift+Up (Enter to confirm, Escape to +## cancel). +## +## 8. "[NEW]" button disabled by default. +## +## Added by Steven Merrill +## +## 9. Ability to start a new tab or cycle through tabs via user +## commands: tabbedex:(new|next|prev)_tab . +## e.g. (in .Xdefaults) URxvt.keysym.M-t: perl:tabbedex:new_tab +## (see the urxvt man file for more info about keysym) +## +## 10. Fix an issue whereby on_user_command would not properly get sent +## to other extension packages if the mouse was not over the urxvt +## window. +## +## Added by Thomas Jost: +## +## 11. Add several user commands: tabbedex:rename_tab, +## tabbedex:move_tab_(left|right). +## e.g. (see 9.) URxvt.keysym.C-S-Left: perl:tabbex:move_tab_left +## +## 12. Ability to disable the default keybindings using the +## no-tabbedex-keys resource. +## +## Added by xanf (Illya Klymov): +## +## 13. Ability to display non-latin characters in tab title. +## +## Added by jpkotta: +## +## 14. Tabs inherit command line options. +## +## Added by Mark Pustjens +## +## 15. Resources are now read respecting the -name option. +## +## 16. Ability to prevent the last tab from closing. +## Use the following in your ~/.Xdefaults to enable: +## URXvt.tabbed.reopen-on-close: yes +## + +use Encode qw(decode); + +sub update_autohide { + my ($self, $reconfigure) = @_; + my $oldh = $self->{tabheight}; + if ($self->{autohide} && @{ $self->{tabs} } <= 1 && + ! (@{ $self->{tabs} } == 1 && $self->{tabs}[-1]->{name})) { + $self->{tabheight} = 0; + } else { + $self->{tabheight} = $self->{maxtabheight}; + } + if ($reconfigure && $self->{tabheight} != $oldh) { + $self->configure; + $self->copy_properties; + } +} + + +sub tab_activity_mark ($$) { + my ($self, $tab) = @_; + return ' ' unless defined $tab->{lastActivity}; + return ' ' if $tab == $self->{cur}; + if (defined $self->{timeouts}) { + my $diff = int urxvt::NOW - $tab->{lastActivity}; + for my $spec (@{ $self->{timeouts} }) { + return $spec->[1] if $diff > $spec->[0]; + } + } + '*'; +} + + +sub refresh { + my ($self) = @_; + + # autohide makes it zero + return unless $self->{tabheight}; + + my $ncol = $self->ncol; + + my $text = " " x $ncol; + my $rend = [($self->{rs_tabbar}) x $ncol]; + + my ($ofs, $idx, @ofs) = (0, 0); + + if ($self->{new_button}) { + substr $text, 0, 7, "[NEW] |"; + @$rend[0 .. 5] = ($self->{rs_tab}) x 6; + push @ofs, [0, 6, -1 ]; + $ofs = 7; + } + + for my $tab (@{ $self->{tabs} }) { + my $name = $tab->{name} ? $tab->{name} : $idx; + my $act = $self->tab_activity_mark($tab); + my $txt = sprintf "%s%s%s", $act, $name, $act; + my $len = length $txt; + + substr $text, $ofs, $len + 1, "$txt|"; + @$rend[$ofs .. $ofs + $len - 1] = ($self->{rs_tab}) x $len + if $tab == $self->{cur}; + + push @ofs, [ $ofs, $ofs + $len, $idx ]; + ++$idx; + $ofs += $len + 1; + } + + substr $text, --$ofs, 1, ' '; # remove last '|' + + if ($self->{tab_title} && $ofs + 3 < $ncol) { + my $term = $self->{term}; + my @str = $term->XGetWindowProperty($term->parent, $self->{tab_title}); + if (@str && $str[2]) { + my $str = '| ' . decode("utf8", $str[2]); + my $len = length $str; + $len = $ncol - $ofs if $ofs + $len > $ncol; + substr $text, $ofs, $len, substr $str, 0, $len; + @$rend[$ofs + 2 .. $ofs + $len - 1] = ($self->{rs_title}) x ($len - 2); + } + } + + $self->{tabofs} = \@ofs; + + $self->ROW_t (0, $text, 0, 0, $ncol); + $self->ROW_r (0, $rend, 0, 0, $ncol); + + $self->want_refresh; +} + + +sub new_tab { + my ($self, @argv) = @_; + + my $offset = $self->fheight; + + $self->{tabheight} = $self->{maxtabheight} + unless $self->{autohide} && !(defined $self->{tabs} && @{ $self->{tabs} }); + + # save a backlink to us, make sure tabbedex is inactive + push @urxvt::TERM_INIT, sub { + my ($term) = @_; + $term->{parent} = $self; + + for (0 .. urxvt::NUM_RESOURCES - 1) { + my $value = $self->{resource}[$_]; + + $term->resource ("+$_" => $value) + if defined $value; + } + + foreach my $opt (keys %urxvt::OPTION) { + my $value = $self->{option}{$opt}; + $term->option($urxvt::OPTION{$opt}, $value); + } + + $term->resource (perl_ext_2 => $term->resource ("perl_ext_2") . ",-tabbedex"); + }; + + push @urxvt::TERM_EXT, urxvt::ext::tabbedex::tab::; + + my $term = new urxvt::term + $self->env, $urxvt::RXVTNAME, + -embed => $self->parent, + @argv; +} + + +sub configure { + my ($self) = @_; + + my $tab = $self->{cur}; + + # this is an extremely dirty way to force a configurenotify, but who cares + $tab->XMoveResizeWindow ( + $tab->parent, + 0, $self->{tabheight} + 1, + $self->width, $self->height - $self->{tabheight} + ); + $tab->XMoveResizeWindow ( + $tab->parent, + 0, $self->{tabheight}, + $self->width, $self->height - $self->{tabheight} + ); +} + + +sub copy_properties { + my ($self) = @_; + my $tab = $self->{cur}; + + my $wm_normal_hints = $self->XInternAtom ("WM_NORMAL_HINTS"); + + my $current = delete $self->{current_properties}; + + # pass 1: copy over properties different or nonexisting + for my $atom ($tab->XListProperties ($tab->parent)) { + my ($type, $format, $items) = $self->XGetWindowProperty ($tab->parent, $atom); + + # fix up size hints + if ($atom == $wm_normal_hints) { + my (@hints) = unpack "l!*", $items; + + $hints[$_] += $self->{tabheight} for (4, 6, 16); + + $items = pack "l!*", @hints; + } + + my $cur = delete $current->{$atom}; + + # update if changed, we assume empty items and zero type and format will not happen + $self->XChangeProperty ($self->parent, $atom, $type, $format, $items) + if $cur->[0] != $type or $cur->[1] != $format or $cur->[2] ne $items; + + $self->{current_properties}{$atom} = [$type, $format, $items]; + } + + # pass 2, delete all extraneous properties + $self->XDeleteProperty ($self->parent, $_) for keys %$current; +} + + +sub my_resource { + my $self = shift; + $self->x_resource ("tabbed.$_[0]"); +} + + +sub make_current { + my ($self, $tab) = @_; + + if (my $cur = $self->{cur}) { + delete $cur->{lastActivity}; + $cur->XUnmapWindow ($cur->parent) if $cur->mapped; + $cur->focus_out; + } + + $self->{cur} = $tab; + + $self->configure; + $self->copy_properties; + + $tab->focus_out; # just in case, should be a nop + $tab->focus_in if $self->focus; + + $tab->XMapWindow ($tab->parent); + delete $tab->{lastActivity}; + $self->refresh; + + (); +} + + +sub on_focus_in { + my ($self, $event) = @_; + $self->{cur}->focus_in; + (); +} + +sub on_focus_out { + my ($self, $event) = @_; + $self->{cur}->focus_out; + (); +} + +sub on_key_press { + my ($self, $event) = @_; + $self->{cur}->key_press ($event->{state}, $event->{keycode}, $event->{time}); + 1; +} + +sub on_key_release { + my ($self, $event) = @_; + $self->{cur}->key_release ($event->{state}, $event->{keycode}, $event->{time}); + 1; +} + +sub on_button_release { + my ($self, $event) = @_; + + if ($event->{row} == 0) { + my $col = $event->{col}; + for my $button (@{ $self->{tabofs} }) { + last if $col < $button->[0]; + next unless $col <= $button->[1]; + if ($button->[2] == -1) { + $self->new_tab; + } else { + $self->make_current($self->{tabs}[$button->[2]]); + } + } + return 1; + } + + (); +} + +sub on_init { + my ($self) = @_; + + $self->{resource} = [map $self->resource ("+$_"), 0 .. urxvt::NUM_RESOURCES - 1]; + + $self->resource (int_bwidth => 0); + $self->resource (pty_fd => -1); + + $self->{option} = {}; + for my $key (keys %urxvt::OPTION) { + $self->{option}{$key} = $self->option($urxvt::OPTION{$key}); + } + + # this is for the tabs terminal; order is important + $self->option ($urxvt::OPTION{scrollBar}, 0); + + my $fg = $self->my_resource ("tabbar-fg"); + my $bg = $self->my_resource ("tabbar-bg"); + my $tabfg = $self->my_resource ("tab-fg"); + my $tabbg = $self->my_resource ("tab-bg"); + my $titfg = $self->my_resource ("title-fg"); + my $titbg = $self->my_resource ("title-bg"); + + defined $fg or $fg = 3; + defined $bg or $bg = 0; + defined $tabfg or $tabfg = 0; + defined $tabbg or $tabbg = 1; + defined $titfg or $titfg = 2; + defined $titbg or $titbg = 0; + + $self->{rs_tabbar} = urxvt::SET_COLOR (urxvt::DEFAULT_RSTYLE, $fg + 2, $bg + 2); + $self->{rs_tab} = urxvt::SET_COLOR (urxvt::DEFAULT_RSTYLE, $tabfg + 2, $tabbg + 2); + $self->{rs_title} = urxvt::SET_COLOR (urxvt::DEFAULT_RSTYLE, $titfg + 2, $titbg + 2); + + + my $timeouts = $self->my_resource ("tabbar-timeouts"); + $timeouts = '16:.:8:::4:+' unless defined $timeouts; + if ($timeouts ne '') { + my @timeouts; + while ($timeouts =~ /^(\d+):(.)(?::(.*))?$/) { + push @timeouts, [ int $1, $2 ]; + $timeouts = defined $3 ? $3 : ''; + } + if (@timeouts) { + $self->{timeouts} = [ sort { $b->[0] <=> $a-> [0] } @timeouts ]; + } + } + + $self->{new_button} = + ($self->my_resource ('new-button') or 'false') !~ /^(?:false|0|no)/i; + $self->{tab_title} = + ($self->my_resource ('title') or 'true') !~ /^(?:false|0|no)/i; + $self->{autohide} = + ($self->my_resource ('autohide') or 'false') !~ /^(?:false|0|no)/i; + $self->{no_default_keys} = + ($self->my_resource ('no-tabbedex-keys') or 'false') !~ /^(?:false|0|no)/i; + $self->{reopen_on_close} = + ($self->my_resource ('reopen-on-close') or 'false') !~ /^(?:false|0|no)/i; + + (); +} + + +sub on_start { + my ($self) = @_; + + $self->{maxtabheight} = $self->int_bwidth + $self->fheight + $self->lineSpace; + $self->{tabheight} = $self->{autohide} ? 0 : $self->{maxtabheight}; + + $self->{running_user_command} = 0; + + $self->cmd_parse ("\033[?25l"); + + my @argv = $self->argv; + + do { + shift @argv; + } while @argv && $argv[0] ne "-e"; + + if ($self->{tab_title}) { + $self->{tab_title} = $self->{term}->XInternAtom("_NET_WM_NAME", 1); + } + + $self->new_tab (@argv); + + if (defined $self->{timeouts}) { + my $interval = ($self->{timeouts}[@{ $self->{timeouts} } - 1]->[0]); + $interval = int($interval / 4); + $self->{timer} = urxvt::timer->new + ->interval($interval < 1 ? 1 : $interval) + ->cb ( sub { $self->refresh; } ); + } + + (); +} + + +sub on_configure_notify { + my ($self, $event) = @_; + + $self->configure; + $self->refresh; + + (); +} + + +sub on_user_command { + my ($self, $event) = @_; + + $self->{cur}->{term}->{parent}->tab_user_command($self->{cur}, $event, 1); + + (); +} + + +sub on_wm_delete_window { + my ($self) = @_; + $_->destroy for @{ $self->{tabs} }; + 1; +} + + +sub tab_start { + my ($self, $tab) = @_; + + $tab->XChangeInput ($tab->parent, urxvt::PropertyChangeMask); + + push @{ $self->{tabs} }, $tab; + +# $tab->{name} ||= scalar @{ $self->{tabs} }; + $self->make_current ($tab); + + (); +} + + +sub tab_destroy { + my ($self, $tab) = @_; + + if ($self->{reopen_on_close} && $#{ $self->{tabs} } == 0) { + $self->new_tab; + $self->make_current ($self->{tabs}[-1]); + } + + $self->{tabs} = [ grep $_ != $tab, @{ $self->{tabs} } ]; + $self->update_autohide (); + + if (@{ $self->{tabs} }) { + if ($self->{cur} == $tab) { + delete $self->{cur}; + $self->make_current ($self->{tabs}[-1]); + } else { + $self->refresh; + } + } else { + # delay destruction a tiny bit + $self->{destroy} = urxvt::iw->new->start->cb (sub { $self->destroy }); + } + + (); +} + + +sub tab_key_press { + my ($self, $tab, $event, $keysym, $str) = @_; + + if ($tab->{is_inputting_name}) { + if ($keysym == 0xff0d || $keysym == 0xff8d) { # enter + $tab->{name} = $tab->{new_name}; + $tab->{is_inputting_name} = 0; + $self->update_autohide (1); + } elsif ($keysym == 0xff1b) { # escape + $tab->{name} = $tab->{old_name}; + $tab->{is_inputting_name} = 0; + $self->update_autohide (1); + } elsif ($keysym == 0xff08) { # backspace + substr $tab->{new_name}, -1, 1, ""; + $tab->{name} = "$tab->{new_name}█"; + } elsif ($str !~ /[\x00-\x1f\x80-\xaf]/) { + $tab->{new_name} .= $str; + $tab->{name} = "$tab->{new_name}█"; + } + $self->refresh; + return 1; + } + + return () if ($self->{no_default_keys}); + + if ($event->{state} & urxvt::ShiftMask) { + if ($keysym == 0xff51 || $keysym == 0xff53) { + if (@{ $self->{tabs} } > 1) { + $self->change_tab($tab, $keysym - 0xff52); + } + return 1; + + } elsif ($keysym == 0xff54) { + $self->new_tab; + return 1; + + } elsif ($keysym == 0xff52) { + $self->rename_tab($tab); + return 1; + } + } elsif ($event->{state} & urxvt::ControlMask) { + if ($keysym == 0xff51 || $keysym == 0xff53) { + $self->move_tab($tab, $keysym - 0xff52); + return 1; + } + } + + (); +} + + +sub tab_property_notify { + my ($self, $tab, $event) = @_; + + $self->copy_properties + if $event->{window} == $tab->parent; + + (); +} + + +sub tab_add_lines { + my ($self, $tab) = @_; + my $mark = $self->tab_activity_mark($tab); + $tab->{lastActivity} = int urxvt::NOW; + $self->refresh if $mark ne $self->tab_activity_mark($tab); + (); +} + + +sub tab_user_command { + my ($self, $tab, $cmd, $proxy_events) = @_; + + if ($cmd eq 'tabbedex:new_tab') { + $self->new_tab; + } + elsif ($cmd eq 'tabbedex:next_tab') { + $self->change_tab($tab, 1); + } + elsif ($cmd eq 'tabbedex:prev_tab') { + $self->change_tab($tab, -1); + } + elsif ($cmd eq 'tabbedex:move_tab_left') { + $self->move_tab($tab, -1); + } + elsif ($cmd eq 'tabbedex:move_tab_right') { + $self->move_tab($tab, 1); + } + elsif ($cmd eq 'tabbedex:rename_tab') { + $self->rename_tab($tab); + } + else { + # Proxy the user command through to the tab's term, while taking care not + # to get caught in an infinite loop. + if ($proxy_events && $self->{running_user_command} == 0) { + $self->{running_user_command} = 1; + urxvt::invoke($tab->{term}, 20, $cmd); + $self->{running_user_command} = 0; + } + } + + (); +} + +sub change_tab { + my ($self, $tab, $direction) = @_; + + my $idx = 0; + ++$idx while $self->{tabs}[$idx] != $tab; + $idx += $direction; + $self->make_current ($self->{tabs}[$idx % @{ $self->{tabs}}]); + + (); +} + +sub move_tab { + my ($self, $tab, $direction) = @_; + + if (@{ $self->{tabs} } > 1) { + my $idx1 = 0; + ++$idx1 while $self->{tabs}[$idx1] != $tab; + my $idx2 = ($idx1 + $direction) % @{ $self->{tabs} }; + + ($self->{tabs}[$idx1], $self->{tabs}[$idx2]) = + ($self->{tabs}[$idx2], $self->{tabs}[$idx1]); + $self->make_current ($self->{tabs}[$idx2]); + } + + (); +} + +sub rename_tab { + my ($self, $tab) = @_; + + $tab->{is_inputting_name} = 1; + $tab->{old_name} = $tab->{name} ? $tab->{name} : ""; + $tab->{new_name} = ""; + $tab->{name} = "█"; + $self->update_autohide (1); + $self->refresh; + + (); +} + +package urxvt::ext::tabbedex::tab; + +# helper extension implementing the subwindows of a tabbed terminal. +# simply proxies all interesting calls back to the tabbedex class. + +{ + for my $hook qw(start destroy user_command key_press property_notify add_lines) { + eval qq{ + sub on_$hook { + my \$parent = \$_[0]{term}{parent} + or return; + \$parent->tab_$hook (\@_) + } + }; + die if $@; + } +} diff --git a/home/.urxvt/ext/url-select-plus b/home/.urxvt/ext/url-select-plus new file mode 100644 index 0000000..5575a31 --- /dev/null +++ b/home/.urxvt/ext/url-select-plus @@ -0,0 +1,433 @@ +#! perl -w +# url-select created by: Bert Muennich (http://www.github.com/muennich/urxvt-perls) +# url-select-plus modification created by: spcmd (http://github.com/spcmd) +# License: GPLv2 + +# Use keyboard shortcuts to select and open URLs. +# This should be used as a replacement for the default matcher extension (also a replacement for the 'url-select' extension). +# This modification is named 'url-select-plus' to avoid naming conflict with the original 'url-select'. + +# Usage: +# copy this script to the '/lib/urxvt/perl' directory. +# then put the following lines in your .Xdefaults/.Xresources to load this extension: +# +# URxvt.perl-ext-common: ...,url-select-plus +# URxvt.keysym.M-u: perl:url-select-plus:select_next + +# Also put these options in your .Xdefaults/.Xresources: +# Options: +# URxvt.url-select-plus.autocopy : true/false (If true, selected URLs are copied to PRIMARY) +# URxvt.url-select-plus.button : Mouse button to click-open URLs (default: 2) +# URxvt.url-select-plus.launcher: : Browser/command to open selected URL with +# URxvt.url-select-plus.altlauncher : Alternative browser/command to open selected URL with +# URxvt.url-select-plus.mediaplayer : Mediaplayer to open selected URL with +# URxvt.url-select-plus.imgviewer : Image viewer to open selected URL with +# URxvt.url-select-plus.underline : If set to true, all URLs get underlined + +# Key bindings: +# 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 +# O: Open selected URL in alternative browser +# m: Open selected URL in mediaplayer (e.g.: mpv, mplayer) +# i: Open selected URL in image viewer (e.g.: feh) +# y: Copy (yank) selected URL and deactivate selection mode +# q/Escape: Deactivate URL selection mode + +use strict; + +# 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-plus.launcher')) { + @{$self->{browser}} = split /\s+/, $self->x_resource('url-select-plus.launcher'); + } else { + @{$self->{browser}} = ('x-www-browser'); + } + if ($self->x_resource('url-select-plus.altlauncher')) { + @{$self->{altbrowser}} = split /\s+/, $self->x_resource('url-select-plus.altlauncher'); + } else { + @{$self->{altbrowser}} = ('x-www-browser'); + } + if ($self->x_resource('url-select-plus.mediaplayer')) { + @{$self->{mediap}} = split /\s+/, $self->x_resource('url-select-plus.mediaplayer'); + } + if ($self->x_resource('url-select-plus.imgviewer')) { + @{$self->{imgv}} = split /\s+/, $self->x_resource('url-select-plus.imgviewer'); + } + if ($self->x_resource('url-select-plus.underline') eq 'true') { + $self->enable(line_update => \&line_update); + } + if ($self->x_resource('url-select-plus.autocopy') eq 'true') { + $self->{autocopy} = 1; + } + + $self->{state} = 0; + + for my $mod (split '', $self->x_resource("url-select-plus.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-plus:" . $action); +} + + +sub on_user_command { + my ($self, $cmd) = @_; + + if ($cmd eq 'url-select-plus: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') { + $self->exec_async(@{$self->{browser}}, ${$self->{found}[$self->{n}]}[4]); + deactivate($self) unless $char eq 'o'; + } elsif ($keysym == 0x004f || $char eq 'O') { + $self->exec_async(@{$self->{altbrowser}}, ${$self->{found}[$self->{n}]}[4]); + deactivate($self) unless $char eq 'O'; + } elsif ($keysym == 0x006d || $char eq 'm') { + $self->exec_async(@{$self->{mediap}}, ${$self->{found}[$self->{n}]}[4]); + deactivate($self); + } elsif ($keysym == 0x0069 || $char eq 'i') { + $self->exec_async(@{$self->{imgv}}, ${$self->{found}[$self->{n}]}[4]); + deactivate($self); + } 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}) { + $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); + my $keyhints = sprintf("%s", "Keys: o/Enter=launcher; O=alternative launcher; m=mediaplayer; i=image viewer; y=yank"); + + if ($self->top_row == 0) { + $text = $keyhints ." | " . $text ."All"; + } elsif ($self->view_start() == $self->top_row) { + $text = $keyhints ." | " . $text ."Top"; + } elsif ($self->view_start() == 0) { + $text = $keyhints ." | " . $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/home/.urxvt/ext/vtwheel b/home/.urxvt/ext/vtwheel new file mode 100644 index 0000000..baa8b92 --- /dev/null +++ b/home/.urxvt/ext/vtwheel @@ -0,0 +1,44 @@ +#!/usr/bin/env perl -w +# Implementa el desplazamiento del ratón cuando se utiliza vim, less, man, etc +# 1. Colocar este script en alguno de los siguientes directorios: +# /usr/lib/urxvt/perl/ +# "$HOME"/.urxvt/ext/perl/ +# 2. Después, agregar: +# URxvt.perl-ext-common:vtewheel +# En el fichero .Xdefaults o .Xresources +# 3. Finalmente ejecutar (según sea el caso) para cargar la nueva configuración: +# xrdb -merge "$HOME/.Xdefaults" +# xrdb -merge "$HOME/.Xresources" +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 (); +} diff --git a/home/.urxvt/urxvt/ext/font-size b/home/.urxvt/urxvt/ext/font-size new file mode 100644 index 0000000..4d10830 --- /dev/null +++ b/home/.urxvt/urxvt/ext/font-size @@ -0,0 +1,462 @@ +#!/usr/bin/env perl +# +# On-the-fly adjusting of the font size in urxvt +# +# Copyright (c) 2008 David O'Neill +# 2012 Noah K. Tilton +# 2009-2012 Simon Lundström +# 2012-2016 Jan Larres +# +# 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 +# +# X11 fonts background: +# http://keithp.com/~keithp/talks/xtc2001/paper/ + +#: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 +The extension automatically binds Ctrl++ to the 'increase' function, +Ctrl+- to 'decrease', and Ctrl+0 to 'reset'. To use the other available functions +or change the keys, add some keybindings of your own: + 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_init { + my ($self) = @_; + + $self->bind_action ("C-plus", "%:increase") + or warn "unable to register 'C-plus' as font-size increase hotkey\n"; + $self->bind_action ("C-minus", "%:decrease") + or warn "unable to register 'C-minus' as font-size decrease hotkey\n"; + $self->bind_action ("C-0", "%:reset") + or warn "unable to register 'C-0' as font-size reset hotkey\n"; +} + +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 () { + 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/home/.urxvt/urxvt/ext/fullscreen b/home/.urxvt/urxvt/ext/fullscreen new file mode 100644 index 0000000..8e49228 --- /dev/null +++ b/home/.urxvt/urxvt/ext/fullscreen @@ -0,0 +1,8 @@ +#! perl + +sub on_user_command { + my ($self, $cmd) = @_; + if ($cmd eq "fullscreen:switch") { + my $dummy = `wmctrl -r :ACTIVE: -b toggle,fullscreen,above` ; + } +} diff --git a/home/.urxvt/urxvt/ext/tabbedalt b/home/.urxvt/urxvt/ext/tabbedalt new file mode 100755 index 0000000..de5e310 --- /dev/null +++ b/home/.urxvt/urxvt/ext/tabbedalt @@ -0,0 +1,674 @@ + +#! perl +# Tabbed perl extension for rxvt-unicode terminal emulator. +# Modified by Roman Dobosz +# +# 2008-08-22 18:01:55 +# - Modified shortcuts for tab navigation - now it uses shift + left/right +# arrow to navigate, also creating new shell is changed to CTRL+Shift+n. +# - Added shortcuts to move tab between others witch CTRL left/right arrow +# - Added some predefined actions - CTRL+Shift+r for "su -" command and +# CTRL+Shift+m for "mc" and other like named ssh sessions. +# - Added labels for custom shells (like "root", "mc" and so on) +# +# Please note, I don't know Perl! +# +# 2009-11-23 11:11:19 +# - Added shortcuts for apps with Mod4 key (mutt as an example) +# +# 2009-11-23 13:25:13 +# - Merged activity indicator from +# http://mina86.com/2009/05/16/tabbed-urxvt-extension/#more but without +# changes on tabs (like adding term title just behind all tabs). New +# resources can be use to change defaults (as in original solution): +# - tabbed-timeouts with format: +# ( ":" ":")* ":" ":" +# default '16:.:8:::4:+'. Asterisk is always present as a first indicator +# character, just like in original tabbed extension. +# - new-button, default to 'true'. Used to disable [NEW] button. +# +# 2009-11-24 23:34:51 +# - Added possibility to quick switch between first ten tabs with predefined +# combination of CTRL+1..0 keys, which will activate proper tab. +# - Added possibility to remove numbers from tab names by setting resource +# tab-numbers to false. +# +# 2009-11-25 21:40:30 +# - Added colors for tabs, which have activity on them. First is to be set +# when first activity (active-fg, defaults to red) appear on inactive tab. +# Last one (actived-fg, blue by default) is set when there is no more +# possible timeouts. Third one (actives-fg, purple) is set on all in between +# of these two. +# +# 2010-07-25 13:49:01 +# - Integrated renaming ability for tabs from stepb +# (http://github.com/stepb/urxvt-tabbedex) +# +# 2010-08-12 20:54:46 +# - Added functionality to create definitions of custom shells as a X +# resource, under common tabcmds name. This functionality also deprecates +# feature called here as a predefined actions. Without any configuration +# only simple shell is available under CTRL+SHIFT+N shortcut. After creating +# first custom shell this default is not available. +# +# Let's assume, that one want to mimic previous configuration, that means +# three kind of custom shells: simple one (default shell in the system), +# midnight commander and root (namely - su command). Three resources should +# be created: +# +# URxvt.tabbedalt.tabcmds.1: N|shell +# URxvt.tabbedalt.tabcmds.2: R|root|su - +# URxvt.tabbedalt.tabcmds.3: M|mc|mc +# +# URxvt.tabbedalt.tabcmds.[number] is a ordinal number, started from 1. There +# shouldn't be gaps between numbers, otherwise custom shells defined after a +# gap will not work. +# +# Resource values are two or three pipe separated values, which are in order: +# - shortcut key, which will be used for invoking custom shell together with +# CTRL+SHIFT keys. Mod4 (aka Super or Windows key) are not supported, and +# most probably will be removed from script soon, as lots of window +# managers out there make a big use of those keys. +# Note: There is limitation for characters used as a shortcut. Because some +# of them are used for control terminal itself (i.e. CTRL+SHIFT+D may not +# work), and also other characters (digits, some special characters etc.). +# Letters are case insensitive. +# - name of the tab, it could be anything but the pipe. +# - optional command. If omitted, simple shell will be launched. +# +# 2010-08-28 10:17:02 +# - Removed tab_property_notify hook, because in certain circumstances it +# provides memory consumption. It is especially well seen by running +# mocp[1] and play internet radio station (i.e digitalgunfire.com, but +# there can be others). Observe memory taken by urxvt with top or ps. Also, +# original tabbed extension is affected. +# +# This change will affect i.e. dynamic font change - it will not expand +# window to reflect size of a font. Switching to next tab and back will +# rearrange content of a tab to current window size. +# +# If anyone have a better idea how to fix memory consumption which is taking +# place in copy_properties(), please step forward :) +# +# [1] http://moc.daper.net +# +# 2011-07-12 21:05:26 +# - Fixed defaults for not defined tabcommands - now it is possible to use +# tabbed just as described. +# - Added some sort of primitive session ability, defined via resource +# session, which should contain pipe separated shortcuts defined in tabcmds +# resource. If there is no shortcuts (or wrong was defined), plain shell tab +# will appear. +# +# 2013-11-12 09:23:49 +# - Restored tab_property_notify hook. Whatever was the cause of the memory +# consumption is gone or doesn't have anything to do with that function. +# +# 2013-11-26 19:31:55 +# - Added parentheses for hook, should work on Debian now. +# +# 2019-06-05 10:55:37 +# - fixed couple of bugs regarding session +# - changed default colors to more sane values +# +# 2019-09-13 15:15:18 +# - Added shortcut for creating new shell like in original tabbed +# (SHIFT+Down). It can be disabled by an option "disable-shift-down". More +# information in README. +# - Cleaned up a bit the code and comments. + +sub tab_activity_mark ($$) { + my ($self, $tab) = @_; + return ' ' unless defined $tab->{lastActivity}; + return ' ' if $tab == $self->{cur}; + if (defined $self->{timeouts}) { + my $diff = int urxvt::NOW - $tab->{lastActivity}; + for my $spec (@{ $self->{timeouts} }) { + return $spec->[1] if $diff > $spec->[0]; + } + } + + '*'; +} + +sub refresh { + my ($self) = @_; + + my $ncol = $self->ncol; + + my $text = " " x $ncol; + my $rend = [($self->{rs_tabbar}) x $ncol]; + + my ($ofs, $idx, @ofs) = (0, 0); + + if ($self->{new_button}) { + substr $text, 0, 7, "[NEW] |"; + @$rend[0 .. 5] = ($self->{rs_tab}) x 6; + push @ofs, [0, 6, sub { $_[0]->new_tab("shell") }]; + $ofs = 7; + } + + for my $tab (@{ $self->{tabs} }) { + $idx++; + my $act = $self->tab_activity_mark($tab); + my $txt; + + if ($self->{tab_numbers}){ + $txt = sprintf "%d-%s", $idx, $tab->{name}; + }else{ + $txt = sprintf "%s", $tab->{name}; + } + + $txt = sprintf "%s%s%s", $act, $txt, $act; + + my $len = length $txt; + + # fill offset in $text with $txt + "|" + substr $text, $ofs, $len + 1, "$txt|"; + + # find and fill with proper colors + + + if ($tab == $self->{cur}) { + @$rend[$ofs .. $ofs + $len - 1] = ($self->{rs_tab}) x $len; + } else { + if ($act eq "*") { + @$rend[$ofs .. $ofs + $len - 1] = ($self->{rs_tab_act}) x $len; + } elsif ($act eq $self->{timeouts}[0][1]) { + @$rend[$ofs .. $ofs + $len - 1] = ($self->{rs_tab_acd}) x $len; + } elsif ($act ne " ") { + @$rend[$ofs .. $ofs + $len - 1] = ($self->{rs_tab_acs}) x $len; + } + } + + # sub with make current will activate events with mouse buttons + push @ofs, [ $ofs, $ofs + $len, sub { $_[0]->make_current ($tab) } ]; + $ofs += $len + 1; + } + + $self->{tabofs} = \@ofs; + + $self->ROW_t (0, $text, 0, 0, $ncol); + $self->ROW_r (0, $rend, 0, 0, $ncol); + + $self->want_refresh; +} + +sub new_tab { + my ($self, @argv) = @_; + + my $tab_name = shift @argv; + + # save a backlink to us, make sure tabbed is inactive + push @urxvt::TERM_INIT, sub { + my ($term) = @_; + $term->{parent} = $self; + + for (0 .. urxvt::NUM_RESOURCES - 1) { + my $value = $self->{resource}[$_]; + + $term->resource ("+$_" => $value) + if defined $value; + } + + $term->resource (perl_ext_2 => $term->resource ("perl_ext_2") . ",-tabbedalt"); + }; + + push @urxvt::TERM_EXT, urxvt::ext::tabbedalt::tab::; + + my $term = new urxvt::term + $self->env, $urxvt::RXVTNAME, + -embed => $self->parent, + @argv, + ; + # add name to new created tab. + $self->{tabs}[-1]->{name} = $tab_name; +} + +sub configure { + my ($self) = @_; + + my $tab = $self->{cur}; + + # this is an extremely dirty way to force a configurenotify, but who cares + $tab->XMoveResizeWindow ( + $tab->parent, + 0, $self->{tabheight} + 1, + $self->width, $self->height - $self->{tabheight} + ); + $tab->XMoveResizeWindow ( + $tab->parent, + 0, $self->{tabheight}, + $self->width, $self->height - $self->{tabheight} + ); +} + +# this is needed just to properly resize terminal to fill available space +# without it Window Maker will make window smaller then required, therefore +# we'll get ugly border. +sub on_resize_all_windows { + my ($self, $width, $height) = @_; + + 1 +} + +sub copy_properties { + my ($self) = @_; + my $tab = $self->{cur}; + + my $wm_normal_hints = $self->XInternAtom ("WM_NORMAL_HINTS"); + + my $current = delete $self->{current_properties}; + + # pass 1: copy over properties different or nonexisting + for my $atom ($tab->XListProperties ($tab->parent)) { + my ($type, $format, $items) = $self->XGetWindowProperty ($tab->parent, $atom); + + # fix up size hints + if ($atom == $wm_normal_hints) { + my (@hints) = unpack "l!*", $items; + + $hints[$_] += $self->{tabheight} for (0, 1, 4, 6, 16); + + $items = pack "l!*", @hints; + } + + my $cur = delete $current->{$atom}; + + # update if changed, we assume empty items and zero type and format will not happen + $self->XChangeProperty ($self->parent, $atom, $type, $format, $items) + if $cur->[0] != $type or $cur->[1] != $format or $cur->[2] ne $items; + + $self->{current_properties}{$atom} = [$type, $format, $items]; + } + + # pass 2, delete all extraneous properties + $self->XDeleteProperty ($self->parent, $_) for keys %$current; +} + +sub make_current { + my ($self, $tab) = @_; + + if (my $cur = $self->{cur}) { + delete $cur->{lastActivity}; + $cur->XUnmapWindow ($cur->parent) if $cur->mapped; + $cur->focus_out; + } + + $self->{cur} = $tab; + + $self->configure; + $self->copy_properties; + + $tab->focus_out; # just in case, should be a nop + $tab->focus_in if $self->focus; + + $tab->XMapWindow ($tab->parent); + delete $tab->{lastActivity}; + $self->refresh; + + () +} + +sub on_focus_in { + my ($self, $event) = @_; + + $self->{cur}->focus_in; + + () +} + +sub on_focus_out { + my ($self, $event) = @_; + + $self->{cur}->focus_out; + + () +} + +sub on_tt_write { + my ($self, $octets) = @_; + + $self->{cur}->tt_write ($octets); + + 1 +} + +sub on_key_press { + my ($self, $event) = @_; + + $self->{cur}->key_press ($event->{state}, $event->{keycode}, $event->{time}); + + 1 +} + +sub on_key_release { + my ($self, $event) = @_; + + $self->{cur}->key_release ($event->{state}, $event->{keycode}, $event->{time}); + + 1 +} + +sub on_button_press { + 1 +} + +sub on_button_release { + my ($self, $event) = @_; + + if ($event->{row} == 0) { + for my $button (@{ $self->{tabofs} }) { + $button->[2]->($self, $event) + if $event->{col} >= $button->[0] + && $event->{col} < $button->[1]; + } + } + + 1 +} + +sub on_motion_notify { + 1 +} + +sub on_init { + my ($self) = @_; + + $self->{resource} = [map $self->resource ("+$_"), 0 .. urxvt::NUM_RESOURCES - 1]; + + $self->resource (int_bwidth => 0); + $self->resource (name => "URxvt.tabbedalt"); + $self->resource (pty_fd => -1); + + $self->option ($urxvt::OPTION{scrollBar}, 0); + + my $fg = $self->x_resource ("tabbar-fg"); + my $bg = $self->x_resource ("tabbar-bg"); + my $tabfg = $self->x_resource ("tab-fg"); + my $tabbg = $self->x_resource ("tab-bg"); + my $active = $self->x_resource ("active-fg"); + my $actives = $self->x_resource ("actives-fg"); + my $actived = $self->x_resource ("actived-fg"); + + defined $fg or $fg = 8; + defined $bg or $bg = 0; + defined $tabfg or $tabfg = 15; + defined $tabbg or $tabbg = 8; + defined $active or $active = 1; + defined $actives or $actives = 5; + defined $actived or $actived = 4; + + $self->{rs_tabbar} = urxvt::SET_COLOR (urxvt::DEFAULT_RSTYLE, $fg + 2, $bg + 2); + $self->{rs_tab} = urxvt::SET_COLOR (urxvt::DEFAULT_RSTYLE, $tabfg + 2, $tabbg + 2); + $self->{rs_tab_act} = urxvt::SET_COLOR (urxvt::DEFAULT_RSTYLE, $active + 2, $bg + 2); + $self->{rs_tab_acs} = urxvt::SET_COLOR (urxvt::DEFAULT_RSTYLE, $actives + 2, $bg + 2); + $self->{rs_tab_acd} = urxvt::SET_COLOR (urxvt::DEFAULT_RSTYLE, $actived + 2, $bg + 2); + + my $timeouts = $self->x_resource ("tabbar-timeouts"); + $timeouts = '16:.:8:::4:+' unless defined $timeouts; + + if ($timeouts ne '') { + my @timeouts; + while ($timeouts =~ /^(\d+):(.)(?::(.*))?$/) { + push @timeouts, [ int $1, $2 ]; + $timeouts = defined $3 ? $3 : ''; + } + if (@timeouts) { + $self->{timeouts} = [ sort { $b->[0] <=> $a-> [0] } @timeouts ]; + } + } + + $self->{new_button} = + ($self->x_resource ('new-button') or 'true') !~ /^(?:false|0|no)/i; + + $self->{tab_numbers} = + ($self->x_resource ('tab-numbers') or 'true') !~ /^(?:false|0|no)/i; + + $self->{disable_shift_down} = + ($self->x_resource ('disable-shift-down') + or 'false') =~ /^(?:true|1|yes)/i; + + %{$self->{tabcmds}} = (); + for (my $idx = 1; defined (my $res = $self->x_resource("tabcmds.$idx")); $idx++) { + + chomp($res); + (my @args) = split('\|', $res); + my $key = uc(shift(@args)); + + if ($#args == 0) { + $self->{tabcmds}{$key} = [ $args[0] ]; + } else { + # split command, insert '-e' before it, re-add tab name at the + # beginning + (my @new_args) = ('-e'); + push @new_args, split / /, $args[1]; + unshift @new_args, $args[0]; + $self->{tabcmds}{$key} = [ @new_args ]; + } + } + + @{$self->{session}} = split('\|', $self->x_resource("session")) or (); + + (); + +} + +sub on_start { + my ($self) = @_; + + $self->{tabheight} = $self->int_bwidth + $self->fheight + $self->lineSpace; + + $self->cmd_parse ("\033[?25l"); + + my @argv = $self->argv; + + do { + shift @argv; + } while @argv && $argv[0] ne "-e"; + + # Ugly as hell ``session'' implementation + if (!(@argv) && (qx(ps x|grep "[ ]urxvt\$"|wc -l) < 2) && scalar(@{$self->{session}})){ + + my $count = 0; + my @command; + for my $item (@{$self->{session}}){ + if (exists($self->{tabcmds}{uc($item)})) { + $self->new_tab(@{$self->{tabcmds}{uc($item)}}); + $count++; + } + } + if ($count == 0) { + # no keys was valid, failsafe shell. + $self->new_tab ("shell", @argv); + } + + } else { + $self->new_tab ("shell", @argv); + } + + if (defined $self->{timeouts}) { + my $interval = ($self->{timeouts}[@{ $self->{timeouts} } - 1]->[0]); + $interval = int($interval / 4); + $self->{timer} = urxvt::timer->new + ->interval($interval < 1 ? 1 : $interval) + ->cb ( sub { $self->refresh; } ); + } + + () +} + +sub on_configure_notify { + my ($self, $event) = @_; + + $self->configure; + $self->refresh; + + () +} + +sub on_wm_delete_window { + my ($self) = @_; + + $_->destroy for @{ $self->{tabs} }; + + 1 +} + +sub tab_start { + my ($self, $tab) = @_; + + $tab->XChangeInput ($tab->parent, urxvt::PropertyChangeMask); + + push @{ $self->{tabs} }, $tab; + +# $tab->{name} ||= scalar @{ $self->{tabs} }; + $self->make_current ($tab); + + () +} + +sub tab_destroy { + my ($self, $tab) = @_; + + $self->{tabs} = [ grep $_ != $tab, @{ $self->{tabs} } ]; + + if (@{ $self->{tabs} }) { + if ($self->{cur} == $tab) { + delete $self->{cur}; + $self->make_current ($self->{tabs}[-1]); + } else { + $self->refresh; + } + } else { + # delay destruction a tiny bit + $self->{destroy} = urxvt::iw->new->start->cb (sub { $self->destroy }); + } + + () +} + +sub tab_key_press { + my ($self, $tab, $event, $keysym, $str) = @_; + + # defaults + if ($tab->{is_inputting_name}) { + if ($keysym == 0xff0d || $keysym == 0xff8d) { # enter + $tab->{name} = $tab->{new_name}; + $tab->{is_inputting_name} = 0; + } elsif ($keysym == 0xff1b) { # escape + $tab->{name} = $tab->{old_name}; + $tab->{is_inputting_name} = 0; + } elsif ($keysym == 0xff08) { # backspace + substr $tab->{new_name}, -1, 1, ""; + $tab->{name} = "$tab->{new_name}█"; + } elsif ($str !~ /[\x00-\x1f\x80-\xaf]/) { + $tab->{new_name} .= $str; + $tab->{name} = "$tab->{new_name}█"; + } + $self->refresh; + return 1; + } + + if ($event->{state} & urxvt::ShiftMask) { + if ($event->{state} & urxvt::ControlMask) { + if (exists($self->{tabcmds}{chr($keysym)})) { + # Execute user defined classes of shell programs. + $self->new_tab(@{$self->{tabcmds}{chr($keysym)}}); + return 1; + } elsif ($self->{disable_shift_down} and $keysym == 0x4e) { + # As a failsafe watch under CTRL+SHIFT+N for shell class (if + # SHIFT+DOWN is disabled). + $self->new_tab("shell"); + return 1; + } + } elsif ($keysym == 0xff51 || $keysym == 0xff53) { + my ($idx) = grep $self->{tabs}[$_] == $tab, 0 .. $#{ $self->{tabs} }; + + --$idx if $keysym == 0xff51; + ++$idx if $keysym == 0xff53; + + $self->make_current ($self->{tabs}[$idx % @{ $self->{tabs}}]); + + return 1; + } elsif ($keysym == 0xff52) { + $tab->{is_inputting_name} = 1; + $tab->{old_name} = $tab->{name} ? $tab->{name} : ""; + $tab->{new_name} = ""; + $tab->{name} = "█"; + $self->refresh; + return 1; + } elsif (not $self->{disable_shift_down} and $keysym == 0xff54) { + # Run shell on SHIFT+DOWN, if enabled. + $self->new_tab("shell"); + return 1; + } + } + elsif ($event->{state} & urxvt::ControlMask) { + if ($keysym == 0xff51 || $keysym == 0xff53) { + # tab movement + my ($idx1) = grep $self->{tabs}[$_] == $tab, 0 .. $#{ $self->{tabs} }; + my $idx2 = ($idx1 + ($keysym == 0xff51 ? -1 : +1)) % @{ $self->{tabs} }; + + ($self->{tabs}[$idx1], $self->{tabs}[$idx2]) = + ($self->{tabs}[$idx2], $self->{tabs}[$idx1]); + + $self->make_current ($self->{tabs}[$idx2]); + + return 1; + } elsif ($keysym > 0x2f and $keysym < 0x40) { + # make ctrl+1...0 switch to proper tab + my $num = $keysym - 0x30; + if ($num == 0) { + $num = 10; + } + $num--; + + if ($#{$self->{tabs}} >= $num){ + $self->make_current ($self->{tabs}[$num]); + } + + return 1; + } + } + + () +} + + +sub tab_add_lines { + my ($self, $tab) = @_; + my $mark = $self->tab_activity_mark($tab); + $tab->{lastActivity} = int urxvt::NOW; + $self->refresh if $mark ne $self->tab_activity_mark($tab); + (); +} + +sub tab_property_notify { + my ($self, $tab, $event) = @_; + + $self->copy_properties + if $event->{window} == $tab->parent; + + () +} + +package urxvt::ext::tabbedalt::tab; + +# helper extension implementing the subwindows of a tabbed terminal. +# simply proxies all interesting calls back to the tabbed class. + +{ + for my $hook (qw(start destroy key_press add_lines property_notify)) { + eval qq{ + sub on_$hook { + my \$parent = \$_[0]{term}{parent} + or return; + \$parent->tab_$hook (\@_) + } + }; + die if $@; + } +} + +# vim: tabstop=3 softtabstop=3 shiftwidth=3 expandtab