profanity_fe 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/profanity +1692 -0
- data/lib/profanity_fe/countdown_window.rb +72 -0
- data/lib/profanity_fe/indicator_window.rb +48 -0
- data/lib/profanity_fe/progress_window.rb +67 -0
- data/lib/profanity_fe/text_window.rb +174 -0
- metadata +49 -0
data/bin/profanity
ADDED
@@ -0,0 +1,1692 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: US-ASCII
|
3
|
+
=begin
|
4
|
+
|
5
|
+
ProfanityFE v0.5.0
|
6
|
+
Copyright (C) 2013 Matthew Lowe
|
7
|
+
|
8
|
+
This program is free software; you can redistribute it and/or modify
|
9
|
+
it under the terms of the GNU General Public License as published by
|
10
|
+
the Free Software Foundation; either version 2 of the License, or
|
11
|
+
(at your option) any later version.
|
12
|
+
|
13
|
+
This program is distributed in the hope that it will be useful,
|
14
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16
|
+
GNU General Public License for more details.
|
17
|
+
|
18
|
+
You should have received a copy of the GNU General Public License along
|
19
|
+
with this program; if not, write to the Free Software Foundation, Inc.,
|
20
|
+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
21
|
+
|
22
|
+
matt@io4.us
|
23
|
+
|
24
|
+
=end
|
25
|
+
|
26
|
+
$version = '0.5.0'
|
27
|
+
|
28
|
+
require 'thread'
|
29
|
+
require 'socket'
|
30
|
+
require 'rexml/document'
|
31
|
+
require 'curses'
|
32
|
+
include Curses
|
33
|
+
|
34
|
+
require 'profanity_fe/text_window'
|
35
|
+
require 'profanity_fe/progress_window'
|
36
|
+
require 'profanity_fe/countdown_window'
|
37
|
+
require 'profanity_fe/indicator_window'
|
38
|
+
|
39
|
+
Curses.init_screen
|
40
|
+
Curses.start_color
|
41
|
+
Curses.cbreak
|
42
|
+
Curses.noecho
|
43
|
+
|
44
|
+
server = nil
|
45
|
+
command_buffer = String.new
|
46
|
+
command_buffer_pos = 0
|
47
|
+
command_buffer_offset = 0
|
48
|
+
command_history = Array.new
|
49
|
+
command_history_pos = 0
|
50
|
+
max_command_history = 20
|
51
|
+
min_cmd_length_for_history = 4
|
52
|
+
$server_time_offset = 0
|
53
|
+
skip_server_time_offset = false
|
54
|
+
key_binding = Hash.new
|
55
|
+
key_action = Hash.new
|
56
|
+
need_prompt = false
|
57
|
+
prompt_text = ">"
|
58
|
+
stream_handler = Hash.new
|
59
|
+
indicator_handler = Hash.new
|
60
|
+
progress_handler = Hash.new
|
61
|
+
countdown_handler = Hash.new
|
62
|
+
command_window = nil
|
63
|
+
command_window_layout = nil
|
64
|
+
# We need a mutex for the settings because highlights can be accessed during a
|
65
|
+
# reload. For now, it is just used to protect access to HIGHLIGHT, but if we
|
66
|
+
# ever support reloading other settings in the future it will have to protect
|
67
|
+
# those.
|
68
|
+
SETTINGS_LOCK = Mutex.new
|
69
|
+
HIGHLIGHT = Hash.new
|
70
|
+
PRESET = Hash.new
|
71
|
+
LAYOUT = Hash.new
|
72
|
+
WINDOWS = Hash.new
|
73
|
+
SCROLL_WINDOW = Array.new
|
74
|
+
|
75
|
+
def add_prompt(window, prompt_text, cmd="")
|
76
|
+
window.add_string("#{prompt_text}#{cmd}", [ h={ :start => 0, :end => (prompt_text.length + cmd.length), :fg => '555555' } ])
|
77
|
+
end
|
78
|
+
|
79
|
+
for arg in ARGV
|
80
|
+
if arg =~ /^\-\-help|^\-h|^\-\?/
|
81
|
+
puts ""
|
82
|
+
puts "Profanity FrontEnd v#{$version}"
|
83
|
+
puts ""
|
84
|
+
puts " --port=<port>"
|
85
|
+
puts " --default-color-id=<id>"
|
86
|
+
puts " --default-background-color-id=<id>"
|
87
|
+
puts " --custom-colors=<on|off>"
|
88
|
+
puts " --settings-file=<filename>"
|
89
|
+
puts ""
|
90
|
+
exit
|
91
|
+
elsif arg =~ /^\-\-port=([0-9]+)$/
|
92
|
+
PORT = $1.to_i
|
93
|
+
elsif arg =~ /^\-\-default\-color\-id=([0-9]+)$/
|
94
|
+
DEFAULT_COLOR_ID = $1.to_i
|
95
|
+
elsif arg =~ /^\-\-default\-background\-color\-id=([0-9]+)$/
|
96
|
+
DEFAULT_BACKGROUND_COLOR_ID = $1.to_i
|
97
|
+
elsif arg =~ /^\-\-custom\-colors=(on|off|yes|no)$/
|
98
|
+
fix_setting = { 'on' => true, 'yes' => true, 'off' => false, 'no' => false }
|
99
|
+
CUSTOM_COLORS = fix_setting[$1]
|
100
|
+
elsif arg =~ /^\-\-settings\-file=(.*?)$/
|
101
|
+
SETTINGS_FILENAME = $1
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def log(value)
|
106
|
+
File.open('profanity.log', 'a') { |f| f.puts value }
|
107
|
+
end
|
108
|
+
|
109
|
+
unless defined?(PORT)
|
110
|
+
PORT = 8000
|
111
|
+
end
|
112
|
+
unless defined?(DEFAULT_COLOR_ID)
|
113
|
+
DEFAULT_COLOR_ID = 7
|
114
|
+
end
|
115
|
+
unless defined?(DEFAULT_BACKGROUND_COLOR_ID)
|
116
|
+
DEFAULT_BACKGROUND_COLOR_ID = 0
|
117
|
+
end
|
118
|
+
unless defined?(SETTINGS_FILENAME)
|
119
|
+
SETTINGS_FILENAME = File.expand_path('~/.profanity.xml')
|
120
|
+
end
|
121
|
+
unless defined?(CUSTOM_COLORS)
|
122
|
+
CUSTOM_COLORS = Curses.can_change_color?
|
123
|
+
end
|
124
|
+
|
125
|
+
DEFAULT_COLOR_CODE = Curses.color_content(DEFAULT_COLOR_ID).collect { |num| ((num/1000.0)*255).round.to_s(16) }.join('').rjust(6, '0')
|
126
|
+
DEFAULT_BACKGROUND_COLOR_CODE = Curses.color_content(DEFAULT_BACKGROUND_COLOR_ID).collect { |num| ((num/1000.0)*255).round.to_s(16) }.join('').rjust(6, '0')
|
127
|
+
|
128
|
+
unless File.exists?(SETTINGS_FILENAME)
|
129
|
+
|
130
|
+
File.open(SETTINGS_FILENAME, 'w') { |file| file.write "<settings>
|
131
|
+
<highlight fg='9090ff'>^(?:You gesture|You intone a phrase of elemental power|You recite a series of mystical phrases|You trace a series of glowing runes|Your hands glow with power as you invoke|You trace a simple rune while intoning|You trace a sign while petitioning the spirits|You trace an intricate sign that contorts in the air).*$</highlight>
|
132
|
+
<highlight fg='9090ff'>^(?:Cast Roundtime 3 Seconds\\.|Your spell is ready\\.)$</highlight>
|
133
|
+
<highlight fg='9090ff'>^.*remaining\\. \\]$</highlight>
|
134
|
+
<highlight fg='88aaff'>([A-Z][a-z]+ disk)</highlight>
|
135
|
+
<highlight fg='555555'>^\\[.*?\\](?:>|>).*$</highlight>
|
136
|
+
<highlight fg='555555'>\\([0-9][0-9]\\:[0-9][0-9]\\:[0-9][0-9]\\)$</highlight>
|
137
|
+
<highlight fg='0000ff'>^\\[LNet\\]</highlight>
|
138
|
+
<highlight fg='008000'>^\\[code\\]</highlight>
|
139
|
+
<highlight fg='808000'>^\\[Shattered\\]</highlight>
|
140
|
+
<highlight fg='ffffff'>^\\[Private(?:To)?\\]</highlight>
|
141
|
+
<highlight fg='008000'>^--- Lich:.*</highlight>
|
142
|
+
<highlight fg='565656'>\\((?:calmed|dead|flying|hiding|kneeling|prone|sitting|sleeping|stunned)\\)</highlight>
|
143
|
+
<highlight fg='ff0000'>^.* throws (?:his|her) arms skyward!$|swirling black void|(?:Dozens of flaming meteors light the sky nearby!|Several flaming meteors light the nearby sky!|Several flaming rocks burst from the sky and smite the area!|A low roar of quickly parting air can be heard above!)</highlight>
|
144
|
+
<highlight fg='ffffff'>^.*(?:falls slack against the floor|falls slack against the ground|falls to the floor, motionless|falls to the ground dead|falls to the ground motionless|and dies|and lies still|goes still|going still)\\.$</highlight>
|
145
|
+
<highlight fg='ffffff'>^.* is stunned!$|^You come out of hiding\\.$</highlight>
|
146
|
+
<highlight fg='ffaaaa'>.*ruining your hiding place\\.$|^You are no longer hidden\\.$|^\\s*You are (?:stunned|knocked to the ground).*|^You are unable to remain hidden!$|^You are visible again\\.$|^You fade into sight\\.$|^You fade into view.*|^You feel drained!$|^You have overextended yourself!$|^You feel yourself going into shock!$</highlight>
|
147
|
+
<preset id='whisper' fg='66ff66'/>
|
148
|
+
<preset id='speech' fg='66ff66'/>
|
149
|
+
<preset id='roomName' fg='ffffff'/>
|
150
|
+
<preset id='monsterbold' fg='d2bc2a'/>
|
151
|
+
<preset id='familiar' bg='00001a'/>
|
152
|
+
<preset id='thoughts' bg='001a00'/>
|
153
|
+
<preset id='voln' bg='001a00'/>
|
154
|
+
<key id='alt'>
|
155
|
+
<key id='f' macro='something'/>
|
156
|
+
</key>
|
157
|
+
<key id='enter' action='send_command'/>
|
158
|
+
<key id='left' action='cursor_left'/>
|
159
|
+
<key id='right' action='cursor_right'/>
|
160
|
+
<key id='ctrl+left' action='cursor_word_left'/>
|
161
|
+
<key id='ctrl+right' action='cursor_word_right'/>
|
162
|
+
<key id='home' action='cursor_home'/>
|
163
|
+
<key id='end' action='cursor_end'/>
|
164
|
+
<key id='backspace' action='cursor_backspace'/>
|
165
|
+
<key id='win_backspace' action='cursor_backspace'/>
|
166
|
+
<key id='ctrl+?' action='cursor_backspace'/>
|
167
|
+
<key id='delete' action='cursor_delete'/>
|
168
|
+
<key id='tab' action='switch_current_window'/>
|
169
|
+
<key id='alt+page_up' action='scroll_current_window_up_one'/>
|
170
|
+
<key id='alt+page_down' action='scroll_current_window_down_one'/>
|
171
|
+
<key id='page_up' action='scroll_current_window_up_page'/>
|
172
|
+
<key id='page_down' action='scroll_current_window_down_page'/>
|
173
|
+
<key id='up' action='previous_command'/>
|
174
|
+
<key id='down' action='next_command'/>
|
175
|
+
<key id='ctrl+up' action='send_last_command'/>
|
176
|
+
<key id='alt+up' action='send_second_last_command'/>
|
177
|
+
<key id='resize' action='resize'/>
|
178
|
+
<key id='ctrl+d' macro='\\xstance defensive\\r'/>
|
179
|
+
<key id='ctrl+o' macro='\\xstance offensive\\r'/>
|
180
|
+
<key id='ctrl+g' macro='\\xremove my buckler\\r'/>
|
181
|
+
<key id='ctrl+p' macro='\\xwear my buckler\\r'/>
|
182
|
+
<key id='ctrl+f' macro='\\xtell familiar to '/>
|
183
|
+
<layout id='default'>
|
184
|
+
<window class='text' top='6' left='12' width='cols-12' height='lines-7' value='main' buffer-size='2000' />
|
185
|
+
<window class='text' top='0' left='0' height='6' width='cols' value='lnet,thoughts,voln' buffer-size='1000' />
|
186
|
+
<window class='text' top='7' left='0' width='11' height='lines-31' value='death,logons' buffer-size='500' />
|
187
|
+
|
188
|
+
<window class='indicator' top='lines-1' left='12' height='1' width='1' label='>' value='prompt' fg='444444,44444'/>
|
189
|
+
<window class='command' top='lines-1' left='13' width='cols-13' height='1' />
|
190
|
+
|
191
|
+
<window class='progress' top='lines-11' left='0' width='11' height='1' label='stance:' value='stance' bg='290055'/>
|
192
|
+
<window class='progress' top='lines-10' left='0' width='11' height='1' label='mind:' value='mind' bg='663000,442000'/>
|
193
|
+
<window class='progress' top='lines-8' left='0' width='11' height='1' label='health:' value='health' bg='004800,003300'/>
|
194
|
+
<window class='progress' top='lines-7' left='0' width='11' height='1' label='spirit:' value='spirit' bg='333300,222200'/>
|
195
|
+
<window class='progress' top='lines-6' left='0' width='11' height='1' label='mana:' value='mana' bg='0000a0,000055'/>
|
196
|
+
<window class='progress' top='lines-5' left='0' width='11' height='1' label='stam:' value='stamina' bg='003333,002222'/>
|
197
|
+
<window class='progress' top='lines-3' left='0' width='11' height='1' label='load:' value='encumbrance' bg='990033,4c0019' fg='nil,nil,nil,444444'/>
|
198
|
+
|
199
|
+
<window class='countdown' top='lines-2' left='0' width='11' height='1' label='stun:' value='stunned' fg='444444,dddddd' bg='nil,aa0000'/>
|
200
|
+
<window class='countdown' top='lines-1' left='0' width='11' height='1' label='rndtime:' value='roundtime' fg='444444,dddddd,dddddd,dddddd' bg='nil,aa0000,0000aa'/>
|
201
|
+
|
202
|
+
<window class='indicator' top='lines-15' left='1' height='1' width='1' label='^' value='compass:up' fg='444444,ffff00'/>
|
203
|
+
<window class='indicator' top='lines-14' left='1' height='1' width='1' label='o' value='compass:out' fg='444444,ffff00'/>
|
204
|
+
<window class='indicator' top='lines-13' left='1' height='1' width='1' label='v' value='compass:down' fg='444444,ffff00'/>
|
205
|
+
<window class='indicator' top='lines-15' left='5' height='1' width='1' label='*' value='compass:nw' fg='444444,ffff00'/>
|
206
|
+
<window class='indicator' top='lines-14' left='5' height='1' width='1' label='<' value='compass:w' fg='444444,ffff00'/>
|
207
|
+
<window class='indicator' top='lines-13' left='5' height='1' width='1' label='*' value='compass:sw' fg='444444,ffff00'/>
|
208
|
+
<window class='indicator' top='lines-15' left='7' height='1' width='1' label='^' value='compass:n' fg='444444,ffff00'/>
|
209
|
+
<window class='indicator' top='lines-13' left='7' height='1' width='1' label='v' value='compass:s' fg='444444,ffff00'/>
|
210
|
+
<window class='indicator' top='lines-15' left='9' height='1' width='1' label='*' value='compass:ne' fg='444444,ffff00'/>
|
211
|
+
<window class='indicator' top='lines-14' left='9' height='1' width='1' label='>' value='compass:e' fg='444444,ffff00'/>
|
212
|
+
<window class='indicator' top='lines-13' left='9' height='1' width='1' label='*' value='compass:se' fg='444444,ffff00'/>
|
213
|
+
|
214
|
+
<window class='indicator' top='lines-23' left='1' height='1' width='1' label='e' value='leftEye' fg='444444,ffff00,ff6600,ff0000,00ffff,0066ff,0000ff'/>
|
215
|
+
<window class='indicator' top='lines-23' left='5' height='1' width='1' label='e' value='rightEye' fg='444444,ffff00,ff6600,ff0000,00ffff,0066ff,0000ff'/>
|
216
|
+
<window class='indicator' top='lines-22' left='3' height='1' width='1' label='O' value='head' fg='444444,ffff00,ff6600,ff0000,00ffff,0066ff,0000ff'/>
|
217
|
+
<window class='indicator' top='lines-21' left='2' height='1' width='1' label='/' value='leftArm' fg='444444,ffff00,ff6600,ff0000,00ffff,0066ff,0000ff'/>
|
218
|
+
<window class='indicator' top='lines-21' left='3' height='1' width='1' label='|' value='chest' fg='444444,ffff00,ff6600,ff0000,00ffff,0066ff,0000ff'/>
|
219
|
+
<window class='indicator' top='lines-21' left='4' height='1' width='1' label='\\' value='rightArm' fg='444444,ffff00,ff6600,ff0000,00ffff,0066ff,0000ff'/>
|
220
|
+
<window class='indicator' top='lines-20' left='1' height='1' width='1' label='o' value='leftHand' fg='444444,ffff00,ff6600,ff0000,00ffff,0066ff,0000ff'/>
|
221
|
+
<window class='indicator' top='lines-20' left='3' height='1' width='1' label='|' value='abdomen' fg='444444,ffff00,ff6600,ff0000,00ffff,0066ff,0000ff'/>
|
222
|
+
<window class='indicator' top='lines-20' left='5' height='1' width='1' label='o' value='rightHand' fg='444444,ffff00,ff6600,ff0000,00ffff,0066ff,0000ff'/>
|
223
|
+
<window class='indicator' top='lines-19' left='1' height='2' width='2' label=' /o' value='leftLeg' fg='444444,ffff00,ff6600,ff0000,00ffff,0066ff,0000ff'/>
|
224
|
+
<window class='indicator' top='lines-19' left='4' height='2' width='2' label='\\ o' value='rightLeg' fg='444444,ffff00,ff6600,ff0000,00ffff,0066ff,0000ff'/>
|
225
|
+
<window class='indicator' top='lines-23' left='8' height='1' width='2' label='ns' value='nsys' fg='444444,ffff00,ff6600,ff0000,00ffff,0066ff,0000ff'/>
|
226
|
+
<window class='indicator' top='lines-21' left='8' height='1' width='2' label='nk' value='neck' fg='444444,ffff00,ff6600,ff0000,00ffff,0066ff,0000ff'/>
|
227
|
+
<window class='indicator' top='lines-19' left='8' height='1' width='2' label='bk' value='back' fg='444444,ffff00,ff6600,ff0000,00ffff,0066ff,0000ff'/>
|
228
|
+
|
229
|
+
<window class='indicator' top='lines-17' left='0' height='1' width='3' label='psn' value='poisoned' fg='444444,ff0000'/>
|
230
|
+
<window class='indicator' top='lines-17' left='4' height='1' width='3' label='dis' value='diseased' fg='444444,ff0000'/>
|
231
|
+
<window class='indicator' top='lines-17' left='8' height='1' width='3' label='bld' value='bleeding' fg='444444,ff0000'/>
|
232
|
+
</layout>
|
233
|
+
</settings>
|
234
|
+
" }
|
235
|
+
|
236
|
+
end
|
237
|
+
|
238
|
+
xml_escape_list = {
|
239
|
+
'<' => '<',
|
240
|
+
'>' => '>',
|
241
|
+
'"' => '"',
|
242
|
+
''' => "'",
|
243
|
+
'&' => '&',
|
244
|
+
# '
' => "\n",
|
245
|
+
}
|
246
|
+
|
247
|
+
key_name = {
|
248
|
+
'ctrl+a' => 1,
|
249
|
+
'ctrl+b' => 2,
|
250
|
+
# 'ctrl+c' => 3,
|
251
|
+
'ctrl+d' => 4,
|
252
|
+
'ctrl+e' => 5,
|
253
|
+
'ctrl+f' => 6,
|
254
|
+
'ctrl+g' => 7,
|
255
|
+
'ctrl+h' => 8,
|
256
|
+
'win_backspace' => 8,
|
257
|
+
'ctrl+i' => 9,
|
258
|
+
'tab' => 9,
|
259
|
+
'ctrl+j' => 10,
|
260
|
+
'enter' => 10,
|
261
|
+
'ctrl+k' => 11,
|
262
|
+
'ctrl+l' => 12,
|
263
|
+
'return' => 13,
|
264
|
+
'ctrl+m' => 13,
|
265
|
+
'ctrl+n' => 14,
|
266
|
+
'ctrl+o' => 15,
|
267
|
+
'ctrl+p' => 16,
|
268
|
+
# 'ctrl+q' => 17,
|
269
|
+
'ctrl+r' => 18,
|
270
|
+
# 'ctrl+s' => 19,
|
271
|
+
'ctrl+t' => 20,
|
272
|
+
'ctrl+u' => 21,
|
273
|
+
'ctrl+v' => 22,
|
274
|
+
'ctrl+w' => 23,
|
275
|
+
'ctrl+x' => 24,
|
276
|
+
'ctrl+y' => 25,
|
277
|
+
# 'ctrl+z' => 26,
|
278
|
+
'alt' => 27,
|
279
|
+
'escape' => 27,
|
280
|
+
'ctrl+?' => 127,
|
281
|
+
'down' => 258,
|
282
|
+
'up' => 259,
|
283
|
+
'left' => 260,
|
284
|
+
'right' => 261,
|
285
|
+
'home' => 262,
|
286
|
+
'backspace' => 263,
|
287
|
+
'f1' => 265,
|
288
|
+
'f2' => 266,
|
289
|
+
'f3' => 267,
|
290
|
+
'f4' => 268,
|
291
|
+
'f5' => 269,
|
292
|
+
'f6' => 270,
|
293
|
+
'f7' => 271,
|
294
|
+
'f8' => 272,
|
295
|
+
'f9' => 273,
|
296
|
+
'f10' => 274,
|
297
|
+
'f11' => 275,
|
298
|
+
'f12' => 276,
|
299
|
+
'delete' => 330,
|
300
|
+
'insert' => 331,
|
301
|
+
'page_down' => 338,
|
302
|
+
'page_up' => 339,
|
303
|
+
'end' => 360,
|
304
|
+
'resize' => 410,
|
305
|
+
'ctrl+delete' => 513,
|
306
|
+
'alt+down' => 517,
|
307
|
+
'ctrl+down' => 519,
|
308
|
+
'alt+left' => 537,
|
309
|
+
'ctrl+left' => 539,
|
310
|
+
'alt+page_down' => 542,
|
311
|
+
'alt+page_up' => 547,
|
312
|
+
'alt+right' => 552,
|
313
|
+
'ctrl+right' => 554,
|
314
|
+
'alt+up' => 558,
|
315
|
+
'ctrl+up' => 560,
|
316
|
+
}
|
317
|
+
|
318
|
+
if CUSTOM_COLORS
|
319
|
+
COLOR_ID_LOOKUP = Hash.new
|
320
|
+
COLOR_ID_LOOKUP[DEFAULT_COLOR_CODE] = DEFAULT_COLOR_ID
|
321
|
+
COLOR_ID_LOOKUP[DEFAULT_BACKGROUND_COLOR_CODE] = DEFAULT_BACKGROUND_COLOR_ID
|
322
|
+
COLOR_ID_HISTORY = Array.new
|
323
|
+
for num in 0...Curses.colors
|
324
|
+
unless (num == DEFAULT_COLOR_ID) or (num == DEFAULT_BACKGROUND_COLOR_ID)
|
325
|
+
COLOR_ID_HISTORY.push(num)
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
def get_color_id(code)
|
330
|
+
if color_id = COLOR_ID_LOOKUP[code]
|
331
|
+
color_id
|
332
|
+
else
|
333
|
+
color_id = COLOR_ID_HISTORY.shift
|
334
|
+
COLOR_ID_LOOKUP.delete_if { |k,v| v == color_id }
|
335
|
+
sleep 0.01 # somehow this keeps Curses.init_color from failing sometimes
|
336
|
+
Curses.init_color(color_id, ((code[0..1].to_s.hex/255.0)*1000).round, ((code[2..3].to_s.hex/255.0)*1000).round, ((code[4..5].to_s.hex/255.0)*1000).round)
|
337
|
+
COLOR_ID_LOOKUP[code] = color_id
|
338
|
+
COLOR_ID_HISTORY.push(color_id)
|
339
|
+
color_id
|
340
|
+
end
|
341
|
+
end
|
342
|
+
else
|
343
|
+
COLOR_CODE = [ '000000', '800000', '008000', '808000', '000080', '800080', '008080', 'c0c0c0', '808080', 'ff0000', '00ff00', 'ffff00', '0000ff', 'ff00ff', '00ffff', 'ffffff', '000000', '00005f', '000087', '0000af', '0000d7', '0000ff', '005f00', '005f5f', '005f87', '005faf', '005fd7', '005fff', '008700', '00875f', '008787', '0087af', '0087d7', '0087ff', '00af00', '00af5f', '00af87', '00afaf', '00afd7', '00afff', '00d700', '00d75f', '00d787', '00d7af', '00d7d7', '00d7ff', '00ff00', '00ff5f', '00ff87', '00ffaf', '00ffd7', '00ffff', '5f0000', '5f005f', '5f0087', '5f00af', '5f00d7', '5f00ff', '5f5f00', '5f5f5f', '5f5f87', '5f5faf', '5f5fd7', '5f5fff', '5f8700', '5f875f', '5f8787', '5f87af', '5f87d7', '5f87ff', '5faf00', '5faf5f', '5faf87', '5fafaf', '5fafd7', '5fafff', '5fd700', '5fd75f', '5fd787', '5fd7af', '5fd7d7', '5fd7ff', '5fff00', '5fff5f', '5fff87', '5fffaf', '5fffd7', '5fffff', '870000', '87005f', '870087', '8700af', '8700d7', '8700ff', '875f00', '875f5f', '875f87', '875faf', '875fd7', '875fff', '878700', '87875f', '878787', '8787af', '8787d7', '8787ff', '87af00', '87af5f', '87af87', '87afaf', '87afd7', '87afff', '87d700', '87d75f', '87d787', '87d7af', '87d7d7', '87d7ff', '87ff00', '87ff5f', '87ff87', '87ffaf', '87ffd7', '87ffff', 'af0000', 'af005f', 'af0087', 'af00af', 'af00d7', 'af00ff', 'af5f00', 'af5f5f', 'af5f87', 'af5faf', 'af5fd7', 'af5fff', 'af8700', 'af875f', 'af8787', 'af87af', 'af87d7', 'af87ff', 'afaf00', 'afaf5f', 'afaf87', 'afafaf', 'afafd7', 'afafff', 'afd700', 'afd75f', 'afd787', 'afd7af', 'afd7d7', 'afd7ff', 'afff00', 'afff5f', 'afff87', 'afffaf', 'afffd7', 'afffff', 'd70000', 'd7005f', 'd70087', 'd700af', 'd700d7', 'd700ff', 'd75f00', 'd75f5f', 'd75f87', 'd75faf', 'd75fd7', 'd75fff', 'd78700', 'd7875f', 'd78787', 'd787af', 'd787d7', 'd787ff', 'd7af00', 'd7af5f', 'd7af87', 'd7afaf', 'd7afd7', 'd7afff', 'd7d700', 'd7d75f', 'd7d787', 'd7d7af', 'd7d7d7', 'd7d7ff', 'd7ff00', 'd7ff5f', 'd7ff87', 'd7ffaf', 'd7ffd7', 'd7ffff', 'ff0000', 'ff005f', 'ff0087', 'ff00af', 'ff00d7', 'ff00ff', 'ff5f00', 'ff5f5f', 'ff5f87', 'ff5faf', 'ff5fd7', 'ff5fff', 'ff8700', 'ff875f', 'ff8787', 'ff87af', 'ff87d7', 'ff87ff', 'ffaf00', 'ffaf5f', 'ffaf87', 'ffafaf', 'ffafd7', 'ffafff', 'ffd700', 'ffd75f', 'ffd787', 'ffd7af', 'ffd7d7', 'ffd7ff', 'ffff00', 'ffff5f', 'ffff87', 'ffffaf', 'ffffd7', 'ffffff', '080808', '121212', '1c1c1c', '262626', '303030', '3a3a3a', '444444', '4e4e4e', '585858', '626262', '6c6c6c', '767676', '808080', '8a8a8a', '949494', '9e9e9e', 'a8a8a8', 'b2b2b2', 'bcbcbc', 'c6c6c6', 'd0d0d0', 'dadada', 'e4e4e4', 'eeeeee' ][0...Curses.colors]
|
344
|
+
COLOR_ID_LOOKUP = Hash.new
|
345
|
+
|
346
|
+
def get_color_id(code)
|
347
|
+
if color_id = COLOR_ID_LOOKUP[code]
|
348
|
+
color_id
|
349
|
+
else
|
350
|
+
least_error = nil
|
351
|
+
least_error_id = nil
|
352
|
+
COLOR_CODE.each_index { |color_id|
|
353
|
+
error = ((COLOR_CODE[color_id][0..1].hex - code[0..1].hex)**2) + ((COLOR_CODE[color_id][2..3].hex - code[2..3].hex)**2) + ((COLOR_CODE[color_id][4..6].hex - code[4..6].hex)**2)
|
354
|
+
if least_error.nil? or (error < least_error)
|
355
|
+
least_error = error
|
356
|
+
least_error_id = color_id
|
357
|
+
end
|
358
|
+
}
|
359
|
+
COLOR_ID_LOOKUP[code] = least_error_id
|
360
|
+
least_error_id
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
#COLOR_PAIR_LIST = Array.new
|
366
|
+
#for num in 1...Curses::color_pairs
|
367
|
+
# COLOR_PAIR_LIST.push h={ :color_id => nil, :background_id => nil, :id => num }
|
368
|
+
#end
|
369
|
+
|
370
|
+
#157+12+1 = 180
|
371
|
+
#38+1+6 = 45
|
372
|
+
#32767
|
373
|
+
|
374
|
+
COLOR_PAIR_ID_LOOKUP = Hash.new
|
375
|
+
COLOR_PAIR_HISTORY = Array.new
|
376
|
+
|
377
|
+
# fixme: high color pair id's change text?
|
378
|
+
# A_NORMAL = 0
|
379
|
+
# A_STANDOUT = 65536
|
380
|
+
# A_UNDERLINE = 131072
|
381
|
+
# 15000 = black background, dark blue-green text
|
382
|
+
# 10000 = dark yellow background, black text
|
383
|
+
# 5000 = black
|
384
|
+
# 2000 = black
|
385
|
+
# 1000 = highlights show up black
|
386
|
+
# 100 = normal
|
387
|
+
# 500 = black and some underline
|
388
|
+
|
389
|
+
for num in 1...Curses::color_pairs # fixme: things go to hell at about pair 256
|
390
|
+
#for num in 1...([Curses::color_pairs, 256].min)
|
391
|
+
COLOR_PAIR_HISTORY.push(num)
|
392
|
+
end
|
393
|
+
|
394
|
+
def get_color_pair_id(fg_code, bg_code)
|
395
|
+
if fg_code.nil?
|
396
|
+
fg_id = DEFAULT_COLOR_ID
|
397
|
+
else
|
398
|
+
fg_id = get_color_id(fg_code)
|
399
|
+
end
|
400
|
+
if bg_code.nil?
|
401
|
+
bg_id = DEFAULT_BACKGROUND_COLOR_ID
|
402
|
+
else
|
403
|
+
bg_id = get_color_id(bg_code)
|
404
|
+
end
|
405
|
+
if (COLOR_PAIR_ID_LOOKUP[fg_id]) and (color_pair_id = COLOR_PAIR_ID_LOOKUP[fg_id][bg_id])
|
406
|
+
color_pair_id
|
407
|
+
else
|
408
|
+
color_pair_id = COLOR_PAIR_HISTORY.shift
|
409
|
+
COLOR_PAIR_ID_LOOKUP.each { |w,x| x.delete_if { |y,z| z == color_pair_id } }
|
410
|
+
sleep 0.01
|
411
|
+
Curses.init_pair(color_pair_id, fg_id, bg_id)
|
412
|
+
COLOR_PAIR_ID_LOOKUP[fg_id] ||= Hash.new
|
413
|
+
COLOR_PAIR_ID_LOOKUP[fg_id][bg_id] = color_pair_id
|
414
|
+
COLOR_PAIR_HISTORY.push(color_pair_id)
|
415
|
+
color_pair_id
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
# Implement support for basic readline-style kill and yank (cut and paste)
|
420
|
+
# commands. Successive calls to delete_word, backspace_word, kill_forward, and
|
421
|
+
# kill_line will accumulate text into the kill_buffer as long as no other
|
422
|
+
# commands have changed the command buffer. These commands call kill_before to
|
423
|
+
# reset the kill_buffer if the command buffer has changed, add the newly
|
424
|
+
# deleted text to the kill_buffer, and finally call kill_after to remember the
|
425
|
+
# state of the command buffer for next time.
|
426
|
+
kill_buffer = ''
|
427
|
+
kill_original = ''
|
428
|
+
kill_last = ''
|
429
|
+
kill_last_pos = 0
|
430
|
+
kill_before = proc {
|
431
|
+
if kill_last != command_buffer || kill_last_pos != command_buffer_pos
|
432
|
+
kill_buffer = ''
|
433
|
+
kill_original = command_buffer
|
434
|
+
end
|
435
|
+
}
|
436
|
+
kill_after = proc {
|
437
|
+
kill_last = command_buffer.dup
|
438
|
+
kill_last_pos = command_buffer_pos
|
439
|
+
}
|
440
|
+
|
441
|
+
fix_layout_number = proc { |str|
|
442
|
+
str = str.gsub('lines', Curses.lines.to_s).gsub('cols', Curses.cols.to_s)
|
443
|
+
str.untaint
|
444
|
+
begin
|
445
|
+
proc { $SAFE = 1; eval(str) }.call.to_i
|
446
|
+
rescue
|
447
|
+
$stderr.puts $!
|
448
|
+
$stderr.puts $!.backtrace[0..1]
|
449
|
+
0
|
450
|
+
end
|
451
|
+
}
|
452
|
+
|
453
|
+
load_layout = proc { |layout_id|
|
454
|
+
if xml = LAYOUT[layout_id]
|
455
|
+
old_windows = IndicatorWindow.list | TextWindow.list | CountdownWindow.list | ProgressWindow.list
|
456
|
+
|
457
|
+
previous_indicator_handler = indicator_handler
|
458
|
+
indicator_handler = Hash.new
|
459
|
+
|
460
|
+
previous_stream_handler = stream_handler
|
461
|
+
stream_handler = Hash.new
|
462
|
+
|
463
|
+
previous_progress_handler = progress_handler
|
464
|
+
progress_handler = Hash.new
|
465
|
+
|
466
|
+
previous_countdown_handler = countdown_handler
|
467
|
+
progress_handler = Hash.new
|
468
|
+
|
469
|
+
xml.elements.each { |e|
|
470
|
+
if e.name == 'window'
|
471
|
+
height, width, top, left = fix_layout_number.call(e.attributes['height']), fix_layout_number.call(e.attributes['width']), fix_layout_number.call(e.attributes['top']), fix_layout_number.call(e.attributes['left'])
|
472
|
+
if (height > 0) and (width > 0) and (top >= 0) and (left >= 0) and (top < Curses.lines) and (left < Curses.cols)
|
473
|
+
if e.attributes['class'] == 'indicator'
|
474
|
+
if e.attributes['value'] and (window = previous_indicator_handler[e.attributes['value']])
|
475
|
+
previous_indicator_handler[e.attributes['value']] = nil
|
476
|
+
old_windows.delete(window)
|
477
|
+
else
|
478
|
+
window = IndicatorWindow.new(height, width, top, left)
|
479
|
+
end
|
480
|
+
window.layout = [ e.attributes['height'], e.attributes['width'], e.attributes['top'], e.attributes['left'] ]
|
481
|
+
window.scrollok(false)
|
482
|
+
window.label = e.attributes['label'] if e.attributes['label']
|
483
|
+
window.fg = e.attributes['fg'].split(',').collect { |val| if val == 'nil'; nil; else; val; end } if e.attributes['fg']
|
484
|
+
window.bg = e.attributes['bg'].split(',').collect { |val| if val == 'nil'; nil; else; val; end } if e.attributes['bg']
|
485
|
+
if e.attributes['value']
|
486
|
+
indicator_handler[e.attributes['value']] = window
|
487
|
+
end
|
488
|
+
window.redraw
|
489
|
+
elsif e.attributes['class'] == 'text'
|
490
|
+
if width > 1
|
491
|
+
if e.attributes['value'] and (window = previous_stream_handler[previous_stream_handler.keys.find { |key| e.attributes['value'].split(',').include?(key) }])
|
492
|
+
previous_stream_handler[e.attributes['value']] = nil
|
493
|
+
old_windows.delete(window)
|
494
|
+
else
|
495
|
+
window = TextWindow.new(height, width - 1, top, left)
|
496
|
+
window.scrollbar = Curses::Window.new(window.maxy, 1, window.begy, window.begx + window.maxx)
|
497
|
+
end
|
498
|
+
window.layout = [ e.attributes['height'], e.attributes['width'], e.attributes['top'], e.attributes['left'] ]
|
499
|
+
window.scrollok(true)
|
500
|
+
window.max_buffer_size = e.attributes['buffer-size'] || 1000
|
501
|
+
e.attributes['value'].split(',').each { |str|
|
502
|
+
stream_handler[str] = window
|
503
|
+
}
|
504
|
+
end
|
505
|
+
elsif e.attributes['class'] == 'countdown'
|
506
|
+
if e.attributes['value'] and (window = previous_countdown_handler[e.attributes['value']])
|
507
|
+
previous_countdown_handler[e.attributes['value']] = nil
|
508
|
+
old_windows.delete(window)
|
509
|
+
else
|
510
|
+
window = CountdownWindow.new(height, width, top, left)
|
511
|
+
end
|
512
|
+
window.layout = [ e.attributes['height'], e.attributes['width'], e.attributes['top'], e.attributes['left'] ]
|
513
|
+
window.scrollok(false)
|
514
|
+
window.label = e.attributes['label'] if e.attributes['label']
|
515
|
+
window.fg = e.attributes['fg'].split(',').collect { |val| if val == 'nil'; nil; else; val; end } if e.attributes['fg']
|
516
|
+
window.bg = e.attributes['bg'].split(',').collect { |val| if val == 'nil'; nil; else; val; end } if e.attributes['bg']
|
517
|
+
if e.attributes['value']
|
518
|
+
countdown_handler[e.attributes['value']] = window
|
519
|
+
end
|
520
|
+
window.update
|
521
|
+
elsif e.attributes['class'] == 'progress'
|
522
|
+
if e.attributes['value'] and (window = previous_progress_handler[e.attributes['value']])
|
523
|
+
previous_progress_handler[e.attributes['value']] = nil
|
524
|
+
old_windows.delete(window)
|
525
|
+
else
|
526
|
+
window = ProgressWindow.new(height, width, top, left)
|
527
|
+
end
|
528
|
+
window.layout = [ e.attributes['height'], e.attributes['width'], e.attributes['top'], e.attributes['left'] ]
|
529
|
+
window.scrollok(false)
|
530
|
+
window.label = e.attributes['label'] if e.attributes['label']
|
531
|
+
window.fg = e.attributes['fg'].split(',').collect { |val| if val == 'nil'; nil; else; val; end } if e.attributes['fg']
|
532
|
+
window.bg = e.attributes['bg'].split(',').collect { |val| if val == 'nil'; nil; else; val; end } if e.attributes['bg']
|
533
|
+
if e.attributes['value']
|
534
|
+
progress_handler[e.attributes['value']] = window
|
535
|
+
end
|
536
|
+
window.redraw
|
537
|
+
elsif e.attributes['class'] == 'command'
|
538
|
+
unless command_window
|
539
|
+
command_window = Curses::Window.new(height, width, top, left)
|
540
|
+
end
|
541
|
+
command_window_layout = [ e.attributes['height'], e.attributes['width'], e.attributes['top'], e.attributes['left'] ]
|
542
|
+
command_window.scrollok(false)
|
543
|
+
command_window.keypad(true)
|
544
|
+
end
|
545
|
+
end
|
546
|
+
end
|
547
|
+
}
|
548
|
+
if current_scroll_window = TextWindow.list[0]
|
549
|
+
current_scroll_window.update_scrollbar
|
550
|
+
end
|
551
|
+
for window in old_windows
|
552
|
+
IndicatorWindow.list.delete(window)
|
553
|
+
TextWindow.list.delete(window)
|
554
|
+
CountdownWindow.list.delete(window)
|
555
|
+
ProgressWindow.list.delete(window)
|
556
|
+
if window.class == TextWindow
|
557
|
+
window.scrollbar.close
|
558
|
+
end
|
559
|
+
window.close
|
560
|
+
end
|
561
|
+
Curses.doupdate
|
562
|
+
end
|
563
|
+
}
|
564
|
+
|
565
|
+
do_macro = nil
|
566
|
+
|
567
|
+
setup_key = proc { |xml,binding|
|
568
|
+
if key = xml.attributes['id']
|
569
|
+
if key =~ /^[0-9]+$/
|
570
|
+
key = key.to_i
|
571
|
+
elsif (key.class) == String and (key.length == 1)
|
572
|
+
nil
|
573
|
+
else
|
574
|
+
key = key_name[key]
|
575
|
+
end
|
576
|
+
if key
|
577
|
+
if macro = xml.attributes['macro']
|
578
|
+
binding[key] = proc { do_macro.call(macro) }
|
579
|
+
elsif xml.attributes['action'] and action = key_action[xml.attributes['action']]
|
580
|
+
binding[key] = action
|
581
|
+
else
|
582
|
+
binding[key] ||= Hash.new
|
583
|
+
xml.elements.each { |e|
|
584
|
+
setup_key.call(e, binding[key])
|
585
|
+
}
|
586
|
+
end
|
587
|
+
end
|
588
|
+
end
|
589
|
+
}
|
590
|
+
|
591
|
+
load_settings_file = proc { |reload|
|
592
|
+
SETTINGS_LOCK.synchronize {
|
593
|
+
begin
|
594
|
+
HIGHLIGHT.clear()
|
595
|
+
File.open(SETTINGS_FILENAME) { |file|
|
596
|
+
xml_doc = REXML::Document.new(file)
|
597
|
+
xml_root = xml_doc.root
|
598
|
+
xml_root.elements.each { |e|
|
599
|
+
if e.name == 'highlight'
|
600
|
+
begin
|
601
|
+
r = Regexp.new(e.text)
|
602
|
+
rescue
|
603
|
+
r = nil
|
604
|
+
$stderr.puts e.to_s
|
605
|
+
$stderr.puts $!
|
606
|
+
end
|
607
|
+
if r
|
608
|
+
HIGHLIGHT[r] = [ e.attributes['fg'], e.attributes['bg'], e.attributes['ul'] ]
|
609
|
+
end
|
610
|
+
end
|
611
|
+
# These are things that we ignore if we're doing a reload of the settings file
|
612
|
+
if !reload
|
613
|
+
if e.name == 'preset'
|
614
|
+
PRESET[e.attributes['id']] = [ e.attributes['fg'], e.attributes['bg'] ]
|
615
|
+
elsif (e.name == 'layout') and (layout_id = e.attributes['id'])
|
616
|
+
LAYOUT[layout_id] = e
|
617
|
+
elsif e.name == 'key'
|
618
|
+
setup_key.call(e, key_binding)
|
619
|
+
end
|
620
|
+
end
|
621
|
+
}
|
622
|
+
}
|
623
|
+
rescue
|
624
|
+
$stdout.puts $!
|
625
|
+
$stdout.puts $!.backtrace[0..1]
|
626
|
+
log $!
|
627
|
+
log $!.backtrace[0..1]
|
628
|
+
|
629
|
+
end
|
630
|
+
}
|
631
|
+
}
|
632
|
+
|
633
|
+
command_window_put_ch = proc { |ch|
|
634
|
+
if (command_buffer_pos - command_buffer_offset + 1) >= command_window.maxx
|
635
|
+
command_window.setpos(0,0)
|
636
|
+
command_window.delch
|
637
|
+
command_buffer_offset += 1
|
638
|
+
command_window.setpos(0, command_buffer_pos - command_buffer_offset)
|
639
|
+
end
|
640
|
+
command_buffer.insert(command_buffer_pos, ch)
|
641
|
+
command_buffer_pos += 1
|
642
|
+
command_window.insch(ch)
|
643
|
+
command_window.setpos(0, command_buffer_pos - command_buffer_offset)
|
644
|
+
}
|
645
|
+
|
646
|
+
do_macro = proc { |macro|
|
647
|
+
# fixme: gsub %whatever
|
648
|
+
backslash = false
|
649
|
+
at_pos = nil
|
650
|
+
backfill = nil
|
651
|
+
macro.split('').each_with_index { |ch, i|
|
652
|
+
if backslash
|
653
|
+
if ch == '\\'
|
654
|
+
command_window_put_ch.call('\\')
|
655
|
+
elsif ch == 'x'
|
656
|
+
command_buffer.clear
|
657
|
+
command_buffer_pos = 0
|
658
|
+
command_buffer_offset = 0
|
659
|
+
command_window.deleteln
|
660
|
+
command_window.setpos(0,0)
|
661
|
+
elsif ch == 'r'
|
662
|
+
at_pos = nil
|
663
|
+
key_action['send_command'].call
|
664
|
+
elsif ch == '@'
|
665
|
+
command_window_put_ch.call('@')
|
666
|
+
elsif ch == '?'
|
667
|
+
backfill = i - 3
|
668
|
+
else
|
669
|
+
nil
|
670
|
+
end
|
671
|
+
backslash = false
|
672
|
+
else
|
673
|
+
if ch == '\\'
|
674
|
+
backslash = true
|
675
|
+
elsif ch == '@'
|
676
|
+
at_pos = command_buffer_pos
|
677
|
+
else
|
678
|
+
command_window_put_ch.call(ch)
|
679
|
+
end
|
680
|
+
end
|
681
|
+
}
|
682
|
+
if at_pos
|
683
|
+
while at_pos < command_buffer_pos
|
684
|
+
key_action['cursor_left'].call
|
685
|
+
end
|
686
|
+
while at_pos > command_buffer_pos
|
687
|
+
key_action['cursor_right'].call
|
688
|
+
end
|
689
|
+
end
|
690
|
+
command_window.noutrefresh
|
691
|
+
if backfill then
|
692
|
+
command_window.setpos(0,backfill)
|
693
|
+
command_buffer_pos = backfill
|
694
|
+
backfill = nil
|
695
|
+
end
|
696
|
+
Curses.doupdate
|
697
|
+
}
|
698
|
+
|
699
|
+
key_action['resize'] = proc {
|
700
|
+
# fixme: re-word-wrap
|
701
|
+
window = Window.new(0,0,0,0)
|
702
|
+
window.refresh
|
703
|
+
window.close
|
704
|
+
first_text_window = true
|
705
|
+
for window in TextWindow.list.to_a
|
706
|
+
window.resize(fix_layout_number.call(window.layout[0]), fix_layout_number.call(window.layout[1]) - 1)
|
707
|
+
window.move(fix_layout_number.call(window.layout[2]), fix_layout_number.call(window.layout[3]))
|
708
|
+
window.scrollbar.resize(window.maxy, 1)
|
709
|
+
window.scrollbar.move(window.begy, window.begx + window.maxx)
|
710
|
+
window.scroll(-window.maxy)
|
711
|
+
window.scroll(window.maxy)
|
712
|
+
window.clear_scrollbar
|
713
|
+
if first_text_window
|
714
|
+
window.update_scrollbar
|
715
|
+
first_text_window = false
|
716
|
+
end
|
717
|
+
window.noutrefresh
|
718
|
+
end
|
719
|
+
for window in [ IndicatorWindow.list.to_a, ProgressWindow.list.to_a, CountdownWindow.list.to_a ].flatten
|
720
|
+
window.resize(fix_layout_number.call(window.layout[0]), fix_layout_number.call(window.layout[1]))
|
721
|
+
window.move(fix_layout_number.call(window.layout[2]), fix_layout_number.call(window.layout[3]))
|
722
|
+
window.noutrefresh
|
723
|
+
end
|
724
|
+
if command_window
|
725
|
+
command_window.resize(fix_layout_number.call(command_window_layout[0]), fix_layout_number.call(command_window_layout[1]))
|
726
|
+
command_window.move(fix_layout_number.call(command_window_layout[2]), fix_layout_number.call(command_window_layout[3]))
|
727
|
+
command_window.noutrefresh
|
728
|
+
end
|
729
|
+
Curses.doupdate
|
730
|
+
}
|
731
|
+
|
732
|
+
key_action['cursor_left'] = proc {
|
733
|
+
if (command_buffer_offset > 0) and (command_buffer_pos - command_buffer_offset == 0)
|
734
|
+
command_buffer_pos -= 1
|
735
|
+
command_buffer_offset -= 1
|
736
|
+
command_window.insch(command_buffer[command_buffer_pos])
|
737
|
+
else
|
738
|
+
command_buffer_pos = [command_buffer_pos - 1, 0].max
|
739
|
+
end
|
740
|
+
command_window.setpos(0, command_buffer_pos - command_buffer_offset)
|
741
|
+
command_window.noutrefresh
|
742
|
+
Curses.doupdate
|
743
|
+
}
|
744
|
+
|
745
|
+
key_action['cursor_right'] = proc {
|
746
|
+
if ((command_buffer.length - command_buffer_offset) >= (command_window.maxx - 1)) and (command_buffer_pos - command_buffer_offset + 1) >= command_window.maxx
|
747
|
+
if command_buffer_pos < command_buffer.length
|
748
|
+
command_window.setpos(0,0)
|
749
|
+
command_window.delch
|
750
|
+
command_buffer_offset += 1
|
751
|
+
command_buffer_pos += 1
|
752
|
+
command_window.setpos(0, command_buffer_pos - command_buffer_offset)
|
753
|
+
unless command_buffer_pos >= command_buffer.length
|
754
|
+
command_window.insch(command_buffer[command_buffer_pos])
|
755
|
+
end
|
756
|
+
end
|
757
|
+
else
|
758
|
+
command_buffer_pos = [command_buffer_pos + 1, command_buffer.length].min
|
759
|
+
command_window.setpos(0, command_buffer_pos - command_buffer_offset)
|
760
|
+
end
|
761
|
+
command_window.noutrefresh
|
762
|
+
Curses.doupdate
|
763
|
+
}
|
764
|
+
|
765
|
+
key_action['cursor_word_left'] = proc {
|
766
|
+
if command_buffer_pos > 0
|
767
|
+
if m = command_buffer[0...(command_buffer_pos-1)].match(/.*(\w[^\w\s]|\W\w|\s\S)/)
|
768
|
+
new_pos = m.begin(1) + 1
|
769
|
+
else
|
770
|
+
new_pos = 0
|
771
|
+
end
|
772
|
+
if (command_buffer_offset > new_pos)
|
773
|
+
command_window.setpos(0, 0)
|
774
|
+
command_buffer[new_pos, (command_buffer_offset - new_pos)].split('').reverse.each { |ch| command_window.insch(ch) }
|
775
|
+
command_buffer_pos = new_pos
|
776
|
+
command_buffer_offset = new_pos
|
777
|
+
else
|
778
|
+
command_buffer_pos = new_pos
|
779
|
+
end
|
780
|
+
command_window.setpos(0, command_buffer_pos - command_buffer_offset)
|
781
|
+
command_window.noutrefresh
|
782
|
+
Curses.doupdate
|
783
|
+
end
|
784
|
+
}
|
785
|
+
|
786
|
+
key_action['cursor_word_right'] = proc {
|
787
|
+
if command_buffer_pos < command_buffer.length
|
788
|
+
if m = command_buffer[command_buffer_pos..-1].match(/\w[^\w\s]|\W\w|\s\S/)
|
789
|
+
new_pos = command_buffer_pos + m.begin(0) + 1
|
790
|
+
else
|
791
|
+
new_pos = command_buffer.length
|
792
|
+
end
|
793
|
+
overflow = new_pos - command_window.maxx - command_buffer_offset + 1
|
794
|
+
if overflow > 0
|
795
|
+
command_window.setpos(0,0)
|
796
|
+
overflow.times {
|
797
|
+
command_window.delch
|
798
|
+
command_buffer_offset += 1
|
799
|
+
}
|
800
|
+
command_window.setpos(0, command_window.maxx - overflow)
|
801
|
+
command_window.addstr command_buffer[(command_window.maxx - overflow + command_buffer_offset),overflow]
|
802
|
+
end
|
803
|
+
command_buffer_pos = new_pos
|
804
|
+
command_window.setpos(0, command_buffer_pos - command_buffer_offset)
|
805
|
+
command_window.noutrefresh
|
806
|
+
Curses.doupdate
|
807
|
+
end
|
808
|
+
}
|
809
|
+
|
810
|
+
key_action['cursor_home'] = proc {
|
811
|
+
command_buffer_pos = 0
|
812
|
+
command_window.setpos(0, 0)
|
813
|
+
for num in 1..command_buffer_offset
|
814
|
+
begin
|
815
|
+
command_window.insch(command_buffer[command_buffer_offset-num])
|
816
|
+
rescue
|
817
|
+
File.open('profanity.log', 'a') { |f| f.puts "command_buffer: #{command_buffer.inspect}"; f.puts "command_buffer_offset: #{command_buffer_offset.inspect}"; f.puts "num: #{num.inspect}"; f.puts $!; f.puts $!.backtrace[0...4] }
|
818
|
+
exit
|
819
|
+
end
|
820
|
+
end
|
821
|
+
command_buffer_offset = 0
|
822
|
+
command_window.noutrefresh
|
823
|
+
Curses.doupdate
|
824
|
+
}
|
825
|
+
|
826
|
+
key_action['cursor_end'] = proc {
|
827
|
+
if command_buffer.length < (command_window.maxx - 1)
|
828
|
+
command_buffer_pos = command_buffer.length
|
829
|
+
command_window.setpos(0, command_buffer_pos)
|
830
|
+
else
|
831
|
+
scroll_left_num = command_buffer.length - command_window.maxx + 1 - command_buffer_offset
|
832
|
+
command_window.setpos(0, 0)
|
833
|
+
scroll_left_num.times {
|
834
|
+
command_window.delch
|
835
|
+
command_buffer_offset += 1
|
836
|
+
}
|
837
|
+
command_buffer_pos = command_buffer_offset + command_window.maxx - 1 - scroll_left_num
|
838
|
+
command_window.setpos(0, command_buffer_pos - command_buffer_offset)
|
839
|
+
scroll_left_num.times {
|
840
|
+
command_window.addch(command_buffer[command_buffer_pos])
|
841
|
+
command_buffer_pos += 1
|
842
|
+
}
|
843
|
+
end
|
844
|
+
command_window.noutrefresh
|
845
|
+
Curses.doupdate
|
846
|
+
}
|
847
|
+
|
848
|
+
key_action['cursor_backspace'] = proc {
|
849
|
+
if command_buffer_pos > 0
|
850
|
+
command_buffer_pos -= 1
|
851
|
+
if command_buffer_pos == 0
|
852
|
+
command_buffer = command_buffer[(command_buffer_pos+1)..-1]
|
853
|
+
else
|
854
|
+
command_buffer = command_buffer[0..(command_buffer_pos-1)] + command_buffer[(command_buffer_pos+1)..-1]
|
855
|
+
end
|
856
|
+
command_window.setpos(0, command_buffer_pos - command_buffer_offset)
|
857
|
+
command_window.delch
|
858
|
+
if (command_buffer.length - command_buffer_offset + 1) > command_window.maxx
|
859
|
+
command_window.setpos(0, command_window.maxx - 1)
|
860
|
+
command_window.addch command_buffer[command_window.maxx - command_buffer_offset - 1]
|
861
|
+
command_window.setpos(0, command_buffer_pos - command_buffer_offset)
|
862
|
+
end
|
863
|
+
command_window.noutrefresh
|
864
|
+
Curses.doupdate
|
865
|
+
end
|
866
|
+
}
|
867
|
+
|
868
|
+
class String
|
869
|
+
def alnum?
|
870
|
+
!!match(/^[[:alnum:]]+$/)
|
871
|
+
end
|
872
|
+
def digits?
|
873
|
+
!!match(/^[[:digit:]]+$/)
|
874
|
+
end
|
875
|
+
def punct?
|
876
|
+
!!match(/^[[:punct:]]+$/)
|
877
|
+
end
|
878
|
+
def space?
|
879
|
+
!!match(/^[[:space:]]+$/)
|
880
|
+
end
|
881
|
+
end
|
882
|
+
|
883
|
+
key_action['cursor_delete'] = proc {
|
884
|
+
if (command_buffer.length > 0) and (command_buffer_pos < command_buffer.length)
|
885
|
+
if command_buffer_pos == 0
|
886
|
+
command_buffer = command_buffer[(command_buffer_pos+1)..-1]
|
887
|
+
elsif command_buffer_pos < command_buffer.length
|
888
|
+
command_buffer = command_buffer[0..(command_buffer_pos-1)] + command_buffer[(command_buffer_pos+1)..-1]
|
889
|
+
end
|
890
|
+
command_window.delch
|
891
|
+
if (command_buffer.length - command_buffer_offset + 1) > command_window.maxx
|
892
|
+
command_window.setpos(0, command_window.maxx - 1)
|
893
|
+
command_window.addch command_buffer[command_window.maxx - command_buffer_offset - 1]
|
894
|
+
command_window.setpos(0, command_buffer_pos - command_buffer_offset)
|
895
|
+
end
|
896
|
+
command_window.noutrefresh
|
897
|
+
Curses.doupdate
|
898
|
+
end
|
899
|
+
}
|
900
|
+
|
901
|
+
|
902
|
+
key_action['cursor_backspace_word'] = proc {
|
903
|
+
num_deleted = 0
|
904
|
+
deleted_alnum = false
|
905
|
+
deleted_nonspace = false
|
906
|
+
while command_buffer_pos > 0 do
|
907
|
+
next_char = command_buffer[command_buffer_pos - 1]
|
908
|
+
if num_deleted == 0 || (!deleted_alnum && next_char.punct?) || (!deleted_nonspace && next_char.space?) || next_char.alnum?
|
909
|
+
deleted_alnum = deleted_alnum || next_char.alnum?
|
910
|
+
deleted_nonspace = !next_char.space?
|
911
|
+
num_deleted += 1
|
912
|
+
kill_before.call
|
913
|
+
kill_buffer = next_char + kill_buffer
|
914
|
+
key_action['cursor_backspace'].call
|
915
|
+
kill_after.call
|
916
|
+
else
|
917
|
+
break
|
918
|
+
end
|
919
|
+
end
|
920
|
+
}
|
921
|
+
|
922
|
+
key_action['cursor_delete_word'] = proc {
|
923
|
+
num_deleted = 0
|
924
|
+
deleted_alnum = false
|
925
|
+
deleted_nonspace = false
|
926
|
+
while command_buffer_pos < command_buffer.length do
|
927
|
+
next_char = command_buffer[command_buffer_pos]
|
928
|
+
if num_deleted == 0 || (!deleted_alnum && next_char.punct?) || (!deleted_nonspace && next_char.space?) || next_char.alnum?
|
929
|
+
deleted_alnum = deleted_alnum || next_char.alnum?
|
930
|
+
deleted_nonspace = !next_char.space?
|
931
|
+
num_deleted += 1
|
932
|
+
kill_before.call
|
933
|
+
kill_buffer = kill_buffer + next_char
|
934
|
+
key_action['cursor_delete'].call
|
935
|
+
kill_after.call
|
936
|
+
else
|
937
|
+
break
|
938
|
+
end
|
939
|
+
end
|
940
|
+
}
|
941
|
+
|
942
|
+
key_action['cursor_kill_forward'] = proc {
|
943
|
+
if command_buffer_pos < command_buffer.length
|
944
|
+
kill_before.call
|
945
|
+
if command_buffer_pos == 0
|
946
|
+
kill_buffer = kill_buffer + command_buffer
|
947
|
+
command_buffer = ''
|
948
|
+
else
|
949
|
+
kill_buffer = kill_buffer + command_buffer[command_buffer_pos..-1]
|
950
|
+
command_buffer = command_buffer[0..(command_buffer_pos-1)]
|
951
|
+
end
|
952
|
+
kill_after.call
|
953
|
+
command_window.clrtoeol
|
954
|
+
command_window.noutrefresh
|
955
|
+
Curses.doupdate
|
956
|
+
end
|
957
|
+
}
|
958
|
+
|
959
|
+
key_action['cursor_kill_line'] = proc {
|
960
|
+
if command_buffer.length != 0
|
961
|
+
kill_before.call
|
962
|
+
kill_buffer = kill_original
|
963
|
+
command_buffer = ''
|
964
|
+
command_buffer_pos = 0
|
965
|
+
command_buffer_offset = 0
|
966
|
+
kill_after.call
|
967
|
+
command_window.setpos(0, 0)
|
968
|
+
command_window.clrtoeol
|
969
|
+
command_window.noutrefresh
|
970
|
+
Curses.doupdate
|
971
|
+
end
|
972
|
+
}
|
973
|
+
|
974
|
+
key_action['cursor_yank'] = proc {
|
975
|
+
kill_buffer.each_char { |c| command_window_put_ch.call(c) }
|
976
|
+
}
|
977
|
+
|
978
|
+
key_action['switch_current_window'] = proc {
|
979
|
+
if current_scroll_window = TextWindow.list[0]
|
980
|
+
current_scroll_window.clear_scrollbar
|
981
|
+
end
|
982
|
+
TextWindow.list.push(TextWindow.list.shift)
|
983
|
+
if current_scroll_window = TextWindow.list[0]
|
984
|
+
current_scroll_window.update_scrollbar
|
985
|
+
end
|
986
|
+
command_window.noutrefresh
|
987
|
+
Curses.doupdate
|
988
|
+
}
|
989
|
+
|
990
|
+
key_action['scroll_current_window_up_one'] = proc {
|
991
|
+
if current_scroll_window = TextWindow.list[0]
|
992
|
+
current_scroll_window.scroll(-1)
|
993
|
+
end
|
994
|
+
command_window.noutrefresh
|
995
|
+
Curses.doupdate
|
996
|
+
}
|
997
|
+
|
998
|
+
key_action['scroll_current_window_down_one'] = proc {
|
999
|
+
if current_scroll_window = TextWindow.list[0]
|
1000
|
+
current_scroll_window.scroll(1)
|
1001
|
+
end
|
1002
|
+
command_window.noutrefresh
|
1003
|
+
Curses.doupdate
|
1004
|
+
}
|
1005
|
+
|
1006
|
+
key_action['scroll_current_window_up_page'] = proc {
|
1007
|
+
if current_scroll_window = TextWindow.list[0]
|
1008
|
+
current_scroll_window.scroll(0 - current_scroll_window.maxy + 1)
|
1009
|
+
end
|
1010
|
+
command_window.noutrefresh
|
1011
|
+
Curses.doupdate
|
1012
|
+
}
|
1013
|
+
|
1014
|
+
key_action['scroll_current_window_down_page'] = proc {
|
1015
|
+
if current_scroll_window = TextWindow.list[0]
|
1016
|
+
current_scroll_window.scroll(current_scroll_window.maxy - 1)
|
1017
|
+
end
|
1018
|
+
command_window.noutrefresh
|
1019
|
+
Curses.doupdate
|
1020
|
+
}
|
1021
|
+
|
1022
|
+
key_action['scroll_current_window_bottom'] = proc {
|
1023
|
+
if current_scroll_window = TextWindow.list[0]
|
1024
|
+
current_scroll_window.scroll(current_scroll_window.max_buffer_size)
|
1025
|
+
end
|
1026
|
+
command_window.noutrefresh
|
1027
|
+
Curses.doupdate
|
1028
|
+
}
|
1029
|
+
|
1030
|
+
key_action['previous_command'] = proc {
|
1031
|
+
if command_history_pos < (command_history.length - 1)
|
1032
|
+
command_history[command_history_pos] = command_buffer.dup
|
1033
|
+
command_history_pos += 1
|
1034
|
+
command_buffer = command_history[command_history_pos].dup
|
1035
|
+
command_buffer_offset = [ (command_buffer.length - command_window.maxx + 1), 0 ].max
|
1036
|
+
command_buffer_pos = command_buffer.length
|
1037
|
+
command_window.setpos(0, 0)
|
1038
|
+
command_window.deleteln
|
1039
|
+
command_window.addstr command_buffer[command_buffer_offset,(command_buffer.length - command_buffer_offset)]
|
1040
|
+
command_window.setpos(0, command_buffer_pos - command_buffer_offset)
|
1041
|
+
command_window.noutrefresh
|
1042
|
+
Curses.doupdate
|
1043
|
+
end
|
1044
|
+
}
|
1045
|
+
|
1046
|
+
key_action['next_command'] = proc {
|
1047
|
+
if command_history_pos == 0
|
1048
|
+
unless command_buffer.empty?
|
1049
|
+
command_history[command_history_pos] = command_buffer.dup
|
1050
|
+
command_history.unshift String.new
|
1051
|
+
command_buffer.clear
|
1052
|
+
command_window.deleteln
|
1053
|
+
command_buffer_pos = 0
|
1054
|
+
command_buffer_offset = 0
|
1055
|
+
command_window.setpos(0,0)
|
1056
|
+
command_window.noutrefresh
|
1057
|
+
Curses.doupdate
|
1058
|
+
end
|
1059
|
+
else
|
1060
|
+
command_history[command_history_pos] = command_buffer.dup
|
1061
|
+
command_history_pos -= 1
|
1062
|
+
command_buffer = command_history[command_history_pos].dup
|
1063
|
+
command_buffer_offset = [ (command_buffer.length - command_window.maxx + 1), 0 ].max
|
1064
|
+
command_buffer_pos = command_buffer.length
|
1065
|
+
command_window.setpos(0, 0)
|
1066
|
+
command_window.deleteln
|
1067
|
+
command_window.addstr command_buffer[command_buffer_offset,(command_buffer.length - command_buffer_offset)]
|
1068
|
+
command_window.setpos(0, command_buffer_pos - command_buffer_offset)
|
1069
|
+
command_window.noutrefresh
|
1070
|
+
Curses.doupdate
|
1071
|
+
end
|
1072
|
+
}
|
1073
|
+
|
1074
|
+
key_action['switch_arrow_mode'] = proc {
|
1075
|
+
if key_binding[Curses::KEY_UP] == key_action['previous_command']
|
1076
|
+
key_binding[Curses::KEY_UP] = key_action['scroll_current_window_up_page']
|
1077
|
+
key_binding[Curses::KEY_DOWN] = key_action['scroll_current_window_down_page']
|
1078
|
+
else
|
1079
|
+
key_binding[Curses::KEY_UP] = key_action['previous_command']
|
1080
|
+
key_binding[Curses::KEY_DOWN] = key_action['next_command']
|
1081
|
+
end
|
1082
|
+
}
|
1083
|
+
|
1084
|
+
key_action['send_command'] = proc {
|
1085
|
+
cmd = command_buffer.dup
|
1086
|
+
command_buffer.clear
|
1087
|
+
command_buffer_pos = 0
|
1088
|
+
command_buffer_offset = 0
|
1089
|
+
need_prompt = false
|
1090
|
+
if window = stream_handler['main']
|
1091
|
+
add_prompt(window, prompt_text, cmd)
|
1092
|
+
end
|
1093
|
+
command_window.deleteln
|
1094
|
+
command_window.setpos(0,0)
|
1095
|
+
command_window.noutrefresh
|
1096
|
+
Curses.doupdate
|
1097
|
+
command_history_pos = 0
|
1098
|
+
# Remember all digit commands because they are likely spells for voodoo.lic
|
1099
|
+
if (cmd.length >= min_cmd_length_for_history || cmd.digits?) and (cmd != command_history[1])
|
1100
|
+
if command_history[0].nil? or command_history[0].empty?
|
1101
|
+
command_history[0] = cmd
|
1102
|
+
else
|
1103
|
+
command_history.unshift cmd
|
1104
|
+
end
|
1105
|
+
command_history.unshift String.new
|
1106
|
+
end
|
1107
|
+
if cmd =~ /^\.quit/
|
1108
|
+
exit
|
1109
|
+
elsif cmd =~ /^\.key/i
|
1110
|
+
window = stream_handler['main']
|
1111
|
+
window.add_string("* ")
|
1112
|
+
window.add_string("* Waiting for key press...")
|
1113
|
+
command_window.noutrefresh
|
1114
|
+
Curses.doupdate
|
1115
|
+
window.add_string("* Detected keycode: #{command_window.getch.to_s}")
|
1116
|
+
window.add_string("* ")
|
1117
|
+
Curses.doupdate
|
1118
|
+
elsif cmd =~ /^\.copy/
|
1119
|
+
# fixme
|
1120
|
+
elsif cmd =~ /^\.fixcolor/i
|
1121
|
+
if CUSTOM_COLORS
|
1122
|
+
COLOR_ID_LOOKUP.each { |code,id|
|
1123
|
+
Curses.init_color(id, ((code[0..1].to_s.hex/255.0)*1000).round, ((code[2..3].to_s.hex/255.0)*1000).round, ((code[4..5].to_s.hex/255.0)*1000).round)
|
1124
|
+
}
|
1125
|
+
end
|
1126
|
+
elsif cmd =~ /^\.resync/i
|
1127
|
+
skip_server_time_offset = false
|
1128
|
+
elsif cmd =~ /^\.reload/i
|
1129
|
+
load_settings_file.call(true)
|
1130
|
+
elsif cmd =~ /^\.layout\s+(.+)/
|
1131
|
+
load_layout.call($1)
|
1132
|
+
key_action['resize'].call
|
1133
|
+
elsif cmd =~ /^\.arrow/i
|
1134
|
+
key_action['switch_arrow_mode'].call
|
1135
|
+
elsif cmd =~ /^\.e (.*)/
|
1136
|
+
eval(cmd.sub(/^\.e /, ''))
|
1137
|
+
else
|
1138
|
+
server.puts cmd.sub(/^\./, ';')
|
1139
|
+
end
|
1140
|
+
}
|
1141
|
+
|
1142
|
+
key_action['send_last_command'] = proc {
|
1143
|
+
if cmd = command_history[1]
|
1144
|
+
if window = stream_handler['main']
|
1145
|
+
add_prompt(window, prompt_text, cmd)
|
1146
|
+
#window.add_string(">#{cmd}", [ h={ :start => 0, :end => (cmd.length + 1), :fg => '555555' } ])
|
1147
|
+
command_window.noutrefresh
|
1148
|
+
Curses.doupdate
|
1149
|
+
end
|
1150
|
+
if cmd =~ /^\.quit/i
|
1151
|
+
exit
|
1152
|
+
elsif cmd =~ /^\.fixcolor/i
|
1153
|
+
if CUSTOM_COLORS
|
1154
|
+
COLOR_ID_LOOKUP.each { |code,id|
|
1155
|
+
Curses.init_color(id, ((code[0..1].to_s.hex/255.0)*1000).round, ((code[2..3].to_s.hex/255.0)*1000).round, ((code[4..5].to_s.hex/255.0)*1000).round)
|
1156
|
+
}
|
1157
|
+
end
|
1158
|
+
elsif cmd =~ /^\.resync/i
|
1159
|
+
skip_server_time_offset = false
|
1160
|
+
elsif cmd =~ /^\.arrow/i
|
1161
|
+
key_action['switch_arrow_mode'].call
|
1162
|
+
elsif cmd =~ /^\.e (.*)/
|
1163
|
+
eval(cmd.sub(/^\.e /, ''))
|
1164
|
+
else
|
1165
|
+
server.puts cmd.sub(/^\./, ';')
|
1166
|
+
end
|
1167
|
+
end
|
1168
|
+
}
|
1169
|
+
|
1170
|
+
key_action['send_second_last_command'] = proc {
|
1171
|
+
if cmd = command_history[2]
|
1172
|
+
if window = stream_handler['main']
|
1173
|
+
add_prompt(window, prompt_text, cmd)
|
1174
|
+
#window.add_string(">#{cmd}", [ h={ :start => 0, :end => (cmd.length + 1), :fg => '555555' } ])
|
1175
|
+
command_window.noutrefresh
|
1176
|
+
Curses.doupdate
|
1177
|
+
end
|
1178
|
+
if cmd =~ /^\.quit/i
|
1179
|
+
exit
|
1180
|
+
elsif cmd =~ /^\.fixcolor/i
|
1181
|
+
if CUSTOM_COLORS
|
1182
|
+
COLOR_ID_LOOKUP.each { |code,id|
|
1183
|
+
Curses.init_color(id, ((code[0..1].to_s.hex/255.0)*1000).round, ((code[2..3].to_s.hex/255.0)*1000).round, ((code[4..5].to_s.hex/255.0)*1000).round)
|
1184
|
+
}
|
1185
|
+
end
|
1186
|
+
elsif cmd =~ /^\.resync/i
|
1187
|
+
skip_server_time_offset = false
|
1188
|
+
elsif cmd =~ /^\.arrow/i
|
1189
|
+
key_action['switch_arrow_mode'].call
|
1190
|
+
elsif cmd =~ /^\.e (.*)/
|
1191
|
+
eval(cmd.sub(/^\.e /, ''))
|
1192
|
+
else
|
1193
|
+
server.puts cmd.sub(/^\./, ';')
|
1194
|
+
end
|
1195
|
+
end
|
1196
|
+
}
|
1197
|
+
|
1198
|
+
new_stun = proc { |seconds|
|
1199
|
+
if window = countdown_handler['stunned']
|
1200
|
+
temp_stun_end = Time.now.to_f - $server_time_offset.to_f + seconds.to_f
|
1201
|
+
window.end_time = temp_stun_end
|
1202
|
+
window.update
|
1203
|
+
need_update = true
|
1204
|
+
Thread.new {
|
1205
|
+
while (countdown_handler['stunned'].end_time == temp_stun_end) and (countdown_handler['stunned'].value > 0)
|
1206
|
+
sleep 0.15
|
1207
|
+
if countdown_handler['stunned'].update
|
1208
|
+
command_window.noutrefresh
|
1209
|
+
Curses.doupdate
|
1210
|
+
end
|
1211
|
+
end
|
1212
|
+
}
|
1213
|
+
end
|
1214
|
+
}
|
1215
|
+
|
1216
|
+
load_settings_file.call(false)
|
1217
|
+
load_layout.call('default')
|
1218
|
+
|
1219
|
+
TextWindow.list.each { |w| w.maxy.times { w.add_string "\n" } }
|
1220
|
+
|
1221
|
+
server = TCPSocket.open('127.0.0.1', PORT)
|
1222
|
+
|
1223
|
+
Thread.new { sleep 15; skip_server_time_offset = false }
|
1224
|
+
|
1225
|
+
Thread.new {
|
1226
|
+
begin
|
1227
|
+
line = nil
|
1228
|
+
need_update = false
|
1229
|
+
line_colors = Array.new
|
1230
|
+
open_monsterbold = Array.new
|
1231
|
+
open_preset = Array.new
|
1232
|
+
open_style = nil
|
1233
|
+
open_color = Array.new
|
1234
|
+
current_stream = nil
|
1235
|
+
|
1236
|
+
handle_game_text = proc { |text|
|
1237
|
+
|
1238
|
+
for escapable in xml_escape_list.keys
|
1239
|
+
search_pos = 0
|
1240
|
+
while (pos = text.index(escapable, search_pos))
|
1241
|
+
text = text.sub(escapable, xml_escape_list[escapable])
|
1242
|
+
line_colors.each { |h|
|
1243
|
+
h[:start] -= (escapable.length - 1) if h[:start] > pos
|
1244
|
+
h[:end] -= (escapable.length - 1) if h[:end] > pos
|
1245
|
+
}
|
1246
|
+
if open_style and (open_style[:start] > pos)
|
1247
|
+
open_style[:start] -= (escapable.length - 1)
|
1248
|
+
end
|
1249
|
+
end
|
1250
|
+
end
|
1251
|
+
|
1252
|
+
if text =~ /^\[.*?\]>/
|
1253
|
+
need_prompt = false
|
1254
|
+
elsif text =~ /^\s*You are stunned for ([0-9]+) rounds?/
|
1255
|
+
new_stun.call($1.to_i * 5)
|
1256
|
+
elsif text =~ /^Deep and resonating, you feel the chant that falls from your lips instill within you with the strength of your faith\. You crouch beside [A-Z][a-z]+ and gently lift (?:he|she|him|her) into your arms, your muscles swelling with the power of your deity, and cradle (?:him|her) close to your chest\. Strength and life momentarily seep from your limbs, causing them to feel laden and heavy, and you are overcome with a sudden weakness\. With a sigh, you are able to lay [A-Z][a-z]+ back down\.$|^Moisture beads upon your skin and you feel your eyes cloud over with the darkness of a rising storm\. Power builds upon the air and when you utter the last syllable of your spell thunder rumbles from your lips\. The sound ripples upon the air, and colling with [A-Z][a-z']+ prone form and a brilliant flash transfers the spiritual energy between you\.$|^Lifting your finger, you begin to chant and draw a series of conjoined circles in the air\. Each circle turns to mist and takes on a different hue - white, blue, black, red, and green\. As the last ring is completed, you spread your fingers and gently allow your tips to touch each color before pushing the misty creation towards [A-Z][a-z]+\. A shock of energy courses through your body as the mist seeps into [A-Z][a-z']+ chest and life is slowly returned to (?:his|her) body\.$|^Crouching beside the prone form of [A-Z][a-z]+, you softly issue the last syllable of your chant\. Breathing deeply, you take in the scents around you and let the feel of your surroundings infuse you\. With only your gaze, you track the area and recreate the circumstances of [A-Z][a-z']+ within your mind\. Touching [A-Z][a-z]+, you follow the lines of the web that holds (?:his|her) soul in place and force it back into (?:his|her) body\. Raw energy courses through you and you feel your sense of justice and vengeance filling [A-Z][a-z]+ with life\.$|^Murmuring softly, you call upon your connection with the Destroyer,? and feel your words twist into an alien, spidery chant\. Dark shadows laced with crimson swirl before your eyes and at your forceful command sink into the chest of [A-Z][a-z]+\. The transference of energy is swift and immediate as you bind [A-Z][a-z]+ back into (?:his|her) body\.$|^Rich and lively, the scent of wild flowers suddenly fills the air as you finish your chant, and you feel alive with the energy of spring\. With renewal at your fingertips, you gently touch [A-Z][a-z]+ on the brow and revel in the sweet rush of energy that passes through you into (?:him|her|his)\.$|^Breathing slowly, you extend your senses towards the world around you and draw into you the very essence of nature\. You shift your gaze towards [A-z][a-z]+ and carefully release the energy you've drawn into yourself towards (?:him|her)\. A rush of energy briefly flows between the two of you as you feel life slowly return to (?:him|her)\.$|^Your surroundings grow dim\.\.\.you lapse into a state of awareness only, unable to do anything\.\.\.$|^Murmuring softly, a mournful chant slips from your lips and you feel welts appear upon your wrists\. Dipping them briefly, you smear the crimson liquid the leaks from these sudden wounds in a thin line down [A-Z][a-z']+ face\. Tingling with each second that your skin touches (?:his|hers), you feel the transference of your raw energy pass into [A-Z][a-z]+ and momentarily reel with the pain of its release\. Slowly, the wounds on your wrists heal, though a lingering throb remains\.$|^Emptying all breathe from your body, you slowly still yourself and close your eyes\. You reach out with all of your senses and feel a film shift across your vision\. Opening your eyes, you gaze through a white haze and find images of [A-Z][a-z]+ floating above his prone form\. Acts of [A-Z][a-z]'s? past, present, and future play out before your clouded vision\. With conviction and faith, you pluck a future image of [A-Z][a-z]+ from the air and coax (?:he|she|his|her) back into (?:he|she|his|her) body\. Slowly, the film slips from your eyes and images fade away\.$|^Thin at first, a fine layer of rime tickles your hands and fingertips\. The hoarfrost smoothly glides between you and [A-Z][a-z]+, turning to a light powder as it traverses the space\. The white substance clings to [A-Z][a-z]+'s? eyelashes and cheeks for a moment before it becomes charged with spiritual power, then it slowly melts away\.$|^As you begin to chant,? you notice the scent of dry, dusty parchment and feel a cool mist cling to your skin somewhere near your feet\. You sense the ethereal tendrils of the mist as they coil about your body and notice that the world turns to a yellowish hue as the mist settles about your head\. Focusing on [A-Z][a-z]+, you feel the transfer of energy pass between you as you return (?:him|her) to life\.$|^Wrapped in an aura of chill, you close your eyes and softly begin to chant\. As the cold air that surrounds you condenses you feel it slowly ripple outward in waves that turn the breath of those nearby into a fine mist\. This mist swiftly moves to encompass you and you feel a pair of wings arc over your back\. With the last words of your chant, you open your eyes and watch as foggy wings rise above you and gently brush against [A-Z][a-z]+\. As they dissipate in a cold rush against [A-Z][a-z]+, you feel a surge of power spill forth from you and into (?:him|her)\.$|^As .*? begins to chant, your spirit is drawn closer to your body by the scent of dusty, dry parchment\. Topaz tendrils coil about .*?, and you feel an ancient presence demand that you return to your body\. All at once .*? focuses upon you and you feel a surge of energy bind you back into your now-living body\.$/
|
1257
|
+
# raise dead stun
|
1258
|
+
new_stun.call(30.6)
|
1259
|
+
elsif text =~ /^Just as you think the falling will never end, you crash through an ethereal barrier which bursts into a dazzling kaleidoscope of color! Your sensation of falling turns to dizziness and you feel unusually heavy for a moment\. Everything seems to stop for a prolonged second and then WHUMP!!!/
|
1260
|
+
# Shadow Valley exit stun
|
1261
|
+
new_stun.call(16.2)
|
1262
|
+
elsif text =~ /^You have.*?(?:case of uncontrollable convulsions|case of sporadic convulsions|strange case of muscle twitching)/
|
1263
|
+
# nsys wound will be correctly set by xml, dont set the scar using health verb output
|
1264
|
+
skip_nsys = true
|
1265
|
+
else
|
1266
|
+
if skip_nsys
|
1267
|
+
skip_nsys = false
|
1268
|
+
elsif window = indicator_handler['nsys']
|
1269
|
+
if text =~ /^You have.*? very difficult time with muscle control/
|
1270
|
+
if window.update(3)
|
1271
|
+
need_update = true
|
1272
|
+
end
|
1273
|
+
elsif text =~ /^You have.*? constant muscle spasms/
|
1274
|
+
if window.update(2)
|
1275
|
+
need_update = true
|
1276
|
+
end
|
1277
|
+
elsif text =~ /^You have.*? developed slurred speech/
|
1278
|
+
if window.update(1)
|
1279
|
+
need_update = true
|
1280
|
+
end
|
1281
|
+
end
|
1282
|
+
end
|
1283
|
+
end
|
1284
|
+
|
1285
|
+
if open_style
|
1286
|
+
h = open_style.dup
|
1287
|
+
h[:end] = text.length
|
1288
|
+
line_colors.push(h)
|
1289
|
+
open_style[:start] = 0
|
1290
|
+
end
|
1291
|
+
for oc in open_color
|
1292
|
+
ocd = oc.dup
|
1293
|
+
ocd[:end] = text.length
|
1294
|
+
line_colors.push(ocd)
|
1295
|
+
oc[:start] = 0
|
1296
|
+
end
|
1297
|
+
|
1298
|
+
if current_stream.nil? or stream_handler[current_stream] or (current_stream =~ /^(?:death|logons|thoughts|voln|familiar|assess|ooc|atomospherics)$/)
|
1299
|
+
SETTINGS_LOCK.synchronize {
|
1300
|
+
HIGHLIGHT.each_pair { |regex,colors|
|
1301
|
+
pos = 0
|
1302
|
+
while (match_data = text.match(regex, pos))
|
1303
|
+
h = {
|
1304
|
+
:start => match_data.begin(0),
|
1305
|
+
:end => match_data.end(0),
|
1306
|
+
:fg => colors[0],
|
1307
|
+
:bg => colors[1],
|
1308
|
+
:ul => colors[2]
|
1309
|
+
}
|
1310
|
+
line_colors.push(h)
|
1311
|
+
pos = match_data.end(0)
|
1312
|
+
end
|
1313
|
+
}
|
1314
|
+
}
|
1315
|
+
end
|
1316
|
+
|
1317
|
+
unless text.empty?
|
1318
|
+
if current_stream
|
1319
|
+
if current_stream == 'thoughts'
|
1320
|
+
if text =~ /^\[.+?\]\-[A-z]+\:[A-Z][a-z]+\: "|^\[server\]\: /
|
1321
|
+
current_stream = 'lnet'
|
1322
|
+
end
|
1323
|
+
end
|
1324
|
+
if window = stream_handler[current_stream]
|
1325
|
+
if current_stream == 'death'
|
1326
|
+
# fixme: has been vaporized!
|
1327
|
+
# fixme: ~ off to a rough start
|
1328
|
+
if text =~ /^\s\*\s(The death cry of )?([A-Z][a-z]+) (?:just bit the dust!|echoes in your mind!)/
|
1329
|
+
front_count = 3
|
1330
|
+
front_count += 17 if $1
|
1331
|
+
name = $2
|
1332
|
+
text = "#{name} #{Time.now.strftime('%l:%M%P').sub(/^0/, '')}"
|
1333
|
+
line_colors.each { |h|
|
1334
|
+
h[:start] -= front_count
|
1335
|
+
h[:end] = [ h[:end], name.length ].min
|
1336
|
+
}
|
1337
|
+
line_colors.delete_if { |h| h[:start] >= h[:end] }
|
1338
|
+
h = {
|
1339
|
+
:start => (name.length+1),
|
1340
|
+
:end => text.length,
|
1341
|
+
:fg => 'ff0000',
|
1342
|
+
}
|
1343
|
+
line_colors.push(h)
|
1344
|
+
end
|
1345
|
+
elsif current_stream == 'logons'
|
1346
|
+
foo = { 'joins the adventure.' => '007700', 'returns home from a hard day of adventuring.' => '777700', 'has disconnected.' => 'aa7733' }
|
1347
|
+
if text =~ /^\s\*\s([A-Z][a-z]+) (#{foo.keys.join('|')})/
|
1348
|
+
name = $1
|
1349
|
+
logon_type = $2
|
1350
|
+
text = "#{name} #{Time.now.strftime('%l:%M%P').sub(/^0/, '')}"
|
1351
|
+
line_colors.each { |h|
|
1352
|
+
h[:start] -= 3
|
1353
|
+
h[:end] = [ h[:end], name.length ].min
|
1354
|
+
}
|
1355
|
+
line_colors.delete_if { |h| h[:start] >= h[:end] }
|
1356
|
+
h = {
|
1357
|
+
:start => (name.length+1),
|
1358
|
+
:end => text.length,
|
1359
|
+
:fg => foo[logon_type],
|
1360
|
+
}
|
1361
|
+
line_colors.push(h)
|
1362
|
+
end
|
1363
|
+
end
|
1364
|
+
unless text =~ /^\[server\]: "(?:kill|connect)/
|
1365
|
+
window.add_string(text, line_colors)
|
1366
|
+
need_update = true
|
1367
|
+
end
|
1368
|
+
elsif current_stream =~ /^(?:death|logons|thoughts|voln|familiar|assess|ooc|atmospherics)$/
|
1369
|
+
if window = stream_handler['main']
|
1370
|
+
if PRESET[current_stream]
|
1371
|
+
line_colors.push(:start => 0, :fg => PRESET[current_stream][0], :bg => PRESET[current_stream][1], :end => text.length)
|
1372
|
+
end
|
1373
|
+
unless text.empty?
|
1374
|
+
if need_prompt
|
1375
|
+
need_prompt = false
|
1376
|
+
add_prompt(window, prompt_text)
|
1377
|
+
end
|
1378
|
+
window.add_string(text, line_colors)
|
1379
|
+
need_update = true
|
1380
|
+
end
|
1381
|
+
end
|
1382
|
+
else
|
1383
|
+
# stream_handler['main'].add_string "#{current_stream}: #{text.inspect}"
|
1384
|
+
end
|
1385
|
+
else
|
1386
|
+
if window = stream_handler['main']
|
1387
|
+
if need_prompt
|
1388
|
+
need_prompt = false
|
1389
|
+
add_prompt(window, prompt_text)
|
1390
|
+
end
|
1391
|
+
window.add_string(text, line_colors)
|
1392
|
+
need_update = true
|
1393
|
+
end
|
1394
|
+
end
|
1395
|
+
end
|
1396
|
+
line_colors = Array.new
|
1397
|
+
open_monsterbold.clear
|
1398
|
+
open_preset.clear
|
1399
|
+
# open_color.clear
|
1400
|
+
}
|
1401
|
+
|
1402
|
+
while (line = server.gets)
|
1403
|
+
line.chomp!
|
1404
|
+
if line.empty?
|
1405
|
+
if current_stream.nil?
|
1406
|
+
if need_prompt
|
1407
|
+
need_prompt = false
|
1408
|
+
add_prompt(stream_handler['main'], prompt_text)
|
1409
|
+
end
|
1410
|
+
stream_handler['main'].add_string String.new
|
1411
|
+
need_update = true
|
1412
|
+
end
|
1413
|
+
else
|
1414
|
+
while (start_pos = (line =~ /(<(prompt|spell|right|left|inv|compass).*?\2>|<.*?>)/))
|
1415
|
+
xml = $1
|
1416
|
+
line.slice!(start_pos, xml.length)
|
1417
|
+
if xml =~ /^<prompt time=('|")([0-9]+)\1.*?>(.*?)><\/prompt>$/
|
1418
|
+
unless skip_server_time_offset
|
1419
|
+
$server_time_offset = Time.now.to_f - $2.to_f
|
1420
|
+
skip_server_time_offset = true
|
1421
|
+
end
|
1422
|
+
new_prompt_text = "#{$3}>"
|
1423
|
+
if prompt_text != new_prompt_text
|
1424
|
+
need_prompt = false
|
1425
|
+
prompt_text = new_prompt_text
|
1426
|
+
add_prompt(stream_handler['main'], new_prompt_text)
|
1427
|
+
if prompt_window = indicator_handler["prompt"]
|
1428
|
+
init_prompt_height, init_prompt_width = fix_layout_number.call(prompt_window.layout[0]), fix_layout_number.call(prompt_window.layout[1])
|
1429
|
+
new_prompt_width = new_prompt_text.length
|
1430
|
+
prompt_window.resize(init_prompt_height, new_prompt_width)
|
1431
|
+
prompt_width_diff = new_prompt_width - init_prompt_width
|
1432
|
+
command_window.resize(fix_layout_number.call(command_window_layout[0]), fix_layout_number.call(command_window_layout[1]) - prompt_width_diff)
|
1433
|
+
ctop, cleft = fix_layout_number.call(command_window_layout[2]), fix_layout_number.call(command_window_layout[3]) + prompt_width_diff
|
1434
|
+
command_window.move(ctop, cleft)
|
1435
|
+
prompt_window.label = new_prompt_text
|
1436
|
+
end
|
1437
|
+
else
|
1438
|
+
need_prompt = true
|
1439
|
+
end
|
1440
|
+
elsif xml =~ /^<spell(?:>|\s.*?>)(.*?)<\/spell>$/
|
1441
|
+
if window = indicator_handler['spell']
|
1442
|
+
window.clear
|
1443
|
+
window.label = $1
|
1444
|
+
window.update($1 == 'None' ? 0 : 1)
|
1445
|
+
need_update = true
|
1446
|
+
end
|
1447
|
+
elsif xml =~ /^<(right|left)(?:>|\s.*?>).*?(\S*?)<\/\1>/
|
1448
|
+
if window = indicator_handler[$1]
|
1449
|
+
window.clear
|
1450
|
+
window.label = $2
|
1451
|
+
window.update($2 == 'Empty' ? 0 : 1)
|
1452
|
+
need_update = true
|
1453
|
+
end
|
1454
|
+
elsif xml =~ /^<roundTime value=('|")([0-9]+)\1/
|
1455
|
+
if window = countdown_handler['roundtime']
|
1456
|
+
temp_roundtime_end = $2.to_i
|
1457
|
+
window.end_time = temp_roundtime_end
|
1458
|
+
window.update
|
1459
|
+
need_update = true
|
1460
|
+
Thread.new {
|
1461
|
+
sleep 0.15
|
1462
|
+
while (countdown_handler['roundtime'].end_time == temp_roundtime_end) and (countdown_handler['roundtime'].value > 0)
|
1463
|
+
sleep 0.15
|
1464
|
+
if countdown_handler['roundtime'].update
|
1465
|
+
command_window.noutrefresh
|
1466
|
+
Curses.doupdate
|
1467
|
+
end
|
1468
|
+
end
|
1469
|
+
}
|
1470
|
+
end
|
1471
|
+
elsif xml =~ /^<castTime value=('|")([0-9]+)\1/
|
1472
|
+
if window = countdown_handler['roundtime']
|
1473
|
+
temp_casttime_end = $2.to_i
|
1474
|
+
window.secondary_end_time = temp_casttime_end
|
1475
|
+
window.update
|
1476
|
+
need_update = true
|
1477
|
+
Thread.new {
|
1478
|
+
while (countdown_handler['roundtime'].secondary_end_time == temp_casttime_end) and (countdown_handler['roundtime'].secondary_value > 0)
|
1479
|
+
sleep 0.15
|
1480
|
+
if countdown_handler['roundtime'].update
|
1481
|
+
command_window.noutrefresh
|
1482
|
+
Curses.doupdate
|
1483
|
+
end
|
1484
|
+
end
|
1485
|
+
}
|
1486
|
+
end
|
1487
|
+
elsif xml =~ /^<compass/
|
1488
|
+
current_dirs = xml.scan(/<dir value="(.*?)"/).flatten
|
1489
|
+
for dir in [ 'up', 'down', 'out', 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw' ]
|
1490
|
+
if window = indicator_handler["compass:#{dir}"]
|
1491
|
+
if window.update(current_dirs.include?(dir))
|
1492
|
+
need_update = true
|
1493
|
+
end
|
1494
|
+
end
|
1495
|
+
end
|
1496
|
+
elsif xml =~ /^<progressBar id='(.*?)' value='[0-9]+' text='\1 (\-?[0-9]+)\/([0-9]+)'/
|
1497
|
+
if window = progress_handler[$1]
|
1498
|
+
if window.update($2.to_i, $3.to_i)
|
1499
|
+
need_update = true
|
1500
|
+
end
|
1501
|
+
end
|
1502
|
+
elsif xml =~ /^<progressBar id='encumlevel' value='([0-9]+)' text='(.*?)'/
|
1503
|
+
if window = progress_handler['encumbrance']
|
1504
|
+
if $2 == 'Overloaded'
|
1505
|
+
value = 110
|
1506
|
+
else
|
1507
|
+
value = $1.to_i
|
1508
|
+
end
|
1509
|
+
if window.update(value, 110)
|
1510
|
+
need_update = true
|
1511
|
+
end
|
1512
|
+
end
|
1513
|
+
elsif xml =~ /^<progressBar id='pbarStance' value='([0-9]+)'/
|
1514
|
+
if window = progress_handler['stance']
|
1515
|
+
if window.update($1.to_i, 100)
|
1516
|
+
need_update = true
|
1517
|
+
end
|
1518
|
+
end
|
1519
|
+
elsif xml =~ /^<progressBar id='mindState' value='(.*?)' text='(.*?)'/
|
1520
|
+
if window = progress_handler['mind']
|
1521
|
+
if $2 == 'saturated'
|
1522
|
+
value = 110
|
1523
|
+
else
|
1524
|
+
value = $1.to_i
|
1525
|
+
end
|
1526
|
+
if window.update(value, 110)
|
1527
|
+
need_update = true
|
1528
|
+
end
|
1529
|
+
end
|
1530
|
+
elsif xml == '<pushBold/>' or xml == '<b>'
|
1531
|
+
h = { :start => start_pos }
|
1532
|
+
if PRESET['monsterbold']
|
1533
|
+
h[:fg] = PRESET['monsterbold'][0]
|
1534
|
+
h[:bg] = PRESET['monsterbold'][1]
|
1535
|
+
end
|
1536
|
+
open_monsterbold.push(h)
|
1537
|
+
elsif xml == '<popBold/>' or xml == '</b>'
|
1538
|
+
if h = open_monsterbold.pop
|
1539
|
+
h[:end] = start_pos
|
1540
|
+
line_colors.push(h) if h[:fg] or h[:bg]
|
1541
|
+
end
|
1542
|
+
elsif xml =~ /^<preset id=('|")(.*?)\1>$/
|
1543
|
+
h = { :start => start_pos }
|
1544
|
+
if PRESET[$2]
|
1545
|
+
h[:fg] = PRESET[$2][0]
|
1546
|
+
h[:bg] = PRESET[$2][1]
|
1547
|
+
end
|
1548
|
+
open_preset.push(h)
|
1549
|
+
elsif xml == '</preset>'
|
1550
|
+
if h = open_preset.pop
|
1551
|
+
h[:end] = start_pos
|
1552
|
+
line_colors.push(h) if h[:fg] or h[:bg]
|
1553
|
+
end
|
1554
|
+
elsif xml =~ /^<color/
|
1555
|
+
h = { :start => start_pos }
|
1556
|
+
if xml =~ /\sfg=('|")(.*?)\1[\s>]/
|
1557
|
+
h[:fg] = $2.downcase
|
1558
|
+
end
|
1559
|
+
if xml =~ /\sbg=('|")(.*?)\1[\s>]/
|
1560
|
+
h[:bg] = $2.downcase
|
1561
|
+
end
|
1562
|
+
if xml =~ /\sul=('|")(.*?)\1[\s>]/
|
1563
|
+
h[:ul] = $2.downcase
|
1564
|
+
end
|
1565
|
+
open_color.push(h)
|
1566
|
+
elsif xml == '</color>'
|
1567
|
+
if h = open_color.pop
|
1568
|
+
h[:end] = start_pos
|
1569
|
+
line_colors.push(h)
|
1570
|
+
end
|
1571
|
+
elsif xml =~ /^<style id=('|")(.*?)\1/
|
1572
|
+
if $2.empty?
|
1573
|
+
if open_style
|
1574
|
+
open_style[:end] = start_pos
|
1575
|
+
if (open_style[:start] < open_style[:end]) and (open_style[:fg] or open_style[:bg])
|
1576
|
+
line_colors.push(open_style)
|
1577
|
+
end
|
1578
|
+
open_style = nil
|
1579
|
+
end
|
1580
|
+
else
|
1581
|
+
open_style = { :start => start_pos }
|
1582
|
+
if PRESET[$2]
|
1583
|
+
open_style[:fg] = PRESET[$2][0]
|
1584
|
+
open_style[:bg] = PRESET[$2][1]
|
1585
|
+
end
|
1586
|
+
end
|
1587
|
+
elsif xml =~ /^<(?:pushStream|component) id=("|')(.*?)\1[^>]*\/?>$/
|
1588
|
+
new_stream = $2
|
1589
|
+
game_text = line.slice!(0, start_pos)
|
1590
|
+
handle_game_text.call(game_text)
|
1591
|
+
current_stream = new_stream
|
1592
|
+
elsif xml =~ /^<popStream/ or xml == '</component>'
|
1593
|
+
game_text = line.slice!(0, start_pos)
|
1594
|
+
handle_game_text.call(game_text)
|
1595
|
+
current_stream = nil
|
1596
|
+
elsif xml =~ /^<progressBar/
|
1597
|
+
nil
|
1598
|
+
elsif xml =~ /^<(?:dialogdata|a|\/a|d|\/d|\/?component|label|skin|output)/
|
1599
|
+
nil
|
1600
|
+
elsif xml =~ /^<indicator id=('|")Icon([A-Z]+)\1 visible=('|")([yn])\3/
|
1601
|
+
if window = countdown_handler[$2.downcase]
|
1602
|
+
window.active = ($4 == 'y')
|
1603
|
+
if window.update
|
1604
|
+
need_update = true
|
1605
|
+
end
|
1606
|
+
end
|
1607
|
+
if window = indicator_handler[$2.downcase]
|
1608
|
+
if window.update($4 == 'y')
|
1609
|
+
need_update = true
|
1610
|
+
end
|
1611
|
+
end
|
1612
|
+
elsif xml =~ /^<image id=('|")(back|leftHand|rightHand|head|rightArm|abdomen|leftEye|leftArm|chest|rightLeg|neck|leftLeg|nsys|rightEye)\1 name=('|")(.*?)\3/
|
1613
|
+
if $2 == 'nsys'
|
1614
|
+
if window = indicator_handler['nsys']
|
1615
|
+
if rank = $4.slice(/[0-9]/)
|
1616
|
+
if window.update(rank.to_i)
|
1617
|
+
need_update = true
|
1618
|
+
end
|
1619
|
+
else
|
1620
|
+
if window.update(0)
|
1621
|
+
need_update = true
|
1622
|
+
end
|
1623
|
+
end
|
1624
|
+
end
|
1625
|
+
else
|
1626
|
+
fix_value = { 'Injury1' => 1, 'Injury2' => 2, 'Injury3' => 3, 'Scar1' => 4, 'Scar2' => 5, 'Scar3' => 6 }
|
1627
|
+
if window = indicator_handler[$2]
|
1628
|
+
if window.update(fix_value[$4] || 0)
|
1629
|
+
need_update = true
|
1630
|
+
end
|
1631
|
+
end
|
1632
|
+
end
|
1633
|
+
elsif xml =~ /^<LaunchURL src="([^"]+)"/
|
1634
|
+
url = "\"https://www.play.net#{$1}\""
|
1635
|
+
# assume linux if not mac
|
1636
|
+
cmd = RUBY_PLATFORM =~ /darwin/ ? "open" : "firefox"
|
1637
|
+
system("#{cmd} #{url}")
|
1638
|
+
else
|
1639
|
+
nil
|
1640
|
+
end
|
1641
|
+
end
|
1642
|
+
handle_game_text.call(line)
|
1643
|
+
end
|
1644
|
+
#
|
1645
|
+
# delay screen update if there are more game lines waiting
|
1646
|
+
#
|
1647
|
+
if need_update and not IO.select([server], nil, nil, 0.01)
|
1648
|
+
need_update = false
|
1649
|
+
command_window.noutrefresh
|
1650
|
+
Curses.doupdate
|
1651
|
+
end
|
1652
|
+
end
|
1653
|
+
stream_handler['main'].add_string ' *'
|
1654
|
+
stream_handler['main'].add_string ' * Connection closed'
|
1655
|
+
stream_handler['main'].add_string ' *'
|
1656
|
+
command_window.noutrefresh
|
1657
|
+
Curses.doupdate
|
1658
|
+
rescue
|
1659
|
+
File.open('profanity.log', 'a') { |f| f.puts $!; f.puts $!.backtrace[0...4] }
|
1660
|
+
exit
|
1661
|
+
end
|
1662
|
+
}
|
1663
|
+
|
1664
|
+
begin
|
1665
|
+
key_combo = nil
|
1666
|
+
loop {
|
1667
|
+
ch = command_window.getch
|
1668
|
+
if key_combo
|
1669
|
+
if key_combo[ch].class == Proc
|
1670
|
+
key_combo[ch].call
|
1671
|
+
key_combo = nil
|
1672
|
+
elsif key_combo[ch].class == Hash
|
1673
|
+
key_combo = key_combo[ch]
|
1674
|
+
else
|
1675
|
+
key_combo = nil
|
1676
|
+
end
|
1677
|
+
elsif key_binding[ch].class == Proc
|
1678
|
+
key_binding[ch].call
|
1679
|
+
elsif key_binding[ch].class == Hash
|
1680
|
+
key_combo = key_binding[ch]
|
1681
|
+
elsif ch.class == String
|
1682
|
+
command_window_put_ch.call(ch)
|
1683
|
+
command_window.noutrefresh
|
1684
|
+
Curses.doupdate
|
1685
|
+
end
|
1686
|
+
}
|
1687
|
+
rescue
|
1688
|
+
File.open('profanity.log', 'a') { |f| f.puts $!; f.puts $!.backtrace[0...4] }
|
1689
|
+
ensure
|
1690
|
+
server.close rescue()
|
1691
|
+
Curses.close_screen
|
1692
|
+
end
|