]>
Commit | Line | Data |
---|---|---|
8b6154e9 I |
1 | #!/usr/bin/env ruby |
2 | ||
3 | require "time" | |
4 | require "ncurses" | |
5 | require "inifile" | |
6 | require "open3" | |
7 | ||
8 | class IO | |
9 | def readline_nonblock | |
10 | buffer = "" | |
11 | buffer << read_nonblock(1) while buffer[-1] != "\n" | |
12 | buffer | |
13 | rescue IO::WaitReadable => blocking | |
14 | if (not buffer.empty?) | |
15 | ungetc(buffer) | |
16 | end | |
17 | raise blocking | |
18 | end | |
19 | end | |
20 | ||
21 | class List_Win | |
22 | def initialize(inifile) | |
23 | @params = inifile | |
24 | @win = Ncurses::WINDOW.new(0, Ncurses.COLS()/4, 0, 0) | |
25 | @win.border(*([0]*8)) | |
26 | @win.keypad(true) | |
27 | @win.timeout(1000) | |
28 | @win.refresh() | |
29 | @entry = 0 | |
30 | end | |
31 | ||
32 | def getch() | |
33 | @win.getch() | |
34 | end | |
35 | def clear() | |
36 | @win.clear() | |
37 | end | |
38 | def resize(x,y) | |
39 | @win.resize(x,y) | |
40 | end | |
41 | def move(x,y) | |
42 | @win.move(x,y) | |
43 | end | |
44 | def refresh() | |
45 | @win.refresh() | |
46 | end | |
47 | ||
48 | def print_list(entry=nil) | |
49 | if(not entry.nil?) | |
50 | @entry = entry | |
51 | end | |
52 | i = 0 | |
53 | @params.each_section do |section| | |
54 | if(@entry == i) | |
55 | @win.attron(Ncurses::A_REVERSE) | |
56 | end | |
57 | @win.move(i+1,1) | |
58 | print_line(@win,@params[section]['Name']) | |
59 | if(@entry == i) | |
60 | @win.attroff(Ncurses::A_REVERSE) | |
61 | end | |
62 | i = i+1 | |
63 | end | |
64 | @win.border(*([0]*8)) | |
65 | @win.move(0,3) | |
66 | @win.addstr("Menu") | |
67 | @win.refresh() | |
68 | end | |
69 | end | |
70 | ||
71 | class Buff_Win | |
72 | def initialize(winsize,winpos,params) | |
73 | @params = params | |
74 | @win = Ncurses::WINDOW.new(0, winsize, 0, winpos) | |
75 | @panel = Ncurses::Panel::PANEL.new(@win) | |
76 | if(@params['Type'] == 'oneshot') | |
77 | @buffer = Buffer.new(0) | |
78 | else | |
79 | @buffer = Buffer.new(@params['Buffer'].to_i) | |
80 | end | |
81 | @proc = nil | |
82 | @curr_offset = 0 | |
83 | @hscroll = 0 | |
84 | update_date() | |
85 | ||
86 | spawn_proc() | |
87 | print_buffer() | |
88 | end | |
89 | ||
90 | def move_resize(winsize,winpos) | |
91 | newwin = Ncurses::WINDOW.new(0, winsize, 0, winpos) | |
92 | Ncurses::Panel.replace_panel(@panel, newwin) | |
93 | Ncurses.delwin(@win) | |
94 | @win = newwin | |
95 | print_buffer() | |
96 | end | |
97 | def update_date() | |
98 | @last_update = Time.now | |
99 | @date = @last_update.strftime("%F %R:%S") | |
100 | end | |
101 | ||
102 | def win_border() | |
103 | @win.border(*([0]*8)) | |
104 | win_header() | |
105 | end | |
106 | def win_header() | |
107 | @win.move(0,3) | |
108 | @win.addnstr(@params['Name'],@win.getmaxx-@date.length-10) | |
109 | @win.move(0,@win.getmaxx-@date.length-2) | |
110 | @win.addstr(@date) | |
111 | end | |
112 | def refresh() | |
113 | @win.refresh() | |
114 | end | |
115 | def clear() | |
116 | @win.clear() | |
117 | end | |
118 | ||
119 | def show_win() | |
120 | Ncurses::Panel.top_panel(@panel) | |
121 | Ncurses::Panel.update_panels | |
122 | end | |
123 | ||
124 | def hscroll(scroll=0) | |
125 | @hscroll += scroll | |
126 | if(@hscroll < 0) | |
127 | @hscroll = 0 | |
128 | end | |
129 | if(@hscroll > @buffer.maxlen()-@win.getmaxx+3) | |
130 | @hscroll = @buffer.maxlen()-@win.getmaxx+3 | |
131 | end | |
132 | print_buffer() | |
133 | refresh() | |
134 | end | |
135 | def scroll(scroll=0,goto=nil,fact=nil) | |
136 | if (not fact.nil?) | |
137 | scroll = (fact * @win.getmaxy.to_f).to_i | |
138 | elsif (not goto.nil?) | |
139 | @curr_offset = (goto * @buffer.size()).to_i | |
140 | scroll = 0 | |
141 | end | |
142 | #@curr_offset -= @win.getmaxy/2 | |
143 | @curr_offset += scroll | |
144 | if(@curr_offset < 0) | |
145 | @curr_offset = 0 | |
146 | end | |
147 | if(@curr_offset > @buffer.size()-@win.getmaxy+2) | |
148 | @curr_offset = @buffer.size()-@win.getmaxy+2 | |
149 | end | |
150 | print_buffer() | |
151 | refresh() | |
152 | end | |
153 | ||
154 | def print_buffer() | |
9b96f7cb | 155 | #clear() |
8b6154e9 I |
156 | win_border() |
157 | j = 1 | |
158 | @buffer.yield(@win.getmaxy-2,@curr_offset) { |l,type| | |
159 | @win.move(j,1) | |
160 | if(type == 1) then @win.attron(Ncurses.COLOR_PAIR(1)) end | |
161 | print_line(@win,l,hscroll=@hscroll) | |
162 | if(type == 1) then @win.attroff(Ncurses.COLOR_PAIR(1)) end | |
163 | j = j+1 | |
164 | } | |
165 | if(@buffer.has_before?) | |
166 | @win.move(2,@win.getmaxx-1) | |
167 | @win.attron(Ncurses::A_REVERSE) | |
168 | @win.addstr("↑") | |
169 | @win.attroff(Ncurses::A_REVERSE) | |
170 | end | |
171 | if(@buffer.has_after?) | |
172 | @win.move(@win.getmaxy-2,@win.getmaxx-1) | |
173 | @win.attron(Ncurses::A_REVERSE) | |
174 | @win.addstr("↓") | |
175 | @win.attroff(Ncurses::A_REVERSE) | |
176 | end | |
177 | end | |
178 | ||
179 | def proc_readlines() | |
180 | begin | |
181 | while true | |
182 | @buffer.push(@proc.readline_nonblock) | |
183 | update_date() | |
184 | end | |
185 | rescue IO::WaitReadable | |
186 | end | |
187 | end | |
188 | ||
9b96f7cb | 189 | def update(force=false) |
8b6154e9 I |
190 | if(@params['Type'] == 'continuous') |
191 | proc_readlines() | |
192 | end | |
193 | if(@params['Type'] == 'oneshot') | |
9b96f7cb | 194 | if(force or (Time.now - @last_update > @params['Periodic'].to_i)) |
8b6154e9 I |
195 | @buffer.clear() |
196 | spawn_proc() | |
197 | end | |
198 | end | |
199 | print_buffer() | |
200 | end | |
201 | ||
202 | def spawn_proc() | |
203 | if(@params['Type'] == 'oneshot') | |
204 | update_date() | |
205 | Open3.popen3(@params["Command"]) { |i,o,e,t| | |
206 | while ((not o.eof?) or (not e.eof?)) | |
207 | rs = IO.select([o,e],nil)[0] | |
208 | r = (rs[0].eof?)? rs[1] : rs[0] | |
209 | ||
210 | if r.fileno == o.fileno | |
211 | @buffer.push(r.readline) | |
212 | elsif r.fileno == e.fileno | |
213 | @buffer.push(r.readline,type=1) | |
214 | end | |
215 | end | |
216 | } | |
217 | elsif(@params['Type'] == 'continuous') | |
218 | @proc = IO.popen(@params["Command"]) | |
219 | proc_readlines() | |
220 | end | |
221 | end | |
222 | end | |
223 | ||
224 | class Buffer | |
225 | def initialize(size) | |
226 | @size = size | |
227 | @buff = [] | |
228 | @buff_type = [] | |
229 | @current = 0 | |
230 | @wrap = false | |
231 | @before = false | |
232 | @after = false | |
233 | end | |
234 | def size() | |
235 | return @buff.length | |
236 | end | |
237 | def maxlen() | |
238 | maxlen = 0 | |
239 | @buff.each do |string| | |
240 | if string.length > maxlen | |
241 | maxlen = string.length | |
242 | end | |
243 | end | |
244 | return maxlen | |
245 | end | |
246 | ||
247 | def push(string,type=0) | |
248 | if(string.chomp.empty?) then string = " " end | |
249 | string.split( /\r?\n/ ).each do |line| | |
250 | @buff[@current] = line | |
251 | @buff_type[@current] = type | |
252 | if(@size > 0) | |
253 | @current = (1+@current) % @size | |
254 | else | |
255 | @current = 1+@current | |
256 | end | |
257 | if(@current == 0) then @wrap = true end | |
258 | end | |
259 | end | |
260 | def yield(size,offset=0,&block) | |
9b96f7cb | 261 | if(size < 0) then size = 0 end |
8b6154e9 I |
262 | range = Range.new(0,@current-1).to_a |
263 | if(@wrap) | |
264 | range = Range.new(@current,@size-1).to_a + range | |
265 | end | |
266 | range = range.last(size+offset)[0,size] | |
267 | @before = (size+offset < @buff.length) | |
268 | @after = (offset != 0 and size < @buff.length) | |
269 | if(block) | |
270 | range.each do |i| | |
271 | yield [@buff[i],@buff_type[i]] | |
272 | end | |
273 | else | |
274 | return range.collect{|r| [@buff[r],@buff_type[r]]} | |
275 | end | |
276 | end | |
277 | def has_after?() | |
278 | return @after | |
279 | end | |
280 | def has_before?() | |
281 | return @before | |
282 | end | |
283 | def clear() | |
284 | @current = 0 | |
285 | @buff = [] | |
286 | @buff_type = [] | |
287 | end | |
288 | end | |
289 | ||
290 | def read_ini() | |
291 | inifile = IniFile.load('monitorrc') | |
292 | return inifile | |
293 | end | |
294 | ||
295 | def print_line(win, str, hscroll=0) | |
9b96f7cb I |
296 | revert_color = false |
297 | str[0,5].match(/\033\[3(.)m/) { |c| #Line starts with an escape sequence. We handle that `a la xterm` | |
298 | Ncurses.init_pair(10, c[1].to_i, Ncurses::COLOR_BLACK) | |
299 | win.attron(Ncurses.COLOR_PAIR(10)) | |
300 | revert_color = true | |
301 | str = str[5,str.length] | |
302 | } | |
303 | str = str.gsub("\011"," ") | |
304 | #Any other control char is ignored and escaped | |
305 | str = str.gsub(/[[:cntrl:]]/) { |m| | |
306 | "^"+(m.ord + 64).chr | |
307 | } | |
8b6154e9 I |
308 | if(hscroll > 0) |
309 | strcut = str[hscroll,str.length] | |
310 | if(strcut.nil? or strcut.empty?) | |
311 | str = "" | |
312 | else | |
313 | str = "…"+strcut | |
314 | end | |
315 | end | |
316 | strlen = str.length | |
317 | winlen = win.getmaxx-win.getcurx-1 | |
318 | if(strlen <= winlen) | |
319 | win.addstr(str + " "*(winlen-strlen)) | |
320 | else | |
321 | win.addstr(str[0,winlen-1]+"…") | |
322 | end | |
9b96f7cb I |
323 | if(revert_color) |
324 | win.attroff(Ncurses.COLOR_PAIR(10)) | |
325 | end | |
8b6154e9 I |
326 | end |
327 | ||
328 | def make_bufwins(inifile) | |
329 | bufwins = [] | |
330 | inifile.each_section do |section| | |
331 | bufwin = Buff_Win.new(Ncurses.COLS()-Ncurses.COLS()/4, | |
332 | Ncurses.COLS()/4, | |
333 | inifile[section]) | |
334 | bufwins.push(bufwin) | |
335 | end | |
336 | return bufwins | |
337 | end | |
338 | ||
339 | def update_buffers(bufwins) | |
340 | bufwins.each do |bufwin| | |
341 | bufwin.update() | |
342 | end | |
343 | end | |
344 | ||
345 | def redraw_all(list,bufwins,curr_bufwin) | |
346 | bufwins.each do |bufwin| | |
347 | bufwin.move_resize(Ncurses.COLS()-Ncurses.COLS()/4,Ncurses.COLS()/4) | |
348 | end | |
349 | list.resize(Ncurses.LINES(), Ncurses.COLS()/4) | |
350 | list.clear() | |
351 | list.print_list() | |
352 | list.refresh() | |
353 | curr_bufwin.refresh() | |
354 | end | |
355 | begin | |
356 | # initialize ncurses | |
357 | Ncurses.initscr | |
358 | Ncurses.start_color | |
359 | Ncurses.cbreak # provide unbuffered input | |
360 | Ncurses.noecho # turn off input echoing | |
361 | #Ncurses.nonl # turn off newline translation | |
362 | #Ncurses.stdscr.intrflush(false) # turn off flush-on-interrupt | |
363 | Ncurses.stdscr.keypad(true) # turn on keypad mode | |
364 | Ncurses.init_pair(1, Ncurses::COLOR_RED, Ncurses::COLOR_BLACK) | |
9b96f7cb | 365 | Ncurses.init_pair(10, Ncurses::COLOR_WHITE, Ncurses::COLOR_BLACK) |
8b6154e9 I |
366 | |
367 | ||
368 | inifile = read_ini() | |
369 | ||
370 | list = List_Win.new(inifile) | |
371 | bufwins = make_bufwins(inifile) | |
372 | entry = 0 | |
373 | cur_bufwin = bufwins[entry] | |
374 | cur_bufwin.show_win() | |
375 | list.print_list() | |
376 | while(ch = list.getch()) do | |
377 | case(ch) | |
378 | when "n".ord | |
379 | entry = (entry +1) % bufwins.length | |
380 | cur_bufwin = bufwins[entry] | |
381 | cur_bufwin.show_win() | |
382 | list.print_list(entry=entry) | |
383 | when "p".ord | |
384 | entry = (entry -1) % bufwins.length | |
385 | cur_bufwin = bufwins[entry] | |
386 | cur_bufwin.show_win() | |
387 | list.print_list(entry=entry) | |
388 | when 12 #ctrl+L | |
389 | redraw_all(list,bufwins,cur_bufwin) | |
9b96f7cb I |
390 | when 18 #ctrl+R |
391 | cur_bufwin.update(force=true) | |
8b6154e9 I |
392 | when Ncurses::KEY_RESIZE |
393 | redraw_all(list,bufwins,cur_bufwin) | |
394 | when Ncurses::KEY_LEFT | |
395 | cur_bufwin.hscroll(scroll=-1) | |
396 | when Ncurses::KEY_RIGHT | |
397 | cur_bufwin.hscroll(scroll=1) | |
398 | when Ncurses::KEY_DOWN | |
399 | cur_bufwin.scroll(scroll=-1) | |
400 | when Ncurses::KEY_UP | |
401 | cur_bufwin.scroll(scroll=1) | |
402 | when Ncurses::KEY_NPAGE | |
403 | cur_bufwin.scroll(scroll=0,goto=nil,fact=-0.75) | |
404 | when Ncurses::KEY_PPAGE | |
405 | cur_bufwin.scroll(scroll=0,goto=nil,fact=0.75) | |
406 | when Ncurses::KEY_HOME | |
407 | cur_bufwin.scroll(scroll=0,goto=1.0) | |
408 | when Ncurses::KEY_END | |
409 | cur_bufwin.scroll(scroll=0,goto=0.0) | |
410 | when Ncurses::ERR | |
411 | update_buffers(bufwins) | |
412 | cur_bufwin.show_win() | |
413 | when "q".ord | |
414 | break | |
415 | else | |
416 | next | |
417 | end | |
418 | end | |
419 | ||
420 | ensure | |
421 | Ncurses.echo | |
422 | Ncurses.nocbreak | |
423 | Ncurses.nl | |
424 | Ncurses.endwin | |
425 | end |