diff options
Diffstat (limited to '')
25 files changed, 5614 insertions, 0 deletions
diff --git a/.irssi/config b/.irssi/config new file mode 100644 index 0000000..c525f83 --- /dev/null +++ b/.irssi/config @@ -0,0 +1,512 @@ +servers = ( + { + address = "chat.freenode.net"; + chatnet = "Freenode"; + port = "6697"; + use_tls = "yes"; + tls_cert = "~/.irssi/certs/freenode.pem"; + tls_verify = "no"; + autoconnect = "yes"; + }, + { + address = "irc.undernet.org"; + chatnet = "Undernet"; + port = "6667"; + }, + { + address = "irc.oftc.net"; + chatnet = "OFTC"; + port = "6697"; + use_tls = "yes"; + tls_cert = "~/.irssi/certs/oftc.pem"; + tls_verify = "yes"; + autoconnect = "yes"; + }, + { + address = "irc.schmorp.de"; + chatnet = "schmorp"; + port = "6667"; + use_tls = "no"; + tls_verify = "no"; + } +); + +chatnets = { + Freenode = { + type = "IRC"; + max_kicks = "1"; + max_msgs = "4"; + max_whois = "1"; + }; + OFTC = { type = "IRC"; max_kicks = "1"; max_msgs = "1"; max_whois = "1"; }; + Undernet = { + type = "IRC"; + max_kicks = "1"; + max_msgs = "1"; + max_whois = "1"; + }; + schmorp = { type = "IRC"; }; + "chtt-s18.slack.com" = { type = "IRC"; nick = "rkavanagh"; }; +}; + +aliases = { + ATAG = "WINDOW SERVER"; + ADDALLCHANS = "SCRIPT EXEC foreach my \\$channel (Irssi::channels()) { Irssi::command(\"CHANNEL ADD -auto \\$channel->{name} \\$channel->{server}->{tag} \\$channel->{key}\")\\;}"; + B = "BAN"; + BACK = "AWAY"; + BANS = "BAN"; + BYE = "QUIT"; + C = "CLEAR"; + CALC = "EXEC - if command -v bc >/dev/null 2>&1\\; then printf '%s=' '$*'\\; echo '$*' | bc -l\\; else echo bc was not found\\; fi"; + CHAT = "DCC CHAT"; + CUBES = "SCRIPT EXEC Irssi::active_win->print(\"%_bases\", MSGLEVEL_CLIENTCRAP) \\; Irssi::active_win->print( do { join '', map { \"%x0\\${_}0\\$_\" } '0'..'9','A'..'F' }, MSGLEVEL_NEVER | MSGLEVEL_CLIENTCRAP) \\; Irssi::active_win->print(\"%_cubes\", MSGLEVEL_CLIENTCRAP) \\; Irssi::active_win->print( do { my \\$y = \\$_*6 \\; join '', map { my \\$x = \\$_ \\; map { \"%x\\$x\\$_\\$x\\$_\" } @{['0'..'9','A'..'Z']}[\\$y .. \\$y+5] } 1..6 }, MSGLEVEL_NEVER | MSGLEVEL_CLIENTCRAP) for 0..5 \\; Irssi::active_win->print(\"%_grays\", MSGLEVEL_CLIENTCRAP) \\; Irssi::active_win->print( do { join '', map { \"%x7\\${_}7\\$_\" } 'A'..'X' }, MSGLEVEL_NEVER | MSGLEVEL_CLIENTCRAP) \\; Irssi::active_win->print(\"%_mIRC extended colours\", MSGLEVEL_CLIENTCRAP) \\; my \\$x \\; \\$x .= sprintf \"\00399,%02d%02d\",\\$_,\\$_ for 0..15 \\; Irssi::active_win->print(\\$x, MSGLEVEL_NEVER | MSGLEVEL_CLIENTCRAP) \\; for my \\$z (0..6) { my \\$x \\; \\$x .= sprintf \"\00399,%02d%02d\",\\$_,\\$_ for 16+(\\$z*12)..16+(\\$z*12)+11 \\; Irssi::active_win->print(\\$x, MSGLEVEL_NEVER | MSGLEVEL_CLIENTCRAP) }"; + DATE = "TIME"; + DEHIGHLIGHT = "DEHILIGHT"; + DESCRIBE = "ACTION"; + DHL = "DEHILIGHT"; + EXEMPTLIST = "MODE $C +e"; + EXIT = "QUIT"; + GOTO = "SCROLLBACK GOTO"; + HIGHLIGHT = "HILIGHT"; + HL = "HILIGHT"; + HOST = "USERHOST"; + INVITELIST = "MODE $C +I"; + J = "JOIN"; + K = "KICK"; + KB = "KICKBAN"; + KN = "KNOCKOUT"; + LAST = "LASTLOG"; + LEAVE = "PART"; + M = "MSG"; + MUB = "UNBAN *"; + N = "NAMES"; + NMSG = "^MSG"; + P = "PART"; + Q = "QUERY"; + RESET = "SET -default"; + RUN = "SCRIPT LOAD"; + SAY = "MSG *"; + SB = "SCROLLBACK"; + SBAR = "STATUSBAR"; + SIGNOFF = "QUIT"; + SV = "MSG * Irssi $J ($V) - http://www.irssi.org"; + T = "TOPIC"; + UB = "UNBAN"; + UMODE = "MODE $N"; + UNSET = "SET -clear"; + W = "/window goto"; + WC = "WINDOW CLOSE"; + WG = "WINDOW GOTO"; + WJOIN = "JOIN -window"; + WI = "WHOIS"; + WII = "WHOIS $0 $0"; + WL = "WINDOW LIST"; + WQUERY = "QUERY -window"; + WW = "WHOWAS"; + 1 = "WINDOW GOTO 1"; + 2 = "WINDOW GOTO 2"; + 3 = "WINDOW GOTO 3"; + 4 = "WINDOW GOTO 4"; + 5 = "WINDOW GOTO 5"; + 6 = "WINDOW GOTO 6"; + 7 = "WINDOW GOTO 7"; + 8 = "WINDOW GOTO 8"; + 9 = "WINDOW GOTO 9"; + 10 = "WINDOW GOTO 10"; + 11 = "WINDOW GOTO 11"; + 12 = "WINDOW GOTO 12"; + 13 = "WINDOW GOTO 13"; + 14 = "WINDOW GOTO 14"; + 15 = "WINDOW GOTO 15"; + 16 = "WINDOW GOTO 16"; + 17 = "WINDOW GOTO 17"; + 18 = "WINDOW GOTO 18"; + 19 = "WINDOW GOTO 19"; + 20 = "WINDOW GOTO 20"; + 21 = "WINDOW GOTO 21"; + 22 = "WINDOW GOTO 22"; + 23 = "WINDOW GOTO 23"; + 24 = "WINDOW GOTO 24"; + 25 = "WINDOW GOTO 25"; + 26 = "WINDOW GOTO 26"; + 27 = "WINDOW GOTO 27"; + 28 = "WINDOW GOTO 28"; + 29 = "WINDOW GOTO 29"; + 30 = "WINDOW GOTO 30"; + 31 = "WINDOW GOTO 31"; + 32 = "WINDOW GOTO 32"; + 33 = "WINDOW GOTO 33"; + 34 = "WINDOW GOTO 34"; + 35 = "WINDOW GOTO 35"; + 36 = "WINDOW GOTO 36"; + 37 = "WINDOW GOTO 37"; + 38 = "WINDOW GOTO 38"; + 39 = "WINDOW GOTO 39"; + 40 = "WINDOW GOTO 40"; + 41 = "WINDOW GOTO 41"; + 42 = "WINDOW GOTO 42"; + 43 = "WINDOW GOTO 43"; + 44 = "WINDOW GOTO 44"; + 45 = "WINDOW GOTO 45"; + 46 = "WINDOW GOTO 46"; + 47 = "WINDOW GOTO 47"; + 48 = "WINDOW GOTO 48"; + 49 = "WINDOW GOTO 49"; + 50 = "WINDOW GOTO 50"; + 51 = "WINDOW GOTO 51"; + 52 = "WINDOW GOTO 52"; + 53 = "WINDOW GOTO 53"; + 54 = "WINDOW GOTO 54"; + 55 = "WINDOW GOTO 55"; + 56 = "WINDOW GOTO 56"; + 57 = "WINDOW GOTO 57"; + 58 = "WINDOW GOTO 58"; + 59 = "WINDOW GOTO 59"; + 60 = "WINDOW GOTO 60"; + 61 = "WINDOW GOTO 61"; + 62 = "WINDOW GOTO 62"; + 63 = "WINDOW GOTO 63"; + 64 = "WINDOW GOTO 64"; + 65 = "WINDOW GOTO 65"; + 66 = "WINDOW GOTO 66"; + 67 = "WINDOW GOTO 67"; + 68 = "WINDOW GOTO 68"; + 69 = "WINDOW GOTO 69"; + 70 = "WINDOW GOTO 70"; + 71 = "WINDOW GOTO 71"; + 72 = "WINDOW GOTO 72"; + 73 = "WINDOW GOTO 73"; + 74 = "WINDOW GOTO 74"; + 75 = "WINDOW GOTO 75"; + 76 = "WINDOW GOTO 76"; + 77 = "WINDOW GOTO 77"; + 78 = "WINDOW GOTO 78"; + 79 = "WINDOW GOTO 79"; + 80 = "WINDOW GOTO 80"; + 81 = "WINDOW GOTO 81"; + 82 = "WINDOW GOTO 82"; + 83 = "WINDOW GOTO 83"; + 84 = "WINDOW GOTO 84"; + 85 = "WINDOW GOTO 85"; + 86 = "WINDOW GOTO 86"; + 87 = "WINDOW GOTO 87"; + 88 = "WINDOW GOTO 88"; + 89 = "WINDOW GOTO 89"; + 90 = "WINDOW GOTO 90"; + 91 = "WINDOW GOTO 91"; + 92 = "WINDOW GOTO 92"; + 93 = "WINDOW GOTO 93"; + 94 = "WINDOW GOTO 94"; + 95 = "WINDOW GOTO 95"; + 96 = "WINDOW GOTO 96"; + 97 = "WINDOW GOTO 97"; + 98 = "WINDOW GOTO 98"; + 99 = "WINDOW GOTO 99"; + wn = "/window goto"; +}; + +statusbar = { + + items = { + + barstart = "{sbstart}"; + barend = "{sbend}"; + + topicbarstart = "{topicsbstart}"; + topicbarend = "{topicsbend}"; + + time = "{sb $Z}"; + user = "{sb {sbnickmode $cumode}$N{sbmode $usermode}{sbaway $A}}"; + + window = "{sb $winref:$tag/$itemname{sbmode $M}}"; + window_empty = "{sb $winref{sbservertag $tag}}"; + + prompt = "{prompt $[.15]itemname}"; + prompt_empty = "{prompt $winname}"; + + topic = " $topic"; + topic_empty = " Irssi v$J - http://www.irssi.org"; + + lag = "{sb Lag: $0-}"; + act = "{sb Act: $0-}"; + more = "-- more --"; + }; + + default = { + + window = { + + disabled = "no"; + type = "window"; + placement = "bottom"; + position = "1"; + visible = "active"; + + items = { + barstart = { priority = "100"; }; + time = { }; + user = { }; + window = { }; + window_empty = { }; + lag = { priority = "-1"; }; + more = { priority = "-1"; alignment = "right"; }; + barend = { priority = "100"; alignment = "right"; }; + usercount = { }; + }; + }; + + window_inact = { + + type = "window"; + placement = "bottom"; + position = "1"; + visible = "inactive"; + + items = { + barstart = { priority = "100"; }; + window = { }; + window_empty = { }; + more = { priority = "-1"; alignment = "right"; }; + barend = { priority = "100"; alignment = "right"; }; + }; + }; + + prompt = { + + type = "root"; + placement = "bottom"; + position = "100"; + visible = "always"; + + items = { + prompt = { priority = "-1"; }; + prompt_empty = { priority = "-1"; }; + input = { priority = "10"; }; + }; + }; + + topic = { + + type = "root"; + placement = "top"; + position = "1"; + visible = "always"; + + items = { + topicbarstart = { priority = "100"; }; + twtopic = { priority = "100"; }; + topicbarend = { priority = "100"; alignment = "right"; }; + }; + }; + }; +}; +settings = { + core = { + real_name = "Ryan Kavanagh"; + user_name = "rak"; + nick = "ryanakca"; + timestamp_format = "%H:%M"; + quit_message = ""; + }; + "fe-text" = { actlist_sort = "refnum"; }; + "fe-common/core" = { + autolog = "yes"; + autolog_path = "~/irclogs/$tag/$0.log"; + theme = "envy"; + }; + "perl/core/scripts" = { + awl_shared_sbar = "OFF"; + chansort_autosort = "yes"; + twtopic_size = "173"; + twtopic_instruct = "no"; + twtopic_auto_resize = "yes"; + twtopic_refresh = "500"; + twtopic_min_scroll = "no"; + twtopic_ar_padding = "0"; + twtopic_init_pause = "200"; + recdep_channels = "Freenode/ OFTC/"; + recdep_period = "3600"; + }; + "irc/core" = { alternate_nick = "ryanakca_"; }; +}; +logs = { }; +hilights = ( + { text = "*ryanakca*"; nick = "no"; word = "yes"; }, + { text = "Ryan Kavanagh"; nick = "no"; word = "yes"; } +); +channels = ( + { name = "#rxvt-unicode"; chatnet = "Freenode"; autojoin = "yes"; }, + { name = "#opensmtpd"; chatnet = "Freenode"; autojoin = "yes"; }, + { name = "#kubuntu-devel"; chatnet = "Freenode"; autojoin = "yes"; }, + { name = "#debian-devel"; chatnet = "OFTC"; autojoin = "yes"; }, + { + name = "#debian-private"; + chatnet = "OFTC"; + autojoin = "yes"; + }, + { name = "#schmorpforge"; chatnet = "schmorp"; autojoin = "yes"; }, + { name = "#debian-multimedia"; chatnet = "OFTC"; autojoin = "yes"; }, + { + name = "#debian-devel-changes"; + chatnet = "OFTC"; + autojoin = "yes"; + }, + { + name = "##computerscience"; + chatnet = "Freenode"; + autojoin = "yes"; + }, + { name = "##math"; chatnet = "Freenode"; autojoin = "yes"; }, + { name = "#debian-quebec"; chatnet = "OFTC"; autojoin = "yes"; }, + { name = "##cclub"; chatnet = "Freenode"; autojoin = "yes"; }, + { name = "#openbsd"; chatnet = "Freenode"; autojoin = "yes"; }, + { name = "##russian"; chatnet = "Freenode"; autojoin = "yes"; }, + { name = "##dependent"; chatnet = "Freenode"; autojoin = "yes"; }, + { name = "##typetheory"; chatnet = "Freenode"; autojoin = "yes"; }, + { name = "##logic"; chatnet = "Freenode"; autojoin = "yes"; }, + { + name = "##categorytheory"; + chatnet = "Freenode"; + autojoin = "yes"; + }, + { name = "#sml"; chatnet = "Freenode"; autojoin = "yes"; } +); +windows = { + 1 = { immortal = "yes"; name = "(status)"; level = "ALL"; }; + 2 = { name = "hilight"; sticky = "yes"; }; + 3 = { + items = ( + { + type = "CHANNEL"; + chat_type = "IRC"; + name = "#kubuntu-devel"; + tag = "Freenode"; + } + ); + }; + 4 = { + items = ( + { + type = "CHANNEL"; + chat_type = "IRC"; + name = "#opensmtpd"; + tag = "Freenode"; + } + ); + }; + 5 = { + items = ( + { + type = "CHANNEL"; + chat_type = "IRC"; + name = "#rxvt-unicode"; + tag = "Freenode"; + } + ); + }; + 6 = { + items = ( + { + type = "CHANNEL"; + chat_type = "IRC"; + name = "#ubuntu"; + tag = "Freenode"; + } + ); + }; + 7 = { + items = ( + { + type = "CHANNEL"; + chat_type = "IRC"; + name = "#debian"; + tag = "OFTC"; + } + ); + }; + 8 = { + items = ( + { + type = "CHANNEL"; + chat_type = "IRC"; + name = "#debian-devel"; + tag = "OFTC"; + } + ); + }; + 9 = { + items = ( + { + type = "CHANNEL"; + chat_type = "IRC"; + name = "#debian-mentors"; + tag = "OFTC"; + } + ); + }; + 10 = { + items = ( + { + type = "CHANNEL"; + chat_type = "IRC"; + name = "#debian-multimedia"; + tag = "OFTC"; + } + ); + }; + 11 = { + items = ( + { + type = "CHANNEL"; + chat_type = "IRC"; + name = "#debian-private"; + tag = "OFTC"; + } + ); + }; + 12 = { + items = ( + { + type = "CHANNEL"; + chat_type = "IRC"; + name = "#schmorpforge"; + tag = "schmorp"; + } + ); + }; +}; +mainwindows = { + 1 = { first_line = "8"; lines = "45"; }; + 2 = { first_line = "1"; lines = "7"; }; +}; +keyboard = ( + { key = "meta-1"; id = "change_window"; data = "1"; }, + { key = "meta-2"; id = "change_window"; data = "2"; }, + { key = "meta-3"; id = "change_window"; data = "3"; }, + { key = "meta-4"; id = "change_window"; data = "4"; }, + { key = "meta-5"; id = "change_window"; data = "5"; }, + { key = "meta-6"; id = "change_window"; data = "6"; }, + { key = "meta-7"; id = "change_window"; data = "7"; }, + { key = "meta-8"; id = "change_window"; data = "8"; }, + { key = "meta-9"; id = "change_window"; data = "9"; }, + { key = "meta-0"; id = "change_window"; data = "10"; }, + { key = "meta-&"; id = "change_window"; data = "1"; }, + { key = "meta-["; id = "change_window"; data = "2"; }, + { key = "meta-{"; id = "change_window"; data = "3"; }, + { key = "meta-}"; id = "change_window"; data = "4"; }, + { key = "meta-("; id = "change_window"; data = "5"; }, + { key = "meta-="; id = "change_window"; data = "6"; }, + { key = "meta-*"; id = "change_window"; data = "7"; }, + { key = "meta-)"; id = "change_window"; data = "8"; }, + { key = "meta-+"; id = "change_window"; data = "9"; }, + { key = "meta-]"; id = "change_window"; data = "10"; }, + { key = "meta-;"; id = "change_window"; data = "11"; }, + { key = "meta-,"; id = "change_window"; data = "12"; }, + { key = "meta-."; id = "change_window"; data = "13"; }, + { key = "meta-y"; id = "change_window"; data = "14"; }, + { key = "meta-f"; id = "change_window"; data = "15"; }, + { key = "meta-g"; id = "change_window"; data = "16"; }, + { key = "meta-c"; id = "change_window"; data = "17"; }, + { key = "meta-r"; id = "change_window"; data = "18"; }, + { key = "meta-l"; id = "change_window"; data = "19"; } +); diff --git a/.irssi/default.theme b/.irssi/default.theme new file mode 100644 index 0000000..6b6aeab --- /dev/null +++ b/.irssi/default.theme @@ -0,0 +1,294 @@ +# When testing changes, the easiest way to reload the theme is with /RELOAD. +# This reloads the configuration file too, so if you did any changes remember +# to /SAVE it first. Remember also that /SAVE overwrites the theme file with +# old data so keep backups :) + +# TEMPLATES: + +# The real text formats that irssi uses are the ones you can find with +# /FORMAT command. Back in the old days all the colors and texts were mixed +# up in those formats, and it was really hard to change the colors since you +# might have had to change them in tens of different places. So, then came +# this templating system. + +# Now the /FORMATs don't have any colors in them, and they also have very +# little other styling. Most of the stuff you need to change is in this +# theme file. If you can't change something here, you can always go back +# to change the /FORMATs directly, they're also saved in these .theme files. + +# So .. the templates. They're those {blahblah} parts you see all over the +# /FORMATs and here. Their usage is simply {name parameter1 parameter2}. +# When irssi sees this kind of text, it goes to find "name" from abstracts +# block below and sets "parameter1" into $0 and "parameter2" into $1 (you +# can have more parameters of course). Templates can have subtemplates. +# Here's a small example: +# /FORMAT format hello {colorify {underline world}} +# abstracts = { colorify = "%G$0-%n"; underline = "%U$0-%U"; } +# When irssi expands the templates in "format", the final string would be: +# hello %G%Uworld%U%n +# ie. underlined bright green "world" text. +# and why "$0-", why not "$0"? $0 would only mean the first parameter, +# $0- means all the parameters. With {underline hello world} you'd really +# want to underline both of the words, not just the hello (and world would +# actually be removed entirely). + +# COLORS: + +# You can find definitions for the color format codes in docs/formats.txt. + +# There's one difference here though. %n format. Normally it means the +# default color of the terminal (white mostly), but here it means the +# "reset color back to the one it was in higher template". For example +# if there was /FORMAT test %g{foo}bar, and foo = "%Y$0%n", irssi would +# print yellow "foo" (as set with %Y) but "bar" would be green, which was +# set at the beginning before the {foo} template. If there wasn't the %g +# at start, the normal behaviour of %n would occur. If you _really_ want +# to use the terminal's default color, use %N. + +############################################################################# + +# default foreground color (%N) - -1 is the "default terminal color" +default_color = "-1"; + +# print timestamp/servertag at the end of line, not at beginning +info_eol = "false"; + +# these characters are automatically replaced with specified color +# (dark grey by default) +replaces = { "[]=" = "%K$*%n"; }; + +abstracts = { + ## + ## generic + ## + + # text to insert at the beginning of each non-message line + line_start = "%B-%n!%B-%n "; + + # timestamp styling, nothing by default + timestamp = "$*"; + + # any kind of text that needs hilighting, default is to bold + hilight = "%_$*%_"; + + # any kind of error message, default is bright red + error = "%R$*%n"; + + # channel name is printed + channel = "%_$*%_"; + + # nick is printed + nick = "%_$*%_"; + + # nick host is printed + nickhost = "[$*]"; + + # server name is printed + server = "%_$*%_"; + + # some kind of comment is printed + comment = "[$*]"; + + # reason for something is printed (part, quit, kick, ..) + reason = "{comment $*}"; + + # mode change is printed ([+o nick]) + mode = "{comment $*}"; + + ## + ## channel specific messages + ## + + # highlighted nick/host is printed (joins) + channick_hilight = "%C$*%n"; + chanhost_hilight = "{nickhost %c$*%n}"; + + # nick/host is printed (parts, quits, etc.) + channick = "%c$*%n"; + chanhost = "{nickhost $*}"; + + # highlighted channel name is printed + channelhilight = "%c$*%n"; + + # ban/ban exception/invite list mask is printed + ban = "%c$*%n"; + + ## + ## messages + ## + + # the basic styling of how to print message, $0 = nick mode, $1 = nick + msgnick = "%K<%n$0$1-%K>%n %|"; + + # message from you is printed. "ownnick" specifies the styling of the + # nick ($0 part in msgnick) and "ownmsgnick" specifies the styling of the + # whole line. + + # Example1: You want the message text to be green: + # ownmsgnick = "{msgnick $0 $1-}%g"; + # Example2.1: You want < and > chars to be yellow: + # ownmsgnick = "%Y{msgnick $0 $1-%Y}%n"; + # (you'll also have to remove <> from replaces list above) + # Example2.2: But you still want to keep <> grey for other messages: + # pubmsgnick = "%K{msgnick $0 $1-%K}%n"; + # pubmsgmenick = "%K{msgnick $0 $1-%K}%n"; + # pubmsghinick = "%K{msgnick $1 $0$2-%n%K}%n"; + # ownprivmsgnick = "%K{msgnick $*%K}%n"; + # privmsgnick = "%K{msgnick %R$*%K}%n"; + + # $0 = nick mode, $1 = nick + ownmsgnick = "{msgnick $0 $1-}"; + ownnick = "%_$*%n"; + + # public message in channel, $0 = nick mode, $1 = nick + pubmsgnick = "{msgnick $0 $1-}"; + pubnick = "%N$*%n"; + + # public message in channel meant for me, $0 = nick mode, $1 = nick + pubmsgmenick = "{msgnick $0 $1-}"; + menick = "%Y$*%n"; + + # public highlighted message in channel + # $0 = highlight color, $1 = nick mode, $2 = nick + pubmsghinick = "{msgnick $1 $0$2-%n}"; + + # channel name is printed with message + msgchannel = "%K:%c$*%n"; + + # private message, $0 = nick, $1 = host + privmsg = "[%R$0%K(%r$1-%K)%n] "; + + # private message from you, $0 = "msg", $1 = target nick + ownprivmsg = "[%r$0%K(%R$1-%K)%n] "; + + # own private message in query + ownprivmsgnick = "{msgnick $*}"; + ownprivnick = "%_$*%n"; + + # private message in query + privmsgnick = "{msgnick %R$*%n}"; + + ## + ## Actions (/ME stuff) + ## + + # used internally by this theme + action_core = "%_ * $*%n"; + + # generic one that's used by most actions + action = "{action_core $*} "; + + # own action, both private/public + ownaction = "{action $*}"; + + # own action with target, both private/public + ownaction_target = "{action_core $0}%K:%c$1%n "; + + # private action sent by others + pvtaction = "%_ (*) $*%n "; + pvtaction_query = "{action $*}"; + + # public action sent by others + pubaction = "{action $*}"; + + + ## + ## other IRC events + ## + + # whois + whois = "%# $[8]0 : $1-"; + + # notices + ownnotice = "[%r$0%K(%R$1-%K)]%n "; + notice = "%K-%M$*%K-%n "; + pubnotice_channel = "%K:%m$*"; + pvtnotice_host = "%K(%m$*%K)"; + servernotice = "%g!$*%n "; + + # CTCPs + ownctcp = "[%r$0%K(%R$1-%K)] "; + ctcp = "%g$*%n"; + + # wallops + wallop = "%_$*%n: "; + wallop_nick = "%n$*"; + wallop_action = "%_ * $*%n "; + + # netsplits + netsplit = "%R$*%n"; + netjoin = "%C$*%n"; + + # /names list + names_prefix = ""; + names_nick = "[%_$0%_$1-] "; + names_nick_op = "{names_nick $*}"; + names_nick_halfop = "{names_nick $*}"; + names_nick_voice = "{names_nick $*}"; + names_users = "[%g$*%n]"; + names_channel = "%G$*%n"; + + # DCC + dcc = "%g$*%n"; + dccfile = "%_$*%_"; + + # DCC chat, own msg/action + dccownmsg = "[%r$0%K($1-%K)%n] "; + dccownnick = "%R$*%n"; + dccownquerynick = "%_$*%n"; + dccownaction = "{action $*}"; + dccownaction_target = "{action_core $0}%K:%c$1%n "; + + # DCC chat, others + dccmsg = "[%G$1-%K(%g$0%K)%n] "; + dccquerynick = "%G$*%n"; + dccaction = "%_ (*dcc*) $*%n %|"; + + ## + ## statusbar + ## + + # default background for all statusbars. You can also give + # the default foreground color for statusbar items. + sb_background = "%4%w"; + + # default backround for "default" statusbar group + #sb_default_bg = "%4"; + # background for prompt / input line + sb_prompt_bg = "%n"; + # background for info statusbar + sb_info_bg = "%8"; + # background for topicbar (same default) + #sb_topic_bg = "%4"; + + # text at the beginning of statusbars. sb-item already puts + # space there,so we don't use anything by default. + sbstart = ""; + # text at the end of statusbars. Use space so that it's never + # used for anything. + sbend = " "; + + topicsbstart = "{sbstart $*}"; + topicsbend = "{sbend $*}"; + + prompt = "[$*] "; + + sb = " %c[%n$*%c]%n"; + sbmode = "(%c+%n$*)"; + sbaway = " (%GzZzZ%n)"; + sbservertag = ":$0 (change with ^X)"; + sbnickmode = "$0"; + + # activity in statusbar + + # ',' separator + sb_act_sep = "%c$*"; + # normal text + sb_act_text = "%c$*"; + # public message + sb_act_msg = "%W$*"; + # hilight + sb_act_hilight = "%M$*"; + # hilight with specified color, $0 = color, $1 = text + sb_act_hilight_color = "$0$1-%n"; +}; diff --git a/.irssi/envy.theme b/.irssi/envy.theme new file mode 100644 index 0000000..6c00bc9 --- /dev/null +++ b/.irssi/envy.theme @@ -0,0 +1,501 @@ +# ___ ___ ___ ___ +# /\ \ /\__\ /\__\ |\__\ +# /::\ \ /::| | /:/ / |:| | +# /:/\:\ \ /:|:| | /:/ / |:| | +# /::\~\:\ \ /:/|:| |__ /:/__/ ___ |:|__|__ +# /:/\:\ \:\__\ /:/ |:| /\__\ |:| | /\__\ /::::\__\ +# \:\~\:\ \/__/ \/__|:|/:/ / |:| |/:/ / /:/~~/~ +# \:\ \:\__\ |:/:/ / |:|__/:/ / /:/ / +# \:\ \/__/ |::/ / \::::/__/ \/__/ +# \:\__\ /:/ / ~~~~ +# \/__/ \/__/ v. 3.6 +# +# theme by rolle (rolle @ QuakeNet, rolle_ @ Ircnet) +# http://rollemaa.org/ +# +# you can find the most recent version here: +# http://rolle.tux.fi + +default_color = "-1"; +# Timestamp/servertag loppuun, ei alkuun +info_eol = "false"; +replaces = { "[]=" = "$*"; }; + +abstracts = { + line_start = "%K"; + timestamp = "%K$*%n"; + hilight = "$*"; + error = "$*"; + channel = "$*"; + nick = "$*"; + nickhost = "$*"; + server = "$*"; + comment = "$*"; + reason = "{comment $*}"; + mode = "{comment $*}"; + channick_hilight = "$*"; + chanhost_hilight = "{nickhost $*}"; + channick = "$*"; + chanhost = "{nickhost $*}"; + channelhilight = "$*"; + ban = "$*"; + # Kaikissa alemmissa: $0 = mode, $1 = nick + msgnick = "$0$1- %|"; + ownmsgnick = "{msgnick %K<$0 %G$1-%K>}%n"; + ownnick = "$*"; + pubmsgnick = "{msgnick %K<$0 %W$1-%K>}%n"; + pubnick = "$*"; + pubmsgmenick = "{msgnick %K<$0 %P$1-%K>}%P"; + menick = "%P$*%n"; + # $0 = hilightin väri, $1 = mode, $2 = nick + pubmsghinick = "{msgnick %K<$1 %P$2-%K>%P}"; + msgchannel = ":$*"; + # $0 = nick, $1 = hosti + privmsg = "[$0($1-)] "; + # $0 = "msg", $1 = target nick + ownprivmsg = "[$0($1-)] "; + ownprivmsgnick = "{msgnick $*}"; + ownprivnick = "%K<%G$*%K>%n"; + privmsgnick = "%K<%B$*%K>%n "; + action_core = " %r>%y>%g> %c$0-"; + action = "{action_core $*} "; + ownaction = "{action $*}"; + ownaction_target = "{action_core $*}:$1 "; + pvtaction = " (>>>) $* "; + pvtaction_query = "{action $*}"; + pubaction = "{action $*}"; + ownnotice = "[$0($1-)] "; + notice = "%g$*%K -> %G"; + whois = "%# $[8]0 : $1-"; + pubnotice_channel = ":$*"; + pvtnotice_host = "($*)"; + servernotice = "%r!$* %n"; + ownctcp = "[$0($1-)] "; + ctcp = "$*"; + wallop = "$*: "; + wallop_nick = "$*"; + wallop_action = " * $* "; + netsplit = "$*"; + netjoin = "%K::%g:%K $*"; + names_prefix = "%K"; + names_nick = "%K[$0$1-] "; + names_nick_op = "%K{names_nick $*}"; + names_nick_halfop = "%K{names_nick $*}"; + names_nick_voice = "%K{names_nick $*}"; + names_users = "%K[$*]"; + names_channel = "%K$*"; + dcc = "$*"; + dccfile = "$*"; + dccownmsg = "[$0($1-)] "; + dccownnick = "$*"; + dccownquerynick = "$*"; + dccownaction = "{action $*}"; + dccownaction_target = "{action_core $0}:$1 "; + dccmsg = "[$1-($0)] "; + dccquerynick = "$*"; + dccaction = " (*dcc*) $* %|"; + sb_background = ""; + sb_window_bg = "%n%2"; + sb_default_bg = ""; + sb_prompt_bg = "%0"; + sb_info_bg = ""; + sb_topic_bg = "%G"; + sbstart = ""; + sbend = " "; + topicsbstart = "{sbstart $*}"; + topicsbend = "{sbend $*}"; + prompt = "%R!%G$*%K: "; + sb = " %w/%W$*%w/%n"; + sbmode = ""; + sbaway = "%r>%n"; + sbservertag = "%W$0%n"; + sbnickmode = ""; + sb_act_sep = "%w$*%n"; + sb_act_text = "%G$*%n"; + sb_act_msg = "%G$*%n"; + sb_act_hilight = "%r($*)%n"; + sb_act_hilight_color = "%r$0$1-%n"; +}; +formats = { + "fe-common/core" = { + query_start = "%K:%K:%g:%K %gStarting query%K in {server $1} with {nick $0}"; + join = "%K::%g:%K {channick_hilight $0} [{chanhost_hilight $1}] has %gjoined%K {channel $2}"; + part = "%r:%K:: {channick $0} [{chanhost $1}] has %rleft%K {channel $2}"; + quit = "%r:%K:: {channick $0} [{chanhost $1}] has %rquit%k ({reason $2})"; + quit_once = "%r:%K:: {channick $0} [{chanhost $1}] has %rquit%K ({reason $2})"; + nick_changed = "%K:%y:%K: {channick_hilight $0} is %ynow known as%K {channick_hilight $1}"; + # own_msg = "{ownmsgnick $2 {ownnick $[-10]0}}$1"; + # own_msg_channel = "{ownmsgnick $3 {ownnick $[-10]0}{msgchannel $1}}$2"; + # pubmsg_me = "{pubmsgmenick $2 {menick $[-10]0}}$1"; + # pubmsg_me_channel = "{pubmsgmenick $3 {menick $[-10]0}{msgchannel $1}}$2"; + # pubmsg_hilight = "{pubmsghinick $0 $3 $[-10]1}$2"; + # pubmsg_hilight_channel = "{pubmsghinick $0 $4 $[-10]1{msgchannel $2}}$3"; + # pubmsg = "{pubmsgnick $2 {pubnick \0030$0}}$1"; + # pubmsg_channel = "{pubmsgnick $3 {pubnick $[-10]0}{msgchannel $1}}$2"; + # line_start = "{line_start}"; + # line_start_irssi = "{line_start}{hilight Irssi} %W|%n "; + line_start_irssi = "{line_start}"; + timestamp = "%K{timestamp $Z} "; + # servertag = "$[-11]0 %W|%n "; + servertag = ""; + daychange = "Day changed to %%d %%b %%Y %n"; + talking_with = "%r:%y:%g:%K You are now talking with {nick $0}"; + refnum_too_low = "%r:%K:: Window number must be greater than 1"; + error_server_sticky = "%r:%K:: Window's server is %rsticky%K and it cannot be changed without -unsticky option"; + set_server_sticky = "%K::%g:%K Window's server %gset sticky%K"; + unset_server_sticky = "%K::%g:%K Window's server isn't sticky anymore"; + window_name_not_unique = "%r:%K:: Window names must be unique"; + window_level = "%K::%g:%K Window level is now $0"; + windowlist_header = "Ref Name Active item Server Level"; + windowlist_line = "$[3]0 %|$[20]1 $[15]2 $[15]3 $4"; + windowlist_footer = ""; + windows_layout_saved = "%K::%g:%K Layout of windows is now %gremembered%K next time you start irssi"; + windows_layout_reset = "%K::%g:%K Layout of windows %greset%K to defaults"; + window_info_header = ""; + window_info_footer = ""; + window_info_refnum = "Window : {hilight #$0}"; + window_info_refnum_sticky = "Window : {hilight #$0 (sticky)}"; + window_info_name = "Name : $0"; + window_info_history = "History : $0"; + window_info_size = "Size : $0x$1"; + window_info_level = "Level : $0"; + window_info_server = "Server : $0"; + window_info_server_sticky = "Server : $0 (sticky)"; + window_info_theme = "Theme : $0$1"; + window_info_bound_items_header = "Bounds : {hilight Name Server tag}"; + window_info_bound_item = " : $[!30]0 $[!15]1 $2"; + window_info_bound_items_footer = ""; + window_info_items_header = "Items : {hilight Name Server tag}"; + window_info_item = " $[7]0: $[!30]1 $2"; + window_info_items_footer = ""; + looking_up = "%K::%g:%K %gLooking up%K {server $0}"; + connecting = "%K::%g:%K %gConnecting%K to {server $0} [$1] port {hilight $2}"; + connection_established = "%K::%g:%K Connection to {server $0} %gestablished%K"; + cant_connect = "%r:%K:: %rUnable to connect%K server {server $0} port {hilight $1} {reason $2}"; + connection_lost = "%r:%K:: %rConnection lost%K to {server $0}"; + lag_disconnected = "%r:%K:: %rNo PONG reply%K from server {server $0} in $1 seconds, disconnecting"; + disconnected = "%r:%K:: %rDisconnected%K from {server $0} {reason $1}"; + server_quit = "%r:%K:: %rDisconnecting%K from server {server $0}: {reason $1}"; + server_changed = "%K:%y:%K: %yChanged%K to {hilight $2} server {server $1}"; + unknown_server_tag = "%r:%K:: %rUnknown%K server tag {server $0}"; + no_connected_servers = "%r:%K:: %rNot connected%K to any servers"; + server_list = "{server $0}: $1:$2 ($3)"; + server_lookup_list = "{server $0}: $1:$2 ($3) (connecting...)"; + server_reconnect_list = "{server $0}: $1:$2 ($3) ($5 left before reconnecting)"; + server_reconnect_removed = "%K::%g:%K %gRemoved reconnection%K to server {server $0} port {hilight $1}"; + server_reconnect_not_found = "%r:%K:: Reconnection tag {server $0} %rnot found%K"; + setupserver_added = "%K::%g:%K Server {server $0} %gsaved%K"; + setupserver_removed = "%K::%g:%K Server {server $0} %gremoved%K"; + setupserver_not_found = "%r:%K:: Server {server $0} %rnot found%K"; + your_nick = "%r:%y:%g:%K Your nickname is {nick $0}"; + kick = "%r:%K:: {channick $0} was %rkicked%K from {channel $1} by {nick $2} ({reason $3})"; + invite = "%K:%y:%K: {channick_hilight $0} %yinvites%K you to {channel $1}"; + not_invited = "You have not been invited to a channel!"; + new_topic = "%K:%y:%K: {channick_hilight $0} %ychanged the topic%K of {channel $1} to: {hilight $2}"; + topic_unset = "%K:%y:%K: {channick_hilight $0} %yunset the topic%K on {channel $1}"; + your_nick_changed = "%K:%y:%K: You're %ynow known as%K {channick_hilight $1}"; + talking_in = "%r:%y:%g:%K You are now talking in {channel $0}"; + not_in_channels = "%r:%y:%g:%K You are not on any channels"; + current_channel = "%r:%y:%g:%K Current channel {channel $0}"; + names = "{names_users Users {names_channel $0}} {comment $1 total}"; + names_prefix = "{names_prefix $0}"; + names_nick_op = "{names_nick_op $0 $1}"; + names_nick_halfop = "{names_nick_halfop $0 $1}"; + names_nick_voice = "{names_nick_voice $0 $1}"; + names_nick = "{names_nick $0 $1}"; + endofnames = "%r:%y:%g:%K {channel $0}: Total of {hilight $1} nicks {comment {hilight $2} ops, {hilight $3} halfops, {hilight $4} voices, {hilight $5} normal}"; + chanlist_header = "%r:%y:%g:%K You are on the following channels:"; + chanlist_line = "{channel $[-9]0} %|+$1 ($2): $3"; + chansetup_not_found = "Channel {channel $0} not found"; + chansetup_added = "Channel {channel $0} saved"; + chansetup_removed = "Channel {channel $0} removed"; + chansetup_header = "Channel Network Password Settings"; + chansetup_line = "{channel $[15]0} %|$[10]1 $[10]2 $3"; + chansetup_footer = ""; + channel_move_notify = "{channel $0} is already joined in window $1, use \"/WINDOW ITEM MOVE $0\" to move it to this window"; + # own_msg_private = "{ownprivmsg msg $0}$1"; + # own_msg_private_query = "{ownprivmsgnick {ownprivnick $[-9]2}}$1"; + # msg_private = "{privmsg $0 $1}$2"; + # msg_private_query = "{privmsgnick $[-9]0}$2"; + no_msgs_got = "%r:%y:%g:%K You have not received a message from anyone yet"; + no_msgs_sent = "%r:%y:%g:%K You have not sent a message to anyone yet"; + query_stop = "%r:%y:%g:%K Closing query with {nick $0}"; + no_query = "%r:%y:%g:%K No query with {nick $0}"; + query_server_changed = "%K:%y:%K: Query with {nick $0} %ychanged%K to server {server $1}"; + query_move_notify = "%r:%y:%g:%K Query with {nick $0} is already created to window $1, use \"/WINDOW ITEM MOVE $0\" to move it to this window"; + hilight_header = "%r:%y:%g:%K Highlights:"; + hilight_line = "$[-4]0 $1 $2 $3$4$5"; + hilight_footer = ""; + hilight_not_found = "%r:%K:: Highlight %rnot found%K: $0"; + hilight_removed = "%K::%g:%K Highlight %gremoved%K: $0"; + alias_added = "%K::%g:%K Alias $0 %gadded%K"; + alias_removed = "%K::%g:%K Alias $0 %gremoved%K"; + alias_not_found = "%r:%K:: %rNo such%K alias: $0"; + aliaslist_header = "%r:%y:%g:%K Aliases:"; + aliaslist_line = "$[10]0 $1"; + aliaslist_footer = ""; + log_opened = "%K::%g:%K Log file {hilight $0} %gopened%K"; + log_closed = "%K::%g:%K Log file {hilight $0} %gclosed%K"; + log_create_failed = "%r:%K:: %rCouldn't create%K log file {hilight $0}: $1"; + log_locked = "%r:%K:: Log file {hilight $0} is %rlocked%K, probably by another running Irssi"; + log_not_open = "%r:%K:: Log file {hilight $0} %rnot open%K"; + log_started = "%K::%g:%K %gStarted%K logging to file {hilight $0}"; + log_stopped = "%r:%K:: %rStopped%K logging to file {hilight $0}"; + log_list_header = "%r:%y:%g:%K Logs:"; + log_list = "$0 $1: $2 $3$4"; + log_list_footer = ""; + windowlog_file = "%K::%g:%K Window LOGFILE set to $0"; + windowlog_file_logging = "%r:%K:: %rCan't change%K window's logfile while log is on"; + no_away_msgs = "%r:%K:: %rNo new%K messages in awaylog"; + away_msgs = "%K::%g:%K {hilight $1} %gnew messages%K in awaylog:"; + module_header = "Module Type Submodules"; + module_line = "$[!20]0 $[7]1 $2"; + module_footer = ""; + module_already_loaded = "%r:%K:: Module {hilight $0/$1} already loaded"; + module_not_loaded = "%r:%K:: Module {hilight $0/$1} %ris not loaded%K"; + module_load_error = "%r:%K:: %rError%K loading module {hilight $0/$1}: $2"; + module_invalid = "%r:%K:: {hilight $0/$1} isn't Irssi module"; + module_loaded = "%K::%g:%K %gLoaded%K module {hilight $0/$1}"; + module_unloaded = "%r:%K:: %rUnloaded%K module {hilight $0/$1}"; + command_unknown = "%r:%K:: %rUnknown%K command: $0"; + command_ambiguous = "%r:%K:: %rAmbiguous%K command: $0"; + option_unknown = "%r:%K:: %rUnknown%K option: $0"; + option_ambiguous = "%r:%K:: %rAmbiguous%K option: $0"; + option_missing_arg = "%r:%K:: %rMissing%K required argument for: $0"; + not_enough_params = "%r:%K:: %rNot enough%K parameters given"; + not_connected = "%r:%K:: %rNot connected%K to server"; + not_joined = "%r:%K:: %rNot joined%K to any channel"; + chan_not_found = "%r:%K:: %rNot joined%K to such channel"; + chan_not_synced = "%r:%K:: Channel %rnot fully synchronized%K yet, try again after a while"; + illegal_proto = "%r:%K:: Command isn't designed for the chat protocol of the active server"; + not_good_idea = "%r:%K:: Doing this is %rnot a good idea%K. Add -YES if you really mean it"; + theme_saved = "%K::%g:%K Theme %gsaved%K to $0"; + theme_save_failed = "%r:%K:: %rError%K saving theme to $0: $1"; + theme_not_found = "%r:%K:: Theme {hilight $0} %rnot found%K"; + theme_changed = "%K:%y:%K: %yUsing%K now theme {hilight $0} ($1)"; + window_theme = "%K::%g:%K %gUsing%K theme {hilight $0} in this window"; + window_theme_default = "%r:%K:: %rNo theme is set%K for this window"; + window_theme_changed = "%K:%y:%K: %yUsing%K now theme {hilight $0} ($1) in this window"; + window_theme_removed = "%K::%g:%K %gRemoved%K theme from this window"; + format_title = "%:[{hilight $0}] - [{hilight $1}]%:"; + format_subtitle = "[{hilight $0}]"; + format_item = "$0 = $1"; + ignored = "%K::%g:%K %gIgnoring%K {hilight $1} from {nick $0}"; + unignored = "%K::%g:%K %gUnignored%K {nick $0}"; + ignore_not_found = "%K::%g:%K {nick $0} %gis not%K being ignored"; + ignore_no_ignores = "%r:%y:%g:%K There are no ignores"; + ignore_header = "%r:%y:%g:%K Ignorance List:"; + ignore_line = "$[-4]0 $1: $2 $3 $4"; + ignore_footer = ""; + unknown_chat_protocol = "%r:%K:: %rUnknown%K chat protocol: $0"; + unknown_chatnet = "%r:%K:: %rUnknown%K chat network: $0 (create it with /IRCNET ADD)"; + not_toggle = "%r:%K:: Value must be either ON, OFF or TOGGLE"; + perl_error = "%r:%K:: Perl %rerror%K: $0"; + bind_key = "$[!20]0 $1 $2"; + bind_unknown_id = "%r:%K:: %rUnknown%K bind action: $0"; + config_saved = "%K::%g:%K %gSaved%K configuration to file $0"; + config_reloaded = "%K::%g:%K %gReloaded%K configuration"; + config_modified = "%r:%y:%g:%K Configuration file was modified since irssi was last started - do you want to overwrite the possible changes?"; + glib_error = "{error GLib $0} $1"; + overwrite_config = "%r:%y:%g:%K Overwrite config (%gy%K/%rN%K)?"; + set_title = "[{hilight $0}]"; + set_item = "$0 = $1"; + set_unknown = "%r:%K:: %rUnknown%K setting $0"; + set_not_boolean = "%r:%K:: Setting {hilight $0} isn't boolean, use /SET"; + translation_not_found = "%r:%K:: %rError%K opening translation table file $0: $1"; + translation_file_error = "%r:%K:: %rError%K parsing translation table file $0"; + pubmsg = "{pubmsgnick $2 {pubnick \00302$0}}$1"; + }; + "fe-common/irc/dcc" = { + dcc_list_header = "{line_start_irssi}{dcc DCC connections:}"; + dcc_list_footer = "{line_start_irssi}{dcc ];}"; + # own_dcc = "{dccownmsg dcc {dccownnick $1}}$2"; + # own_dcc_action = "{dccownaction_target $0 $1}$2"; + # own_dcc_action_query = "{dccownaction $0}$2"; + # own_dcc_ctcp = "{ownctcp ctcp $0}$1 $2"; + # dcc_msg = "{dccmsg dcc $0}$1"; + # action_dcc = "{dccaction $0}$1"; + # action_dcc_query = "{dccaction $0}$1"; + # own_dcc_query = "{ownmsgnick {dccownquerynick $0}}$2"; + # dcc_msg_query = "{privmsgnick $0}$1"; + dcc_ctcp = "%K::%g:%K {dcc >>> DCC CTCP {hilight $1} %greceived%K from {hilight $0}: $2}"; + dcc_chat = "%K::%g:%K {dcc DCC CHAT from {nick $0} [$1 port $2]}"; + dcc_chat_channel = "%K::%g:%K {dcc DCC CHAT from {nick $0} [$1 port $2] %grequested%K in channel {channel $3}}"; + dcc_chat_not_found = "%K::%g:%K {dcc No DCC CHAT %gconnection open%K to {nick $0}}"; + dcc_chat_connected = "%K::%g:%K {dcc DCC CHAT connection with {nick $0} [$1 port $2] %gestablished%K}"; + dcc_chat_disconnected = "%r:%K:: {dcc DCC %rlost chat%K to {nick $0}}"; + dcc_send = "%K::%g:%K {dcc DCC SEND from {nick $0} [$1 port $2]: $3 [$4 bytes]}"; + dcc_send_channel = "%K::%g:%K {dcc DCC SEND from {nick $0} [$1 port $2]: $3 [$4 bytes] %grequested%K in channel {channel $5}}"; + dcc_send_exists = "%r:%K:: {dcc DCC %ralready sending%K file {dccfile $0} for {nick $1}}"; + dcc_send_not_found = "%r:%K:: {dcc DCC %rnot sending%K file {dccfile $1} to {nick $0}}"; + dcc_send_file_open_error = "%r:%K:: {dcc DCC %rcan't open%K file {dccfile $0}: $1}"; + dcc_send_connected = "%K::%g:%K {dcc DCC %gsending%K file {dccfile $0} for {nick $1} [$2 port $3]}"; + dcc_send_complete = "%K::%g:%K {dcc DCC %gsent%K file {dccfile $0} [{hilight $1}kB] for {nick $2} in {hilight $3} secs [{hilight $4kB/s}]}"; + dcc_send_aborted = "%r:%K:: {dcc DCC %raborted%K sending file {dccfile $0} for {nick $1}}"; + dcc_get_not_found = "%r:%K:: {dcc DCC no file offered by {nick $0}}"; + dcc_get_connected = "%K::%g:%K {dcc DCC %greceiving%K file {dccfile $0} from {nick $1} [$2 port $3]}"; + dcc_get_complete = "%K::%g:%K {dcc DCC %greceived%K file {dccfile $0} [$1kB] from {nick $2} in {hilight $3} secs [$4kB/s]}"; + dcc_get_aborted = "%r:%K:: {dcc DCC %raborted%K receiving file {dccfile $0} from {nick $1}}"; + dcc_unknown_ctcp = "%r:%K:: {dcc DCC unknown ctcp {hilight $0} from {nick $1} [$2]}"; + dcc_unknown_reply = "%r:%K:: {dcc DCC unknown reply {hilight $0} from {nick $1} [$2]}"; + dcc_unknown_type = "%r:%K:: {dcc DCC unknown type {hilight $0}}"; + dcc_invalid_ctcp = "%r:%K:: {dcc DCC received CTCP {hilight $0} with %rinvalid%K parameters from {nick $1}}"; + dcc_connect_error = "%r:%K:: {dcc DCC %rcan't connect%K to {hilight $0} port {hilight $1}}"; + dcc_cant_create = "%r:%K:: {dcc DCC %rcan't create%K file {dccfile $0}}"; + dcc_rejected = "%r:%K:: {dcc DCC $0 was %rrejected%K by {nick $1} [{hilight $2}]}"; + dcc_request_send = "%K::%g:%K {dcc DCC $0 %grequest sent%K to {nick $1}: $2"; + dcc_close = "{dcc DCC $0 close for {nick $1} [{hilight $2}]}"; + dcc_lowport = "{dcc Warning: Port sent with DCC request is a lowport ({hilight $0, $1}) - this isn't normal. It is possible the address/port is faked (or maybe someone is just trying to bypass firewall)}"; + dcc_list_line_chat = "%WChat ->%n {dcc $0 $1}"; + dcc_list_line_file = "%WFile ->%n {dcc $0 $1 : $2k of $3k ($4%%) - $5kB/s - $6}"; + }; + "fe-text" = { + lastlog_too_long = "%r:%y:%g:%K /LASTLOG would print $0 lines. If you really want to print all these lines use -force option."; + lastlog_count = "{hilight Lastlog}: $0 lines"; + lastlog_start = "{hilight Lastlog}:"; + lastlog_end = "{hilight End of Lastlog}"; + refnum_not_found = "%r:%K::%K Window number $0 %rnot found%K"; + window_too_small = "%r:%K::%K %rNot enough room%K to resize this window"; + cant_hide_last = "%r:%K::%K You %rcan't hide%K the last window"; + cant_hide_sticky_windows = "%r:%K::%K You %rcan't hide%K sticky windows (use /WINDOW STICK OFF)"; + cant_show_sticky_windows = "%r:%K::%K You %rcan't show%K sticky windows (use /WINDOW STICK OFF)"; + window_not_sticky = "%r:%K::%K Window %ris not%K sticky"; + window_set_sticky = "%K::%g:%K Window %gset%K sticky"; + window_unset_sticky = "%K::%g:%K Window %gis not%K sticky anymore"; + window_info_sticky = "Sticky : $0"; + window_scroll = "%K::%g:%K Window scroll mode is now $0"; + window_scroll_unknown = "%r:%K:: %rUnknown%K scroll mode $0, must be ON, OFF or DEFAULT"; + }; + "fe-common/irc" = { + netsplit = "%r:%K:: %r{netsplit netsplit}%K %|{server $0} <-> {server $1} %rquits%K: $2"; + netsplit_more = "%r:%K:: %r{netsplit netsplit}%K %|{server $0} <-> {server $1} %rquits:%K $2 (+$3 more, use /NETSPLIT to show all of them)"; + netsplit_join = "%K::%g:%K %g{netjoin netsplit}%K - %|%gjoins:%K $0"; + netsplit_join_more = "%%K::%g:%K %g{netjoin netsplit}%K - %|over, %gjoins:%K $0 (+$1 more)"; + no_netsplits = "%r:%y:%g:%K There are no netsplits"; + netsplits_header = "Nick Channel Server Splitted server"; + netsplits_line = "$[9]0 $[10]1 $[20]2 $3"; + netsplits_footer = ""; + ircnet_added = "%K::%g:%K Ircnet $0 %gsaved%K"; + ircnet_removed = "%K::%g:%K Ircnet $0 %gremoved%K"; + ircnet_not_found = "%r:%K:: Ircnet $0 %rnot found%K"; + ircnet_header = "%r:%y:%g:%K Ircnets:"; + ircnet_line = "$0: $1"; + ircnet_footer = ""; + setupserver_header = "Server Port Network Settings"; + setupserver_line = "%|$[!20]0 $[5]1 $[10]2 $3"; + setupserver_footer = ""; + joinerror_toomany = "%r:%K:: Join %rfails%K: {channel $0} (You have joined to too many channels)"; + joinerror_full = "%r:%K:: Join %rfails%K: {channel $0} (Channel is full)"; + joinerror_invite = "%r:%K:: Join %rfails%K: {channel $0} (You must be invited)"; + joinerror_banned = "%r:%K:: Join %rfails%K: {channel $0} (You are banned)"; + joinerror_bad_key = "%r:%K:: Join %rfails%K: {channel $0} (Wrong channel key)"; + joinerror_bad_mask = "%r:%K:: Join %rfails%K: {channel $0} (Bad channel mask)"; + joinerror_unavail = "%r:%K:: Join %rfails%K: {channel $0} (Channel is temporarily unavailable)"; + joinerror_duplicate = "%r:%K:: Channel {channel $0} already exists - %rcannot create%K it"; + channel_rejoin = "%r:%K:: Channel {channel $0} is temporarily %runavailable%K. Setting up a rejoin, to not rejoin, use /rmrejoins."; + inviting = "%K::%g:%K %gInviting%K {nick $0} to {channel $1}"; + channel_created = "%r:%y:%g:%K Channel {channel $0} created %_$1%_"; + url = "%r:%y:%g:%K Home page for {channelhilight $0}: $1"; + topic = "%r:%y:%g:%K Topic for {channel $0}: %_$1%_"; + no_topic = "%r:%y:%g:%K No topic set for %_$0%_"; + topic_info = "%r:%y:%g:%K Topic set by {channick_hilight $0} {mode $1}"; + chanmode_change = "%K:%y:%K: %ymode%K/{channel $0} [{mode $1}] by {nick $2}"; + server_chanmode_change = "%K::%g: {netsplit ServerMode}%K/{channelhilight $0}: {mode $1} by {nick $2}"; + channel_mode = "%K:%y:%K: %ymode%K/{channelhilight $0} [{mode $1}]"; + bantype = "%K:%y:%K: Ban type %ychanged%K to {channel $0}"; + no_bans = "%r:%y:%g:%K No bans in channel {channel $0}"; + banlist = "$0 - {channel $1}: ban {ban $2}"; + banlist_long = "$0 - {channel $1}: ban {ban $2} {comment by {nick $3}, $4 secs ago}"; + ebanlist = "{channel $0}: ban exception {ban $1}"; + ebanlist_long = "{channel $0}: ban exception {ban $1} {comment by {nick $2}, $3 secs ago}"; + no_invitelist = "%r:%y:%g:%K Invite list is empty in channel {channel $0}"; + invitelist = "{channel $0}: invite {ban $1}"; + no_such_channel = "%r:%K::%K {channel $0}: %rNo such%K channel"; + channel_change = "%K:%y:%K: %ymode%K/{channel $0} [{mode $1}]"; + channel_synced = "%r:%y:%g:%K Join to {channel $0} was synced in {hilight $1} secs"; + usermode_change = "%K:%y:%K: %ymode%K/{channel $0} [{mode $0}] by {channick_hilight $1}"; + user_mode = "%r:%y:%g:%K Your user mode is {mode $0}"; + away = "%K::%g:%K You have been %gmarked%K as being away"; + unaway = "%K::%g:%K You are %gno longer marked%K as being away"; + nick_away = "%r:%K::%K {nick $0} is %raway%K: $1"; + no_such_nick = "%r:%K::%K {nick $0}: %rNo such%K nick/channel"; + nick_in_use = "%r:%K::%K Nick {nick $0} is %ralready in use%K"; + nick_unavailable = "%r:%K::%K Nick {nick $0} is temporarily %runavailable%K"; + your_nick_owned = "%r:%K::%K Your nick is %rowned%K by {nick $3} {comment $1@$2}"; + whois = "%K:%K:%g:%K %g%U{nick $0}%U%K ({nickhost $1@$2})%:%r:%y:%g:%K ircname: $3"; + whowas = "%r:%y:%g:%K {nick $0} {nickhost $1@$2}%:%r:%y:%g:%K {whois ircname $3}"; + whois_idle = "%r:%y:%g:%K Idle: %|since $1 days $2 hours $3 mins $4 secs"; + whois_idle_signon = "%r:%y:%g:%K Idle: %|since $1 days $2 hours $3 mins $4 secs {comment Signed on: $5}"; + whois_server = "%r:%y:%g:%K Server: %|$1 {comment $2}"; + whois_oper = "%r:%y:%g:%K Info: %|{hilight $1}"; + whois_registered = "%r:%y:%g:%K Info: %|has registered this nick"; + whois_help = "%r:%y:%g:%K Info: %|available for help"; + whois_modes = "%r:%y:%g:%K Modes: %|{mode $1}"; + whois_realhost = "%r:%y:%g:%K Hostname: %|{hilight $1-}"; + whois_usermode = "%r:%y:%g:%K Usermode: %|{mode $1}"; + whois_channels = "%r:%y:%g:%K Channels: %|{channel $1}"; + whois_away = "%r:%y:%g:%K Away: %|$1"; + whois_special = "%r:%y:%g:%K Info: %|$1"; + whois_extra = "%r:%y:%g:%K Info: %|$1"; + end_of_whois = "%r:%K::%K %rEnd%K of WHOIS%K"; + end_of_whowas = "%r:%y:%g:%K End of WHOWAS"; + whois_not_found = "%r:%K::%K There is %rno such%K nick {channick_hilight $0}"; + who = "{channelhilight $[-10]0} %|{nick $[!9]1} $[!3]2 $[!2]3 $4@$5 {comment {hilight $6}}"; + end_of_who = "%r:%y:%g:%K End of /WHO list"; + own_notice = "{ownnotice notice $0}$1"; + # own_action = "{nick $[-11]0}%n $1"; + # own_action_target = "{ownaction_target $0 $2}$1"; + own_ctcp = "{ownctcp ctcp $0}$1 $2"; + notice_server = "{servernotice $0}$1"; + notice_public = "{notice $0{pubnotice_channel $1}}$2"; + notice_private = "{notice $0{pvtnotice_host $1}}$2"; + # action_private = "{pvtaction $0}$2"; + # action_private_query = "{pvtaction_query $0}$2"; + # action_public = " {nick $[-11]0}%n $1"; + # action_public_channel = "{pubaction $0{msgchannel $1}}$2"; + ctcp_reply = "%K::%g:%K %gCTCP%K {hilight $0} reply from {channick_hilight $1}: $2"; + ctcp_reply_channel = "%K::%g:%K %gCTCP {hilight $0} reply%K from {channick_hilight $1} in channel {channel $3}: $2"; + ctcp_ping_reply = "%K::%g:%K %gCTCP {hilight PING} reply%K from {channick_hilight $0}: $1.$[-3.0]2 seconds"; + ctcp_requested = "%K::%g:%K %K{ctcp {hilight $0} {comment $1} %grequested%K {hilight $2} from {nick $3}}"; + ctcp_requested_unknown = ""; + online = "%r:%y:%g:%K Users online: {hilight $0}"; + pong = "%K::%g:%K PONG %greceived%K from $0: $1"; + wallops = "{wallop WALLOP {wallop_nick $0}} $1"; + action_wallops = "{wallop WALLOP {wallop_action $0}} $1"; + kill = "%r:%K:: You were %r{error killed}%K by {nick $0} {nickhost $1} {reason $2} {comment Path: $3}"; + kill_server = "%r:%K:: You were %r{error killed}%K by {server $0} {reason $1} {comment Path: $2}"; + error = "%r:%K:: %r{error ERROR}%K $0"; + unknown_mode = "%r:%K:: %rUnknown%K mode character $0"; + not_chanop = "%r:%K:: You're %rnot channel operator%K in {channel $0}"; + silenced = "%K::%g:%K %gSilenced%K {nick $0}"; + unsilenced = "%K::%g:%K %gUnsilenced%K {nick $0}"; + silence_line = "{nick $0}: silence {ban $1}"; + ask_oper_pass = "%r:%y:%g:%K Operator password:"; + }; + "fe-common/perl" = { + script_not_found = "%r:%K:: Script {hilight $0} %rnot found%K"; + script_not_loaded = "%r:%K:: Script {hilight $0} %ris not%K loaded"; + script_loaded = "%K::%g:%K %gLoaded%K script {hilight $0}"; + script_unloaded = "%r:%K:: %rUnloaded%K script {hilight $0}"; + no_scripts_loaded = "%r:%y:%g:%K No scripts are loaded"; + script_list_header = "%r:%y:%g:%K Loaded scripts:"; + script_list_line = "$[!15]0 $1"; + script_list_footer = ""; + script_error = "{error %r:%K:: %rError%K in script {hilight $0}:}"; + }; + # "fe-common/irc/notifylist" = { + # notify_join = "{nick $0} [$1@$2] [{hilight $3}] has joined to $4"; + # notify_part = "{nick $0} has left $4"; + # notify_away = "{nick $0} [$5] [$1@$2] [{hilight $3}] is now away: $4"; + # notify_unaway = "{nick $0} [$4] [$1@$2] [{hilight $3}] is now unaway"; + # notify_unidle = "{nick $0} [$5] [$1@$2] [{hilight $3}] just stopped idling"; + # notify_online = "On $0: {hilight $1}"; + # notify_offline = "Offline: $0"; + # notify_list = "$0: $1 $2 $3"; + # notify_list_empty = "The notify list is empty"; + # }; + "Irssi::Script::adv_windowlist" = { + awl_display_key = "$N${cumode_space}$H$C$S"; + }; +}; + diff --git a/.irssi/scripts/adv_windowlist.pl b/.irssi/scripts/adv_windowlist.pl new file mode 100644 index 0000000..91afa1c --- /dev/null +++ b/.irssi/scripts/adv_windowlist.pl @@ -0,0 +1,2555 @@ +use strict; +use warnings; + +our $VERSION = '1.5'; # 5df597ac461465e +our %IRSSI = ( + authors => 'Nei', + contact => 'Nei @ anti@conference.jabber.teamidiot.de', + url => "http://anti.teamidiot.de/", + name => 'adv_windowlist', + description => 'Adds a permanent advanced window list on the right or in a status bar.', + sbitems => 'awl_shared', + license => 'GNU GPLv2 or later', + ); + +# UPGRADE NOTE +# ============ +# for users of 0.7 or earlier series, please note that appearance +# settings have moved to /format, i.e. inside your theme! +# the fifo (screen) has been replaced by an external viewer script + +# Usage +# ===== +# copy the script to ~/.irssi/scripts/ +# +# In irssi: +# +# /run adv_windowlist +# +# In your shell (for example a tmux split): +# +# perl ~/.irssi/scripts/adv_windowlist.pl +# +# To use sbar mode instead: +# +# /toggle awl_viewer +# +# Hint: to get rid of the old [Act:] display +# /statusbar window remove act +# +# to get it back: +# /statusbar window add -after lag -priority 10 act + +# Options +# ======= +# formats can be cleared with /format -delete +# +# /format awl_display_(no)key(_active|_visible) <string> +# * string : Format String for one window. The following $'s are expanded: +# $C : Name +# $N : Number of the Window +# $Q : meta-Keymap +# $H : Start hilighting +# $S : Stop hilighting +# /+++++++++++++++++++++++++++++++++, +# | **** I M P O R T A N T : **** | +# | | +# | don't forget to use $S if you | +# | used $H before! | +# | | +# '+++++++++++++++++++++++++++++++++/ +# key : a key binding that goes to this window could be detected in /bind +# nokey : no such key binding was detected +# active : window would receive the input you are currently typing +# visible : window is also visible on screen but not active (a split window) +# +# /format awl_name_display <string> +# * string : Format String for window names +# $0 : name as formatted by the settings +# +# /format awl_display_header <string> +# * string : Format String for this header line. The following $'s are expanded: +# $C : network tag +# +# /format awl_separator(2) <string> +# * string : Character to use between the channel entries +# variant 2 can be used for alternating separators (only in status bar +# without block display) +# +# /format awl_abbrev_chars <string> +# * string : Character to use when shortening long names. The second character +# will be used if two blocks need to be filled. +# +# /format awl_title <string> +# * string : Text to display in the title string or title bar +# +# /format awl_viewer_item_bg <string> +# * string : Format String specifying the viewer's item background colour +# +# /set awl_prefer_name <ON|OFF> +# * this setting decides whether awl will use the active_name (OFF) or the +# window name as the name/caption in awl_display_*. +# That way you can rename windows using /window name myownname. +# +# /set awl_hide_empty <num> +# * if visible windows without items should be hidden from the window list +# set it to 0 to show all windows +# 1 to hide visible windows without items (negative exempt +# active window) +# +# /set awl_hide_data <num> +# * num : hide the window if its data_level is below num +# set it to 0 to basically disable this feature, +# 1 if you don't want windows without activity to be shown +# 2 to show only those windows with channel text or hilight +# 3 to show only windows with hilight (negative exempt active window) +# +# /set awl_hide_name_data <num> +# * num : hide the name of the window if its data_level is below num +# (only works in status bar without block display) +# you will want to change your formats to add $H...$S around $Q or $N +# if you plan to use this +# +# /set awl_maxlines <num> +# * num : number of lines to use for the window list (0 to disable, negative +# lock) +# +# /set awl_maxcolumns <num> +# * num : number of columns to use for the window list when using the +# tmux integration (0 to disable) +# +# /set awl_block <num> +# * num : width of a column in viewer mode (negative values = block +# display in status bar mode) +# /+++++++++++++++++++++++++++++++++, +# | ****** W A R N I N G ! ****** | +# | | +# | If your block display looks | +# | DISTORTED, you need to add the | +# | following line to your .theme | +# | file under | +# | abstracts = { : | +# | | +# | sb_act_none = "%K$*"; | +# | | +# '+++++++++++++++++++++++++++++++++/ +# +# /set awl_sbar_maxlength <ON|OFF> +# * if you enable the maxlength setting, the block width will be used as a +# maximum length for the non-block status bar mode too. +# +# /set awl_height_adjust <num> +# * num : how many lines to leave empty in viewer mode +# +# /set awl_sort <-data_level|-last_line|refnum> +# * you can change the window sort order with this variable +# -data_level : sort windows with hilight first +# -last_line : sort windows in order of activity +# refnum : sort windows by window number +# active/server/tag : sort by server name +# "-" reverses the sort order +# typechecks are supported via ::, e.g. active::Query or active::Irc::Query +# undefinedness can be checked with ~, e.g. ~active +# string comparison can be done with =, e.g. name=(status) +# to make sort case insensitive, use #i, e.g. name#i +# any key in the window hash can be tested, e.g. active/chat_type=XMPP +# multiple criteria can be separated with , or +, e.g. -data_level+-last_line +# +# /set awl_placement <top|bottom> +# /set awl_position <num> +# * these settings correspond to /statusbar because awl will create +# status bars for you +# (see /help statusbar to learn more) +# +# /set awl_all_disable <ON|OFF> +# * if you set awl_all_disable to ON, awl will also remove the +# last status bar it created if it is empty. +# As you might guess, this only makes sense with awl_hide_data > 0 ;) +# +# /set awl_viewer <ON|OFF> +# * enable the external viewer script +# +# /set awl_viewer_launch <ON|OFF> +# * try to auto-launch the viewer under tmux or with a shell command +# /awl restart is required all auto-launch related settings to take +# effect +# +# /set awl_viewer_tmux_position <left|top|right|bottom|custom> +# * try to split in this direction when using tmux for the viewer +# custom : use custom_command setting +# +# /set awl_viewer_xwin_command <shell command> +# * custom command to run in order to start the viewer when irssi is +# running under X +# %A - gets replaced by the command to run the viewer +# %qA - additionally quote the command +# +# /set awl_viewer_custom_command <shell command> +# * custom command to run in order to start the viewer +# +# /set awl_viewer_launch_env <string> +# * specific environment settings for use on viewer auto-launch, +# without the AWL_ prefix +# +# /set awl_shared_sbar <left<right|OFF> +# * share a status bar for the first awl item, you will need to manually +# /statusbar window add -after lag -priority 10 awl_shared +# left : space in cells occupied on the left of status bar +# right : space occupied on the right +# Note: you need to replace "left" AND "right" with the appropriate numbers! +# +# /set awl_path <path> +# * path to the file which the viewer script reads +# +# /set fancy_abbrev <no|head|strict|fancy> +# * how to shorten too long names +# no : shorten in the middle +# head : always cut off the ends +# strict : shorten repeating substrings +# fancy : combination of no+strict +# +# /set awl_custom_xform <perl code> +# * specify a custom routine to transform window names +# example: s/^#// remove the #-mark of IRC channels +# the special flags $CHANNEL / $TAG / $QUERY / $NAME can be +# tested in conditionals +# +# /set awl_last_line_shade <timeout> +# * set timeout to shade activity base colours, to enable +# you also need to add +-last_line to awl_sort +# (requires 256 colour support) +# +# /set awl_no_mode_hint <ON|OFF> +# * whether to show the hint of running the viewer script in the +# status bar +# +# /set awl_mouse <ON|OFF> +# * enable the terminal mouse in irssi +# (use the awl-patched mouse.pl for gestures and commands if you need +# them and disable mouse_escape) +# +# /set awl_mouse_offset <num> +# * specifies where on the screen is the awl status bar +# (0 = on top/bottom, 1 = one additional line in between, +# e.g. prompt) +# you MUST set this correctly otherwise the mouse coordinates will +# be off +# +# /set mouse_scroll <num> +# * how many lines the mouse wheel scrolls +# +# /set mouse_escape <num> +# * seconds to disable the mouse, when not clicked on the windowlist +# + +# Commands +# ======== +# /awl redraw +# * redraws the windowlist. There may be occasions where the +# windowlist can get destroyed so you can use this command to +# force a redraw. +# +# /awl restart +# * restart the connection to the viewer script. + +# Viewer script +# ============= +# When run from the command line, adv_windowlist acts as the viewer +# script to be used together with the irssi script to display the +# window list in a sidebar/terminal of its own. +# +# One optional parameter is accepted, the awl_path +# +# The viewer can be configured by three environment variables: +# +# AWL_HI9=1 +# * interpret %9 as high-intensity toggle instead of bold. This had +# been the default prior to version 0.9b8 +# +# AWL_AUTOFOCUS=0 +# * disable auto-focus behaviour when activating a window +# +# AWL_NOTITLE=1 +# * disable the title bar + +# Nei =^.^= ( anti@conference.jabber.teamidiot.de ) + +no warnings 'redefine'; +use constant IN_IRSSI => __PACKAGE__ ne 'main' || $ENV{IRSSI_MOCK}; +use constant SCRIPT_FILE => __FILE__; +no if !IN_IRSSI, strict => (qw(subs refs)); +use if IN_IRSSI, Irssi => (); +use if IN_IRSSI, 'Irssi::TextUI' => (); +use v5.10; +use Encode; +use Storable (); +use IO::Socket::UNIX; +use List::Util qw(min max reduce); +use Hash::Util qw(lock_keys); +use Text::ParseWords qw(shellwords); + +BEGIN { + if ($] < 5.012) { + *CORE::GLOBAL::length = *CORE::GLOBAL::length = sub (_) { + defined $_[0] ? CORE::length($_[0]) : undef + }; + *Irssi::active_win = {}; # hide incorrect warning + } +} + +unless (IN_IRSSI) { + local *_ = \@ARGV; + &AwlViewer::main; + exit; +} + + +use constant GLOB_QUEUE_TIMER => 100; + +our $BLOCK_ALL; # localized blocker +my @actString; # status bar texts +my @win_items; +my $currentLines = 0; +my %awins; +my $globTime; # timer to limit remake calls + +my %CHANGED; +my $VIEWER_MODE; +my $MOUSE_ON; +my %mouse_coords; +my %statusbars; +my %S; # settings +my $settings_str = ''; +my $window_sort_func; +my $custom_xform; +my ($sb_base_width, $sb_base_width_pre, $sb_base_width_post); +my $print_text_activity; +my $shade_line_timer; +my ($screenHeight, $screenWidth); +my %viewer; + +my (%keymap, %nummap, %wnmap, %specialmap, %wnmap_exp, %custom_key_map); +my %banned_channels; +my %abbrev_cache; + +use constant setc => 'awl'; + +sub set ($) { + setc . '_' . $_[0] +} + +sub add_statusbar { + for (@_) { + # add subs + my $l = set $_; + { + my $close = $_; + no strict 'refs'; + *{$l} = sub { awl($close, @_) }; + } + Irssi::command("statusbar $l reset"); + Irssi::command("statusbar $l enable"); + if (lc $S{placement} eq 'top') { + Irssi::command("statusbar $l placement top"); + } + if (my $x = $S{position}) { + Irssi::command("statusbar $l position $x"); + } + Irssi::command("statusbar $l add -priority 100 -alignment left barstart"); + Irssi::command("statusbar $l add $l"); + Irssi::command("statusbar $l add -priority 100 -alignment right barend"); + Irssi::command("statusbar $l disable"); + Irssi::statusbar_item_register($l, '$0', $l); + $statusbars{$_} = 1; + Irssi::command("statusbar $l enable"); + } +} + +sub remove_statusbar { + for (@_) { + my $l = set $_; + Irssi::command("statusbar $l disable"); + Irssi::command("statusbar $l reset"); + Irssi::statusbar_item_unregister($l); + { + no strict 'refs'; + undef &{$l}; + } + delete $statusbars{$_}; + } +} + +my $awl_shared_empty = sub { + return if $BLOCK_ALL; + my ($item, $get_size_only) = @_; + $item->default_handler($get_size_only, '', '', 0); +}; + +sub syncLines { + my $maxLines = $S{maxlines}; + my $newLines = ($maxLines > 0 and @actString > $maxLines) ? + $maxLines : + ($maxLines < 0) ? + -$maxLines : + @actString; + $currentLines = 1 if !$currentLines && $S{shared_sbar}; + if ($S{shared_sbar} && !$statusbars{shared}) { + my $l = set 'shared'; + { + no strict 'refs'; + *{$l} = sub { + return if $BLOCK_ALL; + my ($item, $get_size_only) = @_; + + my $text = $actString[0]; + my $title = _get_format(set 'title'); + if (length $title) { + $title =~ s{\\(.)|(.)}{ + defined $2 ? quotemeta $2 + : $1 eq 'V' ? '\u' + : $1 eq ':' ? quotemeta ':%n' + : $1 =~ /^[uUFQE]$/ ? "\\$1" + : quotemeta "\\$1" + }sge; + $title = eval qq{"$title"}; + $title .= ' '; + } + my $pat = defined $text ? "{sb $title\$*}" : '{sb }'; + $text //= ''; + $item->default_handler($get_size_only, $pat, $text, 0); + }; + } + $statusbars{shared} = 1; + remove_statusbar (0) if $statusbars{0}; + } + elsif ($statusbars{shared} && !$S{shared_sbar}) { + add_statusbar (0) if $currentLines && $newLines; + delete $statusbars{shared}; + my $l = set 'shared'; + { + no strict 'refs'; + *{$l} = $awl_shared_empty; + } + } + if ($currentLines == $newLines) { return; } + elsif ($newLines > $currentLines) { + add_statusbar ($currentLines .. ($newLines - 1)); + } + else { + remove_statusbar (reverse ($newLines .. ($currentLines - 1))); + } + $currentLines = $newLines; +} + +sub awl { + return if $BLOCK_ALL; + my ($line, $item, $get_size_only) = @_; + + my $text = $actString[$line]; + my $pat = defined $text ? '{sb $*}' : '{sb }'; + $text //= ''; + $item->default_handler($get_size_only, $pat, $text, 0); +} + +# remove old statusbars +{ my %killBar; + sub get_old_status { + my ($textDest, $cont, $cont_stripped) = @_; + if ($textDest->{level} == 524288 and $textDest->{target} eq '' and !defined $textDest->{server}) { + my $name = quotemeta(set ''); + if ($cont_stripped =~ m/^$name(\d+)\s/) { $killBar{$1} = 1; } + Irssi::signal_stop; + } + } + sub killOldStatus { + %killBar = (); + Irssi::signal_add_first('print text' => 'get_old_status'); + Irssi::command('statusbar'); + Irssi::signal_remove('print text' => 'get_old_status'); + remove_statusbar(keys %killBar); + } +} + +sub _add_map { + my ($type, $target, $map) = @_; + ($type->{$target}) = sort { length $a <=> length $b || $a cmp $b } + $map, exists $type->{$target} ? $type->{$target} : (); +} + +sub get_keymap { + my ($textDest, undef, $cont_stripped) = @_; + if ($textDest->{level} == 524288 and $textDest->{target} eq '' and !defined $textDest->{server}) { + my $one_meta_or_ctrl_key = qr/((?:meta-)*?)(?:(meta-|\^)(\S)|(\w+))/; + $cont_stripped = as_uni($cont_stripped); + if ($cont_stripped =~ m/((?:$one_meta_or_ctrl_key-)*$one_meta_or_ctrl_key)\s+(.*)$/) { + my ($combo, $command) = ($1, $10); + my $map = ''; + while ($combo =~ s/(?:-|^)$one_meta_or_ctrl_key$//) { + my ($level, $ctl, $key, $nkey) = ($1, $2, $3, $4); + my $numlevel = ($level =~ y/-//); + $ctl = '' if !$ctl || $ctl ne '^'; + $map = ('-' x ($numlevel%2)) . ('+' x ($numlevel/2)) . + $ctl . (defined $key ? $key : "\01$nkey\01") . $map; + } + for ($command) { + last unless length $map; + if (/^change_window (\d+)/i) { + _add_map(\%nummap, $1, $map); + } + elsif (/^(?:command window goto|change_window) (\S+)/i) { + my $window = $1; + if ($window !~ /\D/) { + _add_map(\%nummap, $window, $map); + } + elsif (lc $window eq 'active') { + _add_map(\%specialmap, '_active', $map); + } + else { + _add_map(\%wnmap, $window, $map); + } + } + elsif (/^(?:active_window|command (ack))/i) { + _add_map(\%specialmap, '_active', $map); + $viewer{use_ack} = !!$1; + } + elsif (/^command window last/i) { + _add_map(\%specialmap, '_last', $map); + } + elsif (/^(?:upper_window|command window up)/i) { + _add_map(\%specialmap, '_up', $map); + } + elsif (/^(?:lower_window|command window down)/i) { + _add_map(\%specialmap, '_down', $map); + } + elsif (/^key\s+(\w+)/i) { + $custom_key_map{$1} = $map; + } + } + } + Irssi::signal_stop; + } +} + +sub update_keymap { + %nummap = %wnmap = %specialmap = %custom_key_map = (); + Irssi::signal_remove('command bind' => 'watch_keymap'); + Irssi::signal_add_first('print text' => 'get_keymap'); + Irssi::command('bind'); + Irssi::signal_remove('print text' => 'get_keymap'); + for (keys %custom_key_map) { + if (exists $custom_key_map{$_} && + $custom_key_map{$_} =~ s/\01(\w+)\01/exists $custom_key_map{$1} ? $custom_key_map{$1} : "\02"/ge) { + if ($custom_key_map{$_} =~ /\02/) { + delete $custom_key_map{$_}; + } + else { + redo; + } + } + } + for my $keymap (\(%specialmap, %wnmap, %nummap)) { + for (keys %$keymap) { + if ($keymap->{$_} =~ s/\01(\w+)\01/exists $custom_key_map{$1} ? $custom_key_map{$1} : "\02"/ge) { + if ($keymap->{$_} =~ /\02/) { + delete $keymap->{$_}; + } + } + } + } + Irssi::signal_add('command bind' => 'watch_keymap'); + delete $viewer{client_keymap}; + &wl_changed; +} + +# watch keymap changes +sub watch_keymap { + Irssi::timeout_add_once(1000, 'update_keymap', undef); +} + +{ my %strip_table = ( + # fe-common::core::formats.c:format_expand_styles + # delete format_backs format_fores bold_fores other stuff + (map { $_ => '' } (split //, '04261537' . 'kbgcrmyw' . 'KBGCRMYW' . 'U9_8I:|FnN>#[' . 'pP')), + # escape + (map { $_ => $_ } (split //, '{}%')), + ); + sub ir_strip_codes { # strip %codes + my $o = shift; + $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; + $o + } +} +## ir_parse_special -- wrapper around parse_special +## $i - input format +## $args - array ref of arguments to format +## $win - different target window (default current window) +## $flags - different kind of escape flags (default 4|8) +## returns formatted str +sub ir_parse_special { + my $o; + my $i = shift; + my $args = shift // []; + y/ /\177/ for @$args; # hack to escape spaces + my $win = shift || Irssi::active_win; + my $flags = shift // 0x4|0x8; + my @cmd_args = ($i, (join ' ', @$args), $flags); + my $server = Irssi::active_server(); + if (ref $win and ref $win->{active}) { + $o = $win->{active}->parse_special(@cmd_args); + } + elsif (ref $win and ref $win->{active_server}) { + $o = $win->{active_server}->parse_special(@cmd_args); + } + elsif (ref $server) { + $o = $server->parse_special(@cmd_args); + } + else { + $o = &Irssi::parse_special(@cmd_args); + } + $o =~ y/\177/ /; + $o +} + +sub sb_format_expand { # Irssi::current_theme->format_expand wrapper + Irssi::current_theme->format_expand( + $_[0], + ( + Irssi::EXPAND_FLAG_IGNORE_REPLACES + | + ($_[1] ? 0 : Irssi::EXPAND_FLAG_IGNORE_EMPTY) + ) + ) +} + +{ my $term_type = Irssi::version > 20040819 ? 'term_charset' : 'term_type'; + local $@; + eval { require Text::CharWidth; }; + unless ($@) { + *screen_length = sub { Text::CharWidth::mbswidth($_[0]) }; + } + else { + my $err = $@; chomp $err; $err =~ s/\sat .* line \d+\.$//; + #Irssi::print("%_$IRSSI{name}: warning:%_ Text::CharWidth module failed to load. Length calculation may be off! Error was:"); + print "%_$IRSSI{name}:%_ $err"; + *screen_length = sub { + my $temp = shift; + if (lc Irssi::settings_get_str($term_type) eq 'utf-8') { + Encode::_utf8_on($temp); + } + length($temp) + }; + } + sub as_uni { + no warnings 'utf8'; + Encode::decode(Irssi::settings_get_str($term_type), $_[0], 0) + } + sub as_tc { + Encode::encode(Irssi::settings_get_str($term_type), $_[0], 0) + } +} + +sub sb_length { + screen_length(ir_strip_codes($_[0])) +} + +sub run_custom_xform { + local $@; + eval { + $custom_xform->() + }; + if ($@) { + $@ =~ /^(.*)/; + print '%_'.(set 'custom_xform').'%_ died (disabling): '.$1; + $custom_xform = undef; + } +} + +sub remove_uniform { + my $o = shift; + $o =~ s/^xmpp:(.*?[%@]).+\.[^.]+$/$1/ or + $o =~ s#^psyc://.+\.[^.]+/([@~].*)$#$1#; + if ($custom_xform) { + run_custom_xform() for $o; + } + $o +} + +sub remove_uniform_vars { + my $win = shift; + my $name = __PACKAGE__ . '::custom_xform::' . $win->{active}{type} + if ref $win->{active} && $win->{active}{type}; + no strict 'refs'; + local ${$name} = 1 if $name; + remove_uniform(+shift); +} + +sub lc1459 { + my $x = shift; + $x =~ y/][\\^/}{|~/; + lc $x +} + +sub window_list { + sort $window_sort_func Irssi::windows; +} + +sub _calculate_abbrev { + my ($wins, $abbrevList) = @_; + if ($S{fancy_abbrev} !~ /^(no|off|head)/i) { + my @nameList = map { ref $_ ? remove_uniform_vars($_, as_uni($_->get_active_name) // '') : '' } @$wins; + for (my $i = 0; $i < @nameList - 1; ++$i) { + my ($x, $y) = ($nameList[$i], $nameList[$i + 1]); + s/^[+#!=]// for $x, $y; + my $res = exists $abbrev_cache{$x}{$y} ? $abbrev_cache{$x}{$y} + : $abbrev_cache{$x}{$y} = string_LCSS($x, $y); + if (defined $res) { + for ($nameList[$i], $nameList[$i + 1]) { + $abbrevList->{$_} //= int((index $_, $res) + (length $res) / 2); + } + } + } + } +} + +my %act_last_line_shades = ( + r => [qw[ 50 40 30 20 ]], + g => [qw[ 1O 1I 1C 16 ]], + y => [qw[ 5O 4I 3C 26 ]], + b => [qw[ 15 14 13 12 ]], + m => [qw[ 54 43 32 21 ]], + c => [qw[ 1S 1L 1E 17 ]], + w => [qw[ 7W 7T 7Q 3E ]], + K => [qw[ 7M 7K 27 7H ]], + R => [qw[ 60 50 40 30 ]], + G => [qw[ 1U 1O 1I 1C ]], + Y => [qw[ 6U 5O 4I 3C ]], + B => [qw[ 2B 2A 29 28 ]], + M => [qw[ 65 54 43 32 ]], + C => [qw[ 1Z 1S 1L 1E ]], + W => [qw[ 6Z 5S 7R 7O ]], + ); + +sub _format_display { + my (undef, $format, $cformat, $hilight, $name, $number, $key, $win) = @_; + if ($print_text_activity && $S{line_shade}) { + my @hilight_code = split /\177/, sb_format_expand("{$hilight \177}"), 2; + my $max_time = max(1, log($S{line_shade}) - log(1000)); + my $time_delta = min(3, min($max_time, log(max(1, time - $win->{last_line}))) / $max_time * 3); + if ($hilight_code[0] =~ /%(.)/ && exists $act_last_line_shades{$1}) { + $hilight = 'sb_act_hilight_color %X'.$act_last_line_shades{$1}[$time_delta]; + } + } + $cformat = '$0' unless length $cformat; + my %map = ('$C' => $cformat, '$N' => '$1', '$Q' => '$2'); + $format =~ s<(\$.)><$map{$1}//$1>ge; + $format =~ s<\$H((?:\$.|[^\$])*?)\$S><{$hilight $1%n}>g; + my @ret = ir_parse_special(sb_format_expand($format), [$name, $number, $key], $win); + @ret +} + +sub _get_format { + Irssi::current_theme->get_format(__PACKAGE__, @_) +} + +sub _calculate_items { + my ($wins, $abbrevList) = @_; + + my $display_header = _get_format(set 'display_header'); + my $name_format = _get_format(set 'name_display'); + my $abbrev_chars = as_uni(_get_format(set 'abbrev_chars')); + + my %displays; + + my $active = Irssi::active_win; + @win_items = (); + %keymap = (%nummap, %wnmap_exp); + + my ($numPad, $keyPad) = (0, 0); + if ($VIEWER_MODE or $S{block} < 0) { + $numPad = length((sort { length $b <=> length $a } keys %keymap)[0]) // 0; + $keyPad = length((sort { length $b <=> length $a } values %keymap)[0]) // 0; + } + my $last_net; + my ($abbrev1, $abbrev2) = $abbrev_chars =~ /(\X)(.*)/; + my @abbrev_chars = ('~', "\x{301c}"); + unless (defined $abbrev1 && screen_length(as_tc($abbrev1)) == 1) { $abbrev1 = $abbrev_chars[0] } + unless (length $abbrev2) { + $abbrev2 = $abbrev1; + if ($abbrev1 eq $abbrev_chars[0]) { + $abbrev2 = $abbrev_chars[1]; + } + else { + $abbrev2 = $abbrev1; + } + } + if (screen_length(as_tc($abbrev2)) == 1) { + $abbrev2 x= 2; + } + while (screen_length(as_tc($abbrev2)) > 2) { + chop $abbrev2; + } + unless (screen_length(as_tc($abbrev2)) == 2) { + $abbrev2 = $abbrev_chars[1]; + } + for my $win (@$wins) { + my $global_tag_header_mode; + + next unless ref $win; + + my $backup_win = Storable::dclone($win); + delete $backup_win->{active} unless ref $backup_win->{active}; + + $global_tag_header_mode = + $display_header && ($last_net // '') ne ($backup_win->{active}{server}{tag} // ''); + + if ($win->{data_level} < abs $S{hide_data} + && ($win->{refnum} != $active->{refnum} || 0 <= $S{hide_data})) { + next; } + elsif (exists $awins{$win->{refnum}} && $S{hide_empty} && !$win->items + && ($win->{refnum} != $active->{refnum} || 0 <= $S{hide_empty})) { + next; } + + my $colour = $win->{hilight_color} // ''; + my $hilight = do { + if ($win->{data_level} == 0) { 'sb_act_none'; } + elsif ($win->{data_level} == 1) { 'sb_act_text'; } + elsif ($win->{data_level} == 2) { 'sb_act_msg'; } + elsif ($colour ne '') { "sb_act_hilight_color $colour"; } + elsif ($win->{data_level} == 3) { 'sb_act_hilight'; } + else { 'sb_act_special'; } + }; + my $number = $win->{refnum}; + + my ($name, $display, $cdisplay); + if ($global_tag_header_mode) { + $display = $display_header; + $name = as_uni($backup_win->{active}{server}{tag}) // ''; + if ($custom_xform) { + no strict 'refs'; + local ${ __PACKAGE__ . '::custom_xform::TAG' } = 1; + run_custom_xform() for $name; + } + } + else { + my @display = ('display_nokey'); + if (defined $keymap{$number} and $keymap{$number} ne '') { + unshift @display, map { (my $cpy = $_) =~ s/_no/_/; $cpy } @display; + } + if (exists $awins{$number}) { + unshift @display, map { my $cpy = $_; $cpy .= '_visible'; $cpy } @display; + } + if ($active->{refnum} == $number) { + unshift @display, map { my $cpy = $_; $cpy .= '_active'; $cpy } + grep { !/_visible$/ } @display; + } + $display = (grep { length $_ } + map { $displays{$_} //= _get_format(set $_) } + @display)[0]; + $cdisplay = $name_format; + $name = as_uni($win->get_active_name) // ''; + $name = '*' if $S{banned_on} and exists $banned_channels{lc1459($name)}; + $name = remove_uniform_vars($win, $name) if $name ne '*'; + if ($name ne '*' and $win->{name} ne '' and $S{prefer_name}) { + $name = as_uni($win->{name}); + if ($custom_xform) { + no strict 'refs'; + local ${ __PACKAGE__ . '::custom_xform::NAME' } = 1; + run_custom_xform() for $name; + } + } + + if (!$VIEWER_MODE && $S{block} >= 0 && $S{hide_name} + && $win->{data_level} < abs $S{hide_name} + && ($win->{refnum} != $active->{refnum} || 0 <= $S{hide_name})) { + $name = ''; + $cdisplay = ''; + } + } + + $display = "$display%n"; + my $num_ent = (' 'x max(0,$numPad - length $number)) . $number; + my $key_ent = exists $keymap{$number} ? ((' 'x max(0,$keyPad - length $keymap{$number})) . $keymap{$number}) : ' 'x$keyPad; + if ($VIEWER_MODE or $S{sbar_maxlen} or $S{block} < 0) { + my $baseLength = sb_length(_format_display( + '', $display, $cdisplay, $hilight, + 'x', # placeholder + $num_ent, + $key_ent, + $win)) - 1; + my $diff = (abs $S{block}) - (screen_length(as_tc($name)) + $baseLength); + if ($diff < 0) { # too long + my $screen_length = screen_length(as_tc($name)); + if ((abs $diff) >= $screen_length) { $name = '' } # forget it + elsif ((abs $diff) + screen_length(as_tc(substr($name, 0, 1))) >= $screen_length) { $name = substr($name, 0, 1); } + else { + my $ulen = length $name; + my $middle2 = exists $abbrevList->{$name} ? + ($S{fancy_strict}) ? + 2* $abbrevList->{$name} : + (2*($abbrevList->{$name} + $ulen) / 3) : + ($S{fancy_head}) ? + 2*$ulen : + $ulen; + my $first = 1; + while (length $name > 1) { + my $cp = $middle2 >= 0 ? $middle2/2 : -1; # clearing position + my $rm = 2; + # if character at end is wider than 1 cell -> replace it with ~ + if (screen_length(as_tc(substr $name, $cp, 1)) > 1) { + if ($first || $cp < 0) { + $rm = 1; + $first = undef; + } + } + elsif ($cp < 0) { # elsif at end -> replace last 2 characters + --$cp; + } + (substr $name, $cp, $rm) = $abbrev1; + if ($cp > -1 && $rm > 1) { + --$middle2; + } + my $sl = screen_length(as_tc($name)); + if ($sl + $baseLength < abs $S{block}) { + (substr $name, ($middle2+1)/2, 1) = $abbrev2; + last; + } + elsif ($sl + $baseLength == abs $S{block}) { + last; + } + } + } + } + elsif ($VIEWER_MODE or $S{block} < 0) { + $name .= (' ' x $diff); + } + } + + push @win_items, _format_display( + '', $display, $cdisplay, $hilight, + as_tc($name), + $num_ent, + as_tc($key_ent), + $win); + + if ($global_tag_header_mode) { + $last_net = $backup_win->{active}{server}{tag}; + redo; + } + + $mouse_coords{refnum}{$#win_items} = $number; + } +} + +sub _spread_items { + my $width = $screenWidth - $sb_base_width - 1; + my @separator = _get_format(set 'separator'); + if ($S{block} >= 0) { + my $sep2 = _get_format(set 'separator2'); + push @separator, $sep2 if length $sep2 && $sep2 ne $separator[0]; + } + $separator[0] .= '%n'; + my @sepLen = map { sb_length($_) } @separator; + + @actString = (); + my $curLine; + my $curLen = 0; + if ($S{shared_sbar}) { + $curLen += $S{shared_sbar}[0] + 2; + $width -= $S{shared_sbar}[2]; + } + my $mouse_header_check = 0; + for my $it (@win_items) { + my $itemLen = sb_length($it); + if ($curLen) { + if ($curLen + $itemLen + $sepLen[$mouse_header_check % @sepLen] > $width) { + $width += $S{shared_sbar}[2] + if !@actString && $S{shared_sbar}; + push @actString, $curLine; + $curLine = undef; + $curLen = 0; + } + elsif (defined $curLine) { + $curLine .= $separator[$mouse_header_check % @separator]; + $curLen += $sepLen[$mouse_header_check % @sepLen]; + } + } + $curLine .= $it; + if (exists $mouse_coords{refnum}{$mouse_header_check}) { + $mouse_coords{scalar @actString}{ $_ } = $mouse_coords{refnum}{$mouse_header_check} + for $curLen .. $curLen + $itemLen - 1; + } + $curLen += $itemLen; + } + continue { + ++$mouse_header_check; + } + $curLen -= $S{shared_sbar}[0] + if !@actString && $S{shared_sbar}; + push @actString, $curLine if $curLen; +} + +sub remake { + my %abbrevList; + my @wins = window_list(); + if ($VIEWER_MODE or $S{sbar_maxlen} or $S{block} < 0) { + _calculate_abbrev(\@wins, \%abbrevList); + } + + %mouse_coords = ( refnum => +{} ); + _calculate_items(\@wins, \%abbrevList); + + unless ($VIEWER_MODE) { + _spread_items(); + + push @actString, undef unless @actString || $S{all_disable}; + } +} + +sub update_wl { + return if $BLOCK_ALL; + remake(); + + Irssi::statusbar_items_redraw(set $_) for keys %statusbars; + + unless ($VIEWER_MODE) { + Irssi::timeout_add_once(100, 'syncLines', undef); + } + else { + syncViewer(); + } +} + +sub screenFullRedraw { + my ($window) = @_; + if (!ref $window or $window->{refnum} == Irssi::active_win->{refnum}) { + $viewer{fullRedraw} = 1 if $viewer{client}; + $settings_str = ''; + &setup_changed; + } +} + +sub restartViewerServer { + if ($VIEWER_MODE) { + stop_viewer(); + start_viewer(); + } +} + +sub _simple_quote { + my @r = map { + my $x = $_; + $x =~ s/'/'"'"'/g; + $x = "'$x'"; + } @_; + wantarray ? @r : shift @r +} + +sub _viewer_command_replace_format { + my ($ecmd, @args) = @_; + my $file = _simple_quote(SCRIPT_FILE()); + my $path = _simple_quote($viewer{path}); + my @env; + for my $env (shellwords($S{viewer_launch_env})) { + if ($env =~ /^(\w+)(?:=(.*))$/) { + push @env, "AWL_$1=$2" + } + } + my $cmd = join ' ', + (@env ? ('env', _simple_quote(@env)) : ()), + 'perl', $file, '-1', _simple_quote(@args), $path; + $ecmd =~ s{%(%|\w+)}{ + my $sub = $1; + if ($sub eq '%') { + '%' + } + elsif ($sub =~ /^(q*)A(.*)/) { + my $ret = $cmd; + for (1..length $1) { + $ret = _simple_quote($ret); + } + "$ret$2" + } + else { + "%$sub" + } + }gex; + $ecmd +} + +sub start_viewer { + unlink $viewer{path} if -S $viewer{path} || -p _; + + $viewer{server} = IO::Socket::UNIX->new( + Type => SOCK_STREAM, + Local => $viewer{path}, + Listen => 1 + ); + unless ($viewer{server}) { + $viewer{msg} = "Viewer: $!"; + $viewer{retry} = Irssi::timeout_add_once(5000, 'retry_viewer', 1); + return; + } + $viewer{server}->blocking(0); + set_viewer_mode_hint(); + $viewer{server_tag} = Irssi::input_add($viewer{server}->fileno, INPUT_READ, 'vi_connected', undef); + + if ($S{viewer_launch}) { + if (length $ENV{TMUX_PANE} && length $ENV{TMUX} && lc $S{viewer_tmux_position} ne 'custom') { + my $cmd = _viewer_command_replace_format('%qA', '-p', lc $S{viewer_tmux_position}); + Irssi::command("exec - tmux neww -d $cmd 2>&1 &"); + } + elsif (length $ENV{WINDOWID} && length $ENV{DISPLAY} && length $S{viewer_xwin_command} && $S{viewer_xwin_command} =~ /\S/) { + my $cmd = _viewer_command_replace_format($S{viewer_xwin_command}); + Irssi::command("exec - $cmd 2>&1 &"); + } + elsif (length $S{viewer_custom_command} && $S{viewer_custom_command} =~ /\S/) { + my $cmd = _viewer_command_replace_format($S{viewer_custom_command}); + Irssi::command("exec - $cmd 2>&1 &"); + } + } +} + +sub set_viewer_mode_hint { + return unless $viewer{server}; + if ($S{no_mode_hint}) { + $viewer{msg} = undef; + } + else { + my ($name) = __PACKAGE__ =~ /::([^:]+)$/; + $viewer{msg} = "Run $name from the shell or switch to sbar mode"; + } +} + +sub retry_viewer { + start_viewer(); +} + +sub vi_close_client { + Irssi::input_remove(delete $viewer{client_tag}) if exists $viewer{client_tag}; + $viewer{client}->close if $viewer{client}; + delete $viewer{client}; + delete $viewer{client_keymap}; + delete $viewer{client_settings}; + delete $viewer{client_env}; + delete $viewer{fullRedraw}; +} + +sub vi_connected { + vi_close_client(); + $viewer{client} = $viewer{server}->accept or return; + $viewer{client}->blocking(0); + $viewer{client_tag} = Irssi::input_add($viewer{client}->fileno, INPUT_READ, 'vi_clientinput', undef); + syncViewer(); +} + +use constant VIEWER_BLOCK_SIZE => 1024; +sub vi_clientinput { + if ($viewer{client}->read(my $buf, VIEWER_BLOCK_SIZE)) { + $viewer{rcvbuf} .= $buf; + if ($viewer{rcvbuf} =~ s/^(?:(active|\d+)|(last|up|down))\n//igm) { + if (defined $2) { + Irssi::command("window $2"); + } + elsif (lc $1 eq 'active' && $viewer{use_ack}) { + Irssi::command("ack"); + } + else { + Irssi::command("window goto $1"); + } + } + } + else { + vi_close_client(); + Irssi::timeout_add_once(100, 'syncViewer', undef); + } +} + +sub stop_viewer { + Irssi::timeout_remove(delete $viewer{retry}) if exists $viewer{retry}; + vi_close_client(); + Irssi::input_remove(delete $viewer{server_tag}) if exists $viewer{server_tag}; + return unless $viewer{server}; + $viewer{server}->close; + delete $viewer{server}; +} +sub _encode_var { + my $str; + while (@_) { + my ($name, $var) = splice @_, 0, 2; + my $type = ref $var ? $var =~ /HASH/ ? 'map' : $var =~ /ARRAY/ ? 'list' : '' : ''; + $str .= "\n\U$name$type\_begin\n"; + if ($type eq 'map') { + no warnings 'numeric'; + $str .= " $_\n ${$var}{$_}\n" for sort { $a <=> $b || $a cmp $b } keys %$var; + } + elsif ($type eq 'list') { + $str .= " $_\n" for @$var; + } + else { + $str .= " $var\n"; + } + $str .= "\U$name$type\_end\n"; + } + $str +} +sub syncViewer { + if ($viewer{client}) { + @actString = (); + if ($currentLines) { + killOldStatus(); + $currentLines = 0; + } + my $str; + unless ($viewer{client_keymap}) { + $str .= _encode_var('key', +{ %nummap, %specialmap }); + $viewer{client_keymap} = 1; + } + unless ($viewer{client_settings}) { + $str .= _encode_var( + block => $S{block}, + ha => $S{height_adjust}, + mc => $S{maxcolumns}, + ml => $S{maxlines}, + ); + $viewer{client_settings} = 1; + } + unless ($viewer{client_env}) { + $str .= _encode_var(irssienv => +{ + length $ENV{TMUX_PANE} && length $ENV{TMUX} ? + (tmux_pane => $ENV{TMUX_PANE}, + tmux_srv => $ENV{TMUX}) : (), + length $ENV{WINDOWID} ? + (xwinid => $ENV{WINDOWID}) : (), + }); + $viewer{client_env} = 1; + } + my $separator = _get_format(set 'separator'); + my $sepLen = sb_length($separator); + my $item_bg = _get_format(set 'viewer_item_bg'); + my $title = _get_format(set 'title'); + if (length $title) { + $title =~ s{\\(.)|(.)}{ + defined $2 ? quotemeta $2 + : $1 eq 'V' ? '\U' + : $1 eq ':' ? quotemeta '%N' + : $1 =~ /^[uUFQE]$/ ? "\\$1" + : quotemeta "\\$1" + }sge; + $title = eval qq{"$title"}; + } + $str .= _encode_var(redraw => 1) if delete $viewer{fullRedraw}; + $str .= _encode_var(separator => $separator, + seplen => $sepLen, + itembg => $item_bg, + title => $title, + mouse => $mouse_coords{refnum}, + key2 => \%wnmap_exp, + win => \@win_items); + + my $was = $viewer{client}->blocking(1); + $viewer{client}->print($str); + $viewer{client}->blocking($was); + } + elsif ($viewer{server}) { + if (defined $viewer{msg}) { + @actString = ((uc setc()).": $viewer{msg}"); + } + else { + @actString = (); + } + } + elsif (defined $viewer{msg}) { + @actString = ((uc setc()).": $viewer{msg}"); + } + if (@actString) { + Irssi::timeout_add_once(100, 'syncLines', undef); + } + elsif ($currentLines) { + killOldStatus(); + $currentLines = 0; + } +} + +sub reset_awl { + Irssi::timeout_remove($shade_line_timer) if $shade_line_timer; $shade_line_timer = undef; + my $was_sort = $S{sort} // ''; + my $was_xform = $S{xform} // ''; + my $was_shared = $S{shared_sbar}; + my $was_no_hint = $S{no_mode_hint}; + %S = ( + sort => Irssi::settings_get_str( set 'sort'), + fancy_abbrev => Irssi::settings_get_str('fancy_abbrev'), + xform => Irssi::settings_get_str( set 'custom_xform'), + block => Irssi::settings_get_int( set 'block'), + banned_on => Irssi::settings_get_bool('banned_channels_on'), + prefer_name => Irssi::settings_get_bool(set 'prefer_name'), + hide_data => Irssi::settings_get_int( set 'hide_data'), + hide_name => Irssi::settings_get_int( set 'hide_name_data'), + hide_empty => Irssi::settings_get_int( set 'hide_empty'), + sbar_maxlen => Irssi::settings_get_bool(set 'sbar_maxlength'), + placement => Irssi::settings_get_str( set 'placement'), + position => Irssi::settings_get_int( set 'position'), + maxlines => Irssi::settings_get_int( set 'maxlines'), + maxcolumns => Irssi::settings_get_int( set 'maxcolumns'), + all_disable => Irssi::settings_get_bool(set 'all_disable'), + height_adjust => Irssi::settings_get_int( set 'height_adjust'), + mouse_offset => Irssi::settings_get_int( set 'mouse_offset'), + mouse_scroll => Irssi::settings_get_int( 'mouse_scroll'), + mouse_escape => Irssi::settings_get_int( 'mouse_escape'), + line_shade => Irssi::settings_get_time(set 'last_line_shade'), + no_mode_hint => Irssi::settings_get_bool(set 'no_mode_hint'), + viewer_launch => Irssi::settings_get_bool(set 'viewer_launch'), + viewer_launch_env => Irssi::settings_get_str(set 'viewer_launch_env'), + viewer_xwin_command => Irssi::settings_get_str(set 'viewer_xwin_command'), + viewer_custom_command => Irssi::settings_get_str(set 'viewer_custom_command'), + viewer_tmux_position => Irssi::settings_get_str(set 'viewer_tmux_position'), + ); + $S{fancy_strict} = $S{fancy_abbrev} =~ /^strict/i; + $S{fancy_head} = $S{fancy_abbrev} =~ /^head/i; + my $shared = Irssi::settings_get_str(set 'shared_sbar'); + if ($shared =~ /^(\d+)([<])(\d+)$/) { + $S{shared_sbar} = [$1, $2, $3]; + } + else { + Irssi::settings_set_str(set 'shared_sbar', 'OFF'); + $S{shared_sbar} = undef; + } + lock_keys(%S); + if ($was_sort ne $S{sort}) { + $print_text_activity = undef; + my @sort_order = grep { @$_ > 4 } map { + s/^\s*//; + my $reverse = s/^\W*\K[-!]//; + my $undef_check = s/^\W*\K~// ? 1 : undef; + my $equal_check = s/=(.*)\s?$// ? $1 : undef; + s/\s*$//; + my $ignore_case = s/#i$// ? 1 : undef; + + $print_text_activity = 1 if $_ eq 'last_line'; + + my @path = split '/'; + my $class_check = @path && $path[-1] =~ s/(::.*)$// ? $1 : undef; + + [ $reverse ? -1 : 1, $undef_check, $equal_check, $class_check, $ignore_case, @path ] + } "$S{sort}," =~ /([^+,]*|[^+,]*=[^,]*?\s(?=\+)|[^+,]*=[^,]*)[+,]/g; + $window_sort_func = sub { + no warnings qw(numeric uninitialized); + for my $so (@sort_order) { + my @x = map { + my $ret = 0; + $_ = lc1459($_) if defined $_ && !ref $_ && $so->[4]; + $ret = $_ eq ($so->[4] ? lc1459($so->[2]) : $so->[2]) ? 1 : -1 if defined $so->[2]; + $ret = defined $_ ? ($ret || -3) : 3 if $so->[1]; + $ret = ref $_ && $_->isa('Irssi'.$so->[3]) ? 2 : ($ret || -2) if $so->[3]; + -$ret || $_ + } + map { + reduce { return unless ref $a; $a->{$b} } $_, @{$so}[5..$#$so] + } $a, $b; + return ((($x[0] <=> $x[1] || $x[0] cmp $x[1]) * $so->[0]) || next); + } + return ($a->{refnum} <=> $b->{refnum}); + }; + } + if ($was_xform ne $S{xform}) { + if ($S{xform} !~ /\S/) { + $custom_xform = undef; + } + else { + my $script_pkg = __PACKAGE__ . '::custom_xform'; + local $@; + $custom_xform = eval qq{ +package $script_pkg; +use strict; +no warnings; +our (\$QUERY, \$CHANNEL, \$TAG, \$NAME); +return sub { +# line 1 @{[ set 'custom_xform' ]}\n$S{xform}\n}}; + if ($@) { + $@ =~ /^(.*)/; + print '%_'.(set 'custom_xform').'%_ did not compile: '.$1; + } + } + } + + my $new_settings = join "\n", $VIEWER_MODE + ? ("\\", $S{block}, $S{height_adjust}, $S{maxlines}, $S{maxcolumns}) + : ("!", $S{placement}, $S{position}); + + if ($settings_str ne $new_settings) { + @actString = (); + %abbrev_cache = (); + $currentLines = 0; + killOldStatus(); + delete $viewer{client_settings}; + $settings_str = $new_settings; + } + + my $was_mouse_mode = $MOUSE_ON; + if ($MOUSE_ON = Irssi::settings_get_bool(set 'mouse') and !$was_mouse_mode) { + install_mouse(); + } + elsif ($was_mouse_mode and !$MOUSE_ON) { + uninstall_mouse(); + } + + my $path = Irssi::settings_get_str(set 'path'); + my $was_viewer_mode = $VIEWER_MODE; + if ($was_viewer_mode && + defined $viewer{path} && $viewer{path} ne $path) { + stop_viewer(); + $was_viewer_mode = 0; + } + elsif ($was_viewer_mode && $S{no_mode_hint} != $was_no_hint + 0) { + set_viewer_mode_hint(); + } + $viewer{path} = $path; + if ($VIEWER_MODE = Irssi::settings_get_bool(set 'viewer') and !$was_viewer_mode) { + start_viewer(); + } + elsif ($was_viewer_mode and !$VIEWER_MODE) { + stop_viewer(); + } + + %banned_channels = map { lc1459(to_uni($_)) => undef } + split ' ', Irssi::settings_get_str('banned_channels'); + + my @sb_base = split /\177/, sb_format_expand("{sbstart}{sb \177}{sbend}"), 2; + $sb_base_width_pre = sb_length($sb_base[0]); + $sb_base_width_post = max 0, sb_length($sb_base[1])-1; + $sb_base_width = $sb_base_width_pre + $sb_base_width_post; + + if ($print_text_activity && $S{line_shade}) { + $shade_line_timer = Irssi::timeout_add(max(10 * GLOB_QUEUE_TIMER, 100*$S{line_shade}**(1/3)), 'wl_changed', undef); + } + + $CHANGED{AWINS} = 1; +} + +sub stop_mouse_tracking { + print STDERR "\e[?1005l\e[?1000l"; +} +sub start_mouse_tracking { + print STDERR "\e[?1000h\e[?1005h"; +} +sub install_mouse { + Irssi::command_bind('mouse_xterm' => 'mouse_xterm'); + Irssi::command('^bind meta-[M command mouse_xterm'); + Irssi::signal_add_first('gui key pressed' => 'mouse_key_hook'); + start_mouse_tracking(); +} +sub uninstall_mouse { + stop_mouse_tracking(); + Irssi::signal_remove('gui key pressed' => 'mouse_key_hook'); + Irssi::command('^bind -delete meta-[M'); + Irssi::command_unbind('mouse_xterm' => 'mouse_xterm'); +} + +sub awl_mouse_event { + return if $VIEWER_MODE; + if ((($_[0] == 3 and $_[3] == 0) + || $_[0] == 64 || $_[0] == 65) and + $_[1] == $_[4] and $_[2] == $_[5]) { + my $top = lc $S{placement} eq 'top'; + my ($pos, $line) = @_[1 .. 2]; + unless ($top) { + $line -= $screenHeight; + $line += $currentLines; + $line += $S{mouse_offset}; + } + else { + $line -= $S{mouse_offset}; + } + $pos -= $sb_base_width_pre; + return if $line < 0 || $line >= $currentLines; + if ($_[0] == 64) { + Irssi::command('window up'); + } + elsif ($_[0] == 65) { + Irssi::command('window down'); + } + elsif (exists $mouse_coords{$line}{$pos}) { + my $win = $mouse_coords{$line}{$pos}; + Irssi::command('window ' . $win); + } + Irssi::signal_stop; + } +} + +sub mouse_scroll_event { + return unless $S{mouse_scroll}; + if (($_[3] == 64 or $_[3] == 65) and + $_[0] == $_[3] and $_[1] == $_[4] and $_[2] == $_[5]) { + my $cmd = 'scrollback goto ' . ($_[3] == 64 ? '-' : '+') . $S{mouse_scroll}; + Irssi::active_win->command($cmd); + Irssi::signal_stop; + } + elsif ($_[0] == 64 or $_[0] == 65) { + Irssi::signal_stop; + } +} + +sub mouse_escape { + return unless $S{mouse_escape} > 0; + if ($_[0] == 3) { + my $tm = $S{mouse_escape}; + $tm *= 1000 if $tm < 1000; + stop_mouse_tracking(); + Irssi::timeout_add_once($tm, 'start_mouse_tracking', undef); + Irssi::signal_stop; + } +} + +sub UNLOAD { + @actString = (); + killOldStatus(); + stop_viewer() if $VIEWER_MODE; + uninstall_mouse() if $MOUSE_ON; +} + +sub addPrintTextHook { # update on print text + return unless defined $^S; + return if $BLOCK_ALL; + return unless $print_text_activity; + return if $_[0]->{level} == 262144 and $_[0]->{target} eq '' + and !defined($_[0]->{server}); + &wl_changed; +} + +sub block_event_window_change { + Irssi::signal_stop; +} + +sub update_awins { + my @wins = Irssi::windows; + local $BLOCK_ALL = 1; + Irssi::signal_add_first('window changed' => 'block_event_window_change'); + my $bwin = + my $awin = Irssi::active_win; + my $lwin; + my $defer_irssi_broken_last; + unless ($wins[0]{refnum} == $awin->{refnum}) { + # special case: more than 1 last win, so /win last; + # /win last doesn't come back to the current window. eg. after + # connect & autojoin; we can't handle this situation, bail out + $defer_irssi_broken_last = 1; + } + else { + $awin->command('window last'); + $lwin = Irssi::active_win; + $lwin->command('window last'); + $defer_irssi_broken_last = $lwin->{refnum} == $bwin->{refnum}; + } + my $awin_counter = 0; + Irssi::signal_remove('window changed' => 'block_event_window_change'); + unless ($defer_irssi_broken_last) { + # we need to keep the fe-windows code running here + Irssi::signal_add_priority('window changed' => 'block_event_window_change', -99); + %awins = %wnmap_exp = (); + do { + Irssi::active_win->command('window up'); + $awin = Irssi::active_win; + $awins{$awin->{refnum}} = undef; + ++$awin_counter; + } until ($awin->{refnum} == $bwin->{refnum} || $awin_counter >= @wins); + Irssi::signal_remove('window changed' => 'block_event_window_change'); + + Irssi::signal_add_first('window changed' => 'block_event_window_change'); + for my $key (keys %wnmap) { + next unless Irssi::window_find_name($key) || Irssi::window_find_item($key); + $awin->command("window goto $key"); + my $cwin = Irssi::active_win; + $wnmap_exp{ $cwin->{refnum} } = $wnmap{$key}; + $cwin->command('window last') + if $cwin->{refnum} != $awin->{refnum}; + } + for my $win (reverse @wins) { # restore original window order + Irssi::active_win->command('window '.$win->{refnum}); + } + $awin->command('window '.$lwin->{refnum}); # restore last win + Irssi::active_win->command('window last'); + Irssi::signal_remove('window changed' => 'block_event_window_change'); + } + $CHANGED{WL} = 1; +} + +sub resizeTerm { + if (defined (my $r = `stty size 2>/dev/null`)) { + ($screenHeight, $screenWidth) = split ' ', $r; + $CHANGED{SETUP} = 1; + } + else { + $CHANGED{SIZE} = 1; + } +} + +sub awl_refresh { + $globTime = undef; + resizeTerm() if delete $CHANGED{SIZE}; + reset_awl() if delete $CHANGED{SETUP}; + update_awins() if delete $CHANGED{AWINS}; + update_wl() if delete $CHANGED{WL}; +} + +sub termsize_changed { $CHANGED{SIZE} = 1; &queue_refresh; } +sub setup_changed { $CHANGED{SETUP} = 1; &queue_refresh; } +sub awins_changed { $CHANGED{AWINS} = 1; &queue_refresh; } +sub wl_changed { $CHANGED{WL} = 1; &queue_refresh; } + +sub window_changed { + &awins_changed if $_[1]; +} + +sub queue_refresh { + return if $BLOCK_ALL; + Irssi::timeout_remove($globTime) + if defined $globTime; # delay the update further + $globTime = Irssi::timeout_add_once(GLOB_QUEUE_TIMER, 'awl_refresh', undef); +} + +sub awl_init { + termsize_changed(); + update_keymap(); +} + +sub runsub { + my $cmd = shift; + sub { + my ($data, $server, $item) = @_; + Irssi::command_runsub($cmd, $data, $server, $item); + }; +} + +Irssi::signal_register({ + 'gui mouse' => [qw/int int int int int int/], + }); +{ my $broken_expandos = (Irssi::version >= 20081128 && Irssi::version < 20110210) + ? sub { my $x = shift; $x =~ s/\$\{cumode_space\}/ /; $x } : undef; + Irssi::theme_register([ + map { $broken_expandos ? $broken_expandos->($_) : $_ } + set 'display_nokey' => '$N${cumode_space}$H$C$S', + set 'display_key' => '$Q${cumode_space}$H$C$S', + set 'display_nokey_visible' => '%2$N${cumode_space}$H$C$S', + set 'display_key_visible' => '%2$Q${cumode_space}$H$C$S', + set 'display_nokey_active' => '%1$N${cumode_space}$H$C$S', + set 'display_key_active' => '%1$Q${cumode_space}$H$C$S', + set 'display_header' => '%8$C|${N}', + set 'name_display' => '$0', + set 'separator' => ' ', + set 'separator2' => '', + set 'abbrev_chars' => "~\x{301c}", + set 'viewer_item_bg' => sb_format_expand('{sb_background}'), + set 'title' => '\V'.setc().'\:', + ]); +} +Irssi::settings_add_bool(setc, set 'prefer_name', 0); # +Irssi::settings_add_int( setc, set 'hide_empty', 0); # +Irssi::settings_add_int( setc, set 'hide_data', 0); # +Irssi::settings_add_int( setc, set 'hide_name_data', 0); # +Irssi::settings_add_int( setc, set 'maxlines', 9); # +Irssi::settings_add_int( setc, set 'maxcolumns', 4); # +Irssi::settings_add_int( setc, set 'block', 15); # +Irssi::settings_add_bool(setc, set 'sbar_maxlength', 1); # +Irssi::settings_add_int( setc, set 'height_adjust', 2); # +Irssi::settings_add_str( setc, set 'sort', 'refnum'); # +Irssi::settings_add_str( setc, set 'placement', 'bottom'); # +Irssi::settings_add_int( setc, set 'position', 0); # +Irssi::settings_add_bool(setc, set 'all_disable', 1); # +Irssi::settings_add_bool(setc, set 'viewer', 1); # +Irssi::settings_add_str( setc, set 'shared_sbar', 'OFF'); # +Irssi::settings_add_bool(setc, set 'mouse', 0); # +Irssi::settings_add_str( setc, set 'path', Irssi::get_irssi_dir . '/_windowlist'); # +Irssi::settings_add_str( setc, set 'custom_xform', ''); # +Irssi::settings_add_time(setc, set 'last_line_shade', '0'); # +Irssi::settings_add_int( setc, set 'mouse_offset', 1); # +Irssi::settings_add_int( setc, 'mouse_scroll', 3); # +Irssi::settings_add_int( setc, 'mouse_escape', 1); # +Irssi::settings_add_str( setc, 'banned_channels', ''); +Irssi::settings_add_bool(setc, 'banned_channels_on', 1); +Irssi::settings_add_str( setc, 'fancy_abbrev', 'fancy'); # +Irssi::settings_add_bool(setc, set 'no_mode_hint', 0); # +Irssi::settings_add_bool(setc, set 'viewer_launch', 1); # +Irssi::settings_add_str( setc, set 'viewer_launch_env', ''); # +Irssi::settings_add_str( setc, set 'viewer_tmux_position', 'left'); # +Irssi::settings_add_str( setc, set 'viewer_xwin_command', 'xterm +sb -e %A'); # +Irssi::settings_add_str( setc, set 'viewer_custom_command', ''); # + +Irssi::signal_add_last({ + 'setup changed' => 'setup_changed', + 'print text' => 'addPrintTextHook', + 'terminal resized' => 'termsize_changed', + 'setup reread' => 'screenFullRedraw', + 'window hilight' => 'wl_changed', + 'command format' => 'wl_changed', +}); +Irssi::signal_add({ + 'window changed' => 'window_changed', + 'window item changed' => 'wl_changed', + 'window changed automatic' => 'window_changed', + 'window created' => 'awins_changed', + 'window destroyed' => 'awins_changed', + 'window name changed' => 'wl_changed', + 'window refnum changed' => 'wl_changed', +}); +Irssi::signal_add_last('gui mouse' => 'mouse_escape'); +Irssi::signal_add_last('gui mouse' => 'mouse_scroll_event'); +Irssi::signal_add_last('gui mouse' => 'awl_mouse_event'); +Irssi::command_bind( setc() => runsub(setc()) ); +Irssi::command_bind( setc() . ' redraw' => 'screenFullRedraw' ); +Irssi::command_bind( setc() . ' restart' => 'restartViewerServer' ); + +{ + my $l = set 'shared'; + { + no strict 'refs'; + *{$l} = $awl_shared_empty; + } + Irssi::statusbar_item_register($l, '$0', $l); +} + +awl_init(); + +# Mouse script based on irssi mouse patch by mirage +{ my $mouse_status = -1; # -1:off 0,1,2:filling mouse_combo + my @mouse_combo; # 0:button 1:x 2:y + my @mouse_previous; # previous contents of mouse_combo + + sub mouse_xterm_off { + $mouse_status = -1; + } + sub mouse_xterm { + $mouse_status = 0; + Irssi::timeout_add_once(10, 'mouse_xterm_off', undef); + } + + sub mouse_key_hook { + my ($key) = @_; + if ($mouse_status != -1) { + if ($mouse_status == 0) { + @mouse_previous = @mouse_combo; + #if @mouse_combo && $mouse_combo[0] < 64; + } + $mouse_combo[$mouse_status] = $key - 32; + $mouse_status++; + if ($mouse_status == 3) { + $mouse_status = -1; + # match screen coordinates + $mouse_combo[1]--; + $mouse_combo[2]--; + Irssi::signal_emit('gui mouse', @mouse_combo[0 .. 2], @mouse_previous[0 .. 2]); + } + Irssi::signal_stop; + } + } +} + +sub string_LCSS { + my $str = join "\0", @_; + (sort { length $b <=> length $a } $str =~ /(?=(.+).*\0.*\1)/g)[0] +} + +# workaround for issue #271 +{ package Irssi::Nick } + +# workaround for issue #572 +@Irssi::UI::Exec::ISA = 'Irssi::Windowitem' + if Irssi::version >= 20140822 && Irssi::version <= 20161101 && !@Irssi::UI::Exec::ISA; + +UNITCHECK +{ package AwlViewer; + use strict; + use warnings; + no warnings 'redefine'; + use Encode; + use IO::Socket::UNIX; + use IO::Select; + use List::Util qw(max); + use constant BLOCK_SIZE => 1024; + use constant RECONNECT_TIME => 5; + + my $sockpath; + + our $VERSION = '0.8'; + + our ($got_int, $resized, $timeout); + + my %vars; + my (%c2w, @seqlist); + my %mouse_coords; + my (@mouse, @last_mouse); + my ($err, $sock, $loop); + my ($keybuf, $rcvbuf); + my @screen; + my ($screenHeight, $screenWidth); + my ($disp_update, $fs_open, $one_shot_integration, $one_shot_resize); + my $integration_position; + my $show_title_bar; + + sub connect_it { + $sock = IO::Socket::UNIX->new( + Type => SOCK_STREAM, + Peer => $sockpath, + ); + unless ($sock) { + $err = $!; + return; + } + $sock->blocking(0); + $loop->add($sock); + } + + sub remove_conn { + my $fh = shift; + $loop->remove($fh); + $fh->close; + $sock = undef; + %vars = (); + @screen = (); + } + + { package Terminfo; # xterm + sub civis { "\e[?25l" } + sub sc { "\e7" } + sub cup { "\e[" . ($_[0] + 1) . ';' . ($_[1] + 1) . 'H' } + sub el { "\e[K" } + sub rc { "\e8" } + sub cnorm { "\e[?25h" } + sub setab { "\e[4" . $_[0] . 'm' } + sub setaf { "\e[3" . $_[0] . 'm' } + sub setaf16 { "\e[9" . $_[0] . 'm' } + sub setab16 { "\e[10" . $_[0] . 'm' } + sub setaf256 { "\e[38;5;" . $_[0] . 'm' } + sub setab256 { "\e[48;5;" . $_[0] . 'm' } + sub sgr0 { "\e[0m" } + sub bold { "\e[1m" } + sub it { "\e[3m" } + sub ul { "\e[4m" } + sub blink { "\e[5m" } + sub rev { "\e[7m" } + sub op { "\e[39;49m" } + sub exit_bold { "\e[22m" } + sub exit_it { "\e[23m" } + sub exit_ul { "\e[24m" } + sub exit_blink { "\e[25m" } + sub exit_rev { "\e[27m" } + sub smcup { "\e[?1049h" } + sub rmcup { "\e[?1049l" } + sub smmouse { "\e[?1000h\e[?1005h" } + sub rmmouse { "\e[?1005l\e[?1000l" } + } + + sub init { + $sockpath = shift // "$ENV{HOME}/.irssi/_windowlist"; + STDOUT->autoflush(1); + printf "\r%swaiting for %s...", Terminfo::sc, $::IRSSI{name}; + + `stty -icanon -echo`; + + $loop = IO::Select->new; + STDIN->blocking(0); + $loop->add(\*STDIN); + + $SIG{INT} = sub { + $got_int = 1 + }; + $SIG{WINCH} = sub { + $resized = 1 + }; + + $resized = 3; + + $disp_update = 2; + + $show_title_bar = 1; + } + + sub enter_fs { + return if $fs_open; + safe_print(Terminfo::rc, Terminfo::smcup, Terminfo::civis, Terminfo::smmouse); + $fs_open = 1; + } + + sub leave_fs { + return unless $fs_open; + safe_print(Terminfo::rmmouse, Terminfo::cnorm, Terminfo::rmcup); + safe_print(sprintf "\r%swaiting for %s...", Terminfo::sc, $::IRSSI{name}) if $_[0]; + + $fs_open = 0; + } + + sub end_prog { + leave_fs(); + STDIN->blocking(1); + `stty sane`; + printf "\r%s%sthanks for using %s\n", Terminfo::rc, Terminfo::el, $::IRSSI{name}; + } + + sub safe_print { + my $st = STDIN->blocking(1); + print @_; + STDIN->blocking($st); + } + + sub safe_qx { + my $st = STDIN->blocking(1); + my $ret = `$_[0]`; + STDIN->blocking($st); + $ret + } + + sub safe_print_sock { + return unless $sock; + my $was = $sock->blocking(1); + $sock->print(@_); + $sock->blocking($was); + } + + sub process_recv { + my $need = 0; + while ($rcvbuf =~ s/\n(.+)_BEGIN\n((?: .*\n)*)\1_END\n//) { + my $var = lc $1; + my $data = $2; + my @data = split "\n ", "\n$data ", -1; + shift @data; pop @data; + my $itembg = $vars{itembg}; + if ($var =~ s/list$//) { + $vars{$var} = \@data; + } + elsif ($var =~ s/map$//) { + $vars{$var} = +{ @data }; + } + else { + $vars{$var} = join "\n", @data; + } + $need = 1 if $var eq 'win'; + $need = 1 if $var eq 'redraw' && $vars{$var}; + if (($itembg//'') ne ($vars{itembg}//'')) { + $need = $vars{redraw} = 1; + } + _build_keymap() if $var eq 'key2'; + } + $need + } + + { my %ansi_table; + my ($i, $j, $k) = (0, 0, 0); + my %term_state; + sub reset_term_state { my %old_term = %term_state; %term_state = (); %old_term } + sub set_term_state { my %old_term = %term_state; %term_state = @_; %old_term } + %ansi_table = ( + # fe-common::core::formats.c:format_expand_styles + (map { my $t = $i++; ($_ => sub { my $n = $term_state{hicolor} ? \&Terminfo::setab16 : \&Terminfo::setab; + $n->($t) }) } (split //, '01234567' )), + (map { my $t = $j++; ($_ => sub { my $n = $term_state{hicolor} ? \&Terminfo::setaf16 : \&Terminfo::setaf; + $n->($t) }) } (split //, 'krgybmcw' )), + (map { my $t = $k++; ($_ => sub { my $n = $term_state{hicolor} ? \&Terminfo::setaf : \&Terminfo::setaf16; + $n->($t) }) } (split //, 'KRGYBMCW')), + # reset + n => sub { $term_state{hicolor} = 0; my $r = Terminfo::op; + for (qw(blink rev bold)) { + $r .= Terminfo->can("exit_$_")->() if delete $term_state{$_}; + } + { + local $ansi_table{n} = $ansi_table{N}; + $r .= formats_to_ansi_basic($vars{itembg}); + } + $r + }, + N => sub { reset_term_state(); Terminfo::sgr0 }, + # flash/bright + F => sub { my $n = 'blink'; my $e = ($term_state{$n} ^= 1) ? $n : "exit_$n"; Terminfo->can($e)->() }, + # reverse + 8 => sub { my $n = 'rev'; my $e = ($term_state{$n} ^= 1) ? $n : "exit_$n"; Terminfo->can($e)->() }, + # bold + "_" => sub { my $n = 'bold'; my $e = ($term_state{$n} ^= 1) ? $n : "exit_$n"; Terminfo->can($e)->() }, + # underline + U => sub { my $n = 'ul'; my $e = ($term_state{$n} ^= 1) ? $n : "exit_$n"; Terminfo->can($e)->() }, + # italic + I => sub { my $n = 'it'; my $e = ($term_state{$n} ^= 1) ? $n : "exit_$n"; Terminfo->can($e)->() }, + # bold, used as colour modifier if AWL_HI9 is set + 9 => $ENV{AWL_HI9} ? sub { $term_state{hicolor} ^= 1; '' } + : sub { my $n = 'bold'; my $e = ($term_state{$n} ^= 1) ? $n : "exit_$n"; Terminfo->can($e)->() }, + # delete other stuff + (map { $_ => sub { '' } } (split //, ':|>#[')), + # escape + (map { my $close = $_; $_ => sub { $close } } (split //, '{}%')), + ); + for my $base (0 .. 15) { + my $close = $base; + my $idx = ($close&8) | ($close&4)>>2 | ($close&2) | ($close&1)<<2; + $ansi_table{ (sprintf "x0%x", $close) } = + $ansi_table{ (sprintf "x0%X", $close) } = + sub { Terminfo::setab256($idx) }; + $ansi_table{ (sprintf "X0%x", $close) } = + $ansi_table{ (sprintf "X0%X", $close) } = + sub { Terminfo::setaf256($idx) }; + } + for my $plane (1 .. 6) { + for my $coord (0 .. 35) { + my $close = 16 + ($plane-1) * 36 + $coord; + my $ch = $coord < 10 ? $coord : chr( $coord - 10 + ord 'a' ); + $ansi_table{ "x$plane$ch" } = + $ansi_table{ "x$plane\U$ch" } = + sub { Terminfo::setab256($close) }; + $ansi_table{ "X$plane$ch" } = + $ansi_table{ "X$plane\U$ch" } = + sub { Terminfo::setaf256($close) }; + } + } + for my $gray (0 .. 23) { + my $close = 232 + $gray; + my $ch = chr( $gray + ord 'a' ); + $ansi_table{ "x7$ch" } = + $ansi_table{ "x7\U$ch" } = + sub { Terminfo::setab256($close) }; + $ansi_table{ "X7$ch" } = + $ansi_table{ "X7\U$ch" } = + sub { Terminfo::setaf256($close) }; + } + sub formats_to_ansi_basic { + my $o = shift; + $o =~ s/(%(X..|x..|.))/exists $ansi_table{$2} ? $ansi_table{$2}->() : $1/gex; + $o + } + } + + sub _header { + my $str = $vars{title} // uc ::setc(); + my $ccs = qr/%(?:X(?:[1-6][0-9A-Z]|7[A-X])|[0-9BCFGIKMNRUWY_])/i; + (my $stripstr = $str) =~ s/($ccs)//g; + my $space = int( ((abs $vars{block}) - length $stripstr) / (1 + length $stripstr)); + if ($space > 0) { + my $ss = ' ' x $space; + my @x = $str =~ /((?:$ccs)*\X(?:(?:$ccs)*$)?)/g; + $str = join $ss, '', @x, ''; + } + ($stripstr = $str) =~ s/($ccs)//g; + my $pad = max 0, (abs $vars{block}) - length $stripstr; + $str = ' ' x ($pad/2) . $str . ' ' x ($pad/2 + $pad%2); + $str + } + + sub _add_item { + my ($i, $j, $c, $wi, $screen, $mouse) = @_; + $screen->[$i][$j] = "%N%n$wi"; + if (exists $vars{mouse}{$c - 1}) { + $mouse->[$i][$j] = $vars{mouse}{$c - 1}; + } + } + sub update_screen { + $disp_update = 0; + unless ($sock && exists $vars{seplen} && exists $vars{block}) { + leave_fs(1); + return; + } + enter_fs(); + @screen = () if delete $vars{redraw}; + %mouse_coords = (); + my $ncols = ($vars{seplen} + abs $vars{block}) ? + int( ($screenWidth + $vars{seplen}) / ($vars{seplen} + abs $vars{block}) ) : 0; + my $xenl = ($vars{seplen} + abs $vars{block}) + && $ncols > int( ($screenWidth + $vars{seplen} - 1) / ($vars{seplen} + abs $vars{block}) ); + my $nrows = $screenHeight - $vars{ha}; + my @wi = @{$vars{win}//[]}; + my $max_items = $ncols * $nrows; + my $c = $show_title_bar ? 1 : 0; + my $items = @wi + $c; + my $titems = $items > $max_items ? $max_items : $items; + my $i = 0; + my $j = 0; + my @new_screen; + my @new_mouse; + $new_screen[0][0] = _header() #. ' ' x $vars{seplen} + if $show_title_bar; + unless ($nrows > $ncols) { # line layout + ++$j if $show_title_bar; + for my $wi (@wi) { + if ($j >= $ncols) { + $j = 0; + ++$i; + } + last if $i >= $nrows; + _add_item($i, $j, $show_title_bar ? $c : $c + 1, + $wi, \@new_screen, \@new_mouse); + if ($c + 1 < $titems && $j + 1 < $ncols) { + $new_screen[$i][$j] .= $vars{separator}; + } + ++$j; + ++$c; + } + } + else { # column layout + ++$i if $show_title_bar; + for my $wi (@wi) { + if ($i >= $nrows) { + $i = 0; + ++$j; + } + last if $j >= $ncols; + _add_item($i, $j, $show_title_bar ? $c : $c + 1, + $wi, \@new_screen, \@new_mouse); + if ($c + $nrows < $titems) { + $new_screen[$i][$j] .= $vars{separator}; + } + ++$i; + ++$c; + } + } + my $step = $vars{seplen} + abs $vars{block}; + $i = 0; + my $str = Terminfo::sc . Terminfo::sgr0; + for (my $i = 0; $i < @new_screen; ++$i) { + for (my $j = 0; $j < @{$new_screen[$i]}; ++$j) { + if (defined $new_mouse[$i] && defined $new_mouse[$i][$j]) { + my $from = $j * $step; + $mouse_coords{$i}{$_} = $new_mouse[$i][$j] + for $from .. $from + abs $vars{block}; + } + next if defined $screen[$i] && defined $screen[$i][$j] + && $screen[$i][$j] eq $new_screen[$i][$j]; + $str .= Terminfo::cup($i, $j * $step) + . formats_to_ansi_basic($new_screen[$i][$j]) + . Terminfo::sgr0; + $str .= Terminfo::el if $j == $#{$new_screen[$i]} && (!$xenl || $j + 1 != $ncols); + } + } + for (@new_screen .. $screenHeight - 1) { + if (!@screen || defined $screen[$_]) { + $str .= Terminfo::cup($_, 0) . Terminfo::sgr0 . Terminfo::el; + } + } + $str .= Terminfo::rc; + safe_print $str; + @screen = @new_screen; + } + + sub handle_resize { + if (defined (my $r = safe_qx('stty size'))) { + ($screenHeight, $screenWidth) = split ' ', $r; + $resized = 0; + @screen = (); + $disp_update = 1; + if ($one_shot_integration == 2) { + $one_shot_resize--; + } + } + else { + } + } + + sub _build_keymap { + %c2w = reverse( %{$vars{key}}, %{$vars{key2}} ); + if (!grep { /^[+-]./ } keys %c2w) { + %c2w = (%c2w, map { ("-$_" => $c2w{$_}) } grep { !/^\^./ } keys %c2w); + } + %c2w = map { + my $key = $_; + s{^(-)?(\+)?(\^)?(.)}{ + join '', ( + ($1 ? "\e" : ''), + ($2 ? "\e\e" : ''), + ($3 ? "$4"^"@" : $4) + ) + }e; + $_ => $c2w{$key} + } keys %c2w; + @seqlist = sort { length $b <=> length $a } keys %c2w; + } + + sub _match_tmux { + length $ENV{TMUX} && exists $vars{irssienv}{tmux_srv} && length $vars{irssienv}{tmux_pane} + && $ENV{TMUX} eq $vars{irssienv}{tmux_srv} + } + + sub process_keys { + Encode::_utf8_on($keybuf); + my $win; + my $use_mouse; + my $maybe; + KEY: while (length $keybuf && !$maybe) { + $maybe = 0; + if ($keybuf =~ s/^\e\[M(.)(.)(.)//) { + @last_mouse = @mouse;# if @mouse && $mouse[0] < 64; + @mouse = map { -32 + ord } ($1, $2, $3); + $use_mouse = 1; + next KEY; + } + for my $s (@seqlist) { + if ($keybuf =~ s/^\Q$s//) { + $win = $c2w{$s}; + $use_mouse = 0; + next KEY; + } + elsif (length $keybuf < length $s && $s =~ /^\Q$keybuf/) { + $maybe = 1; + } + } + unless ($maybe) { + substr $keybuf, 0, 1, ''; + } + } + if ($use_mouse && @mouse && @last_mouse && + $mouse[2] == $last_mouse[2] && + $mouse[1] == $last_mouse[1] && + ($mouse[0] == 3 || $mouse[0] == 64 || $mouse[0] == 65)) { + if ($mouse[0] == 64) { + $win = 'up'; + } + elsif ($mouse[0] == 65) { + $win = 'down'; + } + elsif (exists $mouse_coords{$mouse[2] - 1}{$mouse[1] - 1}) { + $win = $mouse_coords{$mouse[2] - 1}{$mouse[1] - 1}; + } + elsif ($mouse[2] == 1 && $mouse[1] <= abs $vars{block}) { + $win = $last_mouse[0] != 0 ? 'last' : 'active'; + } + else { + } + } + if (defined $win) { + $win =~ s/^_//; + safe_print_sock("$win\n"); + if (!exists $ENV{AWL_AUTOFOCUS} || $ENV{AWL_AUTOFOCUS}) { + if (_match_tmux()) { + safe_qx("tmux selectp -t $vars{irssienv}{tmux_pane} 2>&1"); + } + elsif (exists $vars{irssienv}{xwinid}) { + safe_qx("wmctrl -ia $vars{irssienv}{xwinid} 2>/dev/null"); + } + } + } + Encode::_utf8_off($keybuf); + } + + sub check_integration { + return unless $vars{irssienv}; + return unless $sock && exists $vars{seplen} && exists $vars{block}; + if ($one_shot_integration == 1) { + my $nrows = $screenHeight - $vars{ha}; + my $ncols = ($vars{seplen} + abs $vars{block}) ? int( ($screenWidth + $vars{seplen}) / ($vars{seplen} + abs $vars{block}) ) : 0; + my $items = ($show_title_bar ? 1 : 0) + @{$vars{win}//[]}; + my $dcols_required = $nrows ? int($items/$nrows) + !!($items%$nrows) : 0; + my $rows_required = $ncols ? int($items/$ncols) + !!($items%$ncols) : 0; + $rows_required = abs $vars{ml} + if ($vars{ml} < 0 || ($vars{ml} > 0 && $rows_required > $vars{ml})); + $dcols_required = abs $vars{mc} + if ($vars{mc} < 0 || ($vars{mc} > 0 && $dcols_required > $vars{mc})); + my $rows = $rows_required + $vars{ha}; + my $cols = ($dcols_required * ($vars{seplen} + abs $vars{block})) - $vars{seplen}; + if (_match_tmux()) { + # int( ($screenWidth + $vars{seplen}) / ($vars{seplen} + abs $vars{block}) ); + my ($pos_flag, $before); + if ($integration_position eq 'left') { + $pos_flag = 'h'; + $before = 1; + } + elsif ($integration_position eq 'top') { + $pos_flag = 'v'; + $before = 1; + } + elsif ($integration_position eq 'right') { + $pos_flag = 'h'; + } + else { + $pos_flag = 'v'; + } + my @cmd = "joinp -d$pos_flag -s $ENV{TMUX_PANE} -t $vars{irssienv}{tmux_pane}"; + push @cmd, "swapp -d -t $ENV{TMUX_PANE} -s $vars{irssienv}{tmux_pane}" + if $before; + $cols = max($cols, 2); + $rows = max($rows, 2); + + safe_qx("tmux " . (join " \\\; ", @cmd) . " 2>&1"); + } + else { + $resized = 1; + #safe_qx("resize -s $screenHeight $cols 2>&1") + # if $cols > 0; + } + $one_shot_integration++; + if ($resized == 1) { + handle_resize(); + resize_integration(); + } + } + elsif ($one_shot_integration == 2) { + resize_integration(1); + } + } + + sub resize_integration { + return unless $one_shot_integration; + return unless ($one_shot_resize//0) < 0 || shift; + my $nrows = $screenHeight - $vars{ha}; + my $ncols = ($vars{seplen} + abs $vars{block}) ? int( ($screenWidth + $vars{seplen}) / ($vars{seplen} + abs $vars{block}) ) : 0; + my $items = ($show_title_bar ? 1 : 0) + @{$vars{win}//[]}; + my $dcols_required = $nrows ? (int($items/$nrows) + !!($items%$nrows)) : 0; + my $rows_required = $ncols ? int($items/$ncols) + !!($items%$ncols) : 0; + $rows_required = abs $vars{ml} + if ($vars{ml} < 0 || ($vars{ml} > 0 && $rows_required > $vars{ml})); + $dcols_required = abs $vars{mc} + if ($vars{mc} < 0 || ($vars{mc} > 0 && $dcols_required > $vars{mc})); + my $rows = $rows_required + $vars{ha}; + my $cols = ($dcols_required * ($vars{seplen} + abs $vars{block})) - $vars{seplen}; + if (_match_tmux()) { + my $pos_flag; + my $before = 0; + if ($integration_position eq 'left') { + $pos_flag = 'h'; + $before = 1; + } + elsif ($integration_position eq 'top') { + $pos_flag = 'v'; + $before = 1; + } + elsif ($integration_position eq 'right') { + $pos_flag = 'h'; + } + else { + $pos_flag = 'v'; + } + my @cmd; + # hard tmux limits + $cols = max($cols, 2); + $rows = max($rows, 2); + if ($pos_flag eq 'h' && $cols != $screenWidth) { + my $change = $screenWidth - $cols; + my $dir = ($before ^ ($change<0)) ? 'L' : 'R'; + push @cmd, "resizep -$dir -t $ENV{TMUX_PANE} @{[abs $change]}"; + #push @cmd, "resizep -x $cols -t $ENV{TMUX_PANE}"; + $one_shot_resize = 1; + } + if ($pos_flag eq 'v' && $rows != $screenHeight) { + #push @cmd, "resizep -y $rows -t $ENV{TMUX_PANE}"; + my $change = $screenHeight - $rows; + my $dir = ($before ^ ($change<0)) ? 'U' : 'D'; + push @cmd, "resizep -$dir -t $ENV{TMUX_PANE} @{[abs $change]}"; + $one_shot_resize = 1; + } + + safe_qx("tmux " . (join " \\\; ", @cmd) . " 2>&1") + if @cmd; + } + else { + $cols = max($cols, 1); + $rows = max($rows, 1); + unless ($nrows > $ncols) { # line layout + if ($rows != $screenHeight) { + safe_qx("resize -s $rows $screenWidth 2>&1"); + $one_shot_resize = 1; + } + } + else { + if ($cols != $screenWidth) { + safe_qx("resize -s $screenHeight $cols 2>&1"); + $one_shot_resize = 1; + } + } + } + if ($resized == 1) { + handle_resize(); + } + } + + sub init_integration { + return unless $one_shot_integration; + if (_match_tmux()) { + } + else { + } + safe_print("\e]2;".(uc ::setc())."\e\\"); + } + + sub main { + require Getopt::Std; + my %opts; + Getopt::Std::getopts('1p:', \%opts); + my $one_shot = $opts{1}; + $integration_position = $opts{p}; + $one_shot_integration = 0+!!$one_shot; + #shift if @_ && $_[0] eq '--'; + &init; + $show_title_bar = 0 if $ENV{AWL_NOTITLE}; + init_integration(); + until ($got_int) { + $timeout = undef; + if ($resized) { + if ($resized == 1) { + $timeout = 1; + $resized++; + } + else { + handle_resize(); + resize_integration(); + } + } + unless ($sock || $timeout) { + connect_it(); + } + $timeout ||= RECONNECT_TIME unless $sock; + update_screen() if $disp_update; + SELECT: while (my @read = $loop->can_read($timeout)) { + for my $fh (@read) { + if ($fh == \*STDIN) { + if (read STDIN, my $buf, BLOCK_SIZE) { + do { + $keybuf .= $buf; + } while read STDIN, $buf, BLOCK_SIZE; + } + else { + $got_int = 1; + last SELECT; + } + } + else { + if ($fh->read(my $buf, BLOCK_SIZE)) { + do { + $rcvbuf .= $buf; + } while $fh->read($buf, BLOCK_SIZE); + } + else { + $disp_update = 1; + remove_conn($fh); + if ($one_shot) { + $got_int = 1; + last SELECT; + } + $timeout ||= RECONNECT_TIME; + } + } + } + $disp_update |= process_recv() if length $rcvbuf; + process_keys() if length $keybuf; + check_integration() if $one_shot; + update_screen() if $disp_update; + } + continue { + } + } + end_prog(); + } +} + +1; + +# Changelog +# ========= +# 1.5 - improve compat. with sideways splits +# +# 1.4 +# - fix line wrapping in some themes, reported by justanotherbody +# - fix named window key detection, reported by madduck +# - make title (in viewer and shared_sbar) configurable +# +# 1.3 - workaround for irssi issue #572 +# 1.2 - new format to choose abbreviation character +# 1.1 - infinite loop on shortening certain window names reported by Kalan +# +# 1.0 +# - new awl_viewer_launch setting and an array of related settings +# - fixed regression bug /exec -interactive +# - fixed some warnings in perl 5.10 reported by kl3 +# - workaround for crash due to infinite recursion in irssi's Perl +# error handling +# +# 0.9 +# - fix endless loop in awin detection code! +# - correct colour swap in awl_viewer +# - fix passing of alternate socket path to the viewer +# - potential undefinedness in mouse refnum hinted at by Canopus +# - fixed regression bug /exec -interactive +# - add case-insensitive modifier to awl_sort +# - run custom_xform on awl_prefer_name also +# - avoid inconsistent active window state after awin detection +# reported by ss +# - revert %9-hack in the viewer prompted by discussion with pierrot +# - fix new warning in perl 5.22 +# +# 0.8 +# - replace fifo mode with external viewer script +# - remove bundled cpan modules +# - work around bogus irssi warning +# - improve mouse support +# - workaround for broken cumode in irssi 0.8.15 +# - fix handling of non-meta windows (uninitialized warning) +# - add 256 colour support, strip true colour codes +# - fix totally bogus $N padding reported by Ed S. +# - make /window goto #name mappings work but ignore non-existant ones +# - improve incomplete reads reported by bcode +# - fix single % in awl_viewer reported by bcode +# - add support for key bindings by nike and ferret +# - coerce utf8 key binds +# - add settings: custom_xform, last_line_shade, hide_name_data +# - abbreviations were broken in some cases +# - fix some misuse of / as cmdchar in mouse script reported by bcode +# - add shared status bar mode +# - ${type} variables for custom_xform setting +# - crash if custom_xform had runtime error +# - update sorting documentation +# - fix odd case in size calculation noted by lasers +# - add missing font styles to the viewer reported by ishanyx +# - add italic +# +# 0.7g +# - remove screen support and replace it with fifo support +# - add double-width support to the shortener +# - correct documentation regarding $T vs. display_header +# - add missing refresh for window item changed (thanks vague) +# - add visible windows +# - add exemptions for active window +# - workaround for hiding the window changes from trackbar +# - hack to force 16colours in screen mode +# - remember last window (reported by earthnative) +# - wrong window focus on new queries (reported by emsid) +# - dataloss bug on trying to remember last window +# +# 0.6d+ +# - add support for network headers +# - fixed regression bug /exec -interactive +# +# 0.6ca+ +# - add screen support (from nicklist.pl) +# - names can now have a max length and window names can be used +# - fixed a bug with block display in screen mode and status bar mode +# - added space handling to ir_fe and removed it again +# - now handling formats on my own +# - started to work on $tag display +# - added warning about missing sb_act_none abstract leading to +# - display*active settings +# - added warning about the bug in awl_display_(no)key_active settings +# - mouse hack +# +# 0.5d +# - add setting to also hide the last status bar if empty (awl_all_disable) +# - reverted to old utf8 code to also calculate broken utf8 length correctly +# - simplified dealing with status bars in wlreset +# - added a little tweak for the renamed term_type somewhere after Irssi 0.8.9 +# - fixed bug in handling channel #$$ +# - reset background colour at the beginning of an entry +# +# 0.4d +# - fixed order of disabling status bars +# - several attempts at special chars, without any real success +# and much more weird new bugs caused by this +# - setting to specify sort order +# - reduced timeout values +# - added awl_hide_data +# - make it so the dynamic sub is actually deleted +# - fix a bug with removing of the last separator +# - take into consideration parse_special +# +# 0.3b +# - automatically kill old status bars +# - reset on /reload +# - position/placement settings +# +# 0.2 +# - automated retrieval of key bindings (thanks grep.pl authors) +# - improved removing of status bars +# - got rid of status chop +# +# 0.1 +# - Based on chanact.pl which was apparently based on lightbar.c and +# nicklist.pl with various other ideas from random scripts. diff --git a/.irssi/scripts/autorun/adv_windowlist.pl b/.irssi/scripts/autorun/adv_windowlist.pl new file mode 120000 index 0000000..d210a5c --- /dev/null +++ b/.irssi/scripts/autorun/adv_windowlist.pl @@ -0,0 +1 @@ +../adv_windowlist.pl
\ No newline at end of file diff --git a/.irssi/scripts/autorun/chansort.pl b/.irssi/scripts/autorun/chansort.pl new file mode 120000 index 0000000..8b25183 --- /dev/null +++ b/.irssi/scripts/autorun/chansort.pl @@ -0,0 +1 @@ +../chansort.pl
\ No newline at end of file diff --git a/.irssi/scripts/autorun/dispatch.pl b/.irssi/scripts/autorun/dispatch.pl new file mode 120000 index 0000000..fbc8677 --- /dev/null +++ b/.irssi/scripts/autorun/dispatch.pl @@ -0,0 +1 @@ +../dispatch.pl
\ No newline at end of file diff --git a/.irssi/scripts/autorun/go.pl b/.irssi/scripts/autorun/go.pl new file mode 120000 index 0000000..c516cee --- /dev/null +++ b/.irssi/scripts/autorun/go.pl @@ -0,0 +1 @@ +../go.pl
\ No newline at end of file diff --git a/.irssi/scripts/autorun/hilightwin.pl b/.irssi/scripts/autorun/hilightwin.pl new file mode 120000 index 0000000..fb9568b --- /dev/null +++ b/.irssi/scripts/autorun/hilightwin.pl @@ -0,0 +1 @@ +../hilightwin.pl
\ No newline at end of file diff --git a/.irssi/scripts/autorun/nickcolor.pl b/.irssi/scripts/autorun/nickcolor.pl new file mode 120000 index 0000000..f4f7c6f --- /dev/null +++ b/.irssi/scripts/autorun/nickcolor.pl @@ -0,0 +1 @@ +../nickcolor.pl
\ No newline at end of file diff --git a/.irssi/scripts/autorun/recentdepart.pl b/.irssi/scripts/autorun/recentdepart.pl new file mode 120000 index 0000000..cafd41b --- /dev/null +++ b/.irssi/scripts/autorun/recentdepart.pl @@ -0,0 +1 @@ +../recentdepart.pl
\ No newline at end of file diff --git a/.irssi/scripts/autorun/splitlong.pl b/.irssi/scripts/autorun/splitlong.pl new file mode 120000 index 0000000..f2fa101 --- /dev/null +++ b/.irssi/scripts/autorun/splitlong.pl @@ -0,0 +1 @@ +../splitlong.pl
\ No newline at end of file diff --git a/.irssi/scripts/autorun/trackbar22.pl b/.irssi/scripts/autorun/trackbar22.pl new file mode 120000 index 0000000..09c98ff --- /dev/null +++ b/.irssi/scripts/autorun/trackbar22.pl @@ -0,0 +1 @@ +../trackbar22.pl
\ No newline at end of file diff --git a/.irssi/scripts/autorun/twtopic.pl b/.irssi/scripts/autorun/twtopic.pl new file mode 120000 index 0000000..a283269 --- /dev/null +++ b/.irssi/scripts/autorun/twtopic.pl @@ -0,0 +1 @@ +../twtopic.pl
\ No newline at end of file diff --git a/.irssi/scripts/autorun/usercount.pl b/.irssi/scripts/autorun/usercount.pl new file mode 120000 index 0000000..1afd2ea --- /dev/null +++ b/.irssi/scripts/autorun/usercount.pl @@ -0,0 +1 @@ +../usercount.pl
\ No newline at end of file diff --git a/.irssi/scripts/chansort.pl b/.irssi/scripts/chansort.pl new file mode 100644 index 0000000..814d041 --- /dev/null +++ b/.irssi/scripts/chansort.pl @@ -0,0 +1,103 @@ +#! /usr/bin/perl +# +# $Id: chansort.pl,v 1.4 2004/11/02 22:52:33 peder Exp $ +# +# Copyright (C) 2004 by Peder Stray <peder@gzip.ninja.no> +# + +use strict; +use Irssi; +use Irssi::Irc; + +# ======[ Script Header ]=============================================== + +use vars qw{$VERSION %IRSSI}; +($VERSION) = '$Revision: 1.4 $' =~ / (\d+\.\d+) /; +%IRSSI = ( + name => 'chansort', + authors => 'Peder Stray', + contact => 'peder@ninja.no', + url => 'http://ninja.no/irssi/chansort.pl', + license => 'GPL', + description => 'Sort all channel and query windows', + ); + +# ======[ Hooks ]======================================================= + +# --------[ sig_sort_trigger ]------------------------------------------ + +sub sig_sort_trigger { + return unless Irssi::settings_get_bool('chansort_autosort'); + cmd_chansort(); +} + +# ======[ Commands ]==================================================== + +# --------[ CHANSORT ]-------------------------------------------------- + +# Usage: /CHANSORT +sub cmd_chansort { + my(@windows); + my($minwin); + + for my $win (Irssi::windows()) { + my $act = $win->{active}; + my $key; + + if ($act->{type} eq 'CHANNEL') { + $key = "C".$act->{server}{tag}.' '.substr($act->{visible_name}, 1); + } + elsif ($act->{type} eq 'QUERY') { + $key = "Q".$act->{server}{tag}.' '.$act->{visible_name}; + } + else { + next; + } + if (!defined($minwin) || $minwin > $win->{refnum}) { + $minwin = $win->{refnum}; + } + push @windows, [ lc $key, $win ]; + + } + + for (sort {$a->[0] cmp $b->[0]} @windows) { + my($key,$win) = @$_; + my($act) = $win->{active}; + +# printf("win[%d->%d]: t[%s] [%s] [%s] {%s}\n", +# $win->{refnum}, +# $minwin, +# $act->{type}, +# $act->{visible_name}, +# $act->{server}{tag}, +# $key, +# ); + + $win->command("window move $minwin"); + $minwin++; + } +} + +# ======[ Setup ]======================================================= + +# --------[ Register commands ]----------------------------------------- + +Irssi::command_bind('chansort', 'cmd_chansort'); + +# --------[ Register settings ]----------------------------------------- + +Irssi::settings_add_bool('chansort', 'chansort_autosort', 0); + +# --------[ Register signals ]------------------------------------------ + +Irssi::signal_add_last('window item name changed', 'sig_sort_trigger'); +Irssi::signal_add_last('channel created', 'sig_sort_trigger'); +Irssi::signal_add_last('query created', 'sig_sort_trigger'); + +# ======[ END ]========================================================= + +# Local Variables: +# header-initial-hide: t +# mode: header-minor +# end: + diff --git a/.irssi/scripts/dispatch.pl b/.irssi/scripts/dispatch.pl new file mode 100644 index 0000000..6050520 --- /dev/null +++ b/.irssi/scripts/dispatch.pl @@ -0,0 +1,26 @@ +use strict; +use warnings; +use Irssi; +use Irssi::Irc; +use vars qw($VERSION %IRSSI); + +$VERSION = "0.0.2"; +%IRSSI = ( + authors => "Sebastian 'yath' Schmidt", + contact => "yathen\@web.de", + name => "Command dispatcher", + description => "This scripts sends unknown commands to the server", + license => "GNU GPLv2", + changed => "Tue Mar 5 14:55:29 CET 2002", +); + +sub event_default_command { + my ($command, $server) = @_; + return if (Irssi::settings_get_bool("dispatch_unknown_commands") == 0 + || !$server); + $server->send_raw($command); + Irssi::signal_stop(); +} + +Irssi::settings_add_bool("misc", "dispatch_unknown_commands", 1); +Irssi::signal_add_first("default command", "event_default_command"); diff --git a/.irssi/scripts/go.pl b/.irssi/scripts/go.pl new file mode 100644 index 0000000..0b0a2a2 --- /dev/null +++ b/.irssi/scripts/go.pl @@ -0,0 +1,105 @@ +use strict; +use vars qw($VERSION %IRSSI); +use Irssi; +use Irssi::Irc; + +# Usage: +# /script load go.pl +# If you are in #irssi you can type /go #irssi or /go irssi or even /go ir ... +# also try /go ir<tab> and /go <tab> (that's two spaces) +# +# The following settings exist: +# +# /SET go_match_case_sensitive [ON|OFF] +# Match window/item names sensitively (the default). Turning this off +# means e.g. "/go foo" would jump to a window named "Foobar", too. +# +# /SET go_match_anchored [ON|OFF] +# Match window/names only at the start of the word (the default). Turning +# this off will mean that strings can match anywhere in the window/names. +# The leading '#' of channel names is optional either way. +# +# /SET go_complete_case_sensitive [ON|OFF] +# When using tab-completion, match case-insensitively (the default). +# Turning this on means that "/go foo<tab>" will *not* suggest "Foobar". +# +# /SET go_complete_anchored [ON|OFF] +# Match window/names only at the start of the word. The default is 'off', +# which causes completion to match anywhere in the window/names during +# completion. The leading '#' of channel names is optional either way. +# + +$VERSION = '1.1'; + +%IRSSI = ( + authors => 'nohar', + contact => 'nohar@freenode', + name => 'go to window', + description => 'Implements /go command that activates a window given a name/partial name. It features a nice completion.', + license => 'GPLv2 or later', + changed => '2017-02-02' +); + +sub _make_regexp { + my ($name, $ci, $aw) = @_; + my $re = "\Q${name}\E"; + $re = "(?i:$re)" unless $ci; + $re = "^#?$re" if $aw; + return $re; +} + +sub signal_complete_go { + my ($complist, $window, $word, $linestart, $want_space) = @_; + my $channel = $window->get_active_name(); + my $k = Irssi::parse_special('$k'); + + return unless ($linestart =~ /^\Q${k}\Ego\b/i); + + my $re = _make_regexp($word, + Irssi::settings_get_bool('go_complete_case_sensitive'), + Irssi::settings_get_bool('go_complete_anchored')); + @$complist = (); + foreach my $w (Irssi::windows) { + my $name = $w->get_active_name(); + if ($word ne "") { + if ($name =~ $re) { + push(@$complist, $name) + } + } else { + push(@$complist, $name); + } + } + Irssi::signal_stop(); +}; + +sub cmd_go +{ + my($chan,$server,$witem) = @_; + + $chan =~ s/ *//g; + my $re = _make_regexp($chan, + Irssi::settings_get_bool('go_match_case_sensitive'), + Irssi::settings_get_bool('go_match_anchored')); + + foreach my $w (Irssi::windows) { + my $name = $w->get_active_name(); + if ($name =~ $re) { + $w->set_active(); + return; + } + } +} + +Irssi::command_bind("go", "cmd_go"); +Irssi::signal_add_first('complete word', 'signal_complete_go'); +Irssi::settings_add_bool('go', 'go_match_case_sensitive', 1); +Irssi::settings_add_bool('go', 'go_complete_case_sensitive', 0); +Irssi::settings_add_bool('go', 'go_match_anchored', 1); +Irssi::settings_add_bool('go', 'go_complete_anchored', 0); + +# Changelog +# +# 2017-02-02 1.1 martin f. krafft <madduck@madduck.net> +# - made case-sensitivity of match configurable +# - made anchoring of search strings configurable +# diff --git a/.irssi/scripts/hilightwin.pl b/.irssi/scripts/hilightwin.pl new file mode 100644 index 0000000..3bb2863 --- /dev/null +++ b/.irssi/scripts/hilightwin.pl @@ -0,0 +1,85 @@ +# +# Print hilighted messages & private messages to window named "hilight" for +# irssi 0.7.99 by Timo Sirainen +# +# Modded a tiny bit by znx to stop private messages entering the hilighted +# window (can be toggled) and to put up a timestamp. +# +# Changed a little by rummik to optionally show network name. Enable with +# `/set hilightwin_shownetwork on` +# + +use strict; +use Irssi; +use POSIX; +use vars qw($VERSION %IRSSI); + +$VERSION = "1.00"; +%IRSSI = ( + authors => "Timo \'cras\' Sirainen, Mark \'znx\' Sangster, Kimberly \'rummik\' Zick", + contact => "tss\@iki.fi, znxster\@gmail.com, git\@zick.kim", + name => "hilightwin", + description => "Print hilighted messages to window named \"hilight\"", + license => "Public Domain", + url => "http://irssi.org/", + changed => "Thu Apr 6 15:30:25 EDT 2017" +); + +sub is_ignored { + my ($dest) = @_; + + my @ignore = split(' ', Irssi::settings_get_str('hilightwin_ignore_targets')); + return 0 if (!@ignore); + + my %targets = map { $_ => 1 } @ignore; + + return 1 if exists($targets{"*"}); + return 1 if exists($targets{$dest->{target}}); + + if ($dest->{server}) { + my $tag = $dest->{server}->{tag}; + return 1 if exists($targets{$tag . "/*"}); + return 1 if exists($targets{$tag . "/" . $dest->{target}}); + } + + return 0; +} + +sub sig_printtext { + my ($dest, $text, $stripped) = @_; + + my $opt = MSGLEVEL_HILIGHT; + my $shownetwork = Irssi::settings_get_bool('hilightwin_show_network'); + + if(Irssi::settings_get_bool('hilightwin_showprivmsg')) { + $opt = MSGLEVEL_HILIGHT|MSGLEVEL_MSGS; + } + + if( + ($dest->{level} & ($opt)) && + ($dest->{level} & MSGLEVEL_NOHILIGHT) == 0 && + (!is_ignored($dest)) + ) { + my $window = Irssi::window_find_name('hilight'); + + if ($dest->{level} & MSGLEVEL_PUBLIC) { + $text = $dest->{target}.": ".$text; + $text = $dest->{server}->{tag} . "/" . $text if ($shownetwork); + } elsif ($shownetwork) { + $text = $dest->{server}->{tag} . ": " . $text; + } + $text =~ s/%/%%/g; + $window->print($text, MSGLEVEL_CLIENTCRAP) if ($window); + } +} + +my $window = Irssi::window_find_name('hilight'); +Irssi::print("Create a window named 'hilight'") if (!$window); + +Irssi::settings_add_bool('hilightwin','hilightwin_showprivmsg',1); +Irssi::settings_add_str('hilightwin', 'hilightwin_ignore_targets', ''); +Irssi::settings_add_bool('hilightwin','hilightwin_show_network', 0); + +Irssi::signal_add('print text', 'sig_printtext'); + +# vim:set ts=4 sw=4 et: diff --git a/.irssi/scripts/nickcolor.pl b/.irssi/scripts/nickcolor.pl new file mode 100644 index 0000000..ff9915d --- /dev/null +++ b/.irssi/scripts/nickcolor.pl @@ -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 ' . $color . '$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'); diff --git a/.irssi/scripts/recentdepart.pl b/.irssi/scripts/recentdepart.pl new file mode 100644 index 0000000..efb8b95 --- /dev/null +++ b/.irssi/scripts/recentdepart.pl @@ -0,0 +1,332 @@ +#!/usr/bin/perl -w +# +# recentdepart.pl +# +# irssi script +# +# This script, when loaded into irssi, will filter quit and parted messages +# for channels listed in recdep_channels for any nick whos last message was +# more then a specified time ago. +# +# It also filters join messages showing only join messages for nicks who recently +# parted. +# +# [settings] +# recdep_channels +# - Should contain a list of chatnet and channel names that recentdepart +# should monitor. Its format is a spcae delimited list of chatnet/#channel +# pairs. Either chatnet or channel are optional but adding a / makes it +# explicitly recognized as a chatnet or a channel name. A special case is just a +# "*" which turns it on globally. +# +# "#irrsi #perl" - enables filtering on the #irssi and #perl +# channels on all chatnets. +# +# "freenode IRCNet/#irssi" - enables filtering for all channels on frenode +# and only the #irssi channel on IRCNet. +# +# "freenode/" - force "freenode" to be interpreted as the chatnet +# name by adding a / to the end. +# +# "/freenode" - forces "freenode" to be interpreted as the channel +# by prefixing it with the / delimiter. +# +# "*" - globally enables filtering. +# +# recdep_period +# - specifies the window of time, after a nick last spoke, for which quit/part +# notices will be let through the filter. +# +# recdep_rejoin +# - specifies a time period durring which a join notice for someone rejoining will +# be shown. Join messages are filtered if the nicks part/quit message was filtered +# or if the nick is gone longer then the rejoin period. +# Set to 0 to turn off filtering of join notices. +# +# recdep_nickperiod +# - specifies a window of time like recdep_period that is used to filter nick change +# notices. Set to 0 to turn off filtering of nick changes. +# +# recdep_use_hideshow +# - whether to use hideshow script instead of ignoring +# + +use strict; +use warnings; +use Irssi; +use Irssi::Irc; + +our $VERSION = "0.7"; +our %IRSSI = ( + authors => 'Matthew Sytsma', + contact => 'spiderpigy@yahoo.com', + name => 'Recently Departed', + description => 'Filters quit/part/join/nick notices based on time since last message. (Similar to weechat\'s smartfilter).', + license => 'GNU GPLv2 or later', + url => '', +); + +# store a hash of configure selected servers/channels +my %chanlist; +# Track recent times by server/nick/channel +# (it is more optimal to go nick/channel then channel/nick because some quit signals are by server not by channel. +# We will only have to loop through open channels that a nick has spoken in which should be less then looping +# through all the monitored channels looking for the nick. +my %nickhash=(); +# Track recent times for parted nicks by server/channel/nick +my %joinwatch=(); +my $use_hide; + +sub on_setup_changed { + my %old_chanlist = %chanlist; + %chanlist = (); + my @pairs = split(/ /, Irssi::settings_get_str("recdep_channels")); + + $use_hide = Irssi::settings_get_bool("recdep_use_hideshow"); + foreach (@pairs) + { + my ($net, $chan, $more) = split(/\//); + if ($more) + { + /\/(.+)/; + $chan = $1; + } +# Irssi::active_win()->print("Initial Net: $net Chan: $chan"); + if (!$net) + { + $net = '*'; + } + + if ($net =~ /^[#!@&]/ && !$chan) + { + $chan = $net; + $net = "*"; + } + + if (!$chan) + { + $chan = "*"; + } + + $chanlist{$net}{$chan} = 1; + } + + # empty the storage in case theres a channel or server we are no longer filtering + %nickhash=(); + %joinwatch=(); +} + +sub check_channel +{ + my ($server, $channel) = @_; + + # quits dont have a channel so we need to see if this server possibly contains this channel + if (!$channel || $channel eq '*') + { + # see if any non chatnet channel listings are open on this server + if (keys %{ $chanlist{'*'} }) + { + foreach my $chan (keys %{ $chanlist{'*'} }) + { + if ($chan eq '*' || $server->channel_find($chan)) + { + return 1; + } + } + } + + # see if there were any channels listed for this chatnet + if (keys %{ $chanlist{$server->{'chatnet'}} }) + { return 1; } + else + { return 0; } + } + + # check for global channel matches and pair matches + return (($chanlist{'*'}{'*'}) || + ($chanlist{'*'}{$channel}) || + ($chanlist{$server->{'chatnet'}}{'*'}) || + ($chanlist{$server->{'chatnet'}}{$channel})); +} + +# Hook for quitting +sub on_quit +{ + my ($server, $nick, $address, $reason) = @_; + + if ($server->{'nick'} eq $nick) + { return; } + + if (check_channel($server, '*')) + { + my $recent = 0; + foreach my $chan (keys %{ $nickhash{$server->{'tag'}}{lc($nick)} }) + { + if (time() - $nickhash{$server->{'tag'}}{lc($nick)}{$chan} < Irssi::settings_get_int("recdep_period")) + { + $recent = 1; + + if (Irssi::settings_get_int("recdep_rejoin") > 0) + { + $joinwatch{$server->{'tag'}}{$chan}{lc($nick)} = time(); + } + } + } + + delete $nickhash{$server->{'tag'}}{lc($nick)}; + + if (!$recent) + { + $use_hide ? $Irssi::scripts::hideshow::hide_next = 1 + : Irssi::signal_stop(); + } + } +} + +# Hook for parting +sub on_part +{ + my ($server, $channel, $nick, $address, $reason) = @_; + + # cleanup if its you who left a channel + if ($server->{'nick'} eq $nick) + { + # slightly painfull cleanup but we shouldn't hit this as often + foreach my $nickd (keys %{ $nickhash{$server->{'tag'}} }) + { + delete $nickhash{$server->{'tag'}}{$nickd}{$channel}; + if (!keys(%{ $nickhash{$server->{'tag'}}{$nickd} })) + { + delete $nickhash{$server->{'tag'}}{$nickd}; + } + } + delete $joinwatch{$server->{'tag'}}{$channel}; + } + elsif (check_channel($server, $channel)) + { + if (!defined $nickhash{$server->{'tag'}}{lc($nick)}{$channel} || time() - $nickhash{$server->{'tag'}}{lc($nick)}{$channel} > Irssi::settings_get_int("recdep_period")) + { + $use_hide ? $Irssi::scripts::hideshow::hide_next = 1 + : Irssi::signal_stop(); + } + elsif (Irssi::settings_get_int("recdep_rejoin") > 0) + { + $joinwatch{$server->{'tag'}}{$channel}{lc($nick)} = time(); + } + + delete $nickhash{$server->{'tag'}}{lc($nick)}{$channel}; + if (!keys(%{ $nickhash{$server->{'tag'}}{lc($nick)} })) + { + delete $nickhash{$server->{'tag'}}{lc($nick)}; + } + } +} + +# Hook for public messages. +sub on_public +{ + my ($server, $msg, $nick, $addr, $target) = @_; + + if (!$target) { return; } + if ($nick =~ /^#/) { return; } + + if ($server->{'nick'} eq $nick) { return; } + + if (check_channel($server, $target)) + { + $nickhash{$server->{'tag'}}{lc($nick)}{$target} = time(); + } +} + +# Hook for people joining +sub on_join +{ + my ($server, $channel, $nick, $address) = @_; + + if ($server->{'nick'} eq $nick) + { return; } + + if (Irssi::settings_get_int("recdep_rejoin") == 0) + { return; } + + if (check_channel($server, $channel)) + { + if (!defined $joinwatch{$server->{'tag'}}{$channel}{lc($nick)} || time() - $joinwatch{$server->{'tag'}}{$channel}{lc($nick)} > Irssi::settings_get_int("recdep_rejoin")) + { + $use_hide ? $Irssi::scripts::hideshow::hide_next = 1 + : Irssi::signal_stop(); + } + } + + # loop through and delete all old nicks from the rejoin hash + # this should be a small loop because it will only inlude nicks who recently left channel and who + # passed the part message filter + foreach my $nickd (keys %{ $joinwatch{$server->{'tag'}}{$channel} }) + { + if (time() - $joinwatch{$server->{'tag'}}{$channel}{lc($nickd)} < Irssi::settings_get_int("recdep_rejoin")) + { next; } + + delete $joinwatch{$server->{'tag'}}{$channel}{lc($nickd)}; + } + if (!keys(%{ $joinwatch{$server->{'tag'}}{$channel} })) + { + delete $joinwatch{$server->{'tag'}}{$channel}; + } +} + +# Hook for nick changes +sub on_nick +{ + my ($server, $new, $old, $address) = @_; + + if ($server->{'nick'} eq $old || $server->{'nick'} eq $new) + { return; } + + if (check_channel($server, '*')) + { + my $recent = 0; + foreach my $chan (keys %{ $nickhash{$server->{'tag'}}{lc($old)} }) + { + if (time() - $nickhash{$server->{'tag'}}{lc($old)}{$chan} < Irssi::settings_get_int("recdep_nickperiod")) + { + $recent = 1; + } + } + + if (!$recent && Irssi::settings_get_int("recdep_nickperiod") > 0) + { + $use_hide ? $Irssi::scripts::hideshow::hide_next = 1 + : Irssi::signal_stop(); + } + + delete $nickhash{$server->{'tag'}}{lc($old)}; + } +} + + +# Hook for cleanup on server quit +sub on_serverquit +{ + my ($server, $msg) = @_; + + delete $nickhash{$server->{'tag'}}; + delete $joinwatch{$server->{'tag'}}; +} + +# Setup hooks on events +Irssi::signal_add_last("message public", "on_public"); +Irssi::signal_add_last("message part", "on_part"); +Irssi::signal_add_last("message quit", "on_quit"); +Irssi::signal_add_last("message nick", "on_nick"); +Irssi::signal_add_last("message join", "on_join"); +Irssi::signal_add_last("server disconnected", "on_serverquit"); +Irssi::signal_add_last("server quit", "on_serverquit"); +Irssi::signal_add('setup changed', "on_setup_changed"); + +# Add settings +Irssi::settings_add_str("recdentpepart", "recdep_channels", '*'); +Irssi::settings_add_int("recdentpepart", "recdep_period", 600); +Irssi::settings_add_int("recdentpepart", "recdep_rejoin", 120); +Irssi::settings_add_int("recdentpepart", "recdep_nickperiod", 600); +Irssi::settings_add_bool("recdentpepart", "recdep_use_hideshow", 0); +on_setup_changed(); diff --git a/.irssi/scripts/splitlong.pl b/.irssi/scripts/splitlong.pl new file mode 100644 index 0000000..3c8a355 --- /dev/null +++ b/.irssi/scripts/splitlong.pl @@ -0,0 +1,60 @@ +# /set splitlong_max_length +# specifies the maximum length of a msg, automatically chosen when set to "0" +# default: 0 +# +# /set splitlong_line_start +# /set splitlong_line_end +# self-explanatory +# defaults: "... ", " ..." +### +use strict; +use vars qw($VERSION %IRSSI); + +use Irssi 20011001; + +$VERSION = "0.20"; +%IRSSI = ( + authors => "Bjoern \'fuchs\' Krombholz", + contact => "bjkro\@gmx.de", + name => "splitlong", + license => "Public Domain", + description => "Split overlong PRIVMSGs to msgs with length allowed by ircd", + changed => "Wed Jun 25 00:17:00 CET 2003", + changes => "Actually the real 0.19 (now 0.20), but upload didn't work some month ago, target problem fixed..." +); + +sub sig_command_msg { + my ($cmd, $server, $winitem) = @_; + my ( $param, $target,$data) = $cmd =~ /^(-\S*\s)?(\S*)\s(.*)/; + + my $maxlength = Irssi::settings_get_int('splitlong_max_length'); + my $lstart = Irssi::settings_get_str('splitlong_line_start'); + my $lend = Irssi::settings_get_str('splitlong_line_end'); + + if ($maxlength == 0) { + # 497 = 510 - length(":" . "!" . " PRIVMSG " . " :"); + $maxlength = 497 - length($server->{nick} . $server->{userhost} . $target); + } + my $maxlength2 = $maxlength - length($lend); + + if (length($data) > ($maxlength)) { + my @spltarr; + + while (length($data) > ($maxlength2)) { + my $pos = rindex($data, " ", $maxlength2); + push @spltarr, substr($data, 0, ($pos < ($maxlength/10 + 4)) ? $maxlength2 : $pos) . $lend; + $data = $lstart . substr($data, ($pos < ($maxlength/10 + 4)) ? $maxlength2 : $pos+1); + } + + push @spltarr, $data; + foreach (@spltarr) { + Irssi::signal_emit("command msg", "$target $_", $server, $winitem); + } + Irssi::signal_stop(); + } +} + +Irssi::settings_add_int('misc', 'splitlong_max_length', 0); +Irssi::settings_add_str('misc', 'splitlong_line_start', "... "); +Irssi::settings_add_str('misc', 'splitlong_line_end', " ..."); +Irssi::command_bind('msg', 'sig_command_msg'); diff --git a/.irssi/scripts/trackbar22.pl b/.irssi/scripts/trackbar22.pl new file mode 100644 index 0000000..0d10882 --- /dev/null +++ b/.irssi/scripts/trackbar22.pl @@ -0,0 +1,508 @@ +# 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.4"; # 1ca8fa9f28b9586 + +%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/', + 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; + Irssi::signal_emit("window trackbar added", $win); + $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}; + no warnings 'uninitialized'; + 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; + redraw_trackbars($newwindow); + 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; + return unless $unseen_trackbar{ $win->{_irssi} }; + + my $view = $win->view; + my $line = $view->get_bookmark('trackbar'); + unless ($line) { + delete $unseen_trackbar{ $win->{_irssi} }; + Irssi::signal_emit("window trackbar seen", $win); + return; + } + my $startline = $view->{startline}; + return unless $startline; + + if ($startline->{info}{time} < $line->{info}{time} + || $startline->{_irssi} == $line->{_irssi}) { + delete $unseen_trackbar{ $win->{_irssi} }; + Irssi::signal_emit("window trackbar seen", $win); + } +} + +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({'window trackbar added' => [qw/Irssi::UI::Window/]}); +Irssi::signal_register({'window trackbar seen' => [qw/Irssi::UI::Window/]}); +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}); diff --git a/.irssi/scripts/twtopic.pl b/.irssi/scripts/twtopic.pl new file mode 100644 index 0000000..a32e3f2 --- /dev/null +++ b/.irssi/scripts/twtopic.pl @@ -0,0 +1,193 @@ +# John Engelbrecht's original versions (<= 1.0.2) were released under the public +# domain. Ryan Kavanagh's changes are distributed under the ISC license: +# +# Copyright (C) 2017 Ryan Kavanagh <rak@ryanak.ca> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. + +use strict; +use vars qw($VERSION %IRSSI); +use Irssi; +use Irssi::Irc; +use Irssi::TextUI; + +$VERSION = '2.0'; +%IRSSI = ( + authors => 'John Engelbrecht, Ryan Kavanagh', + contact => 'jengelbr@yahoo.com, rak@ryanak.ca', + name => 'twtopic.pl', + description => 'Animated Topic bar.', + sbitems => 'twtopic', + license => 'Public Domain, ISC', + changed => 'Wed Jan 03 16:20:17 EST 2017', + url => 'http://irssi.darktalker.net' . "\n", +); + +my $instrut = + ".--------------------------------------------------.\n" + . "| 1.) shell> mkdir ~/.irssi/scripts |\n" + . "| 2.) shell> cp twtopic.pl ~/.irssi/scripts/ |\n" + . "| 3.) shell> mkdir ~/.irssi/scripts/autorun |\n" + . "| 4.) shell> ln -s ~/.irssi/scripts/twtopic.pl \\ |\n" + . "| ~/.irssi/scripts/autorun/twtopic.pl |\n" + . "| 5.) /sbar topic remove topic |\n" + . "| 6.) /sbar topic remove topic_empty |\n" + . "| 7.) /sbar topic add -after topicbarstart |\n" + . "| -priority 100 -alignment left twtopic |\n" + . "| 9.) /toggle twtopic_instruct and last /save |\n" + . "|--------------------------------------------------|\n" + . "| Options: Default: |\n" + . "| /set twtopic_refresh <speed> 150 |\n" + . "| /set twtopic_size <size> 20 |\n" + . "| /set twtopic_padding <size> 20 |\n" + . "| /set twtopic_auto_resize <ON|OFF> OFF |\n" + . "| /set twtopic_ar_padding <size> 0 |\n" + . "| /set twtopic_min_scroll <ON|OFF> OFF |\n" + . "| /set twtopic_init_pause <length> 10 |\n" + . "| /toggle twtopic_instruct |Startup instructions |\n" + . "\`--------------------------------------------------'"; + +my $timeout; +my $start_pos = 0; +my $size; +my $min_scroll; +my $padding; +my $topic = ""; +my @mirc_color_arr = ( + "\0031", "\0035", "\0033", "\0037", "\0032", "\0036", + "\00310", "\0030", "\00314", "\0034", "\0039", "\0038", + "\00312", "\00313", "\00311", "\00315", "\017" +); + +sub setup { + my $window = Irssi::active_win; + if ( Irssi::settings_get_bool('twtopic_auto_resize') ) { + $size = + $window->{'width'} - Irssi::settings_get_int('twtopic_ar_padding'); + } + else { + $size = Irssi::settings_get_int('twtopic_size'); + } + # Subtract 4 because we wrap the topic in '[ ' / ' ]'. + $size = $size - 4; + $min_scroll = Irssi::settings_get_bool('twtopic_min_scroll'); + $padding = Irssi::settings_get_int('twtopic_padding'); + update_topic(); +} + +sub regular_timer { + my $time = Irssi::settings_get_int('twtopic_refresh'); + Irssi::timeout_remove($timeout); + $timeout = Irssi::timeout_add( $time, 'reload', undef ); +} + +sub init_timer { + my $time = Irssi::settings_get_int('twtopic_init_pause'); + Irssi::timeout_remove($timeout); + $timeout = Irssi::timeout_add( $time, 'regular_timer', undef ); +} + +sub show { + my ( $item, $get_size_only ) = @_; + my $text = get(); + $text = "[ " . $text . " ]"; + $item->default_handler( $get_size_only, $text, undef, 1 ); +} + +sub update_topic { + $topic = ""; + my $name = Irssi::active_win()->{active}->{name}; + my $type = Irssi::active_win()->{active}->{type}; + if ( $name eq "" ) { + # We're in the status window + $topic = + "Irssi website: http://www.irssi.org, " + . "Irssi IRC channel: #irssi @ irc://irc.freenode:6667, " + . "twtopic has been written by Tech Wizard and ryanakca"; + } + elsif ( $type eq "QUERY" ) { + $topic = "You are now talking to...... " . $name; + } + else { + my $channel = Irssi::Irc::Server->channel_find($name); + $topic = $channel->{topic}; + foreach (@mirc_color_arr) { $topic =~ s/$_//g; } + } + if ( $topic eq "" ) { + $topic = "=-=-=-=-= No Topic =-=-=-=-=-=-=-"; + } + $topic =~ s/(\00313)+//; + $topic =~ s/(\002)+//; + $topic =~ s/(\001)+//; + # Reset the topic and pause + init_timer(); + # The length of the topic may have changed. We should reset our $start_pos + # to avoid going off the end in case the topic got shorter. + $start_pos = -1; + reload (); +} + +sub get { + my $topiclen = length($topic); + if ( $topiclen <= $size && $min_scroll ) { + return $topic . ( ' ' x ( $size - $topiclen ) ); + } + my $padded = $topic . ( ' ' x $padding ); + my $str = ""; + my $needed = $size - length($str); + while ( $needed > 0 ) { + if ( $needed < length($padded) - $start_pos ) { + $str = $str . substr( $padded, $start_pos, $needed ); + } + elsif ( $needed < length($padded) ) { + $str = + $str + . substr( $padded, $start_pos ) + . substr( $padded, 0, $needed - length($padded) ); + } + else { + $str = + $str + . substr( $padded, $start_pos ) + . substr( $padded, 0, $start_pos ); + } + $needed = $size - length($str); + } + $start_pos = $start_pos + 1 < length($padded) ? $start_pos + 1 : 0; + return $str; +} + + +Irssi::signal_add( 'channel topic changed', 'update_topic' ); +Irssi::signal_add( 'setup changed', 'setup' ); +Irssi::signal_add( 'terminal resized', 'setup' ); +Irssi::signal_add( 'window changed', 'update_topic' ); + +Irssi::settings_add_bool( 'tech_addon', 'twtopic_auto_resize', 0 ); +Irssi::settings_add_bool( 'tech_addon', 'twtopic_instruct', 1 ); +Irssi::settings_add_bool( 'tech_addon', 'twtopic_min_scroll', 0 ); +Irssi::settings_add_int( 'tech_addon', 'twtopic_ar_padding', 10 ); +Irssi::settings_add_int( 'tech_addon', 'twtopic_init_pause', 10 ); +Irssi::settings_add_int( 'tech_addon', 'twtopic_padding', 20 ); +Irssi::settings_add_int( 'tech_addon', 'twtopic_refresh', 150 ); +Irssi::settings_add_int( 'tech_addon', 'twtopic_size', 20 ); + +sub reload { Irssi::statusbar_items_redraw('twtopic'); } + +if ( Irssi::settings_get_bool('twtopic_instruct') ) { + print $instrut; +} + +setup(); + +Irssi::statusbar_item_register( 'twtopic', '$0', 'show' ); diff --git a/.irssi/scripts/usercount.pl b/.irssi/scripts/usercount.pl new file mode 100644 index 0000000..650f9f3 --- /dev/null +++ b/.irssi/scripts/usercount.pl @@ -0,0 +1,184 @@ +use strict; +use Irssi 20040119.2359 (); +use vars qw($VERSION %IRSSI); +$VERSION = "1.19"; +%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', + sbitems => 'usercount', + license => 'GNU GPLv2 or later', + url => 'http://irssi.dgl.cx/', + changes => 'Only show halfops if server supports them', +); + +# Once you have loaded this script run the following command: +# /statusbar window add usercount +# You can also add -alignment left|right option + +# Settings: +# /toggle usercount_show_zero to show item even when there are no users +# /toggle usercount_show_ircops (default off) +# /toggle 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 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); + } + + # Server doesn't support halfops? + if($server->isupport("PREFIX") !~ /\%/) { + $halfops = undef; + } else { + $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'); + |