├── 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 | 
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 |
--------------------------------------------------------------------------------