From a70da6c048c16a40f598ddd1384d44f89a07d980 Mon Sep 17 00:00:00 2001 From: "B. Watson" Date: Thu, 27 Aug 2020 17:35:02 -0400 Subject: Add unifmt.pl (Unicode formatting for irssi) --- slacksrc | 3 - unifmt.pl | 391 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 391 insertions(+), 3 deletions(-) create mode 100644 unifmt.pl diff --git a/slacksrc b/slacksrc index 4cc6d19..9d4f561 100755 --- a/slacksrc +++ b/slacksrc @@ -172,9 +172,6 @@ if [ ! -s $FILELIST ]; then wget -O- $TOPDIR/FILELIST.TXT | \ sed -n 's,.*\./source/\([^/]\+/[^/]\+/\).*$,\1,p' | \ sort -u > $FILELIST - -#TODO: handle extra/ somehow -#wget -O $FILELIST.tmp $TOPDIR/FILELIST.TXT fi rm -f $TMPFILE diff --git a/unifmt.pl b/unifmt.pl new file mode 100644 index 0000000..53302d1 --- /dev/null +++ b/unifmt.pl @@ -0,0 +1,391 @@ +#!/usr/bin/perl + +# to read the docs outside of irssi: perldoc /path/to/unifmt.pl +# in irssi, "/script load unifmt.pl", then "/unifmt_help" + +=encoding utf8 + +=pod + +=head1 NAME + +unifmt.pl - unicode text formatting for irssi + +=head1 SYNOPSIS + +in shell: cp unifmt.pl ~/.irssi/scripts/ + +in irssi: /script load unifmt.pl + +=head1 DESCRIPTION + +unifmt.pl adds keystrokes to irssi that allow you to type double-width +ASCII (Unicode FF00 block) characters, and use Unicode combining +characters to make your text appear underlined (single or double line), +struck out, or "slashed" out. + +Rather than executing the script as an irssi slash-command, the modes are +controlled via keystrokes. This allows the formatted text to be mixed +with normal text on the same line of input. Only one of the underline, +strikeout, slashout modes can be enabled at the same time, but they can +be combined with wide mode. + +=head1 KEYSTROKES + +All the formatting controls must be preceded by a prefix character, +which defaults B<^F> (control-F). The default keystrokes are shown +here; see SETTINGS, below, to change them. + +=over 4 + +=item B<^F w> + +Enables wide formatting. Each character you type is replaced by its +double-width equivalent from the Unicode B range, if +it has one. Only characters B (AKA the printable ASCII +charset) have double-width equivalents. As a special case, the space +(B) character is replaced with B, B, which +is a double-width space. All other characters are treated normally. + + Example: This is wide text + +Wide formatting can be combined with one (at a time) of the other +formatting options. + +=item B<^F _> + +Enables underlining. Each character you type is followed by the Unicode +combining character B, B. Example: U̲n̲d̲e̲r̲l̲i̲n̲e̲d̲ + +=item B<^F => + +Enables double underlining. Each character you type is followed by the Unicode +combining character B, B. Example: U̳n̳d̳e̳r̳l̳i̳n̳e̳d̳ + +=item B<^F -> + +Enables strikethrough. Each character you type is followed by the Unicode +combining character B, B. Example: S̶t̶r̶i̶k̶e̶t̶h̶r̶o̶u̶g̶h̶ + +=item B<^F /> + +Enables slashthrough. Each character you type is followed by the Unicode +combining character B, B. Example: S̸l̸a̸s̸h̸o̸u̸t̸ + +=item B<^F ^F> + +Acts like a single B<^F> was pressed. Does not disable formatting. If +you have a regular irssi keybinding for B<^F>, it will be acted on. +Otherwise, a B<^F> will be inserted into the input buffer. + +=item B<^F F> + +Disables all the formatting modes. Actually, B<^F> followed by any character +not listed above will do the same thing, but I promise not to change +the B<^F F> combo in any future versions of this script. + +=back + +=head1 SETTINGS + +=over 4 + +=item B + +String, the 6 keystrokes used to enable the formatting modes. This +defaults to B. The order is: Prefix, Wide, Underline, +Double-Underline, Strikethrough, Slashthrough. The prefix key is used as +a control key, but when you set it, use a regular alphabetic (e.g. don't +say B<^X>, just say B or B). + +=item B + +Boolean, whether or not to apply formatting to spaces (default: false). +Does not affect wide mode (spaces will always be double-wide). + +=back + +=head1 NOTES + +For any of this to work, you'll have to enable UTF-8 in irssi, and use +a UTF-8 capable terminal. This applies to everyone else too: if you're +sending UTF-8 encoded Unicode to them, their client (and terminal if +it's a terminal client) will have to know how to display it. If not, +they'll see garbage in place of what you intended. + +This script was developed with urxvt (AKA rxvt-unicode). It should work +with any terminal that fully supports UTF-8 and Unicode... but you want +a terminal that supports looking up glyphs from a list of fonts, like +urxvt does. Otherwise, you might have a hard time finding a single font +that has all the glyphs you'll need. + +The editing keys (arrows, home/end, pgup/pgdn, and any alt-? combos) +might act strangely while the formatting modes are enabled, depending +on your terminal and TERM environment variable. Backspace and ^U +(kill line) should still work OK. This will probably be fixed in the +future. Tab-completing nicks doesn't work either, but fixing that will +be a huge PITA (or maybe impossible). + +None of the formatting modes persist past the end of the current line of +input. Pressing Enter always clears all the modes. This is to minimize +annoyance, as there's no visual indicator of which mode(s) you're in. + +Before you use this script on a public channel, you'd better make sure +the channel doesn't have rules against using fancy Unicode. You may +annoy the other users, and/or find yourself banned. + +tmux doesn't seem to be capable of actually displaying the wide + +combining character combinations. They render as plain wide. If you +copy/paste them to another window (a terminal not running tmux for +instance), they show up correctly. So tmux "knows" the formatting is +there, but doesn't display it. + +The underline, strike, slashout combinations don't work with screen, +and probably never will. I'd love to be proven wrong, so let me know if +you get it working there. I at least can see the wide characters with +"screen -U". + +=head1 AUTHOR + +Urchlay + +=head1 LICENSE + +WTFPL: Do WTF you want with this. + +=head1 SEE ALSO + +irssi(1), urxvt(1), unicode(7), utf-8(7) + +=cut + +our $VERSION = "0.1"; +our %IRSSI = ( + authors => 'Urchlay', + contact => 'Urchlay on FreeNode', + name => 'unifmt', + description => 'Fancy Unicode text formatting', + license => 'WTFPL', + url => 'https://slackware.uk/~urchlay/repos/misc-scripts', +); + +use warnings; +use strict; + +# 20200827 bkw: adding gui_input_get_pos to the list of imports causes +# this script to fail to autoload when irssi starts (but it'll load OK +# if manually loaded after startup). I only use it for debugging anyway. +use Irssi qw{ + command command_bind parse_special signal_register + signal_add_first signal_add_last signal_continue signal_emit + signal_stop settings_set_str settings_get_str settings_add_str + settings_get_bool settings_add_bool + }; + +our $SELF = $IRSSI{name}; +our $default_keys = "fw_=-/"; + +# These 3 are controlled by setting unifmt_keys: +our $prefix_key; +our $wide_key; +our %combining_map; + +# Toggled with ^F w +our $widemode = 0; + +# Which of combining_map is active, or 0 for none +our $combining_char = 0; + +# True if the last keypress was ^F +our $was_prefix = 0; + +# 2 if the last keypress was escape, 1 if the last 2 were escape and [, +# 0 otherwise. +our $was_escape = 0; + +# There's no way to enable debugging without editing the script. +our $DEBUG = 0; + +# Only used for debugging. +our $count = 0; + +# Ditto. +sub dump_buf { + my $buf = parse_special('$L', 0, 0); + my $len = length($buf); + my $pos = Irssi::gui_input_get_pos(); + my $out = "pos==" . $pos . " "; + for(my $i = 0; $i < $len; $i++) { + my $star = ($i == $len ? "*" : ""); + $out .= sprintf("$star%02x ", ord(substr($buf, $i, 1))); + } + print $out; +} + +sub get_ctrl_key { + return ord(uc($_[0])) - 0x40; +} + +sub fmt_key { + # \002 is "toggle bold" + return "'\002" . $_[0] . "\002'"; +} + +sub init_keys { + our $default_keys; + + my $keys = settings_get_str('unifmt_keys'); + if(length $keys != 6) { + print "$SELF: Invalid unifmt_keys, should be 6 keystrokes, defaulting to '$default_keys'"; + settings_set_str('unifmt_keys', ($keys = $default_keys) ); + } + + my ($p, $w, $u, $d, $s, $l) = split "", $keys; + + $p = get_ctrl_key($p); + if($p < 0 || $p > 0x1f) { + my $pd = uc substr($default_keys, 0, 1); + print "$SELF: Invalid prefix key, defaulting to " . fmt_key("^" . $pd); + $p = get_ctrl_key($pd); + } + + our $prefix_key = $p; + our $wide_key = $w; + + our %combining_map = ( + $u => 0x332, # underline + $d => 0x333, # double underline + $s => 0x336, # strikethrough + $l => 0x338, # slash-through + ); + + print "$SELF: " . + "prefix " . fmt_key("^" . chr($prefix_key + 0x40)) . ", " . + "wide " . fmt_key($wide_key) . ", " . + "underline " . fmt_key($u) . ", " . + "double " . fmt_key($d) . ", " . + "strike " . fmt_key($s) . ", " . + "slash " . fmt_key($l); +} + +sub handle_keypress { + my $key = shift; + + if($DEBUG) { printf $count++ . ": got key 0x%x", $key; dump_buf(); } + + # hackish way to let most escape codes through unmodified. assumes + # (incorrectly) that all escape codes are either Esc-[-(something), + # 3 bytes... or else Esc-(something that isn't [), 2 bytes. This + # happens to let urxvt's arrow keys and alt-numbers through, at least. + if($was_escape) { + if($was_escape == 2 && $key != ord('[')) { + $was_escape = 0; + } else { + $was_escape--; + } + signal_continue($key); + return; + } + + # don't try to combine with combining chars! + for(values our %combining_map) { + #warn "$key $_"; + if($key == $_) { + signal_continue($key); + return; + } + } + + # ctrl-space is mapped to the null character. make it dump the + # current input buffer contents in hex, if debugging is active. + if($DEBUG && $key == 0) { + dump_buf(); + signal_stop(); + return; + } + + # ^F pressed once: set flag, but don't insert into buffer. + # Pressed twice = unset flag, inset into buffer. + if($key == $prefix_key) { + if($was_prefix) { + $was_prefix = 0; + signal_continue($key); + } else { + $was_prefix = 1; + signal_stop(); + } + return; + } + + # enter/return, turn off formatting + if($key == 0x0d || $key == 0x0a) { + $widemode = $combining_char = $was_prefix = 0; + signal_continue($key); + return; + } + + # backspace/delete and control characters are acted on normally, except + # that escape has to set a flag + if($key == 0x7f || $key < 0x20) { + if($key == 0x1b) { + $was_escape = 2; + } + signal_continue($key); + return; + } + + # last key pressed was ^F, act on it, but don't insert into the buffer + if($was_prefix) { + if($key == ord($wide_key)) { + $widemode = 1; + } else { + $combining_char = $combining_map{chr($key)} || 0; + # unrecognized keys also turn off wide mode + $widemode = 0 unless $combining_char; + } + + $was_prefix = 0; + signal_stop(); + return; + } + + # unicode 0x0f01 to 0x0fee are wide versions of ASCII + if($widemode) { + if($key == 0x20) { + $key = 0x3000; + } elsif($key >= 0x21 && $key <= 0x7e) { + $key += 0xfee0; + } + # else pass it through as-is + } + + signal_continue($key); + + # if it was a space and we're not formatting spaces, we're done + if(($key == 0x20 || $key == 0x3000) && !settings_get_bool('unifmt_spaces')) { + return; + } + + if($combining_char) { + if($DEBUG) { print "combining($key, $combining_char)"; } + signal_emit('gui key pressed', $combining_char); + } +} + +sub unifmt_help { + command("/exec - pod2text " . __FILE__); +} + +### main() +settings_add_str($SELF, 'unifmt_keys', $default_keys); +settings_add_bool($SELF, 'unifmt_spaces', 0); +init_keys(); + +signal_add_last('setup changed', \&init_keys); + +signal_register({ "gui key pressed", [ "integer" ] }); +signal_add_first("gui key pressed", \&handle_keypress); + +command_bind("unifmt_help", \&unifmt_help); + +print "$SELF.pl loaded, /unifmt_help for help" -- cgit v1.2.3