├── README.md ├── CONTRIBUTING.md ├── doc ├── Irssi Powerline.png ├── README.md └── CONTRIBUTING.md ├── config ├── solarized-powerline.theme ├── LICENSE.md └── scripts └── autorun └── adv_windowlist.pl /README.md: -------------------------------------------------------------------------------- 1 | doc/README.md -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | doc/CONTRIBUTING.md -------------------------------------------------------------------------------- /doc/Irssi Powerline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darac/irssi-powerline/HEAD/doc/Irssi Powerline.png -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | Irssi-Powerline 2 | =============== 3 | 4 | An IRSSI theme with powerline goodness. 5 | 6 | Screenshot 7 | ---------- 8 | 9 | ![Irssi-Powerline](https://github.com/darac/irssi-powerline/raw/master/doc/Irssi%20Powerline.png) 10 | 11 | Installation 12 | ------------ 13 | 14 | 0. Clone the repository 15 | 16 | ```Shell 17 | git clone https://github.com/darac/irssi-powerline 18 | ``` 19 | 20 | 1. Copy the theme to your .irssi config directory 21 | 22 | ```Shell 23 | cp irssi-powerline/solarized-powerline.theme ~/.irssi/ 24 | ``` 25 | 26 | 2. Copy adv_windowlist.pl into your scripts/autorun directory 27 | 28 | ```Shell 29 | cp irssi-powerline/scripts/autorun/*.pl ~/.irssi/scripts/autorun 30 | ``` 31 | 32 | 3. If this is your first time using irssi, just copy the config file to your .irssi directory, otherwise, you will need to merge the changes (Basically, copy the `statusbar = { ... }` section across then, in settings ensure the fe-common/core and perl/core/scripts sections are copied over). 33 | -------------------------------------------------------------------------------- /doc/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | I don't expect to be able to encounter every bit of IRSSI's UI in my daily chatting, so if you find some sections that are unthemed, or where the themeing seems a bit off, please do open a ticket or raise a pull-request. 2 | 3 | If you open a bug asking for the theme to be applied somewhere, please let me know how to reproduce the message. 4 | 5 | If you want to raise a pull request, here are some notes to help: 6 | 7 | * I can't seem to theme the /HELP text properly. IRSSI prints the timestamp and then prints the help-text *unformatted*. If we want the expandable first segment, then we have to put up with this. 8 | * I am trying to bring the colouring close to the solarized standards so: 9 | * Most of the UI will be in shades of grey. 10 | * Channels are in GREEN 11 | * Nicks are in BLUE 12 | * "Own Nick" is in YELLOW 13 | * Modes are in PURPLE 14 | * Minimal use of bold (as some terminals conflate 'bold' and 'bright') 15 | * All colouring will be done using the basic 16-colour palette. My understanding is that correct use of solarized means that you should configure your _terminal_ to map the base-16 colours to the correct RGB shades; if that's not possible, then the 256-colour palette has some approximately-equivalent colours. 16 | 17 | 18 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | servers = ( 2 | { address = "irc.dal.net"; chatnet = "DALnet"; port = "6667"; }, 3 | { address = "irc.efnet.org"; chatnet = "EFNet"; port = "6667"; }, 4 | { address = "irc.esper.net"; chatnet = "EsperNet"; port = "6667"; }, 5 | { 6 | address = "chat.freenode.net"; 7 | chatnet = "Freenode"; 8 | port = "6667"; 9 | }, 10 | { 11 | address = "irc.gamesurge.net"; 12 | chatnet = "GameSurge"; 13 | port = "6667"; 14 | }, 15 | { address = "eu.irc6.net"; chatnet = "IRCnet"; port = "6667"; }, 16 | { address = "open.ircnet.net"; chatnet = "IRCnet"; port = "6667"; }, 17 | { 18 | address = "irc.ircsource.net"; 19 | chatnet = "IRCSource"; 20 | port = "6667"; 21 | }, 22 | { address = "irc.netfuze.net"; chatnet = "NetFuze"; port = "6667"; }, 23 | { address = "irc.oftc.net"; chatnet = "OFTC"; port = "6667"; }, 24 | { 25 | address = "irc.quakenet.org"; 26 | chatnet = "QuakeNet"; 27 | port = "6667"; 28 | }, 29 | { address = "irc.rizon.net"; chatnet = "Rizon"; port = "6667"; }, 30 | { address = "silc.silcnet.org"; chatnet = "SILC"; port = "706"; } 31 | ); 32 | 33 | chatnets = { 34 | DALnet = { 35 | type = "IRC"; 36 | max_kicks = "4"; 37 | max_msgs = "20"; 38 | max_whois = "30"; 39 | }; 40 | EFNet = { 41 | type = "IRC"; 42 | max_kicks = "1"; 43 | max_msgs = "4"; 44 | max_whois = "1"; 45 | }; 46 | EsperNet = { 47 | type = "IRC"; 48 | max_kicks = "1"; 49 | max_msgs = "4"; 50 | max_whois = "1"; 51 | }; 52 | Freenode = { 53 | type = "IRC"; 54 | max_kicks = "1"; 55 | max_msgs = "4"; 56 | max_whois = "1"; 57 | }; 58 | GameSurge = { 59 | type = "IRC"; 60 | max_kicks = "1"; 61 | max_msgs = "1"; 62 | max_whois = "1"; 63 | }; 64 | IRCnet = { 65 | type = "IRC"; 66 | max_kicks = "1"; 67 | max_msgs = "1"; 68 | max_whois = "1"; 69 | }; 70 | IRCSource = { 71 | type = "IRC"; 72 | max_kicks = "1"; 73 | max_msgs = "4"; 74 | max_whois = "1"; 75 | }; 76 | NetFuze = { 77 | type = "IRC"; 78 | max_kicks = "1"; 79 | max_msgs = "1"; 80 | max_whois = "1"; 81 | }; 82 | OFTC = { type = "IRC"; max_kicks = "1"; max_msgs = "1"; max_whois = "1"; }; 83 | QuakeNet = { 84 | type = "IRC"; 85 | max_kicks = "1"; 86 | max_msgs = "1"; 87 | max_whois = "1"; 88 | }; 89 | Rizon = { 90 | type = "IRC"; 91 | max_kicks = "1"; 92 | max_msgs = "1"; 93 | max_whois = "1"; 94 | }; 95 | SILC = { type = "SILC"; }; 96 | Undernet = { 97 | type = "IRC"; 98 | max_kicks = "1"; 99 | max_msgs = "1"; 100 | max_whois = "1"; 101 | }; 102 | }; 103 | 104 | channels = ( 105 | { name = "#lobby"; chatnet = "EsperNet"; autojoin = "No"; }, 106 | { name = "#freenode"; chatnet = "Freenode"; autojoin = "No"; }, 107 | { name = "#irssi"; chatnet = "Freenode"; autojoin = "No"; }, 108 | { name = "#gamesurge"; chatnet = "GameSurge"; autojoin = "No"; }, 109 | { name = "#irssi"; chatnet = "IRCNet"; autojoin = "No"; }, 110 | { name = "#ircsource"; chatnet = "IRCSource"; autojoin = "No"; }, 111 | { name = "#netfuze"; chatnet = "NetFuze"; autojoin = "No"; }, 112 | { name = "#oftc"; chatnet = "OFTC"; autojoin = "No"; }, 113 | { name = "silc"; chatnet = "SILC"; autojoin = "No"; } 114 | ); 115 | 116 | aliases = { 117 | ATAG = "WINDOW SERVER"; 118 | ADDALLCHANS = "SCRIPT EXEC foreach my \\$channel (Irssi::channels()) { Irssi::command(\"CHANNEL ADD -auto \\$channel->{name} \\$channel->{server}->{tag} \\$channel->{key}\")\\;}"; 119 | B = "BAN"; 120 | BACK = "AWAY"; 121 | BANS = "BAN"; 122 | BYE = "QUIT"; 123 | C = "CLEAR"; 124 | CALC = "EXEC - if command -v bc >/dev/null 2>&1\\; then printf '%s=' '$*'\\; echo '$*' | bc -l\\; else echo bc was not found\\; fi"; 125 | CHAT = "DCC CHAT"; 126 | 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) }"; 127 | DATE = "TIME"; 128 | DEHIGHLIGHT = "DEHILIGHT"; 129 | DESCRIBE = "ACTION"; 130 | DHL = "DEHILIGHT"; 131 | EXEMPTLIST = "MODE $C +e"; 132 | EXIT = "QUIT"; 133 | GOTO = "SCROLLBACK GOTO"; 134 | HIGHLIGHT = "HILIGHT"; 135 | HL = "HILIGHT"; 136 | HOST = "USERHOST"; 137 | INVITELIST = "MODE $C +I"; 138 | J = "JOIN"; 139 | K = "KICK"; 140 | KB = "KICKBAN"; 141 | KN = "KNOCKOUT"; 142 | LAST = "LASTLOG"; 143 | LEAVE = "PART"; 144 | M = "MSG"; 145 | MUB = "UNBAN *"; 146 | N = "NAMES"; 147 | NMSG = "^MSG"; 148 | P = "PART"; 149 | Q = "QUERY"; 150 | RESET = "SET -default"; 151 | RUN = "SCRIPT LOAD"; 152 | SAY = "MSG *"; 153 | SB = "SCROLLBACK"; 154 | SBAR = "STATUSBAR"; 155 | SIGNOFF = "QUIT"; 156 | SV = "MSG * Irssi $J ($V) - http://www.irssi.org"; 157 | T = "TOPIC"; 158 | UB = "UNBAN"; 159 | UMODE = "MODE $N"; 160 | UNSET = "SET -clear"; 161 | W = "WHO"; 162 | WC = "WINDOW CLOSE"; 163 | WG = "WINDOW GOTO"; 164 | WJOIN = "JOIN -window"; 165 | WI = "WHOIS"; 166 | WII = "WHOIS $0 $0"; 167 | WL = "WINDOW LIST"; 168 | WN = "WINDOW NEW HIDDEN"; 169 | WQUERY = "QUERY -window"; 170 | WW = "WHOWAS"; 171 | 1 = "WINDOW GOTO 1"; 172 | 2 = "WINDOW GOTO 2"; 173 | 3 = "WINDOW GOTO 3"; 174 | 4 = "WINDOW GOTO 4"; 175 | 5 = "WINDOW GOTO 5"; 176 | 6 = "WINDOW GOTO 6"; 177 | 7 = "WINDOW GOTO 7"; 178 | 8 = "WINDOW GOTO 8"; 179 | 9 = "WINDOW GOTO 9"; 180 | 10 = "WINDOW GOTO 10"; 181 | 11 = "WINDOW GOTO 11"; 182 | 12 = "WINDOW GOTO 12"; 183 | 13 = "WINDOW GOTO 13"; 184 | 14 = "WINDOW GOTO 14"; 185 | 15 = "WINDOW GOTO 15"; 186 | 16 = "WINDOW GOTO 16"; 187 | 17 = "WINDOW GOTO 17"; 188 | 18 = "WINDOW GOTO 18"; 189 | 19 = "WINDOW GOTO 19"; 190 | 20 = "WINDOW GOTO 20"; 191 | 21 = "WINDOW GOTO 21"; 192 | 22 = "WINDOW GOTO 22"; 193 | 23 = "WINDOW GOTO 23"; 194 | 24 = "WINDOW GOTO 24"; 195 | 25 = "WINDOW GOTO 25"; 196 | 26 = "WINDOW GOTO 26"; 197 | 27 = "WINDOW GOTO 27"; 198 | 28 = "WINDOW GOTO 28"; 199 | 29 = "WINDOW GOTO 29"; 200 | 30 = "WINDOW GOTO 30"; 201 | 31 = "WINDOW GOTO 31"; 202 | 32 = "WINDOW GOTO 32"; 203 | 33 = "WINDOW GOTO 33"; 204 | 34 = "WINDOW GOTO 34"; 205 | 35 = "WINDOW GOTO 35"; 206 | 36 = "WINDOW GOTO 36"; 207 | 37 = "WINDOW GOTO 37"; 208 | 38 = "WINDOW GOTO 38"; 209 | 39 = "WINDOW GOTO 39"; 210 | 40 = "WINDOW GOTO 40"; 211 | 41 = "WINDOW GOTO 41"; 212 | 42 = "WINDOW GOTO 42"; 213 | 43 = "WINDOW GOTO 43"; 214 | 44 = "WINDOW GOTO 44"; 215 | 45 = "WINDOW GOTO 45"; 216 | 46 = "WINDOW GOTO 46"; 217 | 47 = "WINDOW GOTO 47"; 218 | 48 = "WINDOW GOTO 48"; 219 | 49 = "WINDOW GOTO 49"; 220 | 50 = "WINDOW GOTO 50"; 221 | 51 = "WINDOW GOTO 51"; 222 | 52 = "WINDOW GOTO 52"; 223 | 53 = "WINDOW GOTO 53"; 224 | 54 = "WINDOW GOTO 54"; 225 | 55 = "WINDOW GOTO 55"; 226 | 56 = "WINDOW GOTO 56"; 227 | 57 = "WINDOW GOTO 57"; 228 | 58 = "WINDOW GOTO 58"; 229 | 59 = "WINDOW GOTO 59"; 230 | 60 = "WINDOW GOTO 60"; 231 | 61 = "WINDOW GOTO 61"; 232 | 62 = "WINDOW GOTO 62"; 233 | 63 = "WINDOW GOTO 63"; 234 | 64 = "WINDOW GOTO 64"; 235 | 65 = "WINDOW GOTO 65"; 236 | 66 = "WINDOW GOTO 66"; 237 | 67 = "WINDOW GOTO 67"; 238 | 68 = "WINDOW GOTO 68"; 239 | 69 = "WINDOW GOTO 69"; 240 | 70 = "WINDOW GOTO 70"; 241 | 71 = "WINDOW GOTO 71"; 242 | 72 = "WINDOW GOTO 72"; 243 | 73 = "WINDOW GOTO 73"; 244 | 74 = "WINDOW GOTO 74"; 245 | 75 = "WINDOW GOTO 75"; 246 | 76 = "WINDOW GOTO 76"; 247 | 77 = "WINDOW GOTO 77"; 248 | 78 = "WINDOW GOTO 78"; 249 | 79 = "WINDOW GOTO 79"; 250 | 80 = "WINDOW GOTO 80"; 251 | 81 = "WINDOW GOTO 81"; 252 | 82 = "WINDOW GOTO 82"; 253 | 83 = "WINDOW GOTO 83"; 254 | 84 = "WINDOW GOTO 84"; 255 | 85 = "WINDOW GOTO 85"; 256 | 86 = "WINDOW GOTO 86"; 257 | 87 = "WINDOW GOTO 87"; 258 | 88 = "WINDOW GOTO 88"; 259 | 89 = "WINDOW GOTO 89"; 260 | 90 = "WINDOW GOTO 90"; 261 | 91 = "WINDOW GOTO 91"; 262 | 92 = "WINDOW GOTO 92"; 263 | 93 = "WINDOW GOTO 93"; 264 | 94 = "WINDOW GOTO 94"; 265 | 95 = "WINDOW GOTO 95"; 266 | 96 = "WINDOW GOTO 96"; 267 | 97 = "WINDOW GOTO 97"; 268 | 98 = "WINDOW GOTO 98"; 269 | 99 = "WINDOW GOTO 99"; 270 | }; 271 | 272 | statusbar = { 273 | # formats: 274 | # when using {templates}, the template is shown only if it's argument isn't 275 | # empty unless no argument is given. for example {sb} is printed always, 276 | # but {sb $T} is printed only if $T isn't empty. 277 | 278 | items = { 279 | # start/end text in statusbars 280 | barstart = "{sbstart}"; 281 | barend = "{sbend}"; 282 | 283 | topicbarstart = "{topicsbstart}"; 284 | topicbarend = "{topicsbend}"; 285 | 286 | # treated "normally", you could change the time/user name to whatever 287 | time = "{sb $Z}"; 288 | user = "%3{sb {sbnickmode $[-2]cumode}%k$N %w {sbumode %K$usermode} %y%0{sbaway $A}}"; 289 | 290 | 291 | # treated specially .. window is printed with non-empty windows, 292 | # window_empty is printed with empty windows 293 | window = " {sb $tag/{channel $itemname} %C {sbmode $M}} %C%n "; 294 | window_empty = "{sb {sbservertag $tag}}"; 295 | prompt = "{prompt $[.15]itemname}"; 296 | prompt_empty = "{prompt $winname}"; 297 | 298 | topic = " $topic"; 299 | topic_empty = " Irssi v$J - http://www.irssi.org"; 300 | 301 | # all of these treated specially, they're only displayed when needed 302 | # Powerline 303 | lag = "{sb %r%w%1 ⌚ $0 %r%0%n}"; 304 | act = "{sb %y%0%n%3 %F$0%F %n}"; 305 | more = "%c%0%n%6 more ⬇ %c%0%n"; 306 | 307 | 308 | }; 309 | 310 | # there's two type of statusbars. root statusbars are either at the top 311 | # of the screen or at the bottom of the screen. window statusbars are at 312 | # the top/bottom of each split window in screen. 313 | default = { 314 | # the "default statusbar" to be displayed at the bottom of the window. 315 | # contains all the normal items. 316 | window = { 317 | disabled = "no"; 318 | 319 | # window, root 320 | type = "window"; 321 | # top, bottom 322 | placement = "bottom"; 323 | # number 324 | position = "1"; 325 | # active, inactive, always 326 | visible = "active"; 327 | 328 | # list of items in statusbar in the display order 329 | items = { 330 | barstart = { priority = "100"; }; 331 | user = { }; 332 | window = { }; 333 | window_empty = { }; 334 | lag = { priority = "-1"; }; 335 | more = { priority = "-1"; alignment = "right"; }; 336 | barend = { priority = "100"; alignment = "right"; }; 337 | otr = { }; 338 | usercount = { }; 339 | }; 340 | }; 341 | 342 | # statusbar to use in inactive split windows 343 | window_inact = { 344 | 345 | type = "window"; 346 | placement = "bottom"; 347 | position = "1"; 348 | visible = "inactive"; 349 | 350 | items = { 351 | barstart = { priority = "100"; }; 352 | window = { }; 353 | window_empty = { }; 354 | more = { priority = "-1"; alignment = "right"; }; 355 | barend = { priority = "100"; alignment = "right"; }; 356 | }; 357 | }; 358 | 359 | # we treat input line as yet another statusbar :) It's possible to 360 | # add other items before or after the input line item. 361 | prompt = { 362 | 363 | type = "root"; 364 | placement = "bottom"; 365 | # we want to be at the bottom always 366 | position = "100"; 367 | visible = "always"; 368 | 369 | items = { 370 | prompt = { priority = "-1"; }; 371 | prompt_empty = { priority = "-1"; }; 372 | # treated specially, this is the real input line. 373 | input = { priority = "10"; }; 374 | }; 375 | }; 376 | 377 | # topicbar 378 | topic = { 379 | 380 | type = "root"; 381 | placement = "top"; 382 | position = "1"; 383 | visible = "always"; 384 | 385 | items = { 386 | topicbarstart = { priority = "100"; }; 387 | topicbarend = { priority = "100"; alignment = "right"; }; 388 | topic = { }; 389 | topicempty = { }; 390 | }; 391 | }; 392 | awl_0 = { 393 | items = { 394 | barstart = { priority = "100"; }; 395 | awl_0 = { }; 396 | barend = { priority = "100"; alignment = "right"; }; 397 | }; 398 | }; 399 | awl_1 = { 400 | items = { 401 | barstart = { priority = "100"; }; 402 | awl_1 = { }; 403 | barend = { priority = "100"; alignment = "right"; }; 404 | }; 405 | }; 406 | }; 407 | }; 408 | settings = { 409 | core = { 410 | }; 411 | "fe-common/core" = { 412 | theme = "solarized-powerline"; 413 | hilight_color = "%R"; 414 | }; 415 | "fe-text" = { actlist_sort = "refnum"; }; 416 | "perl/core/scripts" = { 417 | ### For Solarized adv_windowlist.pl script 418 | awl_block = "-20"; 419 | awl_sbar_maxlength = "off"; 420 | awl_hide_empty = "yes"; 421 | awl_mouse = "yes"; 422 | awl_viewer = "no"; 423 | awl_maxlines = "0"; 424 | #fancy_abbrev = "head"; 425 | 426 | ### For Solarized trackbar.pl script 427 | trackbar_style = "%B"; 428 | title_screen_window = "yes"; 429 | awl_shared_sbar = "OFF"; 430 | }; 431 | }; 432 | keyboard = ( 433 | { key = "meta-[M"; id = "command"; data = "mouse_xterm"; } 434 | ); 435 | -------------------------------------------------------------------------------- /solarized-powerline.theme: -------------------------------------------------------------------------------- 1 | # vim: set expandtab: 2 | # irssi theme for the Solarized color palette with Powerline segments 3 | # (Adapted from solarized-universal theme by Huy Z: https://github.com/huyz/irssi-colors-solarized) 4 | # 5 | # Adaped by: 6 | # Paul Saunders http://... 7 | 8 | # When testing changes, the easiest way to reload the theme is with /RELOAD. 9 | # This reloads the configuration file too, so if you did any changes remember 10 | # to /SAVE it first. Remember also that /SAVE overwrites the theme file with 11 | # old data so keep backups :) 12 | 13 | # TEMPLATES: 14 | 15 | # The real text formats that irssi uses are the ones you can find with 16 | # /FORMAT command. Back in the old days all the colors and texts were mixed 17 | # up in those formats, and it was really hard to change the colors since you 18 | # might have had to change them in tens of different places. So, then came 19 | # this templating system. 20 | 21 | # Now the /FORMATs don't have any colors in them, and they also have very 22 | # little other styling. Most of the stuff you need to change is in this 23 | # theme file. If you can't change something here, you can always go back 24 | # to change the /FORMATs directly, they're also saved in these .theme files. 25 | 26 | # So .. the templates. They're those {blahblah} parts you see all over the 27 | # /FORMATs and here. Their usage is simply {name parameter1 parameter2}. 28 | # When irssi sees this kind of text, it goes to find "name" from abstracts 29 | # block below and sets "parameter1" into $0 and "parameter2" into $1 (you 30 | # can have more parameters of course). Templates can have subtemplates. 31 | # Here's a small example: 32 | # /FORMAT format hello {colorify {underline world}} 33 | # abstracts = { colorify = "%G$0-%n"; underline = "%U$0-%U"; } 34 | # When irssi expands the templates in "format", the final string would be: 35 | # hello %G%Uworld%U%n 36 | # ie. underlined bright green "world" text. 37 | # and why "$0-", why not "$0"? $0 would only mean the first parameter, 38 | # $0- means all the parameters. With {underline hello world} you'd really 39 | # want to underline both of the words, not just the hello (and world would 40 | # actually be removed entirely). 41 | 42 | # COLORS: 43 | 44 | # You can find definitions for the color format codes in docs/formats.txt. 45 | 46 | # There's one difference here though. %n format. Normally it means the 47 | # default color of the terminal (white mostly), but here it means the 48 | # "reset color back to the one it was in higher template". For example 49 | # if there was /FORMAT test %g{foo}bar, and foo = "%Y$0%n", irssi would 50 | # print yellow "foo" (as set with %Y) but "bar" would be green, which was 51 | # set at the beginning before the {foo} template. If there wasn't the %g 52 | # at start, the normal behaviour of %n would occur. If you _really_ want 53 | # to use the terminal's default color, use %N. 54 | 55 | ############################################################################# 56 | 57 | # default foreground color (%N) - -1 is the "default terminal color" 58 | default_color = "-1"; 59 | 60 | # print timestamp/servertag at the end of line, not at beginning 61 | info_eol = "false"; 62 | 63 | # these characters are automatically replaced with specified color 64 | # (dark grey by default) 65 | replaces = { "[]=" = "%_$*%_"; }; 66 | 67 | abstracts = { 68 | ## 69 | ## generic 70 | ## 71 | 72 | # text to insert at the beginning of each non-message line 73 | # %N = terminal default (e.g. white on transparent) 74 | # %G = brightgreen (Solarized: base01, i.e. darkest gray) 75 | #line_start = "%N%W %N"; 76 | line_start = "%N"; 77 | 78 | # timestamp styling, nothing by default 79 | # %N%8 = reverse normal (to match line_start) 80 | timestamp = "%k%7$*"; 81 | 82 | # any kind of text that needs hilighting, default is to bold 83 | hilight = "%_$*%_"; 84 | 85 | # any kind of error message, default is bright red 86 | # %r = red (Solarized: red) 87 | error = "%r$*%n"; 88 | 89 | # channel name is printed 90 | # SOLARIZED: Channels are GREEN 91 | channel = "%g$*%n"; 92 | 93 | # nick is printed 94 | # SOLARIZED: Nicks are BLUE. Own nick is Yellow 95 | nick = "%b{nickcolor $*}%n"; 96 | mynick = "%y{nickcolor $*}%n"; 97 | inick = "%4{nickcolor $*}%n"; # Inverted 98 | imynick = "%5{nickcolor $*}%n"; # Inverted 99 | 100 | # nick host is printed 101 | # (that's the "email" address of a user) 102 | nickhost = "$*"; 103 | 104 | # server name is printed 105 | server = "$*"; 106 | iserver = "%6$*%n"; 107 | 108 | # some kind of comment is printed 109 | comment = " $*"; 110 | 111 | # reason for something is printed (part, quit, kick, ..) 112 | reason = "$*"; 113 | 114 | # mode change is printed ([+o nick]) 115 | # SOLARIZED: Modes are purple 116 | mode = "%p$*%n"; 117 | 118 | ## 119 | ## channel specific messages 120 | ## 121 | 122 | # highlighted nick/host is printed (joins) 123 | # %c = cyan (Solarized: cyan) 124 | # %G = brightgreen (Solarized: base01) 125 | # NOTE: %n doesn't work in irssi v0.8.15 for some reason. So we have to end with the same color as line_start 126 | channick_hilight = "%c$*%G"; 127 | chanhost_hilight = "{nickhost $*}"; 128 | 129 | # nick/host is printed (parts, quits, etc.) 130 | channick = "%c$*%G"; 131 | chanhost = "{nickhost $*}"; 132 | 133 | # highlighted channel name is printed 134 | # SOLARIZED: Channels are Green 135 | channelhilight = "%g$*%N"; 136 | ichannelhilight = "%2$*%n%8"; 137 | 138 | # ban/ban exception/invite list mask is printed 139 | # %R = brightred (Solarized: orange) 140 | # %n = return 141 | ban = "%R$*%n"; 142 | 143 | 144 | ## 145 | ## messages 146 | ## 147 | 148 | # the basic styling of how to print message, $0 = nick mode, $1 = nick 149 | msgnick = "$0{nick $1-} %|"; 150 | 151 | # message from you is printed. "msgownnick" specifies the styling of the 152 | # nick ($0 part in msgnick) and "ownmsgnick" specifies the styling of the 153 | # whole line. 154 | 155 | # Example1: You want the message text to be green: 156 | # ownmsgnick = "{msgnick $0 $1-}%g"; 157 | # Example2.1: You want < and > chars to be yellow: 158 | # ownmsgnick = "%Y{msgnick $0 $1-%Y}%n"; 159 | # (you'll also have to remove <> from replaces list above) 160 | # Example2.2: But you still want to keep <> grey for other messages: 161 | # pubmsgnick = "%K{msgnick $0 $1-%K}%n"; 162 | # pubmsgmenick = "%K{msgnick $0 $1-%K}%n"; 163 | # pubmsghinick = "%K{msgnick $1 $0$2-%n%K}%n"; 164 | # ownprivmsgnick = "%K{msgnick $*%K}%n"; 165 | # privmsgnick = "%K{msgnick %R$*%K}%n"; 166 | 167 | # $0 = nick mode, $1 = nick 168 | # %3 = yellow 169 | ownmsgnick = "%3%w{msgnick $0$1-}%N%y %C"; 170 | ownnick = "%k$*"; 171 | 172 | # public message in channel, $0 = nick mode, $1 = nick 173 | # %4 = blue 174 | pubmsgnick = "%4%w{msgnick $0$1-}%N%b %n"; 175 | pubnick = "%k$*"; 176 | 177 | # public message in channel meant for me, $0 = nick mode, $1 = nick 178 | # %5 = magenta 179 | pubmsgmenick = "%5%w{msgnick $0$1-}%N%m %n"; 180 | menick = "%k$*"; 181 | 182 | # public highlighted message in channel 183 | # $0 = highlight color, $1 = nick mode, $2 = nick 184 | pubmsghinick = " %4%w%4{msgnick $1 %w$2-}%N%b %n"; 185 | 186 | # channel name is printed with message 187 | # This is printed whenever the channel name is printed for disambiguation, e.g. 188 | # while there is a query in the same window. Example: <+funnyuser:#test> hello 189 | msgchannel = "%G:%c$*%n"; 190 | 191 | # private message, $0 = nick, $1 = host 192 | # TODO: To test for Solarized 193 | privmsg = " %m$0%G<%n$1-%G>%n "; 194 | 195 | # private message from you, $0 = "msg", $1 = target nick 196 | # TODO: To test for Solarized 197 | ownprivmsg = " %b$0%G<%B$1-%G>%n "; 198 | 199 | # own private message in query 200 | ownprivmsgnick = "%3%w{msgnick $*}%N%y %C"; 201 | ownprivnick = "%k$*"; 202 | 203 | # private message in query 204 | # NOTE: for some reason, we gotta handle both the nick & msg on in this line 205 | privmsgnick = "%5%w{msgnick $*}%N%m %n"; 206 | 207 | ## 208 | ## Actions (/ME stuff) 209 | ## 210 | 211 | # used internally by this theme 212 | action_core = "%N%w %n$*"; 213 | 214 | # generic one that's used by most actions 215 | action = "{action_core %_$*%n} "; 216 | 217 | # own action, both private/public 218 | ownaction = "%N%w {mynick $0}%N%C $1-"; 219 | 220 | # own action with target, both private/public 221 | # NOTE: to test: /action NICK farted. (This will tell NICK that you farted) 222 | # This is like a /me but only you and NICK will see 223 | ownaction_target = "{action_core $0}%G:%y$1%n "; 224 | 225 | # private action sent by others 226 | # %M = brightmagenta (Solarized: violet) 227 | pvtaction = "%m (*) $*%n "; 228 | pvtaction_query = "{action $*}"; 229 | 230 | # public action sent by others 231 | pubaction = "%N%w {nick $0}%N $1-"; 232 | 233 | 234 | ## 235 | ## other IRC events 236 | ## 237 | 238 | # whois 239 | whois = "%k%7WHOIS %N%w%#%N $[8]0  %Y$1-"; 240 | 241 | # notices 242 | ownnotice = "%1%w %k$0 %k%1%8%0 {ichannelhilight $1-} %K%0%N "; 243 | notice = "%1%w %k$0- %r%0%N "; 244 | #notice = "%1%w %W$* %k%8%n "; 245 | pubnotice_channel = "{ichannelhighlight $*}"; 246 | #pubnotice_channel = "%G%n$*"; 247 | pvtnotice_host = " %w%n {hickhost $*}"; 248 | servernotice = "%1%w %k!$* %k%8%n "; 249 | 250 | # CTCPs 251 | ownctcp = "%w%4 %w$0%G %N%b %b$1-%G  "; 252 | ctcp = "%N%w%n %g$*%n"; 253 | 254 | # wallops 255 | wallop = "%c$*%n: "; 256 | wallop_nick = "%n$*"; 257 | wallop_action = "%c * $*%n "; 258 | 259 | # netsplits 260 | netsplit = "%r$*%n"; 261 | netjoin = "%g$*%n"; 262 | 263 | # /names list 264 | names_prefix = "%N%w%n"; 265 | names_nick = " %G%_$0%_$1-%N  "; 266 | names_nick_op = " %w%_$0$_$1-%n  "; 267 | names_nick_halfop = " %Y$_$0$_$1-%n  "; 268 | names_nick_voice = " %C$_$0$_$1-%n  "; 269 | names_users = "%w%2 $* %N%g%n"; 270 | names_channel = " %k$*"; 271 | 272 | # DCC 273 | dcc = "%N%w%n %g$*%n"; 274 | dccfile = "%_$*%_"; 275 | 276 | # DCC chat, own msg/action 277 | dccownmsg = "[%b$0%G($1-%G)%n] "; 278 | dccownnick = "%b$*%n"; 279 | dccownquerynick = "%c$*%n"; 280 | dccownaction = "{action $*}"; 281 | dccownaction_target = "{action_core $0}%G:%c$1%n "; 282 | 283 | # DCC chat, others 284 | dccmsg = " %g$1-%G($0%G)%n "; 285 | dccquerynick = "%g$*%n"; 286 | dccaction = "%c (*dcc*) $*%n %|"; 287 | 288 | ## 289 | ## statusbar 290 | ## 291 | 292 | # default background for all statusbars. You can also give 293 | # the default foreground color for statusbar items. 294 | # NOTE: if all the other overriding settings below are set, this only seems to affect 295 | # the content parts of the adv_windowlist. Default seems to be blue 296 | # NOTE: if not set, the default background seems to be %w%4 = white on blue 297 | # %4 = blue 298 | sb_background = "%k%6"; 299 | 300 | # default backround for "default" statusbar group 301 | # NOTE: this impacts the statusbar of an inactive window and the edges of adv_windowlist 302 | # %7 = white (wich looks gray on a regular terminal with white bg) 303 | sb_default_bg = "%Y%0"; 304 | 305 | # background for topicbar at the top (defaults to sb_default_bg) 306 | sb_topic_bg = "%W%0"; 307 | 308 | # background for the statusbar of active window. You can also give 309 | # the foreground color. 310 | # %2 = green 311 | sb_window_bg = "%w%0"; 312 | 313 | # background for prompt / input line 314 | sb_prompt_bg = "%w"; 315 | # background for info statusbar 316 | # %8 = reverse window 317 | # TODO: To test for Solarized 318 | sb_info_bg = "%8"; 319 | 320 | # text at the beginning of statusbars. sb-item already puts 321 | # space there,so we don't use anything by default. 322 | sbstart = "%2"; 323 | # text at the end of statusbars. Use space so that it's never 324 | # used for anything. 325 | sbend = "%n"; 326 | 327 | topicsbstart = "{sbstart $*}"; 328 | topicsbend = "{sbend $*}"; 329 | 330 | prompt = "%k%2$c $* %N%g%n "; 331 | 332 | # This controls each part of a statusbar, including the outer brackets of adv_windowlist 333 | sb = "$*"; 334 | # The mode is next to your NICK and the channel 335 | sbmode = "%p$*%w"; 336 | sbaway = "%c%w%6 zZzZ %c%0"; 337 | sbservertag = " $0 %b(change with ^X)%n"; 338 | sbnickmode = "$0"; 339 | 340 | # activity in statusbar 341 | 342 | # ',' separator between the window numbers 343 | # %g = green (same color as the active statusbar so we dont' see it) 344 | sb_act_sep = "%Y+"; 345 | # normal text 346 | # This also affects the color of the window with activity in adv_windowlist 347 | sb_act_text = "%C$*"; 348 | # public message (ordinary messages) 349 | sb_act_msg = "%c$*"; 350 | # hilight (when people actually chat) 351 | # This also affects the color of the window with direct message in adv_windowlist 352 | sb_act_hilight = "%m$*"; 353 | # hilight with specified color, $0 = color, $1 = text 354 | sb_act_hilight_color = "%R$1-"; 355 | 356 | ### For usercount.pl script 357 | 358 | sb_usercount = "{sb $0 users %B(%n$1-%B)}"; 359 | sb_uc_ircops = "%k*%n$*"; 360 | sb_uc_ops = "%y@%n$*"; 361 | sb_uc_halfops = "%p%%%n$*"; 362 | sb_uc_voices = "%b+%n$*"; 363 | sb_uc_normal = "%k %n$*"; 364 | sb_uc_space = "%K·%n"; 365 | 366 | ### For adv_windowlist.pl script 367 | 368 | # Mentioned in adv_windowlist but not default_theme 369 | sb_act_none = "$*"; 370 | }; 371 | 372 | formats = { 373 | "fe-common/core" = { 374 | daychange = " %g-----%w-%W-%n Day changed to %%D %W-%w-%g-----%n"; 375 | join = "%k%7JOIN %8 {ichannelhilight $2} %7 {inick $0}%0 %N%8%n%k%N {chanhost_hilight $1}"; 376 | line_start_irssi = "%RIRSSI%N%w %N"; 377 | new_topic = "%k%7TOPIC %8 {ichannelhilight $1} by {inick $0}%8  $2"; 378 | nick_changed = "%k%7RENAME %w%0 {nick $0}  {nick $1} %N%8%n%k%N"; 379 | part = "%k%7PART %8 {ichannelhilight $2} %7 {inick $0}%0 %N%n%k%8%N {reason $3}"; 380 | quit = "%k%7QUIT %8 {inick $0}%0 %N%n%k%8%N {reason $2}"; 381 | your_nick_changed = "%k%7RENAME %w%0 {mynick $0}  {mynick $1} %N%8%n%k%N"; 382 | }; 383 | "fe-common/irc" = { 384 | away = "%k%7AWAY %N%w%N"; 385 | banlist = "%p%7BANS %k $[-3]0 %8 {ichannelhilight $1} %N%8%n%k%N {ban $2}"; 386 | banlist_long = "%p%7BANS %k $[-3]0 %8 {ichannelhilight $1} {comment by {inick $3}%0%w, $4 secs ago} %N%8%n%k%N {ban $2}"; 387 | chanmode_change = "%k%7MODE %N%w%N {channelhilight $0} %w%n {mode $1}%n by {nick $2}"; 388 | channel_created = "%w%N Channel {channelhilight $0} created $1"; 389 | channel_mode = "%w%N mode/{channelhilight $0} {mode $1}"; 390 | ctcp_default_reply = "%W%0%N $0- "; 391 | ctcp_ping_reply = "%w%4 CTCP {hilight PING} reply %n%b%N {nick $0} %y%N%n $1.$[-3.0]2 seconds "; 392 | ctcp_reply = "%w%4 CTCP {hilight $0} reply %n%b%N {nick $1} %y%N $2 "; 393 | ctcp_reply_channel = "%w%4 CTCP {hilight $0} reply %n%b%N {nick $1} %y%N {channel $3} %y%N $2"; 394 | ctcp_requested = "%w%4%8%8 CTCP {hilight $2} from {mynick $4} %n%b%N by {hilight $0} {comment $1} %y%N $3"; 395 | default_event = "%N%w %N%n$1"; 396 | default_event_server = "%k%7$0 %N%w %N%n$1"; 397 | ebanlist = "%p%7BANS %k exception %8 {ichannelhilight $0} %N%8%n%k%N {ban $1}"; 398 | ebanlist_long = "%p%7BANS %k exception %8 {ichannelhilight $0} {comment by {inick $2}%0%w, $3 secs ago} %N%8%n%k%N {ban $1}"; 399 | end_of_who = "%7%kWHO %N%w%N END"; 400 | end_of_whois = "%7%kWHOIS %N%w%N END"; 401 | end_of_whowas = "%7%kWHOWAS %N%w%N END"; 402 | netsplit = "%w%1%8%8 NETSPLIT %k%0%8 {iserver $0} %7%8 {iserver $1}%8 %N%8%n%k%N Quits: $2"; 403 | netsplit_join = "%w%1 NETSPLIT %k%8%N Joins: $0"; 404 | netsplit_join_more = "%w%1 NETSPLIT %k%8%N Joins: $0 (+$1 more)"; 405 | netsplit_more = "%w%1%8%8 NETSPLIT %k%0%8 {iserver $0} %7%8 {iserver $1}%8 %N%8%n%k%N Quits: $2 (+$3 more, use /NETSPLIT to show all of them)"; 406 | no_topic = "%w%N No topic set for {channelhilight $0}"; 407 | own_ctcp = "%w%4 CTCP {hilight $2-}  {inick $0} %n%b%N"; 408 | server_chanmode_change = "%w%1 SERVERMODE %k%8 {channelhilight $0} {mode $1} by {nick $2}"; 409 | topic = "%k%7TOPIC %N%w%N {channel $0}%N  $1"; 410 | topic_info = "%k%7TOPIC %N%w%N {nick $0}%N {nickhost $2}  $1"; 411 | unaway = "%k%7AWAY %N%W%N END"; 412 | usermode_change = "%k%7MODE %N%w%N {mode $0}%N for {mynick $1}"; 413 | who = "%7%kWHO %4%w {inick $[!9]1} %2%b %k$0 %0%g Status: $[!3]2  $[-2]3 hops %N%k%N {nickhost $4@$5} {comment $6}"; 414 | whois = "%7%kWHOIS %N%w%n {nick $0} {nickhost $1@$2}%:{whois ircname $3}"; 415 | whowas = "%7%kWHOWAS %N%w%n {nick $0} {nickhost $1@$2}%:{whois was $3}"; 416 | }; 417 | "Irssi::Script::adv_windowlist" = { 418 | awl_display_header = ""; 419 | awl_display_key = " %0$N  $H$C$S %n"; 420 | awl_display_key_active = "%G%0%8 %7$N  $H$C$S%8 %0%8%8%n"; 421 | awl_display_key_visible = "%k%3 %m$N %k $H$C$S %8%8%n"; 422 | awl_display_nokey = " %0$N  %U$C%U %n"; 423 | awl_display_nokey_active = "%k%4 %w$N %k %U$C%U %8%8%n"; 424 | awl_display_nokey_visible = "%k%3 %m$N %k %U$C%U %8%8%n"; 425 | awl_separator = ""; 426 | }; 427 | }; 428 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /scripts/autorun/adv_windowlist.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | our $VERSION = '1.3'; # 463402cffae35e5 5 | our %IRSSI = ( 6 | authors => 'Nei', 7 | contact => 'Nei @ anti@conference.jabber.teamidiot.de', 8 | url => "http://anti.teamidiot.de/", 9 | name => 'adv_windowlist', 10 | description => 'Adds a permanent advanced window list on the right or in a status bar.', 11 | license => 'GNU GPLv2 or later', 12 | ); 13 | 14 | # UPGRADE NOTE 15 | # ============ 16 | # for users of 0.7 or earlier series, please note that appearance 17 | # settings have moved to /format, i.e. inside your theme! 18 | # the fifo (screen) has been replaced by an external viewer script 19 | 20 | # Usage 21 | # ===== 22 | # copy the script to ~/.irssi/scripts/ 23 | # 24 | # In irssi: 25 | # 26 | # /run adv_windowlist 27 | # 28 | # In your shell (for example a tmux split): 29 | # 30 | # perl ~/.irssi/scripts/adv_windowlist.pl 31 | # 32 | # To use sbar mode instead: 33 | # 34 | # /toggle awl_viewer 35 | # 36 | # Hint: to get rid of the old [Act:] display 37 | # /statusbar window remove act 38 | # 39 | # to get it back: 40 | # /statusbar window add -after lag -priority 10 act 41 | 42 | # Options 43 | # ======= 44 | # formats can be cleared with /format -delete 45 | # 46 | # /format awl_display_(no)key(_active|_visible) 47 | # * string : Format String for one window. The following $'s are expanded: 48 | # $C : Name 49 | # $N : Number of the Window 50 | # $Q : meta-Keymap 51 | # $H : Start hilighting 52 | # $S : Stop hilighting 53 | # /+++++++++++++++++++++++++++++++++, 54 | # | **** I M P O R T A N T : **** | 55 | # | | 56 | # | don't forget to use $S if you | 57 | # | used $H before! | 58 | # | | 59 | # '+++++++++++++++++++++++++++++++++/ 60 | # key : a key binding that goes to this window could be detected in /bind 61 | # nokey : no such key binding was detected 62 | # active : window would receive the input you are currently typing 63 | # visible : window is also visible on screen but not active (a split window) 64 | # 65 | # /format awl_name_display 66 | # * string : Format String for window names 67 | # $0 : name as formatted by the settings 68 | # 69 | # /format awl_display_header 70 | # * string : Format String for this header line. The following $'s are expanded: 71 | # $C : network tag 72 | # 73 | # /format awl_separator(2) 74 | # * string : Character to use between the channel entries 75 | # variant 2 can be used for alternating separators (only in status bar 76 | # without block display) 77 | # 78 | # /format awl_abbrev_chars 79 | # * string : Character to use when shortening long names. The second character 80 | # will be used if two blocks need to be filled. 81 | # 82 | # /format awl_viewer_item_bg 83 | # * string : Format String specifying the viewer's item background colour 84 | # 85 | # /set awl_prefer_name 86 | # * this setting decides whether awl will use the active_name (OFF) or the 87 | # window name as the name/caption in awl_display_*. 88 | # That way you can rename windows using /window name myownname. 89 | # 90 | # /set awl_hide_empty 91 | # * if visible windows without items should be hidden from the window list 92 | # set it to 0 to show all windows 93 | # 1 to hide visible windows without items (negative exempt 94 | # active window) 95 | # 96 | # /set awl_hide_data 97 | # * num : hide the window if its data_level is below num 98 | # set it to 0 to basically disable this feature, 99 | # 1 if you don't want windows without activity to be shown 100 | # 2 to show only those windows with channel text or hilight 101 | # 3 to show only windows with hilight (negative exempt active window) 102 | # 103 | # /set awl_hide_name_data 104 | # * num : hide the name of the window if its data_level is below num 105 | # (only works in status bar without block display) 106 | # you will want to change your formats to add $H...$S around $Q or $N 107 | # if you plan to use this 108 | # 109 | # /set awl_maxlines 110 | # * num : number of lines to use for the window list (0 to disable, negative 111 | # lock) 112 | # 113 | # /set awl_maxcolumns 114 | # * num : number of columns to use for the window list when using the 115 | # tmux integration (0 to disable) 116 | # 117 | # /set awl_block 118 | # * num : width of a column in viewer mode (negative values = block 119 | # display in status bar mode) 120 | # /+++++++++++++++++++++++++++++++++, 121 | # | ****** W A R N I N G ! ****** | 122 | # | | 123 | # | If your block display looks | 124 | # | DISTORTED, you need to add the | 125 | # | following line to your .theme | 126 | # | file under | 127 | # | abstracts = { : | 128 | # | | 129 | # | sb_act_none = "%K$*"; | 130 | # | | 131 | # '+++++++++++++++++++++++++++++++++/ 132 | # 133 | # /set awl_sbar_maxlength 134 | # * if you enable the maxlength setting, the block width will be used as a 135 | # maximum length for the non-block status bar mode too. 136 | # 137 | # /set awl_height_adjust 138 | # * num : how many lines to leave empty in viewer mode 139 | # 140 | # /set awl_sort <-data_level|-last_line|refnum> 141 | # * you can change the window sort order with this variable 142 | # -data_level : sort windows with hilight first 143 | # -last_line : sort windows in order of activity 144 | # refnum : sort windows by window number 145 | # active/server/tag : sort by server name 146 | # "-" reverses the sort order 147 | # typechecks are supported via ::, e.g. active::Query or active::Irc::Query 148 | # undefinedness can be checked with ~, e.g. ~active 149 | # string comparison can be done with =, e.g. name=(status) 150 | # to make sort case insensitive, use #i, e.g. name#i 151 | # any key in the window hash can be tested, e.g. active/chat_type=XMPP 152 | # multiple criteria can be separated with , or +, e.g. -data_level+-last_line 153 | # 154 | # /set awl_placement 155 | # /set awl_position 156 | # * these settings correspond to /statusbar because awl will create 157 | # status bars for you 158 | # (see /help statusbar to learn more) 159 | # 160 | # /set awl_all_disable 161 | # * if you set awl_all_disable to ON, awl will also remove the 162 | # last status bar it created if it is empty. 163 | # As you might guess, this only makes sense with awl_hide_data > 0 ;) 164 | # 165 | # /set awl_viewer 166 | # * enable the external viewer script 167 | # 168 | # /set awl_viewer_launch 169 | # * try to auto-launch the viewer under tmux or with a shell command 170 | # /awl restart is required all auto-launch related settings to take 171 | # effect 172 | # 173 | # /set awl_viewer_tmux_position 174 | # * try to split in this direction when using tmux for the viewer 175 | # custom : use custom_command setting 176 | # 177 | # /set awl_viewer_xwin_command 178 | # * custom command to run in order to start the viewer when irssi is 179 | # running under X 180 | # %A - gets replaced by the command to run the viewer 181 | # %qA - additionally quote the command 182 | # 183 | # /set awl_viewer_custom_command 184 | # * custom command to run in order to start the viewer 185 | # 186 | # /set awl_viewer_launch_env 187 | # * specific environment settings for use on viewer auto-launch, 188 | # without the AWL_ prefix 189 | # 190 | # /set awl_shared_sbar 191 | # * share a status bar for the first awl item, you will need to manually 192 | # /statusbar window add -after lag -priority 10 awl_shared 193 | # left : space in cells occupied on the left of status bar 194 | # right : space occupied on the right 195 | # Note: you need to replace "left" AND "right" with the appropriate numbers! 196 | # 197 | # /set awl_path 198 | # * path to the file which the viewer script reads 199 | # 200 | # /set fancy_abbrev 201 | # * how to shorten too long names 202 | # no : shorten in the middle 203 | # head : always cut off the ends 204 | # strict : shorten repeating substrings 205 | # fancy : combination of no+strict 206 | # 207 | # /set awl_custom_xform 208 | # * specify a custom routine to transform window names 209 | # example: s/^#// remove the #-mark of IRC channels 210 | # the special flags $CHANNEL / $TAG / $QUERY / $NAME can be 211 | # tested in conditionals 212 | # 213 | # /set awl_last_line_shade 214 | # * set timeout to shade activity base colours, to enable 215 | # you also need to add +-last_line to awl_sort 216 | # (requires 256 colour support) 217 | # 218 | # /set awl_no_mode_hint 219 | # * whether to show the hint of running the viewer script in the 220 | # status bar 221 | # 222 | # /set awl_mouse 223 | # * enable the terminal mouse in irssi 224 | # (use the awl-patched mouse.pl for gestures and commands if you need 225 | # them and disable mouse_escape) 226 | # 227 | # /set awl_mouse_offset 228 | # * specifies where on the screen is the awl status bar 229 | # (0 = on top/bottom, 1 = one additional line in between, 230 | # e.g. prompt) 231 | # you MUST set this correctly otherwise the mouse coordinates will 232 | # be off 233 | # 234 | # /set mouse_scroll 235 | # * how many lines the mouse wheel scrolls 236 | # 237 | # /set mouse_escape 238 | # * seconds to disable the mouse, when not clicked on the windowlist 239 | # 240 | 241 | # Commands 242 | # ======== 243 | # /awl redraw 244 | # * redraws the windowlist. There may be occasions where the 245 | # windowlist can get destroyed so you can use this command to 246 | # force a redraw. 247 | # 248 | # /awl restart 249 | # * restart the connection to the viewer script. 250 | 251 | # Viewer script 252 | # ============= 253 | # When run from the command line, adv_windowlist acts as the viewer 254 | # script to be used together with the irssi script to display the 255 | # window list in a sidebar/terminal of its own. 256 | # 257 | # One optional parameter is accepted, the awl_path 258 | # 259 | # The viewer can be configured by three environment variables: 260 | # 261 | # AWL_HI9=1 262 | # * interpret %9 as high-intensity toggle instead of bold. This had 263 | # been the default prior to version 0.9b8 264 | # 265 | # AWL_AUTOFOCUS=0 266 | # * disable auto-focus behaviour when activating a window 267 | # 268 | # AWL_NOTITLE=1 269 | # * disable the title bar 270 | 271 | # Nei =^.^= ( anti@conference.jabber.teamidiot.de ) 272 | 273 | no warnings 'redefine'; 274 | use constant IN_IRSSI => __PACKAGE__ ne 'main' || $ENV{IRSSI_MOCK}; 275 | use constant SCRIPT_FILE => __FILE__; 276 | no if !IN_IRSSI, strict => (qw(subs refs)); 277 | use if IN_IRSSI, Irssi => (); 278 | use if IN_IRSSI, 'Irssi::TextUI' => (); 279 | use v5.10; 280 | use Encode; 281 | use Storable (); 282 | use IO::Socket::UNIX; 283 | use List::Util qw(min max reduce); 284 | use Hash::Util qw(lock_keys); 285 | use Text::ParseWords qw(shellwords); 286 | 287 | BEGIN { 288 | if ($] < 5.012) { 289 | *CORE::GLOBAL::length = *CORE::GLOBAL::length = sub (_) { 290 | defined $_[0] ? CORE::length($_[0]) : undef 291 | }; 292 | *Irssi::active_win = {}; # hide incorrect warning 293 | } 294 | } 295 | 296 | unless (IN_IRSSI) { 297 | local *_ = \@ARGV; 298 | &AwlViewer::main; 299 | exit; 300 | } 301 | 302 | 303 | use constant GLOB_QUEUE_TIMER => 100; 304 | 305 | our $BLOCK_ALL; # localized blocker 306 | my @actString; # status bar texts 307 | my @win_items; 308 | my $currentLines = 0; 309 | my %awins; 310 | my $globTime; # timer to limit remake calls 311 | 312 | my %CHANGED; 313 | my $VIEWER_MODE; 314 | my $MOUSE_ON; 315 | my %mouse_coords; 316 | my %statusbars; 317 | my %S; # settings 318 | my $settings_str = ''; 319 | my $window_sort_func; 320 | my $custom_xform; 321 | my ($sb_base_width, $sb_base_width_pre, $sb_base_width_post); 322 | my $print_text_activity; 323 | my $shade_line_timer; 324 | my ($screenHeight, $screenWidth); 325 | my %viewer; 326 | 327 | my (%keymap, %nummap, %wnmap, %specialmap, %wnmap_exp, %custom_key_map); 328 | my %banned_channels; 329 | my %abbrev_cache; 330 | 331 | use constant setc => 'awl'; 332 | 333 | sub set ($) { 334 | setc . '_' . $_[0] 335 | } 336 | 337 | sub add_statusbar { 338 | for (@_) { 339 | # add subs 340 | my $l = set $_; 341 | { 342 | my $close = $_; 343 | no strict 'refs'; 344 | *{$l} = sub { awl($close, @_) }; 345 | } 346 | Irssi::command("statusbar $l reset"); 347 | Irssi::command("statusbar $l enable"); 348 | if (lc $S{placement} eq 'top') { 349 | Irssi::command("statusbar $l placement top"); 350 | } 351 | if (my $x = $S{position}) { 352 | Irssi::command("statusbar $l position $x"); 353 | } 354 | Irssi::command("statusbar $l add -priority 100 -alignment left barstart"); 355 | Irssi::command("statusbar $l add $l"); 356 | Irssi::command("statusbar $l add -priority 100 -alignment right barend"); 357 | Irssi::command("statusbar $l disable"); 358 | Irssi::statusbar_item_register($l, '$0', $l); 359 | $statusbars{$_} = 1; 360 | Irssi::command("statusbar $l enable"); 361 | } 362 | } 363 | 364 | sub remove_statusbar { 365 | for (@_) { 366 | my $l = set $_; 367 | Irssi::command("statusbar $l disable"); 368 | Irssi::command("statusbar $l reset"); 369 | Irssi::statusbar_item_unregister($l); 370 | { 371 | no strict 'refs'; 372 | undef &{$l}; 373 | } 374 | delete $statusbars{$_}; 375 | } 376 | } 377 | 378 | my $awl_shared_empty = sub { 379 | return if $BLOCK_ALL; 380 | my ($item, $get_size_only) = @_; 381 | $item->default_handler($get_size_only, '', '', 0); 382 | }; 383 | 384 | sub syncLines { 385 | my $maxLines = $S{maxlines}; 386 | my $newLines = ($maxLines > 0 and @actString > $maxLines) ? 387 | $maxLines : 388 | ($maxLines < 0) ? 389 | -$maxLines : 390 | @actString; 391 | $currentLines = 1 if !$currentLines && $S{shared_sbar}; 392 | if ($S{shared_sbar} && !$statusbars{shared}) { 393 | my $l = set 'shared'; 394 | { 395 | no strict 'refs'; 396 | *{$l} = sub { 397 | return if $BLOCK_ALL; 398 | my ($item, $get_size_only) = @_; 399 | 400 | my $text = $actString[0]; 401 | my $pat = defined $text ? '{sb '.ucfirst(setc()).': $*}' : '{sb }'; 402 | $text //= ''; 403 | $item->default_handler($get_size_only, $pat, $text, 0); 404 | }; 405 | } 406 | $statusbars{shared} = 1; 407 | remove_statusbar (0) if $statusbars{0}; 408 | } 409 | elsif ($statusbars{shared} && !$S{shared_sbar}) { 410 | add_statusbar (0) if $currentLines && $newLines; 411 | delete $statusbars{shared}; 412 | my $l = set 'shared'; 413 | { 414 | no strict 'refs'; 415 | *{$l} = $awl_shared_empty; 416 | } 417 | } 418 | if ($currentLines == $newLines) { return; } 419 | elsif ($newLines > $currentLines) { 420 | add_statusbar ($currentLines .. ($newLines - 1)); 421 | } 422 | else { 423 | remove_statusbar (reverse ($newLines .. ($currentLines - 1))); 424 | } 425 | $currentLines = $newLines; 426 | } 427 | 428 | sub awl { 429 | return if $BLOCK_ALL; 430 | my ($line, $item, $get_size_only) = @_; 431 | 432 | my $text = $actString[$line]; 433 | my $pat = defined $text ? '{sb $*}' : '{sb }'; 434 | $text //= ''; 435 | $item->default_handler($get_size_only, $pat, $text, 0); 436 | } 437 | 438 | # remove old statusbars 439 | { my %killBar; 440 | sub get_old_status { 441 | my ($textDest, $cont, $cont_stripped) = @_; 442 | if ($textDest->{level} == 524288 and $textDest->{target} eq '' and !defined $textDest->{server}) { 443 | my $name = quotemeta(set ''); 444 | if ($cont_stripped =~ m/^$name(\d+)\s/) { $killBar{$1} = 1; } 445 | Irssi::signal_stop; 446 | } 447 | } 448 | sub killOldStatus { 449 | %killBar = (); 450 | Irssi::signal_add_first('print text' => 'get_old_status'); 451 | Irssi::command('statusbar'); 452 | Irssi::signal_remove('print text' => 'get_old_status'); 453 | remove_statusbar(keys %killBar); 454 | } 455 | } 456 | 457 | sub get_keymap { 458 | my ($textDest, undef, $cont_stripped) = @_; 459 | if ($textDest->{level} == 524288 and $textDest->{target} eq '' and !defined $textDest->{server}) { 460 | my $one_meta_or_ctrl_key = qr/((?:meta-)*?)(?:(meta-|\^)(\S)|(\w+))/; 461 | $cont_stripped = as_uni($cont_stripped); 462 | if ($cont_stripped =~ m/((?:$one_meta_or_ctrl_key-)*$one_meta_or_ctrl_key)\s+(.*)$/) { 463 | my ($combo, $command) = ($1, $10); 464 | my $map = ''; 465 | while ($combo =~ s/(?:-|^)$one_meta_or_ctrl_key$//) { 466 | my ($level, $ctl, $key, $nkey) = ($1, $2, $3, $4); 467 | my $numlevel = ($level =~ y/-//); 468 | $ctl = '' if !$ctl || $ctl ne '^'; 469 | $map = ('-' x ($numlevel%2)) . ('+' x ($numlevel/2)) . 470 | $ctl . (defined $key ? $key : "\01$nkey\01") . $map; 471 | } 472 | for ($command) { 473 | last unless length $map; 474 | if (/^change_window (\d+)/i) { 475 | $nummap{$1} = $map; 476 | } 477 | elsif (/^command window goto (\S+)/i) { 478 | my $window = $1; 479 | if ($window !~ /\D/) { 480 | $nummap{$window} = $map; 481 | } 482 | elsif (lc $window eq 'active') { 483 | $specialmap{_active} = $map; 484 | } 485 | else { 486 | $wnmap{$window} = $map; 487 | } 488 | } 489 | elsif (/^(?:active_window|command (ack))/i) { 490 | $specialmap{_active} = $map; 491 | $viewer{use_ack} = !!$1; 492 | } 493 | elsif (/^command window last/i) { 494 | $specialmap{_last} = $map; 495 | } 496 | elsif (/^(?:upper_window|command window up)/i) { 497 | $specialmap{_up} = $map; 498 | } 499 | elsif (/^(?:lower_window|command window down)/i) { 500 | $specialmap{_down} = $map; 501 | } 502 | elsif (/^key\s+(\w+)/i) { 503 | $custom_key_map{$1} = $map; 504 | } 505 | } 506 | } 507 | Irssi::signal_stop; 508 | } 509 | } 510 | 511 | sub update_keymap { 512 | %nummap = %wnmap = %specialmap = %custom_key_map = (); 513 | Irssi::signal_remove('command bind' => 'watch_keymap'); 514 | Irssi::signal_add_first('print text' => 'get_keymap'); 515 | Irssi::command('bind'); 516 | Irssi::signal_remove('print text' => 'get_keymap'); 517 | for (keys %custom_key_map) { 518 | if (exists $custom_key_map{$_} && 519 | $custom_key_map{$_} =~ s/\01(\w+)\01/exists $custom_key_map{$1} ? $custom_key_map{$1} : "\02"/ge) { 520 | if ($custom_key_map{$_} =~ /\02/) { 521 | delete $custom_key_map{$_}; 522 | } 523 | else { 524 | redo; 525 | } 526 | } 527 | } 528 | for my $keymap (\(%specialmap, %wnmap, %nummap)) { 529 | for (keys %$keymap) { 530 | if ($keymap->{$_} =~ s/\01(\w+)\01/exists $custom_key_map{$1} ? $custom_key_map{$1} : "\02"/ge) { 531 | if ($keymap->{$_} =~ /\02/) { 532 | delete $keymap->{$_}; 533 | } 534 | } 535 | } 536 | } 537 | Irssi::signal_add('command bind' => 'watch_keymap'); 538 | delete $viewer{client_keymap}; 539 | &wl_changed; 540 | } 541 | 542 | # watch keymap changes 543 | sub watch_keymap { 544 | Irssi::timeout_add_once(1000, 'update_keymap', undef); 545 | } 546 | 547 | { my %strip_table = ( 548 | # fe-common::core::formats.c:format_expand_styles 549 | # delete format_backs format_fores bold_fores other stuff 550 | (map { $_ => '' } (split //, '04261537' . 'kbgcrmyw' . 'KBGCRMYW' . 'U9_8I:|FnN>#[' . 'pP')), 551 | # escape 552 | (map { $_ => $_ } (split //, '{}%')), 553 | ); 554 | sub ir_strip_codes { # strip %codes 555 | my $o = shift; 556 | $o =~ s/(%(%|Z.{6}|z.{6}|X..|x..|.))/exists $strip_table{$2} ? $strip_table{$2} : 557 | $2 =~ m{x(?:0[a-f]|[1-6][0-9a-z]|7[a-x])|z[0-9a-f]{6}}i ? '' : $1/gex; 558 | $o 559 | } 560 | } 561 | ## ir_parse_special -- wrapper around parse_special 562 | ## $i - input format 563 | ## $args - array ref of arguments to format 564 | ## $win - different target window (default current window) 565 | ## $flags - different kind of escape flags (default 4|8) 566 | ## returns formatted str 567 | sub ir_parse_special { 568 | my $o; 569 | my $i = shift; 570 | my $args = shift // []; 571 | y/ /\177/ for @$args; # hack to escape spaces 572 | my $win = shift || Irssi::active_win; 573 | my $flags = shift // 0x4|0x8; 574 | my @cmd_args = ($i, (join ' ', @$args), $flags); 575 | my $server = Irssi::active_server(); 576 | if (ref $win and ref $win->{active}) { 577 | $o = $win->{active}->parse_special(@cmd_args); 578 | } 579 | elsif (ref $win and ref $win->{active_server}) { 580 | $o = $win->{active_server}->parse_special(@cmd_args); 581 | } 582 | elsif (ref $server) { 583 | $o = $server->parse_special(@cmd_args); 584 | } 585 | else { 586 | $o = &Irssi::parse_special(@cmd_args); 587 | } 588 | $o =~ y/\177/ /; 589 | $o 590 | } 591 | 592 | sub sb_format_expand { # Irssi::current_theme->format_expand wrapper 593 | Irssi::current_theme->format_expand( 594 | $_[0], 595 | ( 596 | Irssi::EXPAND_FLAG_IGNORE_REPLACES 597 | | 598 | ($_[1] ? 0 : Irssi::EXPAND_FLAG_IGNORE_EMPTY) 599 | ) 600 | ) 601 | } 602 | 603 | { my $term_type = Irssi::version > 20040819 ? 'term_charset' : 'term_type'; 604 | local $@; 605 | eval { require Text::CharWidth; }; 606 | unless ($@) { 607 | *screen_length = sub { Text::CharWidth::mbswidth($_[0]) }; 608 | } 609 | else { 610 | my $err = $@; chomp $err; $err =~ s/\sat .* line \d+\.$//; 611 | #Irssi::print("%_$IRSSI{name}: warning:%_ Text::CharWidth module failed to load. Length calculation may be off! Error was:"); 612 | print "%_$IRSSI{name}:%_ $err"; 613 | *screen_length = sub { 614 | my $temp = shift; 615 | if (lc Irssi::settings_get_str($term_type) eq 'utf-8') { 616 | Encode::_utf8_on($temp); 617 | } 618 | length($temp) 619 | }; 620 | } 621 | sub as_uni { 622 | no warnings 'utf8'; 623 | Encode::decode(Irssi::settings_get_str($term_type), $_[0], 0) 624 | } 625 | sub as_tc { 626 | Encode::encode(Irssi::settings_get_str($term_type), $_[0], 0) 627 | } 628 | } 629 | 630 | sub sb_length { 631 | screen_length(ir_strip_codes($_[0])) 632 | } 633 | 634 | sub run_custom_xform { 635 | local $@; 636 | eval { 637 | $custom_xform->() 638 | }; 639 | if ($@) { 640 | $@ =~ /^(.*)/; 641 | print '%_'.(set 'custom_xform').'%_ died (disabling): '.$1; 642 | $custom_xform = undef; 643 | } 644 | } 645 | 646 | sub remove_uniform { 647 | my $o = shift; 648 | $o =~ s/^xmpp:(.*?[%@]).+\.[^.]+$/$1/ or 649 | $o =~ s#^psyc://.+\.[^.]+/([@~].*)$#$1#; 650 | if ($custom_xform) { 651 | run_custom_xform() for $o; 652 | } 653 | $o 654 | } 655 | 656 | sub remove_uniform_vars { 657 | my $win = shift; 658 | my $name = __PACKAGE__ . '::custom_xform::' . $win->{active}{type} 659 | if ref $win->{active} && $win->{active}{type}; 660 | no strict 'refs'; 661 | local ${$name} = 1 if $name; 662 | remove_uniform(+shift); 663 | } 664 | 665 | sub lc1459 { 666 | my $x = shift; 667 | $x =~ y/][\\^/}{|~/; 668 | lc $x 669 | } 670 | 671 | sub window_list { 672 | sort $window_sort_func Irssi::windows; 673 | } 674 | 675 | sub _calculate_abbrev { 676 | my ($wins, $abbrevList) = @_; 677 | if ($S{fancy_abbrev} !~ /^(no|off|head)/i) { 678 | my @nameList = map { ref $_ ? remove_uniform_vars($_, as_uni($_->get_active_name) // '') : '' } @$wins; 679 | for (my $i = 0; $i < @nameList - 1; ++$i) { 680 | my ($x, $y) = ($nameList[$i], $nameList[$i + 1]); 681 | s/^[+#!=]// for $x, $y; 682 | my $res = exists $abbrev_cache{$x}{$y} ? $abbrev_cache{$x}{$y} 683 | : $abbrev_cache{$x}{$y} = string_LCSS($x, $y); 684 | if (defined $res) { 685 | for ($nameList[$i], $nameList[$i + 1]) { 686 | $abbrevList->{$_} //= int((index $_, $res) + (length $res) / 2); 687 | } 688 | } 689 | } 690 | } 691 | } 692 | 693 | my %act_last_line_shades = ( 694 | r => [qw[ 50 40 30 20 ]], 695 | g => [qw[ 1O 1I 1C 16 ]], 696 | y => [qw[ 5O 4I 3C 26 ]], 697 | b => [qw[ 15 14 13 12 ]], 698 | m => [qw[ 54 43 32 21 ]], 699 | c => [qw[ 1S 1L 1E 17 ]], 700 | w => [qw[ 7W 7T 7Q 3E ]], 701 | K => [qw[ 7M 7K 27 7H ]], 702 | R => [qw[ 60 50 40 30 ]], 703 | G => [qw[ 1U 1O 1I 1C ]], 704 | Y => [qw[ 6U 5O 4I 3C ]], 705 | B => [qw[ 2B 2A 29 28 ]], 706 | M => [qw[ 65 54 43 32 ]], 707 | C => [qw[ 1Z 1S 1L 1E ]], 708 | W => [qw[ 6Z 5S 7R 7O ]], 709 | ); 710 | 711 | sub _format_display { 712 | my (undef, $format, $cformat, $hilight, $name, $number, $key, $win) = @_; 713 | if ($print_text_activity && $S{line_shade}) { 714 | my @hilight_code = split /\177/, sb_format_expand("{$hilight \177}"), 2; 715 | my $max_time = max(1, log($S{line_shade}) - log(1000)); 716 | my $time_delta = min(3, min($max_time, log(max(1, time - $win->{last_line}))) / $max_time * 3); 717 | if ($hilight_code[0] =~ /%(.)/ && exists $act_last_line_shades{$1}) { 718 | $hilight = 'sb_act_hilight_color %X'.$act_last_line_shades{$1}[$time_delta]; 719 | } 720 | } 721 | $cformat = '$0' unless length $cformat; 722 | my %map = ('$C' => $cformat, '$N' => '$1', '$Q' => '$2'); 723 | $format =~ s<(\$.)><$map{$1}//$1>ge; 724 | $format =~ s<\$H((?:\$.|[^\$])*?)\$S><{$hilight $1%n}>g; 725 | my @ret = ir_parse_special(sb_format_expand($format), [$name, $number, $key], $win); 726 | @ret 727 | } 728 | 729 | sub _calculate_items { 730 | my ($wins, $abbrevList) = @_; 731 | 732 | my $display_header = Irssi::current_theme->get_format(__PACKAGE__, set 'display_header'); 733 | my $name_format = Irssi::current_theme->get_format(__PACKAGE__, set 'name_display'); 734 | my $abbrev_chars = as_uni(Irssi::current_theme->get_format(__PACKAGE__, set 'abbrev_chars')); 735 | 736 | my %displays; 737 | 738 | my $active = Irssi::active_win; 739 | @win_items = (); 740 | %keymap = (%nummap, %wnmap_exp); 741 | 742 | my ($numPad, $keyPad) = (0, 0); 743 | if ($VIEWER_MODE or $S{block} < 0) { 744 | $numPad = length((sort { length $b <=> length $a } keys %keymap)[0]) // 0; 745 | $keyPad = length((sort { length $b <=> length $a } values %keymap)[0]) // 0; 746 | } 747 | my $last_net; 748 | my ($abbrev1, $abbrev2) = $abbrev_chars =~ /(\X)(.*)/; 749 | my @abbrev_chars = ('~', "\x{301c}"); 750 | unless (defined $abbrev1 && screen_length(as_tc($abbrev1)) == 1) { $abbrev1 = $abbrev_chars[0] } 751 | unless (length $abbrev2) { 752 | $abbrev2 = $abbrev1; 753 | if ($abbrev1 eq $abbrev_chars[0]) { 754 | $abbrev2 = $abbrev_chars[1]; 755 | } 756 | else { 757 | $abbrev2 = $abbrev1; 758 | } 759 | } 760 | if (screen_length(as_tc($abbrev2)) == 1) { 761 | $abbrev2 x= 2; 762 | } 763 | while (screen_length(as_tc($abbrev2)) > 2) { 764 | chop $abbrev2; 765 | } 766 | unless (screen_length(as_tc($abbrev2)) == 2) { 767 | $abbrev2 = $abbrev_chars[1]; 768 | } 769 | for my $win (@$wins) { 770 | my $global_hack_alert_tag_header; 771 | 772 | next unless ref $win; 773 | 774 | my $backup_win = Storable::dclone($win); 775 | delete $backup_win->{active} unless ref $backup_win->{active}; 776 | 777 | $global_hack_alert_tag_header = 778 | $display_header && ($last_net // '') ne ($backup_win->{active}{server}{tag} // ''); 779 | 780 | if ($win->{data_level} < abs $S{hide_data} 781 | && ($win->{refnum} != $active->{refnum} || 0 <= $S{hide_data})) { 782 | next; } 783 | elsif (exists $awins{$win->{refnum}} && $S{hide_empty} && !$win->items 784 | && ($win->{refnum} != $active->{refnum} || 0 <= $S{hide_empty})) { 785 | next; } 786 | 787 | my $colour = $win->{hilight_color} // ''; 788 | my $hilight = do { 789 | if ($win->{data_level} == 0) { 'sb_act_none'; } 790 | elsif ($win->{data_level} == 1) { 'sb_act_text'; } 791 | elsif ($win->{data_level} == 2) { 'sb_act_msg'; } 792 | elsif ($colour ne '') { "sb_act_hilight_color $colour"; } 793 | elsif ($win->{data_level} == 3) { 'sb_act_hilight'; } 794 | else { 'sb_act_special'; } 795 | }; 796 | my $number = $win->{refnum}; 797 | 798 | my ($name, $display, $cdisplay); 799 | if ($global_hack_alert_tag_header) { 800 | $display = $display_header; 801 | $name = as_uni($backup_win->{active}{server}{tag}) // ''; 802 | if ($custom_xform) { 803 | no strict 'refs'; 804 | local ${ __PACKAGE__ . '::custom_xform::TAG' } = 1; 805 | run_custom_xform() for $name; 806 | } 807 | } 808 | else { 809 | my @display = ('display_nokey'); 810 | if (defined $keymap{$number} and $keymap{$number} ne '') { 811 | unshift @display, map { (my $cpy = $_) =~ s/_no/_/; $cpy } @display; 812 | } 813 | if (exists $awins{$number}) { 814 | unshift @display, map { my $cpy = $_; $cpy .= '_visible'; $cpy } @display; 815 | } 816 | if ($active->{refnum} == $number) { 817 | unshift @display, map { my $cpy = $_; $cpy .= '_active'; $cpy } 818 | grep { !/_visible$/ } @display; 819 | } 820 | $display = (grep { length $_ } 821 | map { $displays{$_} //= Irssi::current_theme->get_format(__PACKAGE__, set $_) } 822 | @display)[0]; 823 | $cdisplay = $name_format; 824 | $name = as_uni($win->get_active_name) // ''; 825 | $name = '*' if $S{banned_on} and exists $banned_channels{lc1459($name)}; 826 | $name = remove_uniform_vars($win, $name) if $name ne '*'; 827 | if ($name ne '*' and $win->{name} ne '' and $S{prefer_name}) { 828 | $name = as_uni($win->{name}); 829 | if ($custom_xform) { 830 | no strict 'refs'; 831 | local ${ __PACKAGE__ . '::custom_xform::NAME' } = 1; 832 | run_custom_xform() for $name; 833 | } 834 | } 835 | 836 | if (!$VIEWER_MODE && $S{block} >= 0 && $S{hide_name} 837 | && $win->{data_level} < abs $S{hide_name} 838 | && ($win->{refnum} != $active->{refnum} || 0 <= $S{hide_name})) { 839 | $name = ''; 840 | $cdisplay = ''; 841 | } 842 | } 843 | 844 | $display = "$display%n"; 845 | my $num_ent = (' 'x max(0,$numPad - length $number)) . $number; 846 | my $key_ent = exists $keymap{$number} ? ((' 'x max(0,$keyPad - length $keymap{$number})) . $keymap{$number}) : ' 'x$keyPad; 847 | if ($VIEWER_MODE or $S{sbar_maxlen} or $S{block} < 0) { 848 | my $baseLength = sb_length(_format_display( 849 | '', $display, $cdisplay, $hilight, 850 | 'x', # placeholder 851 | $num_ent, 852 | $key_ent, 853 | $win)) - 1; 854 | my $diff = (abs $S{block}) - (screen_length(as_tc($name)) + $baseLength); 855 | if ($diff < 0) { # too long 856 | my $screen_length = screen_length(as_tc($name)); 857 | if ((abs $diff) >= $screen_length) { $name = '' } # forget it 858 | elsif ((abs $diff) + screen_length(as_tc(substr($name, 0, 1))) >= $screen_length) { $name = substr($name, 0, 1); } 859 | else { 860 | my $ulen = length $name; 861 | my $middle2 = exists $abbrevList->{$name} ? 862 | ($S{fancy_strict}) ? 863 | 2* $abbrevList->{$name} : 864 | (2*($abbrevList->{$name} + $ulen) / 3) : 865 | ($S{fancy_head}) ? 866 | 2*$ulen : 867 | $ulen; 868 | my $first = 1; 869 | while (length $name > 1) { 870 | my $cp = $middle2 >= 0 ? $middle2/2 : -1; # clearing position 871 | my $rm = 2; 872 | # if character at end is wider than 1 cell -> replace it with ~ 873 | if (screen_length(as_tc(substr $name, $cp, 1)) > 1) { 874 | if ($first || $cp < 0) { 875 | $rm = 1; 876 | $first = undef; 877 | } 878 | } 879 | elsif ($cp < 0) { # elsif at end -> replace last 2 characters 880 | --$cp; 881 | } 882 | (substr $name, $cp, $rm) = $abbrev1; 883 | if ($cp > -1 && $rm > 1) { 884 | --$middle2; 885 | } 886 | my $sl = screen_length(as_tc($name)); 887 | if ($sl + $baseLength < abs $S{block}) { 888 | (substr $name, ($middle2+1)/2, 1) = $abbrev2; 889 | last; 890 | } 891 | elsif ($sl + $baseLength == abs $S{block}) { 892 | last; 893 | } 894 | } 895 | } 896 | } 897 | elsif ($VIEWER_MODE or $S{block} < 0) { 898 | $name .= (' ' x $diff); 899 | } 900 | } 901 | 902 | push @win_items, _format_display( 903 | '', $display, $cdisplay, $hilight, 904 | as_tc($name), 905 | $num_ent, 906 | as_tc($key_ent), 907 | $win); 908 | 909 | if ($global_hack_alert_tag_header) { 910 | $last_net = $backup_win->{active}{server}{tag}; 911 | redo; 912 | } 913 | 914 | $mouse_coords{refnum}{$#win_items} = $number; 915 | } 916 | } 917 | 918 | sub _spread_items { 919 | my $width = [Irssi::windows]->[0]{width} - $sb_base_width - 1; 920 | my @separator = Irssi::current_theme->get_format(__PACKAGE__, set 'separator'); 921 | if ($S{block} >= 0) { 922 | my $sep2 = Irssi::current_theme->get_format(__PACKAGE__, set 'separator2'); 923 | push @separator, $sep2 if length $sep2 && $sep2 ne $separator[0]; 924 | } 925 | $separator[0] .= '%n'; 926 | my @sepLen = map { sb_length($_) } @separator; 927 | 928 | @actString = (); 929 | my $curLine; 930 | my $curLen = 0; 931 | if ($S{shared_sbar}) { 932 | $curLen += $S{shared_sbar}[0] + 2 + length setc(); 933 | $width -= $S{shared_sbar}[2]; 934 | } 935 | my $mouse_header_check = 0; 936 | for my $it (@win_items) { 937 | my $itemLen = sb_length($it); 938 | if ($curLen) { 939 | if ($curLen + $itemLen + $sepLen[$mouse_header_check % @sepLen] > $width) { 940 | $width += $S{shared_sbar}[2] 941 | if !@actString && $S{shared_sbar}; 942 | push @actString, $curLine; 943 | $curLine = undef; 944 | $curLen = 0; 945 | } 946 | elsif (defined $curLine) { 947 | $curLine .= $separator[$mouse_header_check % @separator]; 948 | $curLen += $sepLen[$mouse_header_check % @sepLen]; 949 | } 950 | } 951 | $curLine .= $it; 952 | if (exists $mouse_coords{refnum}{$mouse_header_check}) { 953 | $mouse_coords{scalar @actString}{ $_ } = $mouse_coords{refnum}{$mouse_header_check} 954 | for $curLen .. $curLen + $itemLen - 1; 955 | } 956 | $curLen += $itemLen; 957 | } 958 | continue { 959 | ++$mouse_header_check; 960 | } 961 | $curLen -= $S{shared_sbar}[0] 962 | if !@actString && $S{shared_sbar}; 963 | push @actString, $curLine if $curLen; 964 | } 965 | 966 | sub remake { 967 | my %abbrevList; 968 | my @wins = window_list(); 969 | if ($VIEWER_MODE or $S{sbar_maxlen} or $S{block} < 0) { 970 | _calculate_abbrev(\@wins, \%abbrevList); 971 | } 972 | 973 | %mouse_coords = ( refnum => +{} ); 974 | _calculate_items(\@wins, \%abbrevList); 975 | 976 | unless ($VIEWER_MODE) { 977 | _spread_items(); 978 | 979 | push @actString, undef unless @actString || $S{all_disable}; 980 | } 981 | } 982 | 983 | sub update_wl { 984 | return if $BLOCK_ALL; 985 | remake(); 986 | 987 | Irssi::statusbar_items_redraw(set $_) for keys %statusbars; 988 | 989 | unless ($VIEWER_MODE) { 990 | Irssi::timeout_add_once(100, 'syncLines', undef); 991 | } 992 | else { 993 | syncViewer(); 994 | } 995 | } 996 | 997 | sub screenFullRedraw { 998 | my ($window) = @_; 999 | if (!ref $window or $window->{refnum} == Irssi::active_win->{refnum}) { 1000 | $viewer{fullRedraw} = 1 if $viewer{client}; 1001 | $settings_str = ''; 1002 | &setup_changed; 1003 | } 1004 | } 1005 | 1006 | sub restartViewerServer { 1007 | if ($VIEWER_MODE) { 1008 | stop_viewer(); 1009 | start_viewer(); 1010 | } 1011 | } 1012 | 1013 | sub _simple_quote { 1014 | my @r = map { 1015 | my $x = $_; 1016 | $x =~ s/'/'"'"'/g; 1017 | $x = "'$x'"; 1018 | } @_; 1019 | wantarray ? @r : shift @r 1020 | } 1021 | 1022 | sub _viewer_command_replace_format { 1023 | my ($ecmd, @args) = @_; 1024 | my $file = _simple_quote(SCRIPT_FILE()); 1025 | my $path = _simple_quote($viewer{path}); 1026 | my @env; 1027 | for my $env (shellwords($S{viewer_launch_env})) { 1028 | if ($env =~ /^(\w+)(?:=(.*))$/) { 1029 | push @env, "AWL_$1=$2" 1030 | } 1031 | } 1032 | my $cmd = join ' ', 1033 | (@env ? ('env', _simple_quote(@env)) : ()), 1034 | 'perl', $file, '-1', _simple_quote(@args), $path; 1035 | $ecmd =~ s{%(%|\w+)}{ 1036 | my $sub = $1; 1037 | if ($sub eq '%') { 1038 | '%' 1039 | } 1040 | elsif ($sub =~ /^(q*)A(.*)/) { 1041 | my $ret = $cmd; 1042 | for (1..length $1) { 1043 | $ret = _simple_quote($ret); 1044 | } 1045 | "$ret$2" 1046 | } 1047 | else { 1048 | "%$sub" 1049 | } 1050 | }gex; 1051 | $ecmd 1052 | } 1053 | 1054 | sub start_viewer { 1055 | unlink $viewer{path} if -S $viewer{path} || -p _; 1056 | 1057 | $viewer{server} = IO::Socket::UNIX->new( 1058 | Type => SOCK_STREAM, 1059 | Local => $viewer{path}, 1060 | Listen => 1 1061 | ); 1062 | unless ($viewer{server}) { 1063 | $viewer{msg} = "Viewer: $!"; 1064 | $viewer{retry} = Irssi::timeout_add_once(5000, 'retry_viewer', 1); 1065 | return; 1066 | } 1067 | $viewer{server}->blocking(0); 1068 | set_viewer_mode_hint(); 1069 | $viewer{server_tag} = Irssi::input_add($viewer{server}->fileno, INPUT_READ, 'vi_connected', undef); 1070 | 1071 | if ($S{viewer_launch}) { 1072 | if (length $ENV{TMUX_PANE} && length $ENV{TMUX} && lc $S{viewer_tmux_position} ne 'custom') { 1073 | my $cmd = _viewer_command_replace_format('%qA', '-p', lc $S{viewer_tmux_position}); 1074 | Irssi::command("exec - tmux neww -d $cmd 2>&1 &"); 1075 | } 1076 | elsif (length $ENV{WINDOWID} && length $ENV{DISPLAY} && length $S{viewer_xwin_command} && $S{viewer_xwin_command} =~ /\S/) { 1077 | my $cmd = _viewer_command_replace_format($S{viewer_xwin_command}); 1078 | Irssi::command("exec - $cmd 2>&1 &"); 1079 | } 1080 | elsif (length $S{viewer_custom_command} && $S{viewer_custom_command} =~ /\S/) { 1081 | my $cmd = _viewer_command_replace_format($S{viewer_custom_command}); 1082 | Irssi::command("exec - $cmd 2>&1 &"); 1083 | } 1084 | } 1085 | } 1086 | 1087 | sub set_viewer_mode_hint { 1088 | return unless $viewer{server}; 1089 | if ($S{no_mode_hint}) { 1090 | $viewer{msg} = undef; 1091 | } 1092 | else { 1093 | my ($name) = __PACKAGE__ =~ /::([^:]+)$/; 1094 | $viewer{msg} = "Run $name from the shell or switch to sbar mode"; 1095 | } 1096 | } 1097 | 1098 | sub retry_viewer { 1099 | start_viewer(); 1100 | } 1101 | 1102 | sub vi_close_client { 1103 | Irssi::input_remove(delete $viewer{client_tag}) if exists $viewer{client_tag}; 1104 | $viewer{client}->close if $viewer{client}; 1105 | delete $viewer{client}; 1106 | delete $viewer{client_keymap}; 1107 | delete $viewer{client_settings}; 1108 | delete $viewer{client_env}; 1109 | delete $viewer{fullRedraw}; 1110 | } 1111 | 1112 | sub vi_connected { 1113 | vi_close_client(); 1114 | $viewer{client} = $viewer{server}->accept or return; 1115 | $viewer{client}->blocking(0); 1116 | $viewer{client_tag} = Irssi::input_add($viewer{client}->fileno, INPUT_READ, 'vi_clientinput', undef); 1117 | syncViewer(); 1118 | } 1119 | 1120 | use constant VIEWER_BLOCK_SIZE => 1024; 1121 | sub vi_clientinput { 1122 | if ($viewer{client}->read(my $buf, VIEWER_BLOCK_SIZE)) { 1123 | $viewer{rcvbuf} .= $buf; 1124 | if ($viewer{rcvbuf} =~ s/^(?:(active|\d+)|(last|up|down))\n//igm) { 1125 | if (defined $2) { 1126 | Irssi::command("window $2"); 1127 | } 1128 | elsif (lc $1 eq 'active' && $viewer{use_ack}) { 1129 | Irssi::command("ack"); 1130 | } 1131 | else { 1132 | Irssi::command("window goto $1"); 1133 | } 1134 | } 1135 | } 1136 | else { 1137 | vi_close_client(); 1138 | Irssi::timeout_add_once(100, 'syncViewer', undef); 1139 | } 1140 | } 1141 | 1142 | sub stop_viewer { 1143 | Irssi::timeout_remove(delete $viewer{retry}) if exists $viewer{retry}; 1144 | vi_close_client(); 1145 | Irssi::input_remove(delete $viewer{server_tag}) if exists $viewer{server_tag}; 1146 | return unless $viewer{server}; 1147 | $viewer{server}->close; 1148 | delete $viewer{server}; 1149 | } 1150 | sub _encode_var { 1151 | my $str; 1152 | while (@_) { 1153 | my ($name, $var) = splice @_, 0, 2; 1154 | my $type = ref $var ? $var =~ /HASH/ ? 'map' : $var =~ /ARRAY/ ? 'list' : '' : ''; 1155 | $str .= "\n\U$name$type\_begin\n"; 1156 | if ($type eq 'map') { 1157 | no warnings 'numeric'; 1158 | $str .= " $_\n ${$var}{$_}\n" for sort { $a <=> $b || $a cmp $b } keys %$var; 1159 | } 1160 | elsif ($type eq 'list') { 1161 | $str .= " $_\n" for @$var; 1162 | } 1163 | else { 1164 | $str .= " $var\n"; 1165 | } 1166 | $str .= "\U$name$type\_end\n"; 1167 | } 1168 | $str 1169 | } 1170 | sub syncViewer { 1171 | if ($viewer{client}) { 1172 | @actString = (); 1173 | if ($currentLines) { 1174 | killOldStatus(); 1175 | $currentLines = 0; 1176 | } 1177 | my $str; 1178 | unless ($viewer{client_keymap}) { 1179 | $str .= _encode_var('key', +{ %nummap, %specialmap }); 1180 | $viewer{client_keymap} = 1; 1181 | } 1182 | unless ($viewer{client_settings}) { 1183 | $str .= _encode_var( 1184 | block => $S{block}, 1185 | ha => $S{height_adjust}, 1186 | mc => $S{maxcolumns}, 1187 | ml => $S{maxlines}, 1188 | ); 1189 | $viewer{client_settings} = 1; 1190 | } 1191 | unless ($viewer{client_env}) { 1192 | $str .= _encode_var(irssienv => +{ 1193 | length $ENV{TMUX_PANE} && length $ENV{TMUX} ? 1194 | (tmux_pane => $ENV{TMUX_PANE}, 1195 | tmux_srv => $ENV{TMUX}) : (), 1196 | length $ENV{WINDOWID} ? 1197 | (xwinid => $ENV{WINDOWID}) : (), 1198 | }); 1199 | $viewer{client_env} = 1; 1200 | } 1201 | my $separator = Irssi::current_theme->get_format(__PACKAGE__, set 'separator'); 1202 | my $sepLen = sb_length($separator); 1203 | my $item_bg = Irssi::current_theme->get_format(__PACKAGE__, set 'viewer_item_bg'); 1204 | $str .= _encode_var(redraw => 1) if delete $viewer{fullRedraw}; 1205 | $str .= _encode_var(separator => $separator, 1206 | seplen => $sepLen, 1207 | itembg => $item_bg, 1208 | mouse => $mouse_coords{refnum}, 1209 | key2 => \%wnmap_exp, 1210 | win => \@win_items); 1211 | 1212 | my $was = $viewer{client}->blocking(1); 1213 | $viewer{client}->print($str); 1214 | $viewer{client}->blocking($was); 1215 | } 1216 | elsif ($viewer{server}) { 1217 | if (defined $viewer{msg}) { 1218 | @actString = ((uc setc()).": $viewer{msg}"); 1219 | } 1220 | else { 1221 | @actString = (); 1222 | } 1223 | } 1224 | elsif (defined $viewer{msg}) { 1225 | @actString = ((uc setc()).": $viewer{msg}"); 1226 | } 1227 | if (@actString) { 1228 | Irssi::timeout_add_once(100, 'syncLines', undef); 1229 | } 1230 | elsif ($currentLines) { 1231 | killOldStatus(); 1232 | $currentLines = 0; 1233 | } 1234 | } 1235 | 1236 | sub reset_awl { 1237 | Irssi::timeout_remove($shade_line_timer) if $shade_line_timer; $shade_line_timer = undef; 1238 | my $was_sort = $S{sort} // ''; 1239 | my $was_xform = $S{xform} // ''; 1240 | my $was_shared = $S{shared_sbar}; 1241 | my $was_no_hint = $S{no_mode_hint}; 1242 | %S = ( 1243 | sort => Irssi::settings_get_str( set 'sort'), 1244 | fancy_abbrev => Irssi::settings_get_str('fancy_abbrev'), 1245 | xform => Irssi::settings_get_str( set 'custom_xform'), 1246 | block => Irssi::settings_get_int( set 'block'), 1247 | banned_on => Irssi::settings_get_bool('banned_channels_on'), 1248 | prefer_name => Irssi::settings_get_bool(set 'prefer_name'), 1249 | hide_data => Irssi::settings_get_int( set 'hide_data'), 1250 | hide_name => Irssi::settings_get_int( set 'hide_name_data'), 1251 | hide_empty => Irssi::settings_get_int( set 'hide_empty'), 1252 | sbar_maxlen => Irssi::settings_get_bool(set 'sbar_maxlength'), 1253 | placement => Irssi::settings_get_str( set 'placement'), 1254 | position => Irssi::settings_get_int( set 'position'), 1255 | maxlines => Irssi::settings_get_int( set 'maxlines'), 1256 | maxcolumns => Irssi::settings_get_int( set 'maxcolumns'), 1257 | all_disable => Irssi::settings_get_bool(set 'all_disable'), 1258 | height_adjust => Irssi::settings_get_int( set 'height_adjust'), 1259 | mouse_offset => Irssi::settings_get_int( set 'mouse_offset'), 1260 | mouse_scroll => Irssi::settings_get_int( 'mouse_scroll'), 1261 | mouse_escape => Irssi::settings_get_int( 'mouse_escape'), 1262 | line_shade => Irssi::settings_get_time(set 'last_line_shade'), 1263 | no_mode_hint => Irssi::settings_get_bool(set 'no_mode_hint'), 1264 | viewer_launch => Irssi::settings_get_bool(set 'viewer_launch'), 1265 | viewer_launch_env => Irssi::settings_get_str(set 'viewer_launch_env'), 1266 | viewer_xwin_command => Irssi::settings_get_str(set 'viewer_xwin_command'), 1267 | viewer_custom_command => Irssi::settings_get_str(set 'viewer_custom_command'), 1268 | viewer_tmux_position => Irssi::settings_get_str(set 'viewer_tmux_position'), 1269 | ); 1270 | $S{fancy_strict} = $S{fancy_abbrev} =~ /^strict/i; 1271 | $S{fancy_head} = $S{fancy_abbrev} =~ /^head/i; 1272 | my $shared = Irssi::settings_get_str(set 'shared_sbar'); 1273 | if ($shared =~ /^(\d+)([<])(\d+)$/) { 1274 | $S{shared_sbar} = [$1, $2, $3]; 1275 | } 1276 | else { 1277 | Irssi::settings_set_str(set 'shared_sbar', 'OFF'); 1278 | $S{shared_sbar} = undef; 1279 | } 1280 | lock_keys(%S); 1281 | if ($was_sort ne $S{sort}) { 1282 | $print_text_activity = undef; 1283 | my @sort_order = grep { @$_ > 4 } map { 1284 | s/^\s*//; 1285 | my $reverse = s/^\W*\K[-!]//; 1286 | my $undef_check = s/^\W*\K~// ? 1 : undef; 1287 | my $equal_check = s/=(.*)\s?$// ? $1 : undef; 1288 | s/\s*$//; 1289 | my $ignore_case = s/#i$// ? 1 : undef; 1290 | 1291 | $print_text_activity = 1 if $_ eq 'last_line'; 1292 | 1293 | my @path = split '/'; 1294 | my $class_check = @path && $path[-1] =~ s/(::.*)$// ? $1 : undef; 1295 | 1296 | [ $reverse ? -1 : 1, $undef_check, $equal_check, $class_check, $ignore_case, @path ] 1297 | } "$S{sort}," =~ /([^+,]*|[^+,]*=[^,]*?\s(?=\+)|[^+,]*=[^,]*)[+,]/g; 1298 | $window_sort_func = sub { 1299 | no warnings qw(numeric uninitialized); 1300 | for my $so (@sort_order) { 1301 | my @x = map { 1302 | my $ret = 0; 1303 | $_ = lc1459($_) if defined $_ && !ref $_ && $so->[4]; 1304 | $ret = $_ eq ($so->[4] ? lc1459($so->[2]) : $so->[2]) ? 1 : -1 if defined $so->[2]; 1305 | $ret = defined $_ ? ($ret || -3) : 3 if $so->[1]; 1306 | $ret = ref $_ && $_->isa('Irssi'.$so->[3]) ? 2 : ($ret || -2) if $so->[3]; 1307 | -$ret || $_ 1308 | } 1309 | map { 1310 | reduce { return unless ref $a; $a->{$b} } $_, @{$so}[5..$#$so] 1311 | } $a, $b; 1312 | return ((($x[0] <=> $x[1] || $x[0] cmp $x[1]) * $so->[0]) || next); 1313 | } 1314 | return ($a->{refnum} <=> $b->{refnum}); 1315 | }; 1316 | } 1317 | if ($was_xform ne $S{xform}) { 1318 | if ($S{xform} !~ /\S/) { 1319 | $custom_xform = undef; 1320 | } 1321 | else { 1322 | my $script_pkg = __PACKAGE__ . '::custom_xform'; 1323 | local $@; 1324 | $custom_xform = eval qq{ 1325 | package $script_pkg; 1326 | use strict; 1327 | no warnings; 1328 | our (\$QUERY, \$CHANNEL, \$TAG, \$NAME); 1329 | return sub { 1330 | # line 1 @{[ set 'custom_xform' ]}\n$S{xform}\n}}; 1331 | if ($@) { 1332 | $@ =~ /^(.*)/; 1333 | print '%_'.(set 'custom_xform').'%_ did not compile: '.$1; 1334 | } 1335 | } 1336 | } 1337 | 1338 | my $new_settings = join "\n", $VIEWER_MODE 1339 | ? ("\\", $S{block}, $S{height_adjust}, $S{maxlines}, $S{maxcolumns}) 1340 | : ("!", $S{placement}, $S{position}); 1341 | 1342 | if ($settings_str ne $new_settings) { 1343 | @actString = (); 1344 | %abbrev_cache = (); 1345 | $currentLines = 0; 1346 | killOldStatus(); 1347 | delete $viewer{client_settings}; 1348 | $settings_str = $new_settings; 1349 | } 1350 | 1351 | my $was_mouse_mode = $MOUSE_ON; 1352 | if ($MOUSE_ON = Irssi::settings_get_bool(set 'mouse') and !$was_mouse_mode) { 1353 | install_mouse(); 1354 | } 1355 | elsif ($was_mouse_mode and !$MOUSE_ON) { 1356 | uninstall_mouse(); 1357 | } 1358 | 1359 | my $path = Irssi::settings_get_str(set 'path'); 1360 | my $was_viewer_mode = $VIEWER_MODE; 1361 | if ($was_viewer_mode && 1362 | defined $viewer{path} && $viewer{path} ne $path) { 1363 | stop_viewer(); 1364 | $was_viewer_mode = 0; 1365 | } 1366 | elsif ($was_viewer_mode && $S{no_mode_hint} != $was_no_hint + 0) { 1367 | set_viewer_mode_hint(); 1368 | } 1369 | $viewer{path} = $path; 1370 | if ($VIEWER_MODE = Irssi::settings_get_bool(set 'viewer') and !$was_viewer_mode) { 1371 | start_viewer(); 1372 | } 1373 | elsif ($was_viewer_mode and !$VIEWER_MODE) { 1374 | stop_viewer(); 1375 | } 1376 | 1377 | %banned_channels = map { lc1459(to_uni($_)) => undef } 1378 | split ' ', Irssi::settings_get_str('banned_channels'); 1379 | 1380 | my @sb_base = split /\177/, sb_format_expand("{sb \177}"), 2; 1381 | $sb_base_width_pre = sb_length($sb_base[0]); 1382 | $sb_base_width_post = sb_length($sb_base[1]); 1383 | $sb_base_width = $sb_base_width_pre + $sb_base_width_post; 1384 | 1385 | if ($print_text_activity && $S{line_shade}) { 1386 | $shade_line_timer = Irssi::timeout_add(max(10 * GLOB_QUEUE_TIMER, 100*$S{line_shade}**(1/3)), 'wl_changed', undef); 1387 | } 1388 | 1389 | $CHANGED{AWINS} = 1; 1390 | } 1391 | 1392 | sub stop_mouse_tracking { 1393 | print STDERR "\e[?1005l\e[?1000l"; 1394 | } 1395 | sub start_mouse_tracking { 1396 | print STDERR "\e[?1000h\e[?1005h"; 1397 | } 1398 | sub install_mouse { 1399 | Irssi::command_bind('mouse_xterm' => 'mouse_xterm'); 1400 | Irssi::command('^bind meta-[M command mouse_xterm'); 1401 | Irssi::signal_add_first('gui key pressed' => 'mouse_key_hook'); 1402 | start_mouse_tracking(); 1403 | } 1404 | sub uninstall_mouse { 1405 | stop_mouse_tracking(); 1406 | Irssi::signal_remove('gui key pressed' => 'mouse_key_hook'); 1407 | Irssi::command('^bind -delete meta-[M'); 1408 | Irssi::command_unbind('mouse_xterm' => 'mouse_xterm'); 1409 | } 1410 | 1411 | sub awl_mouse_event { 1412 | return if $VIEWER_MODE; 1413 | if ((($_[0] == 3 and $_[3] == 0) 1414 | || $_[0] == 64 || $_[0] == 65) and 1415 | $_[1] == $_[4] and $_[2] == $_[5]) { 1416 | my $top = lc $S{placement} eq 'top'; 1417 | my ($pos, $line) = @_[1 .. 2]; 1418 | unless ($top) { 1419 | $line -= $screenHeight; 1420 | $line += $currentLines; 1421 | $line += $S{mouse_offset}; 1422 | } 1423 | else { 1424 | $line -= $S{mouse_offset}; 1425 | } 1426 | $pos -= $sb_base_width_pre; 1427 | return if $line < 0 || $line >= $currentLines; 1428 | if ($_[0] == 64) { 1429 | Irssi::command('window up'); 1430 | } 1431 | elsif ($_[0] == 65) { 1432 | Irssi::command('window down'); 1433 | } 1434 | elsif (exists $mouse_coords{$line}{$pos}) { 1435 | my $win = $mouse_coords{$line}{$pos}; 1436 | Irssi::command('window ' . $win); 1437 | } 1438 | Irssi::signal_stop; 1439 | } 1440 | } 1441 | 1442 | sub mouse_scroll_event { 1443 | return unless $S{mouse_scroll}; 1444 | if (($_[3] == 64 or $_[3] == 65) and 1445 | $_[0] == $_[3] and $_[1] == $_[4] and $_[2] == $_[5]) { 1446 | my $cmd = 'scrollback goto ' . ($_[3] == 64 ? '-' : '+') . $S{mouse_scroll}; 1447 | Irssi::active_win->command($cmd); 1448 | Irssi::signal_stop; 1449 | } 1450 | elsif ($_[0] == 64 or $_[0] == 65) { 1451 | Irssi::signal_stop; 1452 | } 1453 | } 1454 | 1455 | sub mouse_escape { 1456 | return unless $S{mouse_escape} > 0; 1457 | if ($_[0] == 3) { 1458 | my $tm = $S{mouse_escape}; 1459 | $tm *= 1000 if $tm < 1000; 1460 | stop_mouse_tracking(); 1461 | Irssi::timeout_add_once($tm, 'start_mouse_tracking', undef); 1462 | Irssi::signal_stop; 1463 | } 1464 | } 1465 | 1466 | sub UNLOAD { 1467 | @actString = (); 1468 | killOldStatus(); 1469 | stop_viewer() if $VIEWER_MODE; 1470 | uninstall_mouse() if $MOUSE_ON; 1471 | } 1472 | 1473 | sub addPrintTextHook { # update on print text 1474 | return unless defined $^S; 1475 | return if $BLOCK_ALL; 1476 | return unless $print_text_activity; 1477 | return if $_[0]->{level} == 262144 and $_[0]->{target} eq '' 1478 | and !defined($_[0]->{server}); 1479 | &wl_changed; 1480 | } 1481 | 1482 | sub block_event_window_change { 1483 | Irssi::signal_stop; 1484 | } 1485 | 1486 | sub update_awins { 1487 | my @wins = Irssi::windows; 1488 | local $BLOCK_ALL = 1; 1489 | Irssi::signal_add_first('window changed' => 'block_event_window_change'); 1490 | my $bwin = 1491 | my $awin = Irssi::active_win; 1492 | my $lwin; 1493 | my $defer_irssi_broken_last; 1494 | unless ($wins[0]{refnum} == $awin->{refnum}) { 1495 | # special case: more than 1 last win, so /win last; 1496 | # /win last doesn't come back to the current window. eg. after 1497 | # connect & autojoin; we can't handle this situation, bail out 1498 | $defer_irssi_broken_last = 1; 1499 | } 1500 | else { 1501 | $awin->command('window last'); 1502 | $lwin = Irssi::active_win; 1503 | $lwin->command('window last'); 1504 | $defer_irssi_broken_last = $lwin->{refnum} == $bwin->{refnum}; 1505 | } 1506 | my $awin_counter = 0; 1507 | Irssi::signal_remove('window changed' => 'block_event_window_change'); 1508 | unless ($defer_irssi_broken_last) { 1509 | # we need to keep the fe-windows code running here 1510 | Irssi::signal_add_priority('window changed' => 'block_event_window_change', -99); 1511 | %awins = %wnmap_exp = (); 1512 | do { 1513 | Irssi::active_win->command('window up'); 1514 | $awin = Irssi::active_win; 1515 | $awins{$awin->{refnum}} = undef; 1516 | ++$awin_counter; 1517 | } until ($awin->{refnum} == $bwin->{refnum} || $awin_counter >= @wins); 1518 | Irssi::signal_remove('window changed' => 'block_event_window_change'); 1519 | 1520 | Irssi::signal_add_first('window changed' => 'block_event_window_change'); 1521 | for my $key (keys %wnmap) { 1522 | next unless Irssi::window_find_name($key) || Irssi::window_find_item($key); 1523 | $awin->command("window goto $key"); 1524 | my $cwin = Irssi::active_win; 1525 | $wnmap_exp{ $cwin->{refnum} } = $wnmap{$key}; 1526 | $cwin->command('window last') 1527 | if $cwin->{refnum} != $awin->{refnum}; 1528 | } 1529 | for my $win (reverse @wins) { # restore original window order 1530 | Irssi::active_win->command('window '.$win->{refnum}); 1531 | } 1532 | $awin->command('window '.$lwin->{refnum}); # restore last win 1533 | Irssi::active_win->command('window last'); 1534 | Irssi::signal_remove('window changed' => 'block_event_window_change'); 1535 | } 1536 | $CHANGED{WL} = 1; 1537 | } 1538 | 1539 | sub resizeTerm { 1540 | if (defined (my $r = `stty size 2>/dev/null`)) { 1541 | ($screenHeight, $screenWidth) = split ' ', $r; 1542 | $CHANGED{SETUP} = 1; 1543 | } 1544 | else { 1545 | $CHANGED{SIZE} = 1; 1546 | } 1547 | } 1548 | 1549 | sub awl_refresh { 1550 | $globTime = undef; 1551 | resizeTerm() if delete $CHANGED{SIZE}; 1552 | reset_awl() if delete $CHANGED{SETUP}; 1553 | update_awins() if delete $CHANGED{AWINS}; 1554 | update_wl() if delete $CHANGED{WL}; 1555 | } 1556 | 1557 | sub termsize_changed { $CHANGED{SIZE} = 1; &queue_refresh; } 1558 | sub setup_changed { $CHANGED{SETUP} = 1; &queue_refresh; } 1559 | sub awins_changed { $CHANGED{AWINS} = 1; &queue_refresh; } 1560 | sub wl_changed { $CHANGED{WL} = 1; &queue_refresh; } 1561 | 1562 | sub window_changed { 1563 | &awins_changed if $_[1]; 1564 | } 1565 | 1566 | sub queue_refresh { 1567 | return if $BLOCK_ALL; 1568 | Irssi::timeout_remove($globTime) 1569 | if defined $globTime; # delay the update further 1570 | $globTime = Irssi::timeout_add_once(GLOB_QUEUE_TIMER, 'awl_refresh', undef); 1571 | } 1572 | 1573 | sub awl_init { 1574 | termsize_changed(); 1575 | update_keymap(); 1576 | } 1577 | 1578 | sub runsub { 1579 | my $cmd = shift; 1580 | sub { 1581 | my ($data, $server, $item) = @_; 1582 | Irssi::command_runsub($cmd, $data, $server, $item); 1583 | }; 1584 | } 1585 | 1586 | Irssi::signal_register({ 1587 | 'gui mouse' => [qw/int int int int int int/], 1588 | }); 1589 | { my $broken_expandos = (Irssi::version >= 20081128 && Irssi::version < 20110210) 1590 | ? sub { my $x = shift; $x =~ s/\$\{cumode_space\}/ /; $x } : undef; 1591 | Irssi::theme_register([ 1592 | map { $broken_expandos ? $broken_expandos->($_) : $_ } 1593 | set 'display_nokey' => '$N${cumode_space}$H$C$S', 1594 | set 'display_key' => '$Q${cumode_space}$H$C$S', 1595 | set 'display_nokey_visible' => '%2$N${cumode_space}$H$C$S', 1596 | set 'display_key_visible' => '%2$Q${cumode_space}$H$C$S', 1597 | set 'display_nokey_active' => '%1$N${cumode_space}$H$C$S', 1598 | set 'display_key_active' => '%1$Q${cumode_space}$H$C$S', 1599 | set 'display_header' => '%8$C|${N}', 1600 | set 'name_display' => '$0', 1601 | set 'separator' => ' ', 1602 | set 'separator2' => '', 1603 | set 'abbrev_chars' => "~\x{301c}", 1604 | set 'viewer_item_bg' => sb_format_expand('{sb_background}'), 1605 | ]); 1606 | } 1607 | Irssi::settings_add_bool(setc, set 'prefer_name', 0); # 1608 | Irssi::settings_add_int( setc, set 'hide_empty', 0); # 1609 | Irssi::settings_add_int( setc, set 'hide_data', 0); # 1610 | Irssi::settings_add_int( setc, set 'hide_name_data', 0); # 1611 | Irssi::settings_add_int( setc, set 'maxlines', 9); # 1612 | Irssi::settings_add_int( setc, set 'maxcolumns', 4); # 1613 | Irssi::settings_add_int( setc, set 'block', 15); # 1614 | Irssi::settings_add_bool(setc, set 'sbar_maxlength', 1); # 1615 | Irssi::settings_add_int( setc, set 'height_adjust', 2); # 1616 | Irssi::settings_add_str( setc, set 'sort', 'refnum'); # 1617 | Irssi::settings_add_str( setc, set 'placement', 'bottom'); # 1618 | Irssi::settings_add_int( setc, set 'position', 0); # 1619 | Irssi::settings_add_bool(setc, set 'all_disable', 1); # 1620 | Irssi::settings_add_bool(setc, set 'viewer', 1); # 1621 | Irssi::settings_add_str( setc, set 'shared_sbar', 'OFF'); # 1622 | Irssi::settings_add_bool(setc, set 'mouse', 0); # 1623 | Irssi::settings_add_str( setc, set 'path', Irssi::get_irssi_dir . '/_windowlist'); # 1624 | Irssi::settings_add_str( setc, set 'custom_xform', ''); # 1625 | Irssi::settings_add_time(setc, set 'last_line_shade', '0'); # 1626 | Irssi::settings_add_int( setc, set 'mouse_offset', 1); # 1627 | Irssi::settings_add_int( setc, 'mouse_scroll', 3); # 1628 | Irssi::settings_add_int( setc, 'mouse_escape', 1); # 1629 | Irssi::settings_add_str( setc, 'banned_channels', ''); 1630 | Irssi::settings_add_bool(setc, 'banned_channels_on', 1); 1631 | Irssi::settings_add_str( setc, 'fancy_abbrev', 'fancy'); # 1632 | Irssi::settings_add_bool(setc, set 'no_mode_hint', 0); # 1633 | Irssi::settings_add_bool(setc, set 'viewer_launch', 1); # 1634 | Irssi::settings_add_str( setc, set 'viewer_launch_env', ''); # 1635 | Irssi::settings_add_str( setc, set 'viewer_tmux_position', 'left'); # 1636 | Irssi::settings_add_str( setc, set 'viewer_xwin_command', 'xterm +sb -e %A'); # 1637 | Irssi::settings_add_str( setc, set 'viewer_custom_command', ''); # 1638 | 1639 | Irssi::signal_add_last({ 1640 | 'setup changed' => 'setup_changed', 1641 | 'print text' => 'addPrintTextHook', 1642 | 'terminal resized' => 'termsize_changed', 1643 | 'setup reread' => 'screenFullRedraw', 1644 | 'window hilight' => 'wl_changed', 1645 | 'command format' => 'wl_changed', 1646 | }); 1647 | Irssi::signal_add({ 1648 | 'window changed' => 'window_changed', 1649 | 'window item changed' => 'wl_changed', 1650 | 'window changed automatic' => 'window_changed', 1651 | 'window created' => 'awins_changed', 1652 | 'window destroyed' => 'awins_changed', 1653 | 'window name changed' => 'wl_changed', 1654 | 'window refnum changed' => 'wl_changed', 1655 | }); 1656 | Irssi::signal_add_last('gui mouse' => 'mouse_escape'); 1657 | Irssi::signal_add_last('gui mouse' => 'mouse_scroll_event'); 1658 | Irssi::signal_add_last('gui mouse' => 'awl_mouse_event'); 1659 | Irssi::command_bind( setc() => runsub(setc()) ); 1660 | Irssi::command_bind( setc() . ' redraw' => 'screenFullRedraw' ); 1661 | Irssi::command_bind( setc() . ' restart' => 'restartViewerServer' ); 1662 | 1663 | { 1664 | my $l = set 'shared'; 1665 | { 1666 | no strict 'refs'; 1667 | *{$l} = $awl_shared_empty; 1668 | } 1669 | Irssi::statusbar_item_register($l, '$0', $l); 1670 | } 1671 | 1672 | awl_init(); 1673 | 1674 | # Mouse script based on irssi mouse patch by mirage 1675 | { my $mouse_status = -1; # -1:off 0,1,2:filling mouse_combo 1676 | my @mouse_combo; # 0:button 1:x 2:y 1677 | my @mouse_previous; # previous contents of mouse_combo 1678 | 1679 | sub mouse_xterm_off { 1680 | $mouse_status = -1; 1681 | } 1682 | sub mouse_xterm { 1683 | $mouse_status = 0; 1684 | Irssi::timeout_add_once(10, 'mouse_xterm_off', undef); 1685 | } 1686 | 1687 | sub mouse_key_hook { 1688 | my ($key) = @_; 1689 | if ($mouse_status != -1) { 1690 | if ($mouse_status == 0) { 1691 | @mouse_previous = @mouse_combo; 1692 | #if @mouse_combo && $mouse_combo[0] < 64; 1693 | } 1694 | $mouse_combo[$mouse_status] = $key - 32; 1695 | $mouse_status++; 1696 | if ($mouse_status == 3) { 1697 | $mouse_status = -1; 1698 | # match screen coordinates 1699 | $mouse_combo[1]--; 1700 | $mouse_combo[2]--; 1701 | Irssi::signal_emit('gui mouse', @mouse_combo[0 .. 2], @mouse_previous[0 .. 2]); 1702 | } 1703 | Irssi::signal_stop; 1704 | } 1705 | } 1706 | } 1707 | 1708 | sub string_LCSS { 1709 | my $str = join "\0", @_; 1710 | (sort { length $b <=> length $a } $str =~ /(?=(.+).*\0.*\1)/g)[0] 1711 | } 1712 | 1713 | # workaround for issue #271 1714 | { package Irssi::Nick } 1715 | 1716 | # workaround for issue #572 1717 | @Irssi::UI::Exec::ISA = 'Irssi::Windowitem' 1718 | if Irssi::version >= 20140822 && Irssi::version <= 20161101 && !@Irssi::UI::Exec::ISA; 1719 | 1720 | UNITCHECK 1721 | { package AwlViewer; 1722 | use strict; 1723 | use warnings; 1724 | no warnings 'redefine'; 1725 | use Encode; 1726 | use IO::Socket::UNIX; 1727 | use IO::Select; 1728 | use List::Util qw(max); 1729 | use constant BLOCK_SIZE => 1024; 1730 | use constant RECONNECT_TIME => 5; 1731 | 1732 | my $sockpath; 1733 | 1734 | our $VERSION = '0.8'; 1735 | 1736 | our ($got_int, $resized, $timeout); 1737 | 1738 | my %vars; 1739 | my (%c2w, @seqlist); 1740 | my %mouse_coords; 1741 | my (@mouse, @last_mouse); 1742 | my ($err, $sock, $loop); 1743 | my ($keybuf, $rcvbuf); 1744 | my @screen; 1745 | my ($screenHeight, $screenWidth); 1746 | my ($disp_update, $fs_open, $one_shot_integration, $one_shot_resize); 1747 | my $integration_position; 1748 | my $show_title_bar; 1749 | 1750 | sub connect_it { 1751 | $sock = IO::Socket::UNIX->new( 1752 | Type => SOCK_STREAM, 1753 | Peer => $sockpath, 1754 | ); 1755 | unless ($sock) { 1756 | $err = $!; 1757 | return; 1758 | } 1759 | $sock->blocking(0); 1760 | $loop->add($sock); 1761 | } 1762 | 1763 | sub remove_conn { 1764 | my $fh = shift; 1765 | $loop->remove($fh); 1766 | $fh->close; 1767 | $sock = undef; 1768 | %vars = (); 1769 | @screen = (); 1770 | } 1771 | 1772 | { package Terminfo; # xterm 1773 | sub civis { "\e[?25l" } 1774 | sub sc { "\e7" } 1775 | sub cup { "\e[" . ($_[0] + 1) . ';' . ($_[1] + 1) . 'H' } 1776 | sub el { "\e[K" } 1777 | sub rc { "\e8" } 1778 | sub cnorm { "\e[?25h" } 1779 | sub setab { "\e[4" . $_[0] . 'm' } 1780 | sub setaf { "\e[3" . $_[0] . 'm' } 1781 | sub setaf16 { "\e[9" . $_[0] . 'm' } 1782 | sub setab16 { "\e[10" . $_[0] . 'm' } 1783 | sub setaf256 { "\e[38;5;" . $_[0] . 'm' } 1784 | sub setab256 { "\e[48;5;" . $_[0] . 'm' } 1785 | sub sgr0 { "\e[0m" } 1786 | sub bold { "\e[1m" } 1787 | sub it { "\e[3m" } 1788 | sub ul { "\e[4m" } 1789 | sub blink { "\e[5m" } 1790 | sub rev { "\e[7m" } 1791 | sub op { "\e[39;49m" } 1792 | sub exit_bold { "\e[22m" } 1793 | sub exit_it { "\e[23m" } 1794 | sub exit_ul { "\e[24m" } 1795 | sub exit_blink { "\e[25m" } 1796 | sub exit_rev { "\e[27m" } 1797 | sub smcup { "\e[?1049h" } 1798 | sub rmcup { "\e[?1049l" } 1799 | sub smmouse { "\e[?1000h\e[?1005h" } 1800 | sub rmmouse { "\e[?1005l\e[?1000l" } 1801 | } 1802 | 1803 | sub init { 1804 | $sockpath = shift // "$ENV{HOME}/.irssi/_windowlist"; 1805 | STDOUT->autoflush(1); 1806 | printf "\r%swaiting for %s...", Terminfo::sc, $::IRSSI{name}; 1807 | 1808 | `stty -icanon -echo`; 1809 | 1810 | $loop = IO::Select->new; 1811 | STDIN->blocking(0); 1812 | $loop->add(\*STDIN); 1813 | 1814 | $SIG{INT} = sub { 1815 | $got_int = 1 1816 | }; 1817 | $SIG{WINCH} = sub { 1818 | $resized = 1 1819 | }; 1820 | 1821 | $resized = 3; 1822 | 1823 | $disp_update = 2; 1824 | 1825 | $show_title_bar = 1; 1826 | } 1827 | 1828 | sub enter_fs { 1829 | return if $fs_open; 1830 | safe_print(Terminfo::rc, Terminfo::smcup, Terminfo::civis, Terminfo::smmouse); 1831 | $fs_open = 1; 1832 | } 1833 | 1834 | sub leave_fs { 1835 | return unless $fs_open; 1836 | safe_print(Terminfo::rmmouse, Terminfo::cnorm, Terminfo::rmcup); 1837 | safe_print(sprintf "\r%swaiting for %s...", Terminfo::sc, $::IRSSI{name}) if $_[0]; 1838 | 1839 | $fs_open = 0; 1840 | } 1841 | 1842 | sub end_prog { 1843 | leave_fs(); 1844 | STDIN->blocking(1); 1845 | `stty sane`; 1846 | printf "\r%s%sthanks for using %s\n", Terminfo::rc, Terminfo::el, $::IRSSI{name}; 1847 | } 1848 | 1849 | sub safe_print { 1850 | my $st = STDIN->blocking(1); 1851 | print @_; 1852 | STDIN->blocking($st); 1853 | } 1854 | 1855 | sub safe_qx { 1856 | my $st = STDIN->blocking(1); 1857 | my $ret = `$_[0]`; 1858 | STDIN->blocking($st); 1859 | $ret 1860 | } 1861 | 1862 | sub safe_print_sock { 1863 | return unless $sock; 1864 | my $was = $sock->blocking(1); 1865 | $sock->print(@_); 1866 | $sock->blocking($was); 1867 | } 1868 | 1869 | sub process_recv { 1870 | my $need = 0; 1871 | while ($rcvbuf =~ s/\n(.+)_BEGIN\n((?: .*\n)*)\1_END\n//) { 1872 | my $var = lc $1; 1873 | my $data = $2; 1874 | my @data = split "\n ", "\n$data ", -1; 1875 | shift @data; pop @data; 1876 | my $itembg = $vars{itembg}; 1877 | if ($var =~ s/list$//) { 1878 | $vars{$var} = \@data; 1879 | } 1880 | elsif ($var =~ s/map$//) { 1881 | $vars{$var} = +{ @data }; 1882 | } 1883 | else { 1884 | $vars{$var} = join "\n", @data; 1885 | } 1886 | $need = 1 if $var eq 'win'; 1887 | $need = 1 if $var eq 'redraw' && $vars{$var}; 1888 | if (($itembg//'') ne ($vars{itembg}//'')) { 1889 | $need = $vars{redraw} = 1; 1890 | } 1891 | _build_keymap() if $var eq 'key2'; 1892 | } 1893 | $need 1894 | } 1895 | 1896 | { my %ansi_table; 1897 | my ($i, $j, $k) = (0, 0, 0); 1898 | my %term_state; 1899 | sub reset_term_state { my %old_term = %term_state; %term_state = (); %old_term } 1900 | sub set_term_state { my %old_term = %term_state; %term_state = @_; %old_term } 1901 | %ansi_table = ( 1902 | # fe-common::core::formats.c:format_expand_styles 1903 | (map { my $t = $i++; ($_ => sub { my $n = $term_state{hicolor} ? \&Terminfo::setab16 : \&Terminfo::setab; 1904 | $n->($t) }) } (split //, '01234567' )), 1905 | (map { my $t = $j++; ($_ => sub { my $n = $term_state{hicolor} ? \&Terminfo::setaf16 : \&Terminfo::setaf; 1906 | $n->($t) }) } (split //, 'krgybmcw' )), 1907 | (map { my $t = $k++; ($_ => sub { my $n = $term_state{hicolor} ? \&Terminfo::setaf : \&Terminfo::setaf16; 1908 | $n->($t) }) } (split //, 'KRGYBMCW')), 1909 | # reset 1910 | n => sub { $term_state{hicolor} = 0; my $r = Terminfo::op; 1911 | for (qw(blink rev bold)) { 1912 | $r .= Terminfo->can("exit_$_")->() if delete $term_state{$_}; 1913 | } 1914 | { 1915 | local $ansi_table{n} = $ansi_table{N}; 1916 | $r .= formats_to_ansi_basic($vars{itembg}); 1917 | } 1918 | $r 1919 | }, 1920 | N => sub { reset_term_state(); Terminfo::sgr0 }, 1921 | # flash/bright 1922 | F => sub { my $n = 'blink'; my $e = ($term_state{$n} ^= 1) ? $n : "exit_$n"; Terminfo->can($e)->() }, 1923 | # reverse 1924 | 8 => sub { my $n = 'rev'; my $e = ($term_state{$n} ^= 1) ? $n : "exit_$n"; Terminfo->can($e)->() }, 1925 | # bold 1926 | "_" => sub { my $n = 'bold'; my $e = ($term_state{$n} ^= 1) ? $n : "exit_$n"; Terminfo->can($e)->() }, 1927 | # underline 1928 | U => sub { my $n = 'ul'; my $e = ($term_state{$n} ^= 1) ? $n : "exit_$n"; Terminfo->can($e)->() }, 1929 | # italic 1930 | I => sub { my $n = 'it'; my $e = ($term_state{$n} ^= 1) ? $n : "exit_$n"; Terminfo->can($e)->() }, 1931 | # bold, used as colour modifier if AWL_HI9 is set 1932 | 9 => $ENV{AWL_HI9} ? sub { $term_state{hicolor} ^= 1; '' } 1933 | : sub { my $n = 'bold'; my $e = ($term_state{$n} ^= 1) ? $n : "exit_$n"; Terminfo->can($e)->() }, 1934 | # delete other stuff 1935 | (map { $_ => sub { '' } } (split //, ':|>#[')), 1936 | # escape 1937 | (map { my $close = $_; $_ => sub { $close } } (split //, '{}%')), 1938 | ); 1939 | for my $base (0 .. 15) { 1940 | my $close = $base; 1941 | my $idx = ($close&8) | ($close&4)>>2 | ($close&2) | ($close&1)<<2; 1942 | $ansi_table{ (sprintf "x0%x", $close) } = 1943 | $ansi_table{ (sprintf "x0%X", $close) } = 1944 | sub { Terminfo::setab256($idx) }; 1945 | $ansi_table{ (sprintf "X0%x", $close) } = 1946 | $ansi_table{ (sprintf "X0%X", $close) } = 1947 | sub { Terminfo::setaf256($idx) }; 1948 | } 1949 | for my $plane (1 .. 6) { 1950 | for my $coord (0 .. 35) { 1951 | my $close = 16 + ($plane-1) * 36 + $coord; 1952 | my $ch = $coord < 10 ? $coord : chr( $coord - 10 + ord 'a' ); 1953 | $ansi_table{ "x$plane$ch" } = 1954 | $ansi_table{ "x$plane\U$ch" } = 1955 | sub { Terminfo::setab256($close) }; 1956 | $ansi_table{ "X$plane$ch" } = 1957 | $ansi_table{ "X$plane\U$ch" } = 1958 | sub { Terminfo::setaf256($close) }; 1959 | } 1960 | } 1961 | for my $gray (0 .. 23) { 1962 | my $close = 232 + $gray; 1963 | my $ch = chr( $gray + ord 'a' ); 1964 | $ansi_table{ "x7$ch" } = 1965 | $ansi_table{ "x7\U$ch" } = 1966 | sub { Terminfo::setab256($close) }; 1967 | $ansi_table{ "X7$ch" } = 1968 | $ansi_table{ "X7\U$ch" } = 1969 | sub { Terminfo::setaf256($close) }; 1970 | } 1971 | sub formats_to_ansi_basic { 1972 | my $o = shift; 1973 | $o =~ s/(%(X..|x..|.))/exists $ansi_table{$2} ? $ansi_table{$2}->() : $1/gex; 1974 | $o 1975 | } 1976 | } 1977 | 1978 | sub _header { 1979 | my $str = uc ::setc(); 1980 | my $space = int( ((abs $vars{block}) - length $str) / (1 + length $str)); 1981 | if ($space > 0) { 1982 | my $ss = ' ' x $space; 1983 | $str = join $ss, '', (split //, $str), ''; 1984 | } 1985 | my $pad = (abs $vars{block}) - length $str; 1986 | $str = ' ' x ($pad/2) . $str . ' ' x ($pad/2 + $pad%2); 1987 | $str 1988 | } 1989 | 1990 | sub _add_item { 1991 | my ($i, $j, $c, $wi, $screen, $mouse) = @_; 1992 | $screen->[$i][$j] = "%N%n$wi"; 1993 | if (exists $vars{mouse}{$c - 1}) { 1994 | $mouse->[$i][$j] = $vars{mouse}{$c - 1}; 1995 | } 1996 | } 1997 | sub update_screen { 1998 | $disp_update = 0; 1999 | unless ($sock && exists $vars{seplen} && exists $vars{block}) { 2000 | leave_fs(1); 2001 | return; 2002 | } 2003 | enter_fs(); 2004 | @screen = () if delete $vars{redraw}; 2005 | %mouse_coords = (); 2006 | my $ncols = ($vars{seplen} + abs $vars{block}) ? 2007 | int( ($screenWidth + $vars{seplen}) / ($vars{seplen} + abs $vars{block}) ) : 0; 2008 | my $xenl = ($vars{seplen} + abs $vars{block}) 2009 | && $ncols > int( ($screenWidth + $vars{seplen} - 1) / ($vars{seplen} + abs $vars{block}) ); 2010 | my $nrows = $screenHeight - $vars{ha}; 2011 | my @wi = @{$vars{win}//[]}; 2012 | my $max_items = $ncols * $nrows; 2013 | my $c = $show_title_bar ? 1 : 0; 2014 | my $items = @wi + $c; 2015 | my $titems = $items > $max_items ? $max_items : $items; 2016 | my $i = 0; 2017 | my $j = 0; 2018 | my @new_screen; 2019 | my @new_mouse; 2020 | $new_screen[0][0] = _header() . ' ' x $vars{seplen} 2021 | if $show_title_bar; 2022 | unless ($nrows > $ncols) { # line layout 2023 | ++$j if $show_title_bar; 2024 | for my $wi (@wi) { 2025 | if ($j >= $ncols) { 2026 | $j = 0; 2027 | ++$i; 2028 | } 2029 | last if $i >= $nrows; 2030 | _add_item($i, $j, $show_title_bar ? $c : $c + 1, 2031 | $wi, \@new_screen, \@new_mouse); 2032 | if ($c + 1 < $titems && $j + 1 < $ncols) { 2033 | $new_screen[$i][$j] .= $vars{separator}; 2034 | } 2035 | ++$j; 2036 | ++$c; 2037 | } 2038 | } 2039 | else { # column layout 2040 | ++$i if $show_title_bar; 2041 | for my $wi (@wi) { 2042 | if ($i >= $nrows) { 2043 | $i = 0; 2044 | ++$j; 2045 | } 2046 | last if $j >= $ncols; 2047 | _add_item($i, $j, $show_title_bar ? $c : $c + 1, 2048 | $wi, \@new_screen, \@new_mouse); 2049 | if ($c + $nrows < $titems) { 2050 | $new_screen[$i][$j] .= $vars{separator}; 2051 | } 2052 | ++$i; 2053 | ++$c; 2054 | } 2055 | } 2056 | my $step = $vars{seplen} + abs $vars{block}; 2057 | $i = 0; 2058 | my $str = Terminfo::sc . Terminfo::sgr0; 2059 | for (my $i = 0; $i < @new_screen; ++$i) { 2060 | for (my $j = 0; $j < @{$new_screen[$i]}; ++$j) { 2061 | if (defined $new_mouse[$i] && defined $new_mouse[$i][$j]) { 2062 | my $from = $j * $step; 2063 | $mouse_coords{$i}{$_} = $new_mouse[$i][$j] 2064 | for $from .. $from + abs $vars{block}; 2065 | } 2066 | next if defined $screen[$i] && defined $screen[$i][$j] 2067 | && $screen[$i][$j] eq $new_screen[$i][$j]; 2068 | $str .= Terminfo::cup($i, $j * $step) 2069 | . formats_to_ansi_basic($new_screen[$i][$j]) 2070 | . Terminfo::sgr0; 2071 | $str .= Terminfo::el if $j == $#{$new_screen[$i]} && (!$xenl || $j + 1 != $ncols); 2072 | } 2073 | } 2074 | for (@new_screen .. $screenHeight - 1) { 2075 | if (!@screen || defined $screen[$_]) { 2076 | $str .= Terminfo::cup($_, 0) . Terminfo::sgr0 . Terminfo::el; 2077 | } 2078 | } 2079 | $str .= Terminfo::rc; 2080 | safe_print $str; 2081 | @screen = @new_screen; 2082 | } 2083 | 2084 | sub handle_resize { 2085 | if (defined (my $r = safe_qx('stty size'))) { 2086 | ($screenHeight, $screenWidth) = split ' ', $r; 2087 | $resized = 0; 2088 | @screen = (); 2089 | $disp_update = 1; 2090 | if ($one_shot_integration == 2) { 2091 | $one_shot_resize--; 2092 | } 2093 | } 2094 | else { 2095 | } 2096 | } 2097 | 2098 | sub _build_keymap { 2099 | %c2w = reverse( %{$vars{key}}, %{$vars{key2}} ); 2100 | if (!grep { /^[+-]./ } keys %c2w) { 2101 | %c2w = (%c2w, map { ("-$_" => $c2w{$_}) } grep { !/^\^./ } keys %c2w); 2102 | } 2103 | %c2w = map { 2104 | my $key = $_; 2105 | s{^(-)?(\+)?(\^)?(.)}{ 2106 | join '', ( 2107 | ($1 ? "\e" : ''), 2108 | ($2 ? "\e\e" : ''), 2109 | ($3 ? "$4"^"@" : $4) 2110 | ) 2111 | }e; 2112 | $_ => $c2w{$key} 2113 | } keys %c2w; 2114 | @seqlist = sort { length $b <=> length $a } keys %c2w; 2115 | } 2116 | 2117 | sub _match_tmux { 2118 | length $ENV{TMUX} && exists $vars{irssienv}{tmux_srv} && length $vars{irssienv}{tmux_pane} 2119 | && $ENV{TMUX} eq $vars{irssienv}{tmux_srv} 2120 | } 2121 | 2122 | sub process_keys { 2123 | Encode::_utf8_on($keybuf); 2124 | my $win; 2125 | my $use_mouse; 2126 | my $maybe; 2127 | KEY: while (length $keybuf && !$maybe) { 2128 | $maybe = 0; 2129 | if ($keybuf =~ s/^\e\[M(.)(.)(.)//) { 2130 | @last_mouse = @mouse;# if @mouse && $mouse[0] < 64; 2131 | @mouse = map { -32 + ord } ($1, $2, $3); 2132 | $use_mouse = 1; 2133 | next KEY; 2134 | } 2135 | for my $s (@seqlist) { 2136 | if ($keybuf =~ s/^\Q$s//) { 2137 | $win = $c2w{$s}; 2138 | $use_mouse = 0; 2139 | next KEY; 2140 | } 2141 | elsif (length $keybuf < length $s && $s =~ /^\Q$keybuf/) { 2142 | $maybe = 1; 2143 | } 2144 | } 2145 | unless ($maybe) { 2146 | substr $keybuf, 0, 1, ''; 2147 | } 2148 | } 2149 | if ($use_mouse && @mouse && @last_mouse && 2150 | $mouse[2] == $last_mouse[2] && 2151 | $mouse[1] == $last_mouse[1] && 2152 | ($mouse[0] == 3 || $mouse[0] == 64 || $mouse[0] == 65)) { 2153 | if ($mouse[0] == 64) { 2154 | $win = 'up'; 2155 | } 2156 | elsif ($mouse[0] == 65) { 2157 | $win = 'down'; 2158 | } 2159 | elsif (exists $mouse_coords{$mouse[2] - 1}{$mouse[1] - 1}) { 2160 | $win = $mouse_coords{$mouse[2] - 1}{$mouse[1] - 1}; 2161 | } 2162 | elsif ($mouse[2] == 1 && $mouse[1] <= abs $vars{block}) { 2163 | $win = $last_mouse[0] != 0 ? 'last' : 'active'; 2164 | } 2165 | else { 2166 | } 2167 | } 2168 | if (defined $win) { 2169 | $win =~ s/^_//; 2170 | safe_print_sock("$win\n"); 2171 | if (!exists $ENV{AWL_AUTOFOCUS} || $ENV{AWL_AUTOFOCUS}) { 2172 | if (_match_tmux()) { 2173 | safe_qx("tmux selectp -t $vars{irssienv}{tmux_pane} 2>&1"); 2174 | } 2175 | elsif (exists $vars{irssienv}{xwinid}) { 2176 | safe_qx("wmctrl -ia $vars{irssienv}{xwinid} 2>/dev/null"); 2177 | } 2178 | } 2179 | } 2180 | Encode::_utf8_off($keybuf); 2181 | } 2182 | 2183 | sub check_integration { 2184 | return unless $vars{irssienv}; 2185 | return unless $sock && exists $vars{seplen} && exists $vars{block}; 2186 | if ($one_shot_integration == 1) { 2187 | my $nrows = $screenHeight - $vars{ha}; 2188 | my $ncols = ($vars{seplen} + abs $vars{block}) ? int( ($screenWidth + $vars{seplen}) / ($vars{seplen} + abs $vars{block}) ) : 0; 2189 | my $items = ($show_title_bar ? 1 : 0) + @{$vars{win}//[]}; 2190 | my $dcols_required = $nrows ? int($items/$nrows) + !!($items%$nrows) : 0; 2191 | my $rows_required = $ncols ? int($items/$ncols) + !!($items%$ncols) : 0; 2192 | $rows_required = abs $vars{ml} 2193 | if ($vars{ml} < 0 || ($vars{ml} > 0 && $rows_required > $vars{ml})); 2194 | $dcols_required = abs $vars{mc} 2195 | if ($vars{mc} < 0 || ($vars{mc} > 0 && $dcols_required > $vars{mc})); 2196 | my $rows = $rows_required + $vars{ha}; 2197 | my $cols = ($dcols_required * ($vars{seplen} + abs $vars{block})) - $vars{seplen}; 2198 | if (_match_tmux()) { 2199 | # int( ($screenWidth + $vars{seplen}) / ($vars{seplen} + abs $vars{block}) ); 2200 | my ($pos_flag, $before); 2201 | if ($integration_position eq 'left') { 2202 | $pos_flag = 'h'; 2203 | $before = 1; 2204 | } 2205 | elsif ($integration_position eq 'top') { 2206 | $pos_flag = 'v'; 2207 | $before = 1; 2208 | } 2209 | elsif ($integration_position eq 'right') { 2210 | $pos_flag = 'h'; 2211 | } 2212 | else { 2213 | $pos_flag = 'v'; 2214 | } 2215 | my @cmd = "joinp -d$pos_flag -s $ENV{TMUX_PANE} -t $vars{irssienv}{tmux_pane}"; 2216 | push @cmd, "swapp -d -t $ENV{TMUX_PANE} -s $vars{irssienv}{tmux_pane}" 2217 | if $before; 2218 | $cols = max($cols, 2); 2219 | $rows = max($rows, 2); 2220 | 2221 | safe_qx("tmux " . (join " \\\; ", @cmd) . " 2>&1"); 2222 | } 2223 | else { 2224 | $resized = 1; 2225 | #safe_qx("resize -s $screenHeight $cols 2>&1") 2226 | # if $cols > 0; 2227 | } 2228 | $one_shot_integration++; 2229 | if ($resized == 1) { 2230 | handle_resize(); 2231 | resize_integration(); 2232 | } 2233 | } 2234 | elsif ($one_shot_integration == 2) { 2235 | resize_integration(1); 2236 | } 2237 | } 2238 | 2239 | sub resize_integration { 2240 | return unless $one_shot_integration; 2241 | return unless ($one_shot_resize//0) < 0 || shift; 2242 | my $nrows = $screenHeight - $vars{ha}; 2243 | my $ncols = ($vars{seplen} + abs $vars{block}) ? int( ($screenWidth + $vars{seplen}) / ($vars{seplen} + abs $vars{block}) ) : 0; 2244 | my $items = ($show_title_bar ? 1 : 0) + @{$vars{win}//[]}; 2245 | my $dcols_required = $nrows ? (int($items/$nrows) + !!($items%$nrows)) : 0; 2246 | my $rows_required = $ncols ? int($items/$ncols) + !!($items%$ncols) : 0; 2247 | $rows_required = abs $vars{ml} 2248 | if ($vars{ml} < 0 || ($vars{ml} > 0 && $rows_required > $vars{ml})); 2249 | $dcols_required = abs $vars{mc} 2250 | if ($vars{mc} < 0 || ($vars{mc} > 0 && $dcols_required > $vars{mc})); 2251 | my $rows = $rows_required + $vars{ha}; 2252 | my $cols = ($dcols_required * ($vars{seplen} + abs $vars{block})) - $vars{seplen}; 2253 | if (_match_tmux()) { 2254 | my $pos_flag; 2255 | my $before = 0; 2256 | if ($integration_position eq 'left') { 2257 | $pos_flag = 'h'; 2258 | $before = 1; 2259 | } 2260 | elsif ($integration_position eq 'top') { 2261 | $pos_flag = 'v'; 2262 | $before = 1; 2263 | } 2264 | elsif ($integration_position eq 'right') { 2265 | $pos_flag = 'h'; 2266 | } 2267 | else { 2268 | $pos_flag = 'v'; 2269 | } 2270 | my @cmd; 2271 | # hard tmux limits 2272 | $cols = max($cols, 2); 2273 | $rows = max($rows, 2); 2274 | if ($pos_flag eq 'h' && $cols != $screenWidth) { 2275 | my $change = $screenWidth - $cols; 2276 | my $dir = ($before ^ ($change<0)) ? 'L' : 'R'; 2277 | push @cmd, "resizep -$dir -t $ENV{TMUX_PANE} @{[abs $change]}"; 2278 | #push @cmd, "resizep -x $cols -t $ENV{TMUX_PANE}"; 2279 | $one_shot_resize = 1; 2280 | } 2281 | if ($pos_flag eq 'v' && $rows != $screenHeight) { 2282 | #push @cmd, "resizep -y $rows -t $ENV{TMUX_PANE}"; 2283 | my $change = $screenHeight - $rows; 2284 | my $dir = ($before ^ ($change<0)) ? 'U' : 'D'; 2285 | push @cmd, "resizep -$dir -t $ENV{TMUX_PANE} @{[abs $change]}"; 2286 | $one_shot_resize = 1; 2287 | } 2288 | 2289 | safe_qx("tmux " . (join " \\\; ", @cmd) . " 2>&1") 2290 | if @cmd; 2291 | } 2292 | else { 2293 | $cols = max($cols, 1); 2294 | $rows = max($rows, 1); 2295 | unless ($nrows > $ncols) { # line layout 2296 | if ($rows != $screenHeight) { 2297 | safe_qx("resize -s $rows $screenWidth 2>&1"); 2298 | $one_shot_resize = 1; 2299 | } 2300 | } 2301 | else { 2302 | if ($cols != $screenWidth) { 2303 | safe_qx("resize -s $screenHeight $cols 2>&1"); 2304 | $one_shot_resize = 1; 2305 | } 2306 | } 2307 | } 2308 | if ($resized == 1) { 2309 | handle_resize(); 2310 | } 2311 | } 2312 | 2313 | sub init_integration { 2314 | return unless $one_shot_integration; 2315 | if (_match_tmux()) { 2316 | } 2317 | else { 2318 | } 2319 | safe_print("\e]2;".(uc ::setc())."\e\\"); 2320 | } 2321 | 2322 | sub main { 2323 | require Getopt::Std; 2324 | my %opts; 2325 | Getopt::Std::getopts('1p:', \%opts); 2326 | my $one_shot = $opts{1}; 2327 | $integration_position = $opts{p}; 2328 | $one_shot_integration = 0+!!$one_shot; 2329 | #shift if @_ && $_[0] eq '--'; 2330 | &init; 2331 | $show_title_bar = 0 if $ENV{AWL_NOTITLE}; 2332 | init_integration(); 2333 | until ($got_int) { 2334 | $timeout = undef; 2335 | if ($resized) { 2336 | if ($resized == 1) { 2337 | $timeout = 1; 2338 | $resized++; 2339 | } 2340 | else { 2341 | handle_resize(); 2342 | resize_integration(); 2343 | } 2344 | } 2345 | unless ($sock || $timeout) { 2346 | connect_it(); 2347 | } 2348 | $timeout ||= RECONNECT_TIME unless $sock; 2349 | update_screen() if $disp_update; 2350 | SELECT: while (my @read = $loop->can_read($timeout)) { 2351 | for my $fh (@read) { 2352 | if ($fh == \*STDIN) { 2353 | if (read STDIN, my $buf, BLOCK_SIZE) { 2354 | do { 2355 | $keybuf .= $buf; 2356 | } while read STDIN, $buf, BLOCK_SIZE; 2357 | } 2358 | else { 2359 | $got_int = 1; 2360 | last SELECT; 2361 | } 2362 | } 2363 | else { 2364 | if ($fh->read(my $buf, BLOCK_SIZE)) { 2365 | do { 2366 | $rcvbuf .= $buf; 2367 | } while $fh->read($buf, BLOCK_SIZE); 2368 | } 2369 | else { 2370 | $disp_update = 1; 2371 | remove_conn($fh); 2372 | if ($one_shot) { 2373 | $got_int = 1; 2374 | last SELECT; 2375 | } 2376 | $timeout ||= RECONNECT_TIME; 2377 | } 2378 | } 2379 | } 2380 | $disp_update |= process_recv() if length $rcvbuf; 2381 | process_keys() if length $keybuf; 2382 | check_integration() if $one_shot; 2383 | update_screen() if $disp_update; 2384 | } 2385 | continue { 2386 | } 2387 | } 2388 | end_prog(); 2389 | } 2390 | } 2391 | 2392 | 1; 2393 | 2394 | # Changelog 2395 | # ========= 2396 | # 1.3 - workaround for irssi issue #572 2397 | # 1.2 - new format to choose abbreviation character 2398 | # 1.1 - infinite loop on shortening certain window names reported by Kalan 2399 | # 2400 | # 1.0 2401 | # - new awl_viewer_launch setting and an array of related settings 2402 | # - fixed regression bug /exec -interactive 2403 | # - fixed some warnings in perl 5.10 reported by kl3 2404 | # - workaround for crash due to infinite recursion in irssi's Perl 2405 | # error handling 2406 | # 2407 | # 0.9 2408 | # - fix endless loop in awin detection code! 2409 | # - correct colour swap in awl_viewer 2410 | # - fix passing of alternate socket path to the viewer 2411 | # - potential undefinedness in mouse refnum hinted at by Canopus 2412 | # - fixed regression bug /exec -interactive 2413 | # - add case-insensitive modifier to awl_sort 2414 | # - run custom_xform on awl_prefer_name also 2415 | # - avoid inconsistent active window state after awin detection 2416 | # reported by ss 2417 | # - revert %9-hack in the viewer prompted by discussion with pierrot 2418 | # - fix new warning in perl 5.22 2419 | # 2420 | # 0.8 2421 | # - replace fifo mode with external viewer script 2422 | # - remove bundled cpan modules 2423 | # - work around bogus irssi warning 2424 | # - improve mouse support 2425 | # - workaround for broken cumode in irssi 0.8.15 2426 | # - fix handling of non-meta windows (uninitialized warning) 2427 | # - add 256 colour support, strip true colour codes 2428 | # - fix totally bogus $N padding reported by Ed S. 2429 | # - make /window goto #name mappings work but ignore non-existant ones 2430 | # - improve incomplete reads reported by bcode 2431 | # - fix single % in awl_viewer reported by bcode 2432 | # - add support for key bindings by nike and ferret 2433 | # - coerce utf8 key binds 2434 | # - add settings: custom_xform, last_line_shade, hide_name_data 2435 | # - abbreviations were broken in some cases 2436 | # - fix some misuse of / as cmdchar in mouse script reported by bcode 2437 | # - add shared status bar mode 2438 | # - ${type} variables for custom_xform setting 2439 | # - crash if custom_xform had runtime error 2440 | # - update sorting documentation 2441 | # - fix odd case in size calculation noted by lasers 2442 | # - add missing font styles to the viewer reported by ishanyx 2443 | # - add italic 2444 | # 2445 | # 0.7g 2446 | # - remove screen support and replace it with fifo support 2447 | # - add double-width support to the shortener 2448 | # - correct documentation regarding $T vs. display_header 2449 | # - add missing refresh for window item changed (thanks vague) 2450 | # - add visible windows 2451 | # - add exemptions for active window 2452 | # - workaround for hiding the window changes from trackbar 2453 | # - hack to force 16colours in screen mode 2454 | # - remember last window (reported by earthnative) 2455 | # - wrong window focus on new queries (reported by emsid) 2456 | # - dataloss bug on trying to remember last window 2457 | # 2458 | # 0.6d+ 2459 | # - add support for network headers 2460 | # - fixed regression bug /exec -interactive 2461 | # 2462 | # 0.6ca+ 2463 | # - add screen support (from nicklist.pl) 2464 | # - names can now have a max length and window names can be used 2465 | # - fixed a bug with block display in screen mode and status bar mode 2466 | # - added space handling to ir_fe and removed it again 2467 | # - now handling formats on my own 2468 | # - started to work on $tag display 2469 | # - added warning about missing sb_act_none abstract leading to 2470 | # - display*active settings 2471 | # - added warning about the bug in awl_display_(no)key_active settings 2472 | # - mouse hack 2473 | # 2474 | # 0.5d 2475 | # - add setting to also hide the last status bar if empty (awl_all_disable) 2476 | # - reverted to old utf8 code to also calculate broken utf8 length correctly 2477 | # - simplified dealing with status bars in wlreset 2478 | # - added a little tweak for the renamed term_type somewhere after Irssi 0.8.9 2479 | # - fixed bug in handling channel #$$ 2480 | # - reset background colour at the beginning of an entry 2481 | # 2482 | # 0.4d 2483 | # - fixed order of disabling status bars 2484 | # - several attempts at special chars, without any real success 2485 | # and much more weird new bugs caused by this 2486 | # - setting to specify sort order 2487 | # - reduced timeout values 2488 | # - added awl_hide_data 2489 | # - make it so the dynamic sub is actually deleted 2490 | # - fix a bug with removing of the last separator 2491 | # - take into consideration parse_special 2492 | # 2493 | # 0.3b 2494 | # - automatically kill old status bars 2495 | # - reset on /reload 2496 | # - position/placement settings 2497 | # 2498 | # 0.2 2499 | # - automated retrieval of key bindings (thanks grep.pl authors) 2500 | # - improved removing of status bars 2501 | # - got rid of status chop 2502 | # 2503 | # 0.1 2504 | # - Based on chanact.pl which was apparently based on lightbar.c and 2505 | # nicklist.pl with various other ideas from random scripts. 2506 | --------------------------------------------------------------------------------