This commit is contained in:
2017-09-06 18:25:17 +01:00
commit efb71c4e57
28 changed files with 6865 additions and 0 deletions

43
irssi/scripts/awaybar.pl Executable file
View File

@@ -0,0 +1,43 @@
# awaybar.pl -- initially built for Irssi 0.8.9
# thanks to mood.pl for practically allowing me
# to copy the approach..
use strict;
use Irssi;
use Irssi::TextUI;
use vars qw($VERSION %IRSSI);
$VERSION = "0.1.1";
%IRSSI = (
authors => 'Simon Shine',
contact => 'http://shine.eu.org/',
name => 'awaybar',
description => 'Provides a menubar item with away message',
license => 'Public domain',
);
Irssi::statusbar_item_register('awaybar', 0, 'awaybar');
Irssi::signal_add('away mode changed', 'awaybar_redraw');
sub awaybar {
my ($item, $get_size_only) = @_;
my $away_reason = !Irssi::active_server() ? undef : Irssi::active_server()->{away_reason};
if (defined $away_reason && length $away_reason) {
my %r = ('\{' => '(',
'\}' => ')',
'%' => '%%',);
$away_reason =~ s/$_/$r{$_}/g for (keys %r);
#my $format = $theme->format_expand("{sb_awaybar Olet AWAY. Syy: $away_reason}");
my $format = "{sb_awaybar $away_reason}.";
$item->{min_size} = $item->{max_size} = length($away_reason);
$item->default_handler($get_size_only, $format, 0, 1);
} else {
$item->{min_size} = $item->{max_size} = 0;
}
}
sub awaybar_redraw {
Irssi::statusbar_items_redraw('awaybar');
}

2478
irssi/scripts/awl.pl Executable file

File diff suppressed because it is too large Load Diff

145
irssi/scripts/nickcolor.pl Executable file
View File

@@ -0,0 +1,145 @@
use strict;
use Irssi 20020101.0250 ();
use vars qw($VERSION %IRSSI);
$VERSION = "2";
%IRSSI = (
authors => "Timo Sirainen, Ian Peters, David Leadbeater",
contact => "tss\@iki.fi",
name => "Nick Color",
description => "assign a different color for each nick",
license => "Public Domain",
url => "http://irssi.org/",
changed => "Sun 15 Jun 19:10:44 BST 2014",
);
# Settings:
# nickcolor_colors: List of color codes to use.
# e.g. /set nickcolor_colors 2 3 4 5 6 7 9 10 11 12 13
# (avoid 8, as used for hilights in the default theme).
my %saved_colors;
my %session_colors = {};
sub load_colors {
open my $color_fh, "<", "$ENV{HOME}/.irssi/saved_colors";
while (<$color_fh>) {
chomp;
my($nick, $color) = split ":";
$saved_colors{$nick} = $color;
}
}
sub save_colors {
open COLORS, ">", "$ENV{HOME}/.irssi/saved_colors";
foreach my $nick (keys %saved_colors) {
print COLORS "$nick:$saved_colors{$nick}\n";
}
close COLORS;
}
# If someone we've colored (either through the saved colors, or the hash
# function) changes their nick, we'd like to keep the same color associated
# with them (but only in the session_colors, ie a temporary mapping).
sub sig_nick {
my ($server, $newnick, $nick, $address) = @_;
my $color;
$newnick = substr ($newnick, 1) if ($newnick =~ /^:/);
if ($color = $saved_colors{$nick}) {
$session_colors{$newnick} = $color;
} elsif ($color = $session_colors{$nick}) {
$session_colors{$newnick} = $color;
}
}
# This gave reasonable distribution values when run across
# /usr/share/dict/words
sub simple_hash {
my ($string) = @_;
chomp $string;
my @chars = split //, $string;
my $counter;
foreach my $char (@chars) {
$counter += ord $char;
}
my @colors = split / /, Irssi::settings_get_str('nickcolor_colors');
$counter = $colors[$counter % @colors];
return $counter;
}
sub sig_public {
my ($server, $msg, $nick, $address, $target) = @_;
# Has the user assigned this nick a color?
my $color = $saved_colors{$nick};
# Have -we- already assigned this nick a color?
if (!$color) {
$color = $session_colors{$nick};
}
# Let's assign this nick a color
if (!$color) {
$color = simple_hash $nick;
$session_colors{$nick} = $color;
}
$color = sprintf "\003%02d", $color;
$server->command('/^format pubmsg {pubmsgnick $2 {pubnick '.chr(3).$color.'$[-10]0}}$1');
}
sub cmd_color {
my ($data, $server, $witem) = @_;
my ($op, $nick, $color) = split " ", $data;
$op = lc $op;
if (!$op) {
Irssi::print ("No operation given (save/set/clear/list/preview)");
} elsif ($op eq "save") {
save_colors;
} elsif ($op eq "set") {
if (!$nick) {
Irssi::print ("Nick not given");
} elsif (!$color) {
Irssi::print ("Color not given");
} elsif ($color < 2 || $color > 14) {
Irssi::print ("Color must be between 2 and 14 inclusive");
} else {
$saved_colors{$nick} = $color;
}
} elsif ($op eq "clear") {
if (!$nick) {
Irssi::print ("Nick not given");
} else {
delete ($saved_colors{$nick});
}
} elsif ($op eq "list") {
Irssi::print ("\nSaved Colors:");
foreach my $nick (keys %saved_colors) {
Irssi::print (chr (3) . "$saved_colors{$nick}$nick" .
chr (3) . "1 ($saved_colors{$nick})");
}
} elsif ($op eq "preview") {
Irssi::print ("\nAvailable colors:");
foreach my $i (2..14) {
Irssi::print (chr (3) . "$i" . "Color #$i");
}
}
}
load_colors;
Irssi::settings_add_str('misc', 'nickcolor_colors', '2 3 4 5 6 7 9 10 11 12 13');
Irssi::command_bind('color', 'cmd_color');
Irssi::signal_add('message public', 'sig_public');
Irssi::signal_add('event nick', 'sig_nick');

805
irssi/scripts/nicklist.pl Executable file
View File

@@ -0,0 +1,805 @@
# This script adds a nicklist to the right of irssi
# for documentation: see http://wouter.coekaerts.be/site/irssi/nicklist
# Copyright (C) 2002-2007 Wouter Coekaerts <coekie@irssi.org>
#
# 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 2 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, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
use Irssi;
use strict;
use IO::Handle; # for (auto)flush
use Fcntl; # for sysopen
use vars qw($VERSION %IRSSI);
$VERSION = '0.4.6+';
%IRSSI = (
authors => 'Wouter Coekaerts',
contact => 'coekie@irssi.org',
name => 'nicklist',
description => 'draws a nicklist to another terminal, or at the right of your irssi in the same terminal',
license => 'GPLv2+',
url => 'http://wouter.coekaerts.be/irssi',
changed => '05/05/2007'
);
sub cmd_help {
print ( <<EOF
Commands:
NICKLIST HELP
NICKLIST SCROLL <nr of lines>
NICKLIST SCREEN
NICKLIST FIFO
NICKLIST OFF
NICKLIST UPDATE
For help see: http://wouter.coekaerts.be/site/irssi/nicklist
in short:
1. FIFO MODE
- in irssi: /NICKLIST FIFO (only the first time, to create the fifo)
- in a shell, in a window where you want the nicklist: cat ~/.irssi/nicklistfifo
- back in irssi:
/SET nicklist_heigth <height of nicklist>
/SET nicklist_width <width of nicklist>
/NICKLIST FIFO
2. SCREEN MODE
- start irssi inside screen ("screen irssi")
- /NICKLIST SCREEN
EOF
);
}
my $prev_lines = 0; # number of lines in previous written nicklist
my $scroll_pos = 0; # scrolling position
my $cursor_line; # line the cursor is currently on
my ($OFF, $SCREEN, $FIFO) = (0,1,2); # modes
my $mode = $OFF; # current mode
my $need_redraw = 0; # nicklist needs redrawing
my $screen_resizing = 0; # terminal is being resized
my $active_channel; # (REC)
my @nicklist=(); # array of hashes, containing the internal nicklist of the active channel
# nick => realnick
# modeflag => '@', '%', '+', or other mode char
# modepos => number representing the position in which to sort nicks with that mode
# status => (not used yet...)
#my ($STATUS_NORMAL, $STATUS_JOINING, $STATUS_PARTING, $STATUS_QUITING, $STATUS_KICKED, $STATUS_SPLIT) = (0,1,2,3,4,5);
# text => text to be printed
# cmp => text used to compare (sort) nicks
# 'cached' settings
my ($screen_prefix, $irssi_width, %prefix_mode, @prefix_status, $height, $nicklist_width, $check_friends, @prefix_friends);
sub read_settings {
($screen_prefix = Irssi::settings_get_str('nicklist_screen_prefix')) =~ s/\\e/\033/g;
($prefix_mode{'@'} = Irssi::settings_get_str('nicklist_prefix_mode_op')) =~ s/\\e/\033/g;
($prefix_mode{'%'} = Irssi::settings_get_str('nicklist_prefix_mode_halfop')) =~ s/\\e/\033/g;
($prefix_mode{'+'} = Irssi::settings_get_str('nicklist_prefix_mode_voice')) =~ s/\\e/\033/g;
($prefix_mode{' '} = Irssi::settings_get_str('nicklist_prefix_mode_normal')) =~ s/\\e/\033/g;
(my $prefix_mode_other = Irssi::settings_get_str('nicklist_prefix_mode_other')) =~ s/\\e/\033/g;
foreach my $p (split (/ /, $prefix_mode_other)) {
next if $p eq '';
if ($p !~ /(.)=(.*)/) {
Irssi::print("Could not parse nicklist_prefix_mode_other part '$p'. Expected space separated list of <mode character>=<prefix>");
last;
} else {
$prefix_mode{$1} = $2;
}
}
(my $prefix_friends = Irssi::settings_get_str('nicklist_prefix_friends')) =~ s/\\e/\033/g;
foreach my $p (split (/ /, $prefix_friends)) {
next if $p eq '';
if ($p !~ /(.+?)=(.*)/) {
Irssi::print("Could not parse nicklist_prefix_friends part '$p'. Expected space separated list of <flags>=<prefix>");
last;
} else {
push @prefix_friends, {'flags' => $1, 'prefix' => $2};
}
}
$check_friends = ($prefix_friends ne '');
if ($mode != $SCREEN) {
$height = Irssi::settings_get_int('nicklist_height');
}
my $new_nicklist_width = Irssi::settings_get_int('nicklist_width');
if ($new_nicklist_width != $nicklist_width && $mode == $SCREEN) {
sig_terminal_resized();
}
$nicklist_width = $new_nicklist_width;
}
sub update {
read_settings();
make_nicklist();
}
##################
##### OUTPUT #####
##################
### off ###
sub cmd_off {
if ($mode == $SCREEN) {
screen_stop();
} elsif ($mode == $FIFO) {
fifo_stop();
}
}
### fifo ###
sub cmd_fifo_start {
read_settings();
my $path = Irssi::settings_get_str('nicklist_fifo_path');
unless (-p $path) { # not a pipe
if (-e _) { # but a something else
die "$0: $path exists and is not a pipe, please remove it\n";
} else {
require POSIX;
POSIX::mkfifo($path, 0666) or die "can\'t mkfifo $path: $!";
Irssi::print("Fifo created. Start reading it (\"cat $path\") and try again.");
return;
}
}
if (!sysopen(FIFO, $path, O_WRONLY | O_NONBLOCK)) { # or die "can't write $path: $!";
Irssi::print("Couldn\'t write to the fifo ($!). Please start reading the fifo (\"cat $path\") and try again.");
return;
}
FIFO->autoflush(1);
print FIFO "\033[2J\033[1;1H"; # erase screen & jump to 0,0
$cursor_line = 0;
if ($mode == $SCREEN) {
screen_stop();
}
$mode = $FIFO;
make_nicklist();
}
sub fifo_stop {
close FIFO;
$mode = $OFF;
Irssi::print("Fifo closed.");
}
### screen ###
sub cmd_screen_start {
if (!defined($ENV{'STY'})) {
Irssi::print 'screen not detected, screen mode only works inside screen';
return;
}
read_settings();
if ($mode == $SCREEN) {return;}
if ($mode == $FIFO) {
fifo_stop();
}
$mode = $SCREEN;
Irssi::signal_add_last('gui print text finished', \&sig_gui_print_text_finished);
Irssi::signal_add_last('gui page scrolled', \&sig_page_scrolled);
Irssi::signal_add('terminal resized', \&sig_terminal_resized);
screen_size();
make_nicklist();
}
sub screen_stop {
$mode = $OFF;
Irssi::signal_remove('gui print text finished', \&sig_gui_print_text_finished);
Irssi::signal_remove('gui page scrolled', \&sig_page_scrolled);
Irssi::signal_remove('terminal resized', \&sig_terminal_resized);
system 'screen -x '.$ENV{'STY'}.' -X fit';
}
sub screen_size {
if ($mode != $SCREEN) {
return;
}
$screen_resizing = 1;
# fit screen
system 'screen -x '.$ENV{'STY'}.' -X fit';
# we wait a second to make sure the fit command was processed
Irssi::timeout_add_once(1000, \&screen_size_real, []);
}
sub screen_size_real {
# get size (from perldoc -q size)
my ($winsize, $row, $col, $xpixel, $ypixel);
eval 'use Term::ReadKey; ($col, $row, $xpixel, $ypixel) = GetTerminalSize';
# require Term::ReadKey 'GetTerminalSize';
# ($col, $row, $xpixel, $ypixel) = Term::ReadKey::GetTerminalSize;
#};
if ($@) { # no Term::ReadKey, try the ugly way
eval {
require 'sys/ioctl.ph';
# without this reloading doesn't work. workaround for some unknown bug
do 'asm/ioctls.ph';
};
# ugly way not working, let's try something uglier, the dg-hack(tm) (constant for linux only?)
if($@) { no strict 'refs'; *TIOCGWINSZ = sub { return 0x5413 } }
unless (defined &TIOCGWINSZ) {
die "Term::ReadKey not found, and ioctl 'workaround' failed. Install the Term::ReadKey perl module to use screen mode.\n";
}
open(TTY, "+</dev/tty") or die "No tty: $!";
unless (ioctl(TTY, &TIOCGWINSZ, $winsize='')) {
die "Term::ReadKey not found, and ioctl 'workaround' failed ($!). Install the Term::ReadKey perl module to use screen mode.\n";
}
close(TTY);
($row, $col, $xpixel, $ypixel) = unpack('S4', $winsize);
}
# set screen width
$irssi_width = $col-$nicklist_width-1;
$height = $row-1;
system 'screen -x '.$ENV{'STY'}.' -X width -w ' . $irssi_width;
# wait another second for the resizing, and then redraw.
Irssi::timeout_add_once(1000,sub {$screen_resizing = 0; redraw()}, []);
}
sub sig_terminal_resized {
if ($screen_resizing) {
return;
}
$screen_resizing = 1;
Irssi::timeout_add_once(1000,\&screen_size,[]);
}
### both ###
sub nicklist_write_start {
if ($mode == $SCREEN) {
print STDERR "\033P\033[s\033\\"; # save cursor
}
}
sub nicklist_write_end {
if ($mode == $SCREEN) {
print STDERR "\033P\033[u\033\\"; # restore cursor
}
}
sub nicklist_write_line {
my ($line, $data) = @_;
if ($mode == $SCREEN) {
print STDERR "\033P\033[" . ($line+1) . ';'. ($irssi_width+1) .'H'. $screen_prefix . $data . "\033\\";
} elsif ($mode == $FIFO) {
$data = "\033[m$data"; # reset color
if ($line == $cursor_line+1) {
$data = "\n$data"; # next line
} elsif ($line == $cursor_line) {
$data = "\033[1G".$data; # back to beginning of line
} else {
$data = "\033[".($line+1).";0H".$data; # jump
}
$cursor_line=$line;
print(FIFO $data) or fifo_stop();
}
}
sub calc_prefix_friends {
my ($nick) = @_;
return '' unless $check_friends
&& $nick->{'host'}
&& is_friend($active_channel->{'server'}->{'chatnet'}, $active_channel->{'name'}, $nick->{'nick'}, $nick->{'host'});
my $flags = get_flags($active_channel->{'server'}->{'chatnet'}, $active_channel->{'name'}, $nick->{'nick'}, $nick->{'host'});
my $prefix;
foreach my $prefix_friend (@prefix_friends) {
if ($prefix_friend->{'flags'} eq 'noflag') {
if ($flags eq '') {
$prefix = $prefix_friend->{'prefix'};
last;
}
} elsif (check_modes($flags, $prefix_friend->{'flags'})) {
$prefix = $prefix_friend->{'prefix'};
}
}
return $prefix ? $prefix : '';
}
# recalc the text of the nicklist item
sub calc_text {
my ($nick) = @_;
my $tmp = $nicklist_width-3;
(my $text = $nick->{'nick'}) =~ s/^(.{$tmp})..+$/$1\033[34m~/; # strip nick if too long
my $prefix_mode = $prefix_mode{$nick->{'modeflag'}};
if (! defined($prefix_mode) ) {
$prefix_mode = $nick->{'modeflag'};
}
my $prefix_friends = calc_prefix_friends($nick);
$nick->{'text'} =
$prefix_mode .
$prefix_friends .
$text .
(' ' x ($nicklist_width-length($nick->{'nick'})-1)) .
"\033[m"; # reset
$nick->{'cmp'} = $nick->{'modepos'}.lc($nick->{'nick'});
}
# redraw the given nick (nr) if it is visible
sub redraw_nick_nr {
my ($nr) = @_;
my $line = $nr - $scroll_pos;
if ($line >= 0 && $line < $height) {
nicklist_write_line($line, $nicklist[$nr]->{'text'});
}
}
# nick was inserted, redraw area if necessary
sub draw_insert_nick_nr {
my ($nr) = @_;
my $line = $nr - $scroll_pos;
if ($line < 0) { # nick is inserted above visible area
$scroll_pos++; # 'scroll' down :)
} elsif ($line < $height) { # line is visible
if ($mode == $SCREEN) {
need_redraw();
} elsif ($mode == $FIFO) {
my $data = "\033[m\033[L". $nicklist[$nr]->{'text'}; # reset color & insert line & write nick
if ($line == $cursor_line) {
$data = "\033[1G".$data; # back to beginning of line
} else {
$data = "\033[".($line+1).";1H".$data; # jump
}
$cursor_line=$line;
print(FIFO $data) or fifo_stop();
if ($prev_lines < $height) {
$prev_lines++; # the nicklist has one line more
}
}
}
}
sub draw_remove_nick_nr {
my ($nr) = @_;
my $line = $nr - $scroll_pos;
if ($line < 0) { # nick removed above visible area
$scroll_pos--; # 'scroll' up :)
} elsif ($line < $height) { # line is visible
if ($mode == $SCREEN) {
need_redraw();
} elsif ($mode == $FIFO) {
#my $data = "\033[m\033[L[i$line]". $nicklist[$nr]->{'text'}; # reset color & insert line & write nick
my $data = "\033[M"; # delete line
if ($line != $cursor_line) {
$data = "\033[".($line+1)."d".$data; # jump
}
$cursor_line=$line;
print(FIFO $data) or fifo_stop();
if (@nicklist-$scroll_pos >= $height) {
redraw_nick_nr($scroll_pos+$height-1);
}
}
}
}
# redraw the whole nicklist
sub redraw {
$need_redraw = 0;
#make_nicklist();
nicklist_write_start();
my $line = 0;
### draw nicklist ###
for (my $i=$scroll_pos;$line < $height && $i < @nicklist; $i++) {
nicklist_write_line($line++, $nicklist[$i]->{'text'});
}
### clean up other lines ###
my $real_lines = $line;
while($line < $prev_lines) {
nicklist_write_line($line++,' ' x $nicklist_width);
}
$prev_lines = $real_lines;
nicklist_write_end();
}
# redraw (with little delay to avoid redrawing to much)
sub need_redraw {
if(!$need_redraw) {
$need_redraw = 1;
Irssi::timeout_add_once(10,\&redraw,[]);
}
}
sub sig_page_scrolled {
$prev_lines = $height; # we'll need to redraw everything if he scrolled up
need_redraw;
}
# redraw (with delay) if the window is visible (only in screen mode)
sub sig_gui_print_text_finished {
if ($need_redraw) { # there's already a redraw 'queued'
return;
}
my $window = @_[0];
if ($window->{'refnum'} == Irssi::active_win->{'refnum'} || Irssi::settings_get_str('nicklist_screen_split_windows') eq '*') {
need_redraw;
return;
}
foreach my $win (split(/[ ,]/, Irssi::settings_get_str('nicklist_screen_split_windows'))) {
if ($window->{'refnum'} == $win || $window->{'name'} eq $win) {
need_redraw;
return;
}
}
}
###################
##### FRIENDS #####
###################
# checks if $has_modes is in $need_modes, copied from trigger.pl
sub check_modes {
my ($has_modes, $need_modes) = @_;
my $matches;
my $switch = 1; # if a '-' if found, will be 0 (meaning the modes should not be set)
foreach my $need_mode (split /&/,$need_modes) {
$matches = 0;
foreach my $char (split //,$need_mode) {
if ($char eq '-') {
$switch = 0;
} elsif ($char eq '+') {
$switch = 1;
} elsif ((index($has_modes,$char) != -1) == $switch) {
$matches = 1;
last;
}
}
if (!$matches) {
return 0;
}
}
return 1;
}
# get someones flags from people.pl or friends(_shasta).pl, copied from trigger.pl
sub get_flags {
my ($chatnet, $channel, $nick, $address) = @_;
my $flags;
no strict 'refs';
if (defined %{ 'Irssi::Script::people::' }) {
if (defined ($channel)) {
$flags = (&{ 'Irssi::Script::people::find_local_flags' }($chatnet,$channel,$nick,$address));
} else {
$flags = (&{ 'Irssi::Script::people::find_global_flags' }($chatnet,$nick,$address));
}
$flags = join('',keys(%{$flags}));
} else {
my $shasta;
if (defined %{ 'Irssi::Script::friends_shasta::' }) {
$shasta = 'friends_shasta';
} elsif (defined &{ 'Irssi::Script::friends::get_idx' }) {
$shasta = 'friends';
}
if (!$shasta) {
return undef;
}
my $idx = (&{ 'Irssi::Script::'.$shasta.'::get_idx' }($nick,$address));
if ($idx == -1) {
return '';
}
$flags = (&{ 'Irssi::Script::'.$shasta.'::get_friends_flags' }($idx,undef));
if ($channel) {
$flags .= (&{ 'Irssi::Script::'.$shasta.'::get_friends_flags' }($idx,$channel));
}
}
return $flags;
}
sub is_friend {
my ($chatnet, $channel, $nick, $address) = @_;
no strict 'refs';
if (defined %{ 'Irssi::Script::people::' }) {
return (() != &{'Irssi::Script::people::find_users'}($chatnet, $nick, $address));
my $flags;
if (defined ($channel)) {
$flags = (&{ 'Irssi::Script::people::find_local_flags' }($chatnet,$channel,$nick,$address));
} else {
$flags = (&{ 'Irssi::Script::people::find_global_flags' }($chatnet,$nick,$address));
}
return ($flags ne ''); # TODO: test this
} else {
my $shasta;
if (defined %{ 'Irssi::Script::friends_shasta::' }) {
$shasta = 'friends_shasta';
} elsif (defined &{ 'Irssi::Script::friends::get_idx' }) {
$shasta = 'friends';
}
if (!$shasta) {
return undef;
}
my $idx = (&{ 'Irssi::Script::'.$shasta.'::get_idx' }($nick,$address));
return ($idx != -1);
}
}
####################
##### NICKLIST #####
####################
# returns the position of the given nick(as string) in the (internal) nicklist
sub find_nick {
my ($nick) = @_;
for (my $i=0;$i < @nicklist; $i++) {
if ($nicklist[$i]->{'nick'} eq $nick) {
return $i;
}
}
return -1;
}
# find position where nick should be inserted into the list
sub find_insert_pos {
my ($cmp)= @_;
for (my $i=0;$i < @nicklist; $i++) {
if ($nicklist[$i]->{'cmp'} gt $cmp) {
return $i;
}
}
return scalar(@nicklist); #last
}
# make the (internal) nicklist (@nicklist)
sub make_nicklist {
@nicklist = ();
$scroll_pos = 0;
### get & check channel ###
my $channel = Irssi::active_win->{active};
if (!$channel || (ref($channel) ne 'Irssi::Irc::Channel' && ref($channel) ne 'Irssi::Silc::Channel') || $channel->{'type'} ne 'CHANNEL' || ($channel->{chat_type} ne 'SILC' && !$channel->{'names_got'}) ) {
$active_channel = undef;
# no nicklist
} else {
$active_channel = $channel;
### make nicklist ###
foreach my $nick ($channel->nicks()) {
my $thisnick = {'nick' => $nick->{'nick'}};
recalc_nick($thisnick, $nick);
push @nicklist, $thisnick;
}
@nicklist = sort {$a->{'cmp'} cmp $b->{'cmp'}} @nicklist;
}
need_redraw();
}
# insert nick(as hash) into nicklist
# pre: cmp has to be calculated
sub insert_nick {
my ($nick) = @_;
my $nr = find_insert_pos($nick->{'cmp'});
splice @nicklist, $nr, 0, $nick;
draw_insert_nick_nr($nr);
}
# remove nick(as nr) from nicklist
sub remove_nick {
my ($nr) = @_;
splice @nicklist, $nr, 1;
draw_remove_nick_nr($nr);
}
# update the mode and cmp of a nick, based on a nickrec from irssi
sub recalc_nick {
my ($nick, $nickrec) = @_;
if (! $nickrec) {
$nickrec = $active_channel->nick_find($nick->{'nick'});
}
my $nickflags = $active_channel->{'server'}->get_nick_flags() . ' ';
my $flag = (
$nickrec->{'op'} ? '@' :
$nickrec->{'halfop'} ? '%' :
$nickrec->{'voice'} ? '+' :
' '
);
if ($nickrec->{'other'} && index($nickflags, $nick->{'other'}) < index($nickflags, $flag)) {
$flag = chr($nickrec->{'other'});
}
$nick->{'modepos'} = index($nickflags, $flag);
$nick->{'modeflag'} = $flag;
$nick->{'host'} = $nickrec->{'host'};
calc_text($nick);
}
###################
##### ACTIONS #####
###################
# scroll the nicklist, arg = number of lines to scroll, positive = down, negative = up
sub cmd_scroll {
if (!$active_channel) { # not a channel active
return;
}
my @nicks=Irssi::active_win->{active}->nicks;
my $nick_count = scalar(@nicks)+0;
my $channel = Irssi::active_win->{active};
if (!$channel || $channel->{type} ne 'CHANNEL' || !$channel->{names_got} || $nick_count <= Irssi::settings_get_int('nicklist_height')) {
return;
}
$scroll_pos += @_[0];
if ($scroll_pos > $nick_count - $height) {
$scroll_pos = $nick_count - $height;
}
if ($scroll_pos <= 0) {
$scroll_pos = 0;
}
need_redraw();
}
sub is_active_channel {
my ($server,$channel) = @_; # (channel as string)
return ($server && $server->{'tag'} eq $active_channel->{'server'}->{'tag'} && $server->channel_find($channel) && $active_channel && $server->channel_find($channel)->{'name'} eq $active_channel->{'name'});
}
sub sig_channel_wholist { # this is actualy a little late, when the names are received would be better
my ($channel) = @_;
if (Irssi::active_win->{'active'} && Irssi::active_win->{'active'}->{'name'} eq $channel->{'name'}) { # the channel joined is active
make_nicklist
}
}
sub sig_join {
my ($server,$channel,$nick,$address) = @_;
if (!is_active_channel($server,$channel)) {
return;
}
my $newnick = {'nick' => $nick};
recalc_nick($newnick);
insert_nick($newnick);
}
sub sig_kick {
my ($server, $channel, $nick, $kicker, $address, $reason) = @_;
if (!is_active_channel($server,$channel)) {
return;
}
my $nr = find_nick($nick);
if ($nr == -1) {
Irssi::print("nicklist warning: $nick was kicked from $channel, but not found in nicklist");
} else {
remove_nick($nr);
}
}
sub sig_part {
my ($server,$channel,$nick,$address, $reason) = @_;
if (!is_active_channel($server,$channel)) {
return;
}
my $nr = find_nick($nick);
if ($nr == -1) {
Irssi::print("nicklist warning: $nick has parted $channel, but was not found in nicklist");
} else {
remove_nick($nr);
}
}
sub sig_quit {
my ($server,$nick,$address, $reason) = @_;
if ($server->{'tag'} ne $active_channel->{'server'}->{'tag'}) {
return;
}
my $nr = find_nick($nick);
if ($nr != -1) {
remove_nick($nr);
}
}
sub sig_nick {
my ($server, $newnick, $oldnick, $address) = @_;
if ($server->{'tag'} ne $active_channel->{'server'}->{'tag'}) {
return;
}
my $nr = find_nick($oldnick);
if ($nr != -1) { # if nick was found (nickchange is in current channel)
my $nick = $nicklist[$nr];
remove_nick($nr);
$nick->{'nick'} = $newnick;
calc_text($nick);
insert_nick($nick);
}
}
sub sig_mode {
my ($channel, $nick, $setby, $mode, $type) = @_; # (nick and channel as rec)
if ($channel->{'server'}->{'tag'} ne $active_channel->{'server'}->{'tag'} || $channel->{'name'} ne $active_channel->{'name'}) {
return;
}
my $nr = find_nick($nick->{'nick'});
if ($nr == -1) {
Irssi::print("nicklist warning: $nick->{'nick'} had mode set on $channel->{'name'}, but was not found in nicklist");
} else {
my $nicklist_item = $nicklist[$nr];
remove_nick($nr);
recalc_nick($nicklist_item, $nick);
insert_nick($nicklist_item);
}
}
##### command binds #####
Irssi::command_bind 'nicklist' => sub {
my ( $data, $server, $item ) = @_;
$data =~ s/\s+$//g;
Irssi::command_runsub ('nicklist', $data, $server, $item ) ;
};
Irssi::signal_add_first 'default command nicklist' => sub {
# gets triggered if called with unknown subcommand
cmd_help();
};
Irssi::command_bind('nicklist update',\&update);
Irssi::command_bind('nicklist help',\&cmd_help);
Irssi::command_bind('nicklist scroll',\&cmd_scroll);
Irssi::command_bind('nicklist fifo',\&cmd_fifo_start);
Irssi::command_bind('nicklist screen',\&cmd_screen_start);
Irssi::command_bind('nicklist screensize',\&screen_size);
Irssi::command_bind('nicklist off',\&cmd_off);
##### signals #####
Irssi::signal_add_last('window item changed', \&make_nicklist);
Irssi::signal_add_last('window changed', \&make_nicklist);
Irssi::signal_add_last('channel wholist', \&sig_channel_wholist);
Irssi::signal_add_first('message join', \&sig_join); # first, to be before ignores
Irssi::signal_add_first('message part', \&sig_part);
Irssi::signal_add_first('message kick', \&sig_kick);
Irssi::signal_add_first('message quit', \&sig_quit);
Irssi::signal_add_first('message nick', \&sig_nick);
Irssi::signal_add_first('message own_nick', \&sig_nick);
Irssi::signal_add_first('nick mode changed', \&sig_mode);
Irssi::signal_add('setup changed', \&read_settings);
##### settings #####
Irssi::settings_add_str('nicklist', 'nicklist_screen_prefix', '\e[m ');
Irssi::settings_add_str('nicklist', 'nicklist_prefix_mode_op', '\e[32m@\e[39m');
Irssi::settings_add_str('nicklist', 'nicklist_prefix_mode_halfop', '\e[34m%\e[39m');
Irssi::settings_add_str('nicklist', 'nicklist_prefix_mode_voice', '\e[33m+\e[39m');
Irssi::settings_add_str('nicklist', 'nicklist_prefix_mode_normal', ' ');
Irssi::settings_add_str('nicklist', 'nicklist_prefix_mode_other', '&=\e[31m&\e[39m ~=\e[35m~\e[39m');
Irssi::settings_add_str('nicklist', 'nicklist_prefix_friends', 'o=\e[32m v=\e[33m noflag=\e[1m');
Irssi::settings_add_int('nicklist', 'nicklist_width',11);
Irssi::settings_add_int('nicklist', 'nicklist_height',24);
Irssi::settings_add_str('nicklist', 'nicklist_fifo_path', Irssi::get_irssi_dir . '/nicklistfifo');
Irssi::settings_add_str('nicklist', 'nicklist_screen_split_windows', '');
Irssi::settings_add_str('nicklist', 'nicklist_automode', '');
read_settings();
if (uc(Irssi::settings_get_str('nicklist_automode')) eq 'SCREEN') {
cmd_screen_start();
} elsif (uc(Irssi::settings_get_str('nicklist_automode')) eq 'FIFO') {
cmd_fifo_start();
}

View File

@@ -0,0 +1,425 @@
# based on the nicklist.pl script
################################################################################
# tmux_nicklist.pl
# This script integrates tmux and irssi to display a list of nicks in a
# vertical right pane with 20% width. Right now theres no configuration
# or setup, simply initialize the script with irssi and by default you
# will get the nicklist for every channel(customize by altering
# the regex in /set nicklist_channel_re)
#
# /set nicklist_channel_re <regex>
# * only show on channels matching this regular expression
#
# /set nicklist_max_users <num>
# * only show when the channel has so many users or less (0 = always)
#
# /set nicklist_smallest_main <num>
# * only show when main window is larger than this (0 = always)
#
# /set nicklist_pane_width <num>
# * width of the nicklist pane
#
# /set nicklist_color <ON|OFF>
# * colourise the nicks in the nicklist (required nickcolor script
# with get_nick_color2 and debug_ansicolour functions)
#
# /set nicklist_gone_sort <ON|OFF>
# * sort away people below
#
# It supports mouse scrolling and the following keys:
# k/up arrow: up one line
# j/down arrow: down one line
# pageup: up 20 lines
# pagedown: down 20 lines
# gg: go to top
# G: go to bottom
#
# For better integration, unrecognized sequences will be sent to irssi and
# its pane will be focused.
#
# to toggle the nicklist if it is in the way you can make a key binding:
# /bind meta-Z /script exec Irssi::Script::tmux_nicklist_portable::toggle_nicklist
################################################################################
use strict;
use warnings;
use IO::Handle;
use IO::Select;
use POSIX;
use File::Temp qw/ :mktemp /;
use File::Basename;
our $VERSION = '0.1.6'; # aea329934f6a48c
our %IRSSI = (
authors => 'Thiago de Arruda',
contact => 'tpadilha84@gmail.com',
name => 'tmux-nicklist',
description => 'displays a list of nicks in a separate tmux pane',
license => 'WTFPL',
);
# "other" prefixes by danielg4 <daniel@gimpelevich.san-francisco.ca.us>
{ package Irssi::Nick }
if ($#ARGV == -1) {
require Irssi;
my $enabled = 0;
my $nicklist_toggle = 1;
my $script_path = __FILE__;
my $tmpdir;
my $fifo_path;
my $fifo;
my $just_launched;
my $resize_timer;
sub enable_nicklist {
return if ($enabled);
$tmpdir = mkdtemp Irssi::get_irssi_dir()."/nicklist-XXXXXXXX";
$fifo_path = "$tmpdir/fifo";
POSIX::mkfifo($fifo_path, 0600) or die "can't mkfifo $fifo_path: $!";
my $cmd = "perl $script_path $fifo_path $ENV{TMUX_PANE}";
my $width = Irssi::settings_get_int('nicklist_pane_width');
system('tmux', 'split-window', '-dh', '-l', $width, '-t', $ENV{TMUX_PANE}, $cmd);
open_fifo();
Irssi::timeout_remove($just_launched) if defined $just_launched;
$just_launched = Irssi::timeout_add_once(300, sub { $just_launched = undef; }, '');
}
sub open_fifo {
# The next system call will block until the other pane has opened the pipe
# for reading, so synchronization is not an issue here.
open $fifo, ">", $fifo_path or do {
if ($! == 4) {
Irssi::timeout_add_once(300, \&open_fifo, '');
$enabled = -1 unless $enabled;
return;
}
die "can't open $fifo_path: $!";
};
$fifo->autoflush(1);
if ($enabled < -1) {
$enabled = 1;
disable_nicklist();
} elsif ($enabled == -1) {
$enabled = 1;
reset_nicklist("enabled");
} else {
$enabled = 1;
}
}
sub disable_nicklist {
return unless ($enabled);
if ($enabled > 0) {
print $fifo "EXIT\n";
close $fifo;
$fifo = undef;
unlink $fifo_path;
rmdir $tmpdir;
}
$enabled--;
}
sub reset_nicklist {
my $event = shift;
my $active = Irssi::active_win();
my $channel = $active->{active};
return disable_nicklist unless $channel && ref $channel;
if ($event =~ /^nick/) {
# check if that nick event is for the current channel/nicklist
my ($event_channel) = @_;
return unless $channel->{_irssi} == $event_channel->{_irssi};
}
my ($colourer, $ansifier);
if (Irssi::settings_get_bool('nicklist_color')) {
for my $script (sort map { my $z = $_; $z =~ s/::$//; $z } grep { /^nickcolor|nm/ } keys %Irssi::Script::) {
if ($colourer = "Irssi::Script::$script"->can('get_nick_color2')) {
$ansifier = "Irssi::Script::$script"->can('debug_ansicolour');
last;
}
}
}
my $channel_pattern = Irssi::settings_get_str('nicklist_channel_re');
{ local $@;
$channel_pattern = eval { qr/$channel_pattern/ };
$channel_pattern = qr/(?!)/ if $@;
}
my $smallest_main = Irssi::settings_get_int('nicklist_smallest_main');
if (!$nicklist_toggle
|| !$channel || !ref($channel)
|| !$channel->isa('Irssi::Channel')
|| !$channel->{'names_got'}
|| $channel->{'name'} !~ $channel_pattern
|| ($smallest_main && $channel->window->{width} < $smallest_main)) {
disable_nicklist;
} else {
my %colour;
my @nicks = $channel->nicks();
my $max_nicks = Irssi::settings_get_int('nicklist_max_users');
if ($max_nicks && @nicks > $max_nicks) {
disable_nicklist;
} else {
enable_nicklist;
return unless $enabled > 0;
foreach my $nick (sort { $a->{_irssi} <=> $b->{_irssi} } @nicks) {
$colour{$nick->{nick}} = ($ansifier && $colourer) ? $ansifier->($colourer->($active->{active}{server}{tag}, $channel->{name}, $nick->{nick}, 0)) : '';
}
print($fifo "BEGIN\n");
my $gone_sort = Irssi::settings_get_bool('nicklist_gone_sort');
my $prefer_real;
if (exists $Irssi::Script::{'realnames::'}) {
my $code = "Irssi::Script::realnames"->can('use_realnames');
$prefer_real = $code && $code->($channel);
}
my $_real = sub {
my $nick = shift;
$prefer_real && length $nick->{'realname'} ? $nick->{'realname'} : $nick->{'nick'}
};
foreach my $nick (sort {($a->{'op'}?'1':$a->{'halfop'}?'2':$a->{'voice'}?'3':$a->{'other'}>32?'0':'4').($gone_sort?($a->{'gone'}?1:0):'').lc($_real->($a))
cmp ($b->{'op'}?'1':$b->{'halfop'}?'2':$b->{'voice'}?'3':$b->{'other'}>32?'0':'4').($gone_sort?($b->{'gone'}?1:0):'').lc($_real->($b))} @nicks) {
my $colour = $colour{$nick->{nick}} || "\e[39m";
$colour = "\e[37m" if $nick->{'gone'};
print($fifo "NICK");
if ($nick->{'op'}) {
print($fifo "\e[32m\@$colour".$_real->($nick)."\e[39m");
} elsif ($nick->{'halfop'}) {
print($fifo "\e[34m%$colour".$_real->($nick)."\e[39m");
} elsif ($nick->{'voice'}) {
print($fifo "\e[33m+$colour".$_real->($nick)."\e[39m");
} elsif ($nick->{'other'}>32) {
print($fifo "\e[31m".(chr $nick->{'other'})."$colour".$_real->($nick)."\e[39m");
} else {
print($fifo " $colour".$_real->($nick)."\e[39m");
}
print($fifo "\n");
}
print($fifo "END\n");
}
}
}
sub toggle_nicklist {
if ($enabled) {
$nicklist_toggle = undef
} else {
$nicklist_toggle = 1;
}
reset_nicklist("toggle");
}
sub switch_channel {
print $fifo "SWITCH_CHANNEL\n" if $fifo;
&reset_nicklist;
}
sub resized_timed {
Irssi::timeout_remove($resize_timer) if defined $resize_timer;
return if defined $just_launched;
$resize_timer = Irssi::timeout_add_once(1100, \&resized, '');
#resized();
}
sub resized {
$resize_timer = undef;
return if defined $just_launched;
return unless $enabled >= 0;
disable_nicklist;
Irssi::timeout_add_once(200, sub{reset_nicklist("terminal resized")}, '');
}
sub UNLOAD {
disable_nicklist;
}
Irssi::settings_add_str('tmux_nicklist', 'nicklist_channel_re', '.*');
Irssi::settings_add_int('tmux_nicklist', 'nicklist_max_users', 0);
Irssi::settings_add_int('tmux_nicklist', 'nicklist_smallest_main', 0);
Irssi::settings_add_int('tmux_nicklist', 'nicklist_pane_width', 13);
Irssi::settings_add_bool('tmux_nicklist', 'nicklist_color', 1);
Irssi::settings_add_bool('tmux_nicklist', 'nicklist_gone_sort', 0);
Irssi::signal_add_last('window item changed', sub{switch_channel("window item changed",@_)});
Irssi::signal_add_last('window changed', sub{switch_channel("window changed",@_)});
Irssi::signal_add_last('channel joined', sub{switch_channel("channel joined",@_)});
Irssi::signal_add('nicklist new', sub{reset_nicklist("nicklist new",@_)});
Irssi::signal_add('nicklist remove', sub{reset_nicklist("nicklist remove",@_)});
Irssi::signal_add('nicklist changed', sub{reset_nicklist("nicklist changed",@_)});
Irssi::signal_add_first('nick mode changed', sub{reset_nicklist("nick mode changed",@_)});
Irssi::signal_add('gui exit', \&disable_nicklist);
Irssi::signal_add_last('terminal resized', \&resized_timed);
} else {
my $fifo_path = $ARGV[0];
my $irssi_pane = $ARGV[1];
# array to store the current channel nicknames
my @nicknames = ();
# helper functions for manipulating the terminal
# escape sequences taken from
# http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x361.html
sub enable_mouse { print "\e[?1000h"; }
# recognized sequences
my $MOUSE_SCROLL_DOWN="\e[Ma";
my $MOUSE_SCROLL_UP="\e[M`";
my $ARROW_DOWN="\e[B";
my $ARROW_UP="\e[A";
my $UP="k";
my $DOWN="j";
my $PAGE_DOWN="\e[6~";
my $PAGE_UP="\e[5~";
my $GO_TOP="gg";
my $GO_BOTTOM="G";
my $current_line = 0;
my $sequence = '';
my ($rows, $cols);
sub term_size {
split ' ', `stty size`;
}
sub redraw {
my $last_nick_idx = @nicknames;
my $last_idx = $current_line + $rows;
# normalize last visible index
if ($last_idx > ($last_nick_idx)) {
$last_idx = $last_nick_idx;
}
# redraw visible nicks
for my $i (reverse 1..$rows) {
print "\e[$i;1H\e[K";
my $idx = $current_line + $i - 1;
if ($idx < $last_idx) {
my $z = 0; my $col = $cols;
for (split /(\e\[(?:\d|;|:|\?|\s)*.)/, $nicknames[$idx]) {
if ($z ^= 1) {
print +(substr $_, 0, $col) if $col > 0;
$col -= length;
} else {
print
}
}
}
}
}
sub move_down {
$sequence = '';
my $count = int $_[0];
my $nickcount = $#nicknames;
return if ($nickcount <= $rows);
if ($count == -1) {
$current_line = $nickcount - $rows - 1;
redraw;
return;
}
my $visible = $nickcount - $current_line - $count;
if ($visible > $rows) {
$current_line += $count;
redraw;
} elsif (($visible + $count) > $rows) {
# scroll the maximum we can
$current_line = $nickcount - $rows - 1;
redraw;
}
}
sub move_up {
$sequence = '';
my $count = int $_[0];
if ($count == -1) {
$current_line = 0;
redraw;
return;
}
return if ($current_line == 0);
$count = 1 if $count == 0;
$current_line -= $count;
$current_line = 0 if $current_line < 0;
redraw;
}
$SIG{INT} = 'IGNORE';
STDOUT->autoflush(1);
# setup terminal so we can listen for individual key presses without echo
`stty -icanon -echo`;
# open named pipe and setup the 'select' wrapper object for listening on both
# fds(fifo and sdtin)
open my $fifo, "<", $fifo_path or die "can't open $fifo_path: $!";
my $select = IO::Select->new();
my @ready;
$select->add($fifo);
$select->add(\*STDIN);
enable_mouse;
system('tput', 'smcup');
print "\e[?7l"; #system('tput', 'rmam');
system('tput', 'civis');
MAIN: {
while (@ready = $select->can_read) {
foreach my $fd (@ready) {
($rows, $cols) = term_size;
if ($fd == $fifo) {
while (<$fifo>) {
my $line = $_;
if ($line =~ /^BEGIN/) {
@nicknames = ();
} elsif ($line =~ /^SWITCH_CHANNEL/) {
$current_line = 0;
} elsif ($line =~ /^NICK(.+)$/) {
push @nicknames, $1;
} elsif ($line =~ /^END$/) {
redraw;
last;
} elsif ($line =~ /^EXIT$/) {
last MAIN;
}
}
} else {
my $key = '';
sysread(STDIN, $key, 1);
$sequence .= $key;
if ($MOUSE_SCROLL_DOWN =~ /^\Q$sequence\E/) {
if ($MOUSE_SCROLL_DOWN eq $sequence) {
move_down 3;
# mouse scroll has two more bytes that I dont use here
# so consume them now to avoid sending unwanted bytes to
# irssi
sysread(STDIN, $key, 2);
}
} elsif ($MOUSE_SCROLL_UP =~ /^\Q$sequence\E/) {
if ($MOUSE_SCROLL_UP eq $sequence) {
move_up 3;
sysread(STDIN, $key, 2);
}
} elsif ($ARROW_DOWN =~ /^\Q$sequence\E/) {
move_down 1 if ($ARROW_DOWN eq $sequence);
} elsif ($ARROW_UP =~ /^\Q$sequence\E/) {
move_up 1 if ($ARROW_UP eq $sequence);
} elsif ($DOWN =~ /^\Q$sequence\E/) {
move_down 1 if ($DOWN eq $sequence);
} elsif ($UP =~ /^\Q$sequence\E/) {
move_up 1 if ($UP eq $sequence);
} elsif ($PAGE_DOWN =~ /^\Q$sequence\E/) {
move_down $rows/2 if ($PAGE_DOWN eq $sequence);
} elsif ($PAGE_UP =~ /^\Q$sequence\E/) {
move_up $rows/2 if ($PAGE_UP eq $sequence);
} elsif ($GO_BOTTOM =~ /^\Q$sequence\E/) {
move_down -1 if ($GO_BOTTOM eq $sequence);
} elsif ($GO_TOP =~ /^\Q$sequence\E/) {
move_up -1 if ($GO_TOP eq $sequence);
} else {
# Unrecognized sequences will be send to irssi and its pane
# will be focused
system('tmux', 'send-keys', '-t', $irssi_pane, $sequence);
system('tmux', 'select-pane', '-t', $irssi_pane);
$sequence = '';
}
}
}
}
}
close $fifo;
}

View File

@@ -0,0 +1,304 @@
# based on the nicklist.pl script
################################################################################
# tmux_nicklist.pl
# This script integrates tmux and irssi to display a list of nicks in a
# vertical right pane with 20% width. Right now theres no configuration
# or setup, simply initialize the script with irssi and by default you
# will get the nicklist for every channel(customize by altering
# the regex in '$channel_pattern'.
#
# It supports mouse scrolling and the following keys:
# k/up arrow: up one line
# j/down arrow: down one line
# pageup: up 20 lines
# pagedown: down 20 lines
# gg: go to top
# G: go to bottom
#
# For better integration, unrecognized sequences will be sent to irssi and
# its pane will be focused.
################################################################################
use strict;
use IO::Handle;
use IO::Select;
use POSIX;
use File::Temp qw/ :mktemp /;
use File::Basename;
use vars qw($VERSION %IRSSI);
$VERSION = '0.0.0';
%IRSSI = (
authors => 'Thiago de Arruda',
contact => 'tpadilha84@gmail.com',
name => 'tmux-nicklist',
description => 'displays a list of nicks in a separate tmux pane',
license => 'WTFPL',
);
if ($#ARGV == -1) {
require Irssi;
my $enabled = 0;
my $script_path = __FILE__;
my $tmpdir;
my $fifo_path;
# my $channel_pattern = '^&bitlbee$';
my $channel_pattern = '^.+$';
sub enable_nicklist {
return if ($enabled);
$tmpdir = mkdtemp "/tmp/nicklist-XXXXXXXX";
$fifo_path = "$tmpdir/fifo";
POSIX::mkfifo($fifo_path, 0600) or die "can\'t mkfifo $fifo_path: $!";
my $cmd = "perl $script_path $fifo_path $ENV{'TMUX_PANE'}";
system('tmux', 'split-window', '-dh', '-p', '20', $cmd);
# The next system call will block until the other pane has opened the pipe
# for reading, so synchronization is not an issue here.
open (FIFO, "> $fifo_path") or die "can't open $fifo_path: $!";
FIFO->autoflush(1);
$enabled = 1;
}
sub disable_nicklist {
return unless ($enabled);
print(FIFO "EXIT\n");
close FIFO;
unlink $fifo_path;
rmdir $tmpdir;
$enabled = 0;
}
sub reset_nicklist {
my $active = Irssi::active_win();
my $channel = $active->{active};
if ((!$channel ||
(ref($channel) ne 'Irssi::Irc::Channel' && ref($channel) ne
'Irssi::Silc::Channel') || $channel->{'type'} ne 'CHANNEL' ||
($channel->{chat_type} ne 'SILC' && !$channel->{'names_got'})) ||
($channel->{'name'} !~ /$channel_pattern/ )) {
disable_nicklist;
} else {
enable_nicklist;
print(FIFO "BEGIN\n");
foreach my $nick (sort {(($a->{'op'}?'1':$a->{'halfop'}?'2':$a->{'voice'}?'3':'4').lc($a->{'nick'}))
cmp (($b->{'op'}?'1':$b->{'halfop'}?'2':$b->{'voice'}?'3':'4').lc($b->{'nick'}))} $channel->nicks()) {
print(FIFO "NICK");
if ($nick->{'op'}) {
print(FIFO "\e[32m\@$nick->{'nick'}\e[39m");
} elsif ($nick->{'halfop'}) {
print(FIFO "\e[34m%$nick->{'nick'}\e[39m");
} elsif ($nick->{'voice'}) {
print(FIFO "\e[33m+$nick->{'nick'}\e[39m");
} else {
print(FIFO " $nick->{'nick'}");
}
print(FIFO "\n");
}
print(FIFO "END\n");
}
}
sub switch_channel {
print(FIFO "SWITCH_CHANNEL\n");
reset_nicklist;
}
Irssi::signal_add_last('window item changed', \&switch_channel);
Irssi::signal_add_last('window changed', \&switch_channel);
Irssi::signal_add_last('channel wholist', \&reset_nicklist);
# first, to be before ignores
Irssi::signal_add_first('message join', \&reset_nicklist);
Irssi::signal_add_first('message part', \&reset_nicklist);
Irssi::signal_add_first('message kick', \&reset_nicklist);
Irssi::signal_add_first('message quit', \&reset_nicklist);
Irssi::signal_add_first('message nick', \&reset_nicklist);
Irssi::signal_add_first('message own_nick', \&reset_nicklist);
Irssi::signal_add_first('nick mode changed', \&reset_nicklist);
Irssi::signal_add('gui exit', \&disable_nicklist);
} else {
require 'sys/ioctl.ph';
# open STDERR, '>', "$ENV{'HOME'}/.nickbar-errors.log";
my $fifo_path = $ARGV[0];
my $irssi_pane = $ARGV[1];
# array to store the current channel nicknames
my @nicknames = ();
# helper functions for manipulating the terminal
# escape sequences taken from
# http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x361.html
sub clear_screen { print "\e[2J"; }
sub save_cursor { print "\e[s"; }
sub restore_cursor { print "\e[u"; }
sub enable_mouse { print "\e[?1000h"; }
# recognized sequences
my $MOUSE_SCROLL_DOWN="\e[Ma";
my $MOUSE_SCROLL_UP="\e[M`";
my $ARROW_DOWN="\e[B";
my $ARROW_UP="\e[A";
my $UP="k";
my $DOWN="j";
my $PAGE_DOWN="\e[6~";
my $PAGE_UP="\e[5~";
my $GO_TOP="gg";
my $GO_BOTTOM="G";
my $current_line = 0;
my $sequence = '';
sub term_row_count {
# from http://stackoverflow.com/questions/4286158/how-do-i-get-width-and-height-of-my-terminal-with-ioctl
my $terminal_size;
ioctl(STDOUT, TIOCGWINSZ() , $terminal_size);
my ($rows, $cols, $xpix, $ypix) = unpack 'S4', $terminal_size;
return $rows;
}
sub redraw {
my $rows = term_row_count;
my $last_nick_idx = $#nicknames;
my $last_idx = $current_line + $rows;
# normalize last visible index
if ($last_idx > ($last_nick_idx)) {
$last_idx = $last_nick_idx;
}
# redraw visible nicks
restore_cursor;
clear_screen;
for (my $idx = $current_line; $idx <= $last_idx; $idx++) {
print "$nicknames[$idx]\n";
}
}
sub move_down {
$sequence = '';
my $count = $_[0];
my $nickcount = $#nicknames;
my $rows = term_row_count;
return if ($nickcount <= $rows);
if ($count == -1) {
$current_line = $nickcount - $rows - 1;
redraw;
return;
}
my $visible = $nickcount - $current_line - $count;
if ($visible > $rows) {
$current_line += $count;
redraw;
} elsif (($visible + $count) > $rows) {
# scroll the maximum we can
$current_line = $nickcount - $rows - 1;
redraw;
}
}
sub move_up {
$sequence = '';
my $count = $_[0];
if ($count == -1) {
$current_line = 0;
redraw;
return;
}
return if ($current_line == 0);
if (($current_line - $count) >= 0) {
$current_line -= $count;
redraw;
}
}
$SIG{INT} = 'IGNORE';
# setup terminal so we can listen for individual key presses without echo
my ($term, $oterm, $echo, $noecho, $fd_stdin);
$fd_stdin = fileno(STDIN);
$term = POSIX::Termios->new();
$term->getattr($fd_stdin);
$oterm = $term->getlflag();
$echo = ECHO | ECHOK | ICANON;
$noecho = $oterm & ~$echo;
$term->setlflag($noecho);
$term->setcc(VTIME, 1);
$term->setattr($fd_stdin, TCSANOW);
# open named pipe and setup the 'select' wrapper object for listening on both
# fds(fifo and sdtin)
open (FIFO, "< $fifo_path") or die "can't open $fifo_path: $!";
my $fifo = \*FIFO;
my $stdin = \*STDIN;
my $select = IO::Select->new();
my @ready;
$select->add($fifo);
$select->add($stdin);
save_cursor;
enable_mouse;
system('tput', 'civis');
MAIN: {
while (@ready = $select->can_read) {
foreach my $fd (@ready) {
if ($fd == $fifo) {
while (<$fifo>) {
my $line = $_;
if ($line =~ /^BEGIN/) {
@nicknames = ();
} elsif ($line =~ /^SWITCH_CHANNEL/) {
$current_line = 0;
} elsif ($line =~ /^NICK(.+)$/) {
push @nicknames, $1;
} elsif ($line =~ /^END$/) {
redraw;
last;
} elsif ($line =~ /^EXIT$/) {
last MAIN;
}
}
} else {
my $key = '';
sysread(STDIN, $key, 1);
$sequence .= $key;
if ($MOUSE_SCROLL_DOWN =~ /^\Q$sequence\E/) {
if ($MOUSE_SCROLL_DOWN eq $sequence) {
move_down 3;
# mouse scroll has two more bytes that I dont use here
# so consume them now to avoid sending unwanted bytes to
# irssi
sysread(STDIN, $key, 2);
}
} elsif ($MOUSE_SCROLL_UP =~ /^\Q$sequence\E/) {
if ($MOUSE_SCROLL_UP eq $sequence) {
move_up 3;
sysread(STDIN, $key, 2);
}
} elsif ($ARROW_DOWN =~ /^\Q$sequence\E/) {
move_down 1 if ($ARROW_DOWN eq $sequence);
} elsif ($ARROW_UP =~ /^\Q$sequence\E/) {
move_up 1 if ($ARROW_UP eq $sequence);
} elsif ($DOWN =~ /^\Q$sequence\E/) {
move_down 1 if ($DOWN eq $sequence);
} elsif ($UP =~ /^\Q$sequence\E/) {
move_up 1 if ($UP eq $sequence);
} elsif ($PAGE_DOWN =~ /^\Q$sequence\E/) {
move_down 20 if ($PAGE_DOWN eq $sequence);
} elsif ($PAGE_UP =~ /^\Q$sequence\E/) {
move_up 20 if ($PAGE_UP eq $sequence);
} elsif ($GO_BOTTOM =~ /^\Q$sequence\E/) {
move_down -1 if ($GO_BOTTOM eq $sequence);
} elsif ($GO_TOP =~ /^\Q$sequence\E/) {
move_up -1 if ($GO_TOP eq $sequence);
} else {
# Unrecognized sequences will be send to irssi and its pane
# will be focused
system('tmux', 'send-keys', '-l', '-t', $irssi_pane, $sequence);
system('tmux', 'select-pane', '-t', $irssi_pane);
$sequence = '';
}
}
}
}
}
close FIFO;
}

189
irssi/scripts/trackbar.pl Executable file
View File

@@ -0,0 +1,189 @@
# trackbar.pl
#
# This little script will do just one thing: it will draw a line each time you
# switch away from a window. This way, you always know just upto where you've
# been reading that window :) It also removes the previous drawn line, so you
# don't see double lines.
#
# Usage:
#
# The script works right out of the box, but if you want you can change
# the working by /set'ing the following variables:
#
# trackbar_string The characters to repeat to draw the bar
# trackbar_style The style for the bar, %r is red for example
# See formats.txt that came with irssi
#
# /mark is a command that will redraw the line at the bottom. However! This
# requires irssi version after 20021228. otherwise you'll get the error
# redraw: unknown command, and your screen is all goofed up :)
#
# /upgrade & buf.pl notice: This version tries to remove the trackbars before
# the upgrade is done, so buf.pl does not restore them, as they are not removeable
# afterwards by trackbar. Unfortiounatly, to make this work, trackbar and buf.pl
# need to be loaded in a specific order. Please experiment to see which order works
# for you (strangely, it differs from configuration to configuration, something I will
# try to fix in a next version)
#
# Authors:
# - Main maintainer & author: Peter 'kinlo' Leurs
# - Many thanks to Timo 'cras' Sirainen for placing me on my way
# - on-upgrade-remove-line patch by Uwe Dudenhoeffer
#
# Version history:
# 1.4: - Changed our's by my's so the irssi script header is valid
# - Removed utf-8 support. In theory, the script should work w/o any
# problems for utf-8, just set trackbar_string to a valid utf-8 character
# and everything *should* work. However, this script is being plagued by
# irssi internal bugs. The function Irssi::settings_get_str does NOT handle
# unicode strings properly, hence you will notice problems when setting the bar
# to a unicode char. For changing your bar to utf-8 symbols, read the line sub.
# 1.3: - Upgrade now removes the trackbars.
# - Some code cleanups, other defaults
# - /mark sets the line to the bottom
# 1.2: - Support for utf-8
# - How the bar looks can now be configured with trackbar_string
# and trackbar_style
# 1.1: - Fixed bug when closing window
# 1.0: - Initial release
#
#
# Call for help!
#
# There is a trackbar version 2.0 that properly handles resizes and immediate config change
# activation. However, there is/are some bug(s) in irssi's main buffer/window code that causes
# irssi to 'forget' lines, which is ofcourse completly unaccepteable. I haven't found the time
# nor do I know the irssi's internals enough to find and fix this bug, if you want to help, please
# contact me, I'll give you a copy of the 2.0 version that will immediatly show you the problems.
#
# Known bugs:
# - if you /clear a window, it will be uncleared when returning to the window
# - UTF-8 characters in the trackbar_string doesnt work. This is an irssi bug.
# - if you resize your irssi (in xterm or so) the bar is not resized
# - changing the trackbar style is only visible after returning to a window
# however, changing style/resize takes in effect after you left the window.
#
# Whishlist/todo:
# - instead of drawing a line, just invert timestamp or something,
# to save a line (but I don't think this is possible with current irssi)
# - some pageup keybinding possibility, to scroll up upto the trackbar
# - <@coekie> kinlo: if i switch to another window, in another split window, i
# want the trackbar to go down in the previouswindow in that splitwindow :)
# - < bob_2> anyway to clear the line once the window is read?
# - < elho> kinlo: wishlist item: a string that gets prepended to the repeating pattern
# - < elho> an option to still have the timestamp in front of the bar
# - < elho> oh and an option to not draw it in the status window :P
#
# BTW: when you have feature requests, mailing a patch that works is the fastest way
# to get it added :p
use strict;
use 5.6.1;
use Irssi;
use Irssi::TextUI;
my $VERSION = "1.4";
my %IRSSI = (
authors => "Peter 'kinlo' Leurs",
contact => "peter\@pfoe.be",
name => "trackbar",
description => "Shows a bar where you've last read a window",
license => "GPLv2",
url => "http://www.pfoe.be/~peter/trackbar/",
changed => "Thu Feb 20 16:18:08 2003",
);
my %config;
Irssi::settings_add_str('trackbar', 'trackbar_string' => '_');
$config{'trackbar_string'} = Irssi::settings_get_str('trackbar_string');
Irssi::settings_add_str('trackbar', 'trackbar_style' => '%K');
$config{'trackbar_style'} = Irssi::settings_get_str('trackbar_style');
Irssi::signal_add(
'setup changed' => sub {
$config{'trackbar_string'} = Irssi::settings_get_str('trackbar_string');
$config{'trackbar_style'} = Irssi::settings_get_str('trackbar_style');
if ($config{'trackbar_style'} =~ /(?<!%)[^%]|%%|%$/) {
Irssi::print(
"trackbar: %RWarning!%n 'trackbar_style' seems to contain "
. "printable characters. Only use format codes (read "
. "formats.txt).", MSGLEVEL_CLIENTERROR);
}
}
);
Irssi::signal_add(
'window changed' => sub {
my (undef, $oldwindow) = @_;
if ($oldwindow) {
my $line = $oldwindow->view()->get_bookmark('trackbar');
$oldwindow->view()->remove_line($line) if defined $line;
$oldwindow->print(line($oldwindow->{'width'}), MSGLEVEL_NEVER);
$oldwindow->view()->set_bookmark_bottom('trackbar');
}
}
);
sub line {
my $width = shift;
my $string = $config{'trackbar_string'};
$string = '-' unless defined $string;
# There is a bug in irssi's utf-8 handling on config file settings, as you
# can reproduce/see yourself by the following code sniplet:
#
# my $quake = pack 'U*', 8364; # EUR symbol
# Irssi::settings_add_str 'temp', 'temp_foo' => $quake;
# Irssi::print length $quake;
# # prints 1
# Irssi::print length Irssi::settings_get_str 'temp_foo';
# # prints 3
#
#
# Trackbar used to have a workaround, but on recent versions of perl/irssi
# it does no longer work. Therefore, if you want your trackbar to contain
# unicode characters, uncomment the line below for a nice full line, or set
# the string to whatever char you want.
# $string = pack('U*', 0x2500);
my $length = length $string;
if ($length == 0) {
$string = '-';
$length = 1;
}
my $times = $width / $length;
$times = int(1 + $times) if $times != int($times);
$string =~ s/%/%%/g;
return $config{'trackbar_style'} . substr($string x $times, 0, $width);
}
# Remove trackbars on upgrade - but this doesn't really work if the scripts are not loaded in the correct order... watch out!
Irssi::signal_add_first( 'session save' => sub {
for my $window (Irssi::windows) {
next unless defined $window;
my $line = $window->view()->get_bookmark('trackbar');
$window->view()->remove_line($line) if defined $line;
}
}
);
sub cmd_mark {
my $window = Irssi::active_win();
# return unless defined $window;
my $line = $window->view()->get_bookmark('trackbar');
$window->view()->remove_line($line) if defined $line;
$window->print(line($window->{'width'}), MSGLEVEL_NEVER);
$window->view()->set_bookmark_bottom('trackbar');
Irssi::command("redraw");
}
Irssi::command_bind('mark', 'cmd_mark');

500
irssi/scripts/trackbar22.pl Normal file
View File

@@ -0,0 +1,500 @@
# trackbar.pl
#
# This little script will do just one thing: it will draw a line each time you
# switch away from a window. This way, you always know just upto where you've
# been reading that window :) It also removes the previous drawn line, so you
# don't see double lines.
#
# redraw trackbar only works on irssi 0.8.17 or higher.
#
#
# Usage:
#
# The script works right out of the box, but if you want you can change
# the working by /set'ing the following variables:
#
# Setting: trackbar_style
# Description: This setting will be the color of your trackbar line.
# By default the value will be '%K', only Irssi color
# formats are allowed. If you don't know the color formats
# by heart, you can take a look at the formats documentation.
# You will find the proper docs on http://www.irssi.org/docs.
#
# Setting: trackbar_string
# Description: This is the string that your line will display. This can
# be multiple characters or just one. For example: '~-~-'
# The default setting is '-'.
#
# Setting: trackbar_use_status_window
# Description: If this setting is set to OFF, Irssi won't print a trackbar
# in the statuswindow
#
# Setting: trackbar_ignore_windows
# Description: A list of windows where no trackbar should be printed
#
# Setting: trackbar_print_timestamp
# Description: If this setting is set to ON, Irssi will print the formatted
# timestamp in front of the trackbar.
#
# Setting: trackbar_require_seen
# Description: Only clear the trackbar if it has been scrolled to.
#
# /mark is a command that will redraw the line at the bottom.
#
# Command: /trackbar, /trackbar goto
# Description: Jump to where the trackbar is, to pick up reading
#
# Command: /trackbar keep
# Description: Keep this window's trackbar where it is the next time
# you switch windows (then this flag is cleared again)
#
# Command: /mark, /trackbar mark
# Description: Remove the old trackbar and mark the bottom of this
# window with a new trackbar
#
# Command: /trackbar markvisible
# Description: Like mark for all visible windows
#
# Command: /trackbar markall
# Description: Like mark for all windows
#
# Command: /trackbar remove
# Description: Remove this window's trackbar
#
# Command: /trackbar removeall
# Description: Remove all windows' trackbars
#
# Command: /trackbar redraw
# Description: Force redraw of trackbars
#
# For bugreports and other improvements contact one of the authors.
#
# 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 2 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 script; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
##
use strict;
use warnings;
use Irssi;
use Irssi::TextUI;
use Encode;
use POSIX qw(strftime);
use vars qw($VERSION %IRSSI);
$VERSION = "2.2"; # cb3189a33c8e5f9
%IRSSI = (
authors => 'Peter Leurs and Geert Hauwaerts',
contact => 'peter@pfoe.be',
patchers => 'Johan Kiviniemi (UTF-8), Uwe Dudenhoeffer (on-upgrade-remove-line)',
name => 'trackbar',
description => 'Shows a bar where you have last read a window.',
license => 'GNU General Public License',
url => 'http://www.pfoe.be/~peter/trackbar/',
changed => 'Fri Jan 23 23:59:11 2004',
commands => 'trackbar',
);
## Comments and remarks.
#
# This script uses settings.
# Use /SET to change the value or /TOGGLE to switch it on or off.
#
#
# Tip: The command 'trackbar' is very usefull if you bind that to a key,
# so you can easily jump to the trackbar. Please see 'help bind' for
# more information about keybindings in Irssi.
#
# Command: /BIND meta2-P key F1
# /BIND F1 command trackbar
#
##
## Bugfixes and new items in this rewrite.
#
# * Remove all the trackbars before upgrading.
# * New setting trackbar_use_status_window to control the statuswindow trackbar.
# * New setting trackbar_print_timestamp to print a timestamp or not.
# * New command 'trackbar' to scroll up to the trackbar.
# * When resizing your terminal, Irssi will update all the trackbars to the new size.
# * When changing trackbar settings, change all the trackbars to the new settings.
# * New command 'trackbar mark' to draw a new trackbar (The old '/mark').
# * New command 'trackbar markall' to draw a new trackbar in each window.
# * New command 'trackbar remove' to remove the trackbar from the current window.
# * New command 'trackbar removeall' to remove all the trackbars.
# * Don't draw a trackbar in empty windows.
# * Added a version check to prevent Irssi redraw errors.
# * Fixed a bookmark NULL versus 0 bug.
# * Fixed a remove-line bug in Uwe Dudenhoeffer his patch.
# * New command 'help trackbar' to display the trackbar commands.
# * Fixed an Irssi startup bug, now processing each auto-created window.
#
##
## Known bugs and the todolist.
#
# Todo: * Instead of drawing a line, invert the line.
#
##
sub cmd_help {
my ($args) = @_;
if ($args =~ /^trackbar *$/i) {
print CLIENTCRAP <<HELP
%9Syntax:%9
TRACKBAR
TRACKBAR GOTO
TRACKBAR KEEP
TRACKBAR MARK
TRACKBAR MARKVISIBLE
TRACKBAR MARKALL
TRACKBAR REMOVE
TRACKBAR REMOVEALL
TRACKBAR REDRAW
%9Parameters:%9
GOTO: Jump to where the trackbar is, to pick up reading
KEEP: Keep this window's trackbar where it is the next time
you switch windows (then this flag is cleared again)
MARK: Remove the old trackbar and mark the bottom of this
window with a new trackbar
MARKVISIBLE: Like mark for all visible windows
MARKALL: Like mark for all windows
REMOVE: Remove this window's trackbar
REMOVEALL: Remove all windows' trackbars
REDRAW: Force redraw of trackbars
%9Description:%9
Manage a trackbar. Without arguments, it will scroll up to the trackbar.
%9Examples:%9
/TRACKBAR MARK
/TRACKBAR REMOVE
HELP
}
}
Irssi::theme_register([
'trackbar_loaded', '%R>>%n %_Scriptinfo:%_ Loaded $0 version $1 by $2.',
'trackbar_wrong_version', '%R>>%n %_Trackbar:%_ Please upgrade your client to 0.8.17 or above if you would like to use this feature of trackbar.',
'trackbar_all_removed', '%R>>%n %_Trackbar:%_ All the trackbars have been removed.',
'trackbar_not_found', '%R>>%n %_Trackbar:%_ No trackbar found in this window.',
]);
my $old_irssi = Irssi::version < 20140701;
sub check_version {
if ($old_irssi) {
Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'trackbar_wrong_version');
return;
} else {
return 1;
}
}
sub is_utf8 {
lc Irssi::settings_get_str('term_charset') eq 'utf-8'
}
my (%config, %keep_trackbar, %unseen_trackbar);
sub remove_one_trackbar {
my $win = shift;
my $view = shift || $win->view;
my $line = $view->get_bookmark('trackbar');
if (defined $line) {
my $bottom = $view->{bottom};
$view->remove_line($line);
$win->command('^scrollback end') if $bottom && !$win->view->{bottom};
$view->redraw;
}
}
sub add_one_trackbar {
my $win = shift;
my $view = shift || $win->view;
$win->print(line($win->{width}), MSGLEVEL_NEVER);
$view->set_bookmark_bottom('trackbar');
$unseen_trackbar{ $win->{_irssi} } = 1;
$view->redraw;
}
sub update_one_trackbar {
my $win = shift;
my $view = shift || $win->view;
my $force = shift;
my $ignored = win_ignored($win, $view);
remove_one_trackbar($win, $view)
if $force || !defined $force || !$ignored;
add_one_trackbar($win, $view)
if $force || !$ignored;
}
sub win_ignored {
my $win = shift;
my $view = shift || $win->view;
return 1 unless $view->{buffer}{lines_count};
return 1 if $win->{name} eq '(status)' && !$config{use_status_window};
return 1 if grep { $win->{name} eq $_ || $win->{refnum} eq $_
|| $win->get_active_name eq $_ } @{ $config{ignore_windows} };
return 0;
}
sub sig_window_changed {
my ($newwindow, $oldwindow) = @_;
return unless $oldwindow;
trackbar_update_seen($newwindow);
return if delete $keep_trackbar{ $oldwindow->{_irssi} };
trackbar_update_seen($oldwindow);
return if $config{require_seen} && $unseen_trackbar{ $oldwindow->{_irssi } };
update_one_trackbar($oldwindow, undef, 0);
}
sub trackbar_update_seen {
my $win = shift;
return unless $win;
my $view = $win->view;
my $line = $view->get_bookmark('trackbar');
unless ($line) {
delete $unseen_trackbar{ $win->{_irssi} };
return;
}
my $startline = $view->{startline};
return unless $startline;
if ($startline->{info}{time} < $line->{info}{time}
|| $startline->{_irssi} == $line->{_irssi}) {
delete $unseen_trackbar{ $win->{_irssi} };
}
}
sub screen_length;
{ local $@;
eval { require Text::CharWidth; };
unless ($@) {
*screen_length = sub { Text::CharWidth::mbswidth($_[0]) };
}
else {
*screen_length = sub {
my $temp = shift;
if (is_utf8()) {
Encode::_utf8_on($temp);
}
length($temp)
};
}
}
{ my %strip_table = (
(map { $_ => '' } (split //, '04261537' . 'kbgcrmyw' . 'KBGCRMYW' . 'U9_8I:|FnN>#[' . 'pP')),
(map { $_ => $_ } (split //, '{}%')),
);
sub c_length {
my $o = Irssi::strip_codes($_[0]);
$o =~ s/(%(%|Z.{6}|z.{6}|X..|x..|.))/exists $strip_table{$2} ? $strip_table{$2} :
$2 =~ m{x(?:0[a-f]|[1-6][0-9a-z]|7[a-x])|z[0-9a-f]{6}}i ? '' : $1/gex;
screen_length($o)
}
}
sub line {
my ($width, $time) = @_;
my $string = $config{string};
$string = ' ' unless length $string;
$time ||= time;
Encode::_utf8_on($string);
my $length = c_length($string);
my $format = '';
if ($config{print_timestamp}) {
$format = $config{timestamp_str};
$format =~ y/%/\01/;
$format =~ s/\01\01/%/g;
$format = strftime($format, localtime $time);
$format =~ y/\01/%/;
}
my $times = $width / $length;
$times += 1 if $times != int $times;
$format .= $config{style};
$width -= c_length($format);
$string x= $times;
chop $string while length $string && c_length($string) > $width;
return $format . $string;
}
sub remove_all_trackbars {
for my $window (Irssi::windows) {
next unless ref $window;
remove_one_trackbar($window);
}
}
sub UNLOAD {
remove_all_trackbars();
}
sub redraw_trackbars {
return unless check_version();
for my $win (Irssi::windows) {
next unless ref $win;
my $view = $win->view;
my $line = $view->get_bookmark('trackbar');
next unless $line;
my $bottom = $view->{bottom};
$win->print_after($line, MSGLEVEL_NEVER, line($win->{width}, $line->{info}{time}),
$line->{info}{time});
$view->set_bookmark('trackbar', $win->last_line_insert);
$view->redraw;
$view->remove_line($line);
$win->command('^scrollback end') if $bottom && !$win->view->{bottom};
$view->redraw;
}
}
sub goto_trackbar {
my $win = Irssi::active_win;
my $line = $win->view->get_bookmark('trackbar');
if ($line) {
$win->command("scrollback goto ". strftime("%d %H:%M:%S", localtime($line->{info}{time})));
} else {
$win->printformat(MSGLEVEL_CLIENTCRAP, 'trackbar_not_found');
}
}
sub cmd_mark {
update_one_trackbar(Irssi::active_win, undef, 1);
}
sub cmd_markall {
for my $window (Irssi::windows) {
next unless ref $window;
update_one_trackbar($window);
}
}
sub signal_stop {
Irssi::signal_stop;
}
sub cmd_markvisible {
my @wins = Irssi::windows;
my $awin =
my $bwin = Irssi::active_win;
my $awin_counter = 0;
Irssi::signal_add_priority('window changed' => 'signal_stop', -99);
do {
Irssi::active_win->command('window up');
$awin = Irssi::active_win;
update_one_trackbar($awin);
++$awin_counter;
} until ($awin->{refnum} == $bwin->{refnum} || $awin_counter >= @wins);
Irssi::signal_remove('window changed' => 'signal_stop');
}
sub cmd_trackbar_remove_one {
remove_one_trackbar(Irssi::active_win);
}
sub cmd_remove_all_trackbars {
remove_all_trackbars();
Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'trackbar_all_removed');
}
sub cmd_keep_once {
$keep_trackbar{ Irssi::active_win->{_irssi} } = 1;
}
sub trackbar_runsub {
my ($data, $server, $item) = @_;
$data =~ s/\s+$//g;
if ($data) {
Irssi::command_runsub('trackbar', $data, $server, $item);
} else {
goto_trackbar();
}
}
sub update_config {
my $was_status_window = $config{use_status_window};
$config{style} = Irssi::settings_get_str('trackbar_style');
$config{string} = Irssi::settings_get_str('trackbar_string');
$config{require_seen} = Irssi::settings_get_bool('trackbar_require_seen');
$config{ignore_windows} = [ split /[,\s]+/, Irssi::settings_get_str('trackbar_ignore_windows') ];
$config{use_status_window} = Irssi::settings_get_bool('trackbar_use_status_window');
$config{print_timestamp} = Irssi::settings_get_bool('trackbar_print_timestamp');
if (defined $was_status_window && $was_status_window != $config{use_status_window}) {
if (my $swin = Irssi::window_find_name('(status)')) {
if ($config{use_status_window}) {
update_one_trackbar($swin);
}
else {
remove_one_trackbar($swin);
}
}
}
if ($config{print_timestamp}) {
my $ts_format = Irssi::settings_get_str('timestamp_format');
my $ts_theme = Irssi::current_theme->get_format('fe-common/core', 'timestamp');
my $render_str = Irssi::current_theme->format_expand($ts_theme);
(my $ts_escaped = $ts_format) =~ s/([%\$])/$1$1/g;
$render_str =~ s/(?|\$(.)(?!\w)|\$\{(\w+)\})/$1 eq 'Z' ? $ts_escaped : $1/ge;
$config{timestamp_str} = $render_str;
}
redraw_trackbars() unless $old_irssi;
}
Irssi::settings_add_str('trackbar', 'trackbar_string', is_utf8() ? "\x{2500}" : '-');
Irssi::settings_add_str('trackbar', 'trackbar_style', '%K');
Irssi::settings_add_str('trackbar', 'trackbar_ignore_windows', '');
Irssi::settings_add_bool('trackbar', 'trackbar_use_status_window', 1);
Irssi::settings_add_bool('trackbar', 'trackbar_print_timestamp', 0);
Irssi::settings_add_bool('trackbar', 'trackbar_require_seen', 0);
update_config();
Irssi::signal_add_last( 'mainwindow resized' => 'redraw_trackbars')
unless $old_irssi;
Irssi::signal_register({'gui page scrolled' => [qw/Irssi::UI::Window/]});
Irssi::signal_add_last('gui page scrolled' => 'trackbar_update_seen');
Irssi::signal_add('setup changed' => 'update_config');
Irssi::signal_add_priority('session save' => 'remove_all_trackbars', Irssi::SIGNAL_PRIORITY_HIGH-1);
Irssi::signal_add('window changed' => 'sig_window_changed');
Irssi::command_bind('trackbar goto' => 'goto_trackbar');
Irssi::command_bind('trackbar keep' => 'cmd_keep_once');
Irssi::command_bind('trackbar mark' => 'cmd_mark');
Irssi::command_bind('trackbar markvisible' => 'cmd_markvisible');
Irssi::command_bind('trackbar markall' => 'cmd_markall');
Irssi::command_bind('trackbar remove' => 'cmd_trackbar_remove_one');
Irssi::command_bind('trackbar removeall' => 'cmd_remove_all_trackbars');
Irssi::command_bind('trackbar redraw' => 'redraw_trackbars');
Irssi::command_bind('trackbar' => 'trackbar_runsub');
Irssi::command_bind('mark' => 'cmd_mark');
Irssi::command_bind_last('help' => 'cmd_help');
Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'trackbar_loaded', $IRSSI{name}, $VERSION, $IRSSI{authors});

171
irssi/scripts/usercount.pl Executable file
View File

@@ -0,0 +1,171 @@
use Irssi 20020101.0250 ();
$VERSION = "1.16";
%IRSSI = (
authors => 'David Leadbeater, Timo Sirainen, Georg Lukas',
contact => 'dgl@dgl.cx, tss@iki.fi, georg@boerde.de',
name => 'usercount',
description => 'Adds a usercount for a channel as a statusbar item',
license => 'GNU GPLv2 or later',
url => 'http://irssi.dgl.yi.org/',
);
# Once you have loaded this script run the following command:
# /statusbar window add usercount
# You can also add -alignment left|right option
# /set usercount_show_zero on or off to show users when 0 users of that type
# /set usercount_show_ircops (default off)
# /set usercount_show_halfops (default on)
# you can customize the look of this item from theme file:
# sb_usercount = "{sb %_$0%_ nicks ($1-)}";
# sb_uc_ircops = "%_*%_$*";
# sb_uc_ops = "%_@%_$*";
# sb_uc_halfops = "%_%%%_$*";
# sb_uc_voices = "%_+%_$*";
# sb_uc_normal = "$*";
# sb_uc_space = " ";
use strict;
use Irssi::TextUI;
my ($ircops, $ops, $halfops, $voices, $normal, $total);
my ($timeout_tag, $recalc);
# Called to make the status bar item
sub usercount {
my ($item, $get_size_only) = @_;
my $wi = !Irssi::active_win() ? undef : Irssi::active_win()->{active};
if(!ref $wi || $wi->{type} ne "CHANNEL") { # only works on channels
return unless ref $item;
$item->{min_size} = $item->{max_size} = 0;
return;
}
if ($recalc) {
$recalc = 0;
calc_users($wi);
}
my $theme = Irssi::current_theme();
my $format = $theme->format_expand("{sb_usercount}");
if ($format) {
# use theme-specific look
my $ircopstr = $theme->format_expand("{sb_uc_ircops $ircops}",
Irssi::EXPAND_FLAG_IGNORE_EMPTY);
my $opstr = $theme->format_expand("{sb_uc_ops $ops}",
Irssi::EXPAND_FLAG_IGNORE_EMPTY);
my $halfopstr = $theme->format_expand("{sb_uc_halfops $halfops}",
Irssi::EXPAND_FLAG_IGNORE_EMPTY);
my $voicestr = $theme->format_expand("{sb_uc_voices $voices}",
Irssi::EXPAND_FLAG_IGNORE_EMPTY);
my $normalstr = $theme->format_expand("{sb_uc_normal $normal}",
Irssi::EXPAND_FLAG_IGNORE_EMPTY);
my $space = $theme->format_expand('{sb_uc_space}',
Irssi::EXPAND_FLAG_IGNORE_EMPTY);
$space = " " unless $space;
my $str = "";
$str .= $ircopstr.$space if defined $ircops;
$str .= $opstr.$space if defined $ops;
$str .= $halfopstr.$space if defined $halfops;
$str .= $voicestr.$space if defined $voices;
$str .= $normalstr.$space if defined $normal;
$str =~ s/\Q$space\E$//;
$format = $theme->format_expand("{sb_usercount $total $str}",
Irssi::EXPAND_FLAG_IGNORE_REPLACES);
} else {
# use the default look
$format = "{sb \%_$total\%_ nicks \%c(\%n";
$format .= '*'.$ircops.' ' if (defined $ircops);
$format .= '@'.$ops.' ' if (defined $ops);
$format .= '%%'.$halfops.' ' if (defined $halfops);
$format .= "+$voices " if (defined $voices);
$format .= "$normal " if (defined $normal);
$format =~ s/ $//;
$format .= "\%c)}";
}
$item->default_handler($get_size_only, $format, undef, 1);
}
sub calc_users() {
my $channel = shift;
my $server = $channel->{server};
$ircops = $ops = $halfops = $voices = $normal = 0;
for ($channel->nicks()) {
if ($_->{serverop}) {
$ircops++;
}
if ($_->{op}) {
$ops++;
} elsif ($_->{halfop}) {
$halfops++;
} elsif ($_->{voice}) {
$voices++;
} else {
$normal++;
}
}
$total = $ops+$halfops+$voices+$normal;
if (!Irssi::settings_get_bool('usercount_show_zero')) {
$ircops = undef if ($ircops == 0);
$ops = undef if ($ops == 0);
$halfops = undef if ($halfops == 0);
$voices = undef if ($voices == 0);
$normal = undef if ($normal == 0);
}
$halfops = undef unless Irssi::settings_get_bool('usercount_show_halfops');
$ircops = undef unless Irssi::settings_get_bool('usercount_show_ircops');
}
sub refresh {
if ($timeout_tag > 0) {
Irssi::timeout_remove($timeout_tag);
$timeout_tag = 0;
}
Irssi::statusbar_items_redraw('usercount');
}
sub refresh_check {
my $channel = shift;
my $wi = ref Irssi::active_win() ? Irssi::active_win()->{active} : 0;
return unless ref $wi && ref $channel;
return if $wi->{name} ne $channel->{name};
return if $wi->{server}->{tag} ne $channel->{server}->{tag};
# don't refresh immediately, or we'll end up refreshing
# a lot around netsplits
$recalc = 1;
Irssi::timeout_remove($timeout_tag) if ($timeout_tag > 0);
$timeout_tag = Irssi::timeout_add(500, 'refresh', undef);
}
sub refresh_recalc {
$recalc = 1;
refresh();
}
$recalc = 1;
$timeout_tag = 0;
Irssi::settings_add_bool('usercount', 'usercount_show_zero', 1);
Irssi::settings_add_bool('usercount', 'usercount_show_ircops', 0);
Irssi::settings_add_bool('usercount', 'usercount_show_halfops', 1);
Irssi::statusbar_item_register('usercount', undef, 'usercount');
Irssi::statusbars_recreate_items();
Irssi::signal_add_last('nicklist new', 'refresh_check');
Irssi::signal_add_last('nicklist remove', 'refresh_check');
Irssi::signal_add_last('nick mode changed', 'refresh_check');
Irssi::signal_add_last('setup changed', 'refresh_recalc');
Irssi::signal_add_last('window changed', 'refresh_recalc');
Irssi::signal_add_last('window item changed', 'refresh_recalc');