ffi-ncurses 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +22 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +13 -0
- data/History.txt +32 -3
- data/README.rdoc +118 -58
- data/Rakefile +30 -0
- data/examples/acs_chars.rb +53 -0
- data/examples/acs_chars.rbc +1502 -0
- data/examples/{example-attributes.rb → attributes.rb} +0 -0
- data/examples/color.rb +63 -0
- data/examples/cursor.rb +27 -0
- data/examples/example.rb +17 -17
- data/examples/getkey.rb +212 -0
- data/examples/{example-getsetsyx.rb → getsetsyx.rb} +2 -2
- data/examples/globals.rb +38 -0
- data/examples/hello.rb +34 -0
- data/examples/hello.rbc +638 -0
- data/examples/hellowide.rb +59 -0
- data/examples/keys.rb +27 -0
- data/examples/{example-mouse.rb → mouse.rb} +2 -2
- data/examples/multiterm.rb +120 -0
- data/examples/ncurses/LICENSES_for_examples +26 -0
- data/examples/{ncurses-example.rb → ncurses/example.rb} +13 -80
- data/examples/ncurses/hello_ncurses.rb +57 -0
- data/examples/ncurses/rain.rb +220 -0
- data/examples/ncurses/read_line.rb +67 -0
- data/examples/ncurses/subwin.rb +71 -0
- data/examples/ncurses/tclock.rb +227 -0
- data/examples/newterm.rb +65 -0
- data/examples/panel_simple.rb +82 -0
- data/examples/{example-printw-variadic.rb → printw-variadic.rb} +1 -1
- data/examples/ripoffline.rb +86 -0
- data/examples/run-all.sh +14 -0
- data/examples/{example-softkeys.rb → softkeys.rb} +0 -0
- data/examples/{example-stdscr.rb → stdscr.rb} +2 -1
- data/examples/temp_leave.rb +99 -0
- data/examples/viewer.rb +350 -0
- data/examples/wacs_chars.rb +64 -0
- data/examples/windows.rb +73 -0
- data/ffi-ncurses.gemspec +39 -52
- data/lib/ffi-ncurses.rb +214 -474
- data/lib/ffi-ncurses/acs.rb +150 -0
- data/lib/ffi-ncurses/bool_wrappers.rb +66 -0
- data/lib/ffi-ncurses/functions.rb +450 -0
- data/lib/ffi-ncurses/keydefs.rb +136 -99
- data/lib/ffi-ncurses/mouse.rb +106 -106
- data/lib/ffi-ncurses/ncurses.rb +176 -0
- data/lib/ffi-ncurses/{ord-shim.rb → ord_shim.rb} +0 -0
- data/lib/ffi-ncurses/panel.rb +21 -0
- data/lib/ffi-ncurses/typedefs.rb +35 -0
- data/lib/ffi-ncurses/version.rb +7 -0
- data/lib/ffi-ncurses/widechars.rb +137 -0
- data/lib/ffi-ncurses/winstruct.rb +55 -33
- data/spec/attached_functions_spec.rb +42 -0
- metadata +95 -85
- data/examples/example-colour.rb +0 -63
- data/examples/example-cursor.rb +0 -22
- data/examples/example-hello.rb +0 -24
- data/examples/example-jruby.rb +0 -14
- data/examples/example-keys.rb +0 -25
- data/examples/example-windows.rb +0 -24
data/examples/newterm.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'ffi-ncurses'
|
3
|
+
|
4
|
+
# newterm - how to read STDIN and do ncurses at the same time
|
5
|
+
# See ncurses FAQ http://invisible-island.net/ncurses/ncurses.faq.html "Problems with output buffering"
|
6
|
+
|
7
|
+
# Call like this:
|
8
|
+
#
|
9
|
+
# $ echo hello|ruby newterm.rb
|
10
|
+
#
|
11
|
+
# or
|
12
|
+
#
|
13
|
+
# $ ruby newterm.rb < input.txt
|
14
|
+
#
|
15
|
+
# otherwise the program will stall.
|
16
|
+
|
17
|
+
def log(*a)
|
18
|
+
File.open("ncurses.log", "a") do |file|
|
19
|
+
file.puts(a.inspect)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module CLib
|
24
|
+
extend FFI::Library
|
25
|
+
ffi_lib FFI::Library::LIBC
|
26
|
+
# FILE* open and close
|
27
|
+
typedef :pointer, :FILEP
|
28
|
+
attach_function :fopen, [:string, :string], :FILEP
|
29
|
+
attach_function :fclose, [:FILEP], :int
|
30
|
+
end
|
31
|
+
|
32
|
+
begin
|
33
|
+
term = CLib.fopen("/dev/tty", "rb+")
|
34
|
+
screen = FFI::NCurses.newterm(nil, term, term)
|
35
|
+
old_screen = FFI::NCurses.set_term(screen)
|
36
|
+
FFI::NCurses.noecho
|
37
|
+
FFI::NCurses.clear
|
38
|
+
FFI::NCurses.move 1, 1
|
39
|
+
FFI::NCurses.addstr("Press any key to continue")
|
40
|
+
FFI::NCurses.move 3, 1
|
41
|
+
FFI::NCurses.addstr(STDIN.read(3))
|
42
|
+
FFI::NCurses.refresh
|
43
|
+
FFI::NCurses.flushinp
|
44
|
+
FFI::NCurses.getch
|
45
|
+
FFI::NCurses.addstr(STDIN.read)
|
46
|
+
FFI::NCurses.move 5, 1
|
47
|
+
FFI::NCurses.addstr("Press any key to continue")
|
48
|
+
FFI::NCurses.refresh
|
49
|
+
FFI::NCurses.flushinp
|
50
|
+
FFI::NCurses.getch
|
51
|
+
rescue => e
|
52
|
+
log :e1, e
|
53
|
+
ensure
|
54
|
+
begin
|
55
|
+
# Watch the ordering of these calls, i.e. do not call delscreen
|
56
|
+
# before endwin
|
57
|
+
FFI::NCurses.flushinp
|
58
|
+
FFI::NCurses.echo
|
59
|
+
FFI::NCurses.endwin
|
60
|
+
FFI::NCurses.delscreen(screen)
|
61
|
+
CLib.fclose(term)
|
62
|
+
rescue => e
|
63
|
+
log :e2, e
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- mode: ruby; coding: utf-8; -*-
|
3
|
+
require 'ffi-ncurses'
|
4
|
+
|
5
|
+
include FFI::NCurses
|
6
|
+
|
7
|
+
def main
|
8
|
+
my_wins = []
|
9
|
+
my_panels = []
|
10
|
+
lines = 10
|
11
|
+
cols = 40
|
12
|
+
y = 2
|
13
|
+
x = 4
|
14
|
+
|
15
|
+
begin
|
16
|
+
initscr
|
17
|
+
cbreak
|
18
|
+
noecho
|
19
|
+
|
20
|
+
# Create windows for the panels
|
21
|
+
my_wins[0] = newwin(lines, cols, y, x)
|
22
|
+
my_wins[1] = newwin(lines, cols, y + 1, x + 5)
|
23
|
+
my_wins[2] = newwin(lines, cols, y + 2, x + 10)
|
24
|
+
|
25
|
+
|
26
|
+
# Create borders around the windows so that you can see the effect
|
27
|
+
# of panels
|
28
|
+
my_wins.each_with_index do |win, i|
|
29
|
+
box(win, 0, 0)
|
30
|
+
mvwaddstr(win, 2, 2, "Window #{i}")
|
31
|
+
end
|
32
|
+
|
33
|
+
# Attach a panel to each window Order is bottom up #
|
34
|
+
my_panels[0] = new_panel(my_wins[0]) # Push 0, order: stdscr-0
|
35
|
+
my_panels[1] = new_panel(my_wins[2]) # Push 1, order: stdscr-0-1
|
36
|
+
my_panels[2] = new_panel(my_wins[1]) # Push 2, order: stdscr-0-1-2
|
37
|
+
|
38
|
+
# Update the stacking order. panel 2 will be on top
|
39
|
+
update_panels
|
40
|
+
|
41
|
+
# Show it on the screen
|
42
|
+
doupdate
|
43
|
+
|
44
|
+
getch
|
45
|
+
|
46
|
+
# bring panel 1 to top
|
47
|
+
top_panel(my_panels[1])
|
48
|
+
update_panels
|
49
|
+
doupdate
|
50
|
+
|
51
|
+
getch
|
52
|
+
|
53
|
+
# hide panel 0
|
54
|
+
hide_panel(my_panels[0])
|
55
|
+
update_panels
|
56
|
+
doupdate
|
57
|
+
|
58
|
+
getch
|
59
|
+
|
60
|
+
# show panel 0 again (on top)
|
61
|
+
show_panel(my_panels[0])
|
62
|
+
update_panels
|
63
|
+
doupdate
|
64
|
+
|
65
|
+
getch
|
66
|
+
|
67
|
+
# put panel 0 back to bottom of stack
|
68
|
+
bottom_panel(my_panels[0])
|
69
|
+
update_panels
|
70
|
+
doupdate
|
71
|
+
|
72
|
+
getch
|
73
|
+
|
74
|
+
rescue => e
|
75
|
+
endwin
|
76
|
+
raise
|
77
|
+
ensure
|
78
|
+
endwin
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
main
|
@@ -0,0 +1,86 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'ffi-ncurses'
|
3
|
+
|
4
|
+
# how to use ripoffline
|
5
|
+
|
6
|
+
def log(*a)
|
7
|
+
File.open("ncurses.log", "a") do |file|
|
8
|
+
file.puts(a.inspect)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# this works
|
13
|
+
# module FFI
|
14
|
+
# module NCurses
|
15
|
+
# callback :ripoffline_callback, [:pointer, :int], :int
|
16
|
+
# attach_function :ripoffline, [:int, :ripoffline_callback], :int
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
|
20
|
+
# works
|
21
|
+
$ripoffwin = nil
|
22
|
+
ripoffCallback = Proc.new do |win, cols|
|
23
|
+
$ripoffwin = win
|
24
|
+
FFI::NCurses.wbkgd(win, FFI::NCurses::A_REVERSE)
|
25
|
+
FFI::NCurses.werase(win)
|
26
|
+
|
27
|
+
FFI::NCurses.wmove(win, 0, 0)
|
28
|
+
FFI::NCurses.waddstr(win, "ripoff: window %s, %d columns %s" % [win.to_s, cols, Time.now])
|
29
|
+
FFI::NCurses.wnoutrefresh(win)
|
30
|
+
Thread.start do
|
31
|
+
# FFI::NCurses.immedok(win)
|
32
|
+
loop do
|
33
|
+
FFI::NCurses.wmove(win, 0, 0)
|
34
|
+
FFI::NCurses.waddstr(win, "ripoff: window %s, %d columns %s" % [win.to_s, cols, Time.now])
|
35
|
+
FFI::NCurses.wnoutrefresh(win)
|
36
|
+
sleep 1
|
37
|
+
end
|
38
|
+
end
|
39
|
+
FFI::NCurses::OK
|
40
|
+
end
|
41
|
+
|
42
|
+
callback = FFI::Function.new(:int, [:pointer, :int], &ripoffCallback)
|
43
|
+
|
44
|
+
def get_cmd
|
45
|
+
while (ch = FFI::NCurses.getch) == FFI::NCurses::ERR
|
46
|
+
sleep(0.1) # this has better performance than using halfdelay
|
47
|
+
FFI::NCurses.wrefresh($ripoffwin)
|
48
|
+
end
|
49
|
+
ch
|
50
|
+
end
|
51
|
+
|
52
|
+
begin
|
53
|
+
#FFI::NCurses.ripoffline(-1, RipoffCallback) # works
|
54
|
+
FFI::NCurses.ripoffline(1, callback) # rip line off top
|
55
|
+
FFI::NCurses.ripoffline(-1, callback) # rip line off bottom
|
56
|
+
FFI::NCurses.initscr
|
57
|
+
# FFI::NCurses.halfdelay(1)
|
58
|
+
FFI::NCurses.nodelay(FFI::NCurses.stdscr, true) # v. expensive in CPU (unless we sleep as above)
|
59
|
+
FFI::NCurses.noecho
|
60
|
+
FFI::NCurses.clear
|
61
|
+
FFI::NCurses.border(*([0]*8))
|
62
|
+
FFI::NCurses.move(4, 4)
|
63
|
+
FFI::NCurses.addstr("Hello")
|
64
|
+
FFI::NCurses.refresh
|
65
|
+
# FFI::NCurses.getch
|
66
|
+
get_cmd
|
67
|
+
FFI::NCurses.flushinp
|
68
|
+
FFI::NCurses.addstr("World")
|
69
|
+
FFI::NCurses.refresh
|
70
|
+
get_cmd
|
71
|
+
#FFI::NCurses.getch
|
72
|
+
rescue => e
|
73
|
+
FFI::NCurses.flushinp
|
74
|
+
FFI::NCurses.echo
|
75
|
+
FFI::NCurses.endwin
|
76
|
+
log :e1, e
|
77
|
+
raise
|
78
|
+
ensure
|
79
|
+
begin
|
80
|
+
FFI::NCurses.flushinp
|
81
|
+
FFI::NCurses.echo
|
82
|
+
FFI::NCurses.endwin
|
83
|
+
rescue => e
|
84
|
+
log :e2, e
|
85
|
+
end
|
86
|
+
end
|
data/examples/run-all.sh
ADDED
File without changes
|
@@ -6,7 +6,8 @@ require 'ffi-ncurses'
|
|
6
6
|
begin
|
7
7
|
scr = FFI::NCurses.initscr
|
8
8
|
FFI::NCurses.box(FFI::NCurses.stdscr, 0, 0)
|
9
|
-
FFI::NCurses.mvaddstr 1, 1, "#{
|
9
|
+
FFI::NCurses.mvaddstr 1, 1, "initscr: #{scr.inspect}"
|
10
|
+
FFI::NCurses.mvaddstr 2, 1, "stdscr: #{FFI::NCurses.stdscr.inspect}"
|
10
11
|
FFI::NCurses.refresh
|
11
12
|
FFI::NCurses.getch
|
12
13
|
|
@@ -0,0 +1,99 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'ffi-ncurses'
|
3
|
+
|
4
|
+
include FFI::NCurses
|
5
|
+
|
6
|
+
module CLib
|
7
|
+
extend FFI::Library
|
8
|
+
ffi_lib FFI::Library::LIBC
|
9
|
+
# FILE* open and close
|
10
|
+
typedef :pointer, :FILEP
|
11
|
+
attach_function :fdopen, [:int, :string], :FILEP
|
12
|
+
attach_function :fopen, [:string, :string], :FILEP
|
13
|
+
attach_function :fclose, [:FILEP], :int
|
14
|
+
end
|
15
|
+
|
16
|
+
# This is the version from NCURSES HOWTO
|
17
|
+
def do_cmd(cmd)
|
18
|
+
def_prog_mode # Save the tty modes
|
19
|
+
endwin # End curses mode temporarily
|
20
|
+
system(cmd) # Do whatever you like in cooked mode
|
21
|
+
reset_prog_mode # Return to the previous tty mode
|
22
|
+
refresh # Do refresh() to restore the
|
23
|
+
# Screen contents
|
24
|
+
# stored by def_prog_mode()
|
25
|
+
end
|
26
|
+
|
27
|
+
# This is the version from ncurses examples filter.c
|
28
|
+
def do_cmdx(cmd)
|
29
|
+
reset_shell_mode # Restore terminal mode
|
30
|
+
STDOUT.flush
|
31
|
+
system(cmd) # Do whatever you like in cooked mode
|
32
|
+
reset_prog_mode # Return to the previous tty mode
|
33
|
+
touchwin(stdscr)
|
34
|
+
# erase
|
35
|
+
refresh
|
36
|
+
end
|
37
|
+
|
38
|
+
def show_text_window(win, title, text)
|
39
|
+
text_width = text.lines.map{ |x| x.size }.max
|
40
|
+
text_height = text.lines.to_a.size
|
41
|
+
|
42
|
+
width = text_width + 4
|
43
|
+
height = text_height + 4
|
44
|
+
|
45
|
+
rows, cols = getmaxyx(win)
|
46
|
+
|
47
|
+
col = (cols - width)/2
|
48
|
+
row = (rows - height)/2
|
49
|
+
|
50
|
+
frame = newwin(height, width, row, col)
|
51
|
+
keypad frame, true
|
52
|
+
box(frame, 0, 0)
|
53
|
+
|
54
|
+
title = "[ #{title} ]"
|
55
|
+
wmove(frame, 0, (width - title.size)/2)
|
56
|
+
waddstr(frame, title)
|
57
|
+
|
58
|
+
win = derwin(frame, text_height, text_width, 2, 2)
|
59
|
+
wmove(win, 0, 0)
|
60
|
+
waddstr(win, text)
|
61
|
+
wnoutrefresh(win)
|
62
|
+
wgetch(frame)
|
63
|
+
flushinp
|
64
|
+
delwin(win)
|
65
|
+
delwin(frame)
|
66
|
+
end
|
67
|
+
|
68
|
+
def main
|
69
|
+
begin
|
70
|
+
# input = CLib.fdopen(STDIN.fileno, "rb+")
|
71
|
+
# output = CLib.fdopen(STDOUT.fileno, "rb+")
|
72
|
+
# screen = newterm(nil, output, input)
|
73
|
+
# set_term(screen)
|
74
|
+
|
75
|
+
initscr
|
76
|
+
keypad stdscr, true
|
77
|
+
noecho
|
78
|
+
|
79
|
+
addstr("Hello from ncurses\n")
|
80
|
+
refresh
|
81
|
+
addstr("Press any key to enter shell\n")
|
82
|
+
getch
|
83
|
+
addstr("Now entering shell - enter 'exit' or press Ctrl-D to exit\n")
|
84
|
+
refresh
|
85
|
+
sleep 0.2
|
86
|
+
|
87
|
+
cmd = ARGV[0] || "/bin/sh"
|
88
|
+
do_cmd(cmd)
|
89
|
+
flushinp
|
90
|
+
show_text_window(stdscr, "Message", "Back to ncurses\nPress any key to quit")
|
91
|
+
refresh
|
92
|
+
ensure
|
93
|
+
flushinp
|
94
|
+
endwin
|
95
|
+
# delscreen(screen)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
main
|
data/examples/viewer.rb
ADDED
@@ -0,0 +1,350 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- mode: ruby; coding: utf-8 -*-
|
3
|
+
#
|
4
|
+
# A simple file viewer.
|
5
|
+
#
|
6
|
+
# Sean O'Halpin, 2011-09-16
|
7
|
+
#
|
8
|
+
# Possible features to add:
|
9
|
+
# - search
|
10
|
+
# - hex view
|
11
|
+
#
|
12
|
+
require 'ffi-ncurses'
|
13
|
+
|
14
|
+
include FFI::NCurses
|
15
|
+
|
16
|
+
# some helper methods for working with stdlib FILE pointers
|
17
|
+
module CLib
|
18
|
+
extend FFI::Library
|
19
|
+
ffi_lib FFI::Library::LIBC
|
20
|
+
typedef :pointer, :FILEP
|
21
|
+
# FILE* open, close and eof
|
22
|
+
attach_function :fopen, [:string, :string], :FILEP
|
23
|
+
attach_function :fclose, [:FILEP], :int
|
24
|
+
attach_function :feof, [:FILEP], :int
|
25
|
+
attach_function :fflush, [:FILEP], :int
|
26
|
+
attach_function :fputs, [:string, :FILEP], :int
|
27
|
+
end
|
28
|
+
|
29
|
+
$APP_DEBUG = ARGV.delete("--debug")
|
30
|
+
def log(*a)
|
31
|
+
STDERR.puts a.inspect if $APP_DEBUG
|
32
|
+
end
|
33
|
+
|
34
|
+
def redraw_background_frame
|
35
|
+
clear
|
36
|
+
touchwin(stdscr)
|
37
|
+
box(stdscr, 0, 0)
|
38
|
+
title = "[ F1 or ? for help ]"
|
39
|
+
wmove(stdscr, 0, (COLS() - title.size)/2)
|
40
|
+
waddstr(stdscr, title)
|
41
|
+
wnoutrefresh(stdscr)
|
42
|
+
end
|
43
|
+
|
44
|
+
def std_colors
|
45
|
+
init_pair(0, Color::BLACK, Color::BLACK)
|
46
|
+
init_pair(1, Color::RED, Color::BLACK)
|
47
|
+
init_pair(2, Color::GREEN, Color::BLACK)
|
48
|
+
init_pair(3, Color::YELLOW, Color::BLACK)
|
49
|
+
init_pair(4, Color::BLUE, Color::BLACK)
|
50
|
+
init_pair(5, Color::MAGENTA, Color::BLACK)
|
51
|
+
init_pair(6, Color::CYAN, Color::BLACK)
|
52
|
+
init_pair(7, Color::WHITE, Color::BLACK)
|
53
|
+
|
54
|
+
init_pair(8, Color::BLACK, Color::BLACK)
|
55
|
+
init_pair(9, Color::BLACK, Color::RED)
|
56
|
+
init_pair(10, Color::BLACK, Color::GREEN)
|
57
|
+
init_pair(11, Color::BLACK, Color::YELLOW)
|
58
|
+
init_pair(12, Color::BLACK, Color::BLUE)
|
59
|
+
init_pair(13, Color::BLACK, Color::MAGENTA)
|
60
|
+
init_pair(14, Color::BLACK, Color::CYAN)
|
61
|
+
init_pair(15, Color::BLACK, Color::WHITE)
|
62
|
+
end
|
63
|
+
|
64
|
+
def show_text_window(win, title, text)
|
65
|
+
text_width = text.lines.map{ |x| x.size }.max
|
66
|
+
text_height = text.lines.to_a.size
|
67
|
+
|
68
|
+
width = text_width + 4
|
69
|
+
height = text_height + 4
|
70
|
+
|
71
|
+
rows, cols = getmaxyx(win)
|
72
|
+
|
73
|
+
col = (cols - width)/2
|
74
|
+
row = (rows - height)/2
|
75
|
+
|
76
|
+
frame = newwin(height, width, row, col)
|
77
|
+
keypad frame, true
|
78
|
+
box(frame, 0, 0)
|
79
|
+
|
80
|
+
title = "[ #{title} ]"
|
81
|
+
wmove(frame, 0, (width - title.size)/2)
|
82
|
+
waddstr(frame, title)
|
83
|
+
|
84
|
+
win = derwin(frame, text_height, text_width, 2, 2)
|
85
|
+
wmove(win, 0, 0)
|
86
|
+
waddstr(win, text)
|
87
|
+
wnoutrefresh(win)
|
88
|
+
wgetch(frame)
|
89
|
+
flushinp
|
90
|
+
delwin(win)
|
91
|
+
delwin(frame)
|
92
|
+
end
|
93
|
+
|
94
|
+
def update_row_col(left_border, row, col)
|
95
|
+
move(0, left_border)
|
96
|
+
addstr("%03d:%003d" % [row + 1, col + 1])
|
97
|
+
wnoutrefresh(stdscr)
|
98
|
+
end
|
99
|
+
|
100
|
+
@maximized = false
|
101
|
+
def toggle_maximize(term)
|
102
|
+
# Maximize terminal window (xterm only).
|
103
|
+
#
|
104
|
+
# You can do the same with Alt-F10 in Gnome (and it seems more
|
105
|
+
# reliable).
|
106
|
+
CLib.fflush(term)
|
107
|
+
|
108
|
+
# maximize
|
109
|
+
CLib.fputs("\e[9;#{@maximized ? 0 : 1}t", term)
|
110
|
+
CLib.fflush(term)
|
111
|
+
|
112
|
+
@maximized = !@maximized
|
113
|
+
end
|
114
|
+
|
115
|
+
# Helper function to match charcodes or (single character) strings
|
116
|
+
# against keycodes returned by ncursesw.
|
117
|
+
#
|
118
|
+
# Note that this won't work for characters outside Latin-1 as they overlap numerically with
|
119
|
+
# ncurses's KEY_ codes. Should return new class for KEY_ codes.
|
120
|
+
#
|
121
|
+
def KEY(s)
|
122
|
+
case s
|
123
|
+
when String
|
124
|
+
s.unpack("U")[0]
|
125
|
+
else
|
126
|
+
s
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def resize_terminal_window(rows, cols)
|
131
|
+
print("\e[8;#{rows};#{cols};t") # set window size to rows x cols
|
132
|
+
resizeterm(rows, cols) # note: you need to tell ncurses that you've resized the terminal
|
133
|
+
end
|
134
|
+
|
135
|
+
def page_height
|
136
|
+
LINES() - total_tb_border
|
137
|
+
end
|
138
|
+
|
139
|
+
def page_width
|
140
|
+
COLS() - total_lr_border
|
141
|
+
end
|
142
|
+
|
143
|
+
def total_lr_border
|
144
|
+
@left_border + @right_border
|
145
|
+
end
|
146
|
+
|
147
|
+
def total_tb_border
|
148
|
+
@top_border + @bottom_border
|
149
|
+
end
|
150
|
+
|
151
|
+
def view(text, arg_rows = nil, arg_cols = nil)
|
152
|
+
@top_border = @bottom_border = 1
|
153
|
+
@left_border = @right_border = 1
|
154
|
+
# set_escdelay(10) if respond_to?(:set_escdelay) # Centos 5.1 :S
|
155
|
+
lines = text.lines.to_a
|
156
|
+
maxx = lines.map{|x| x.size }.max + total_lr_border
|
157
|
+
maxy = lines.size + total_tb_border
|
158
|
+
|
159
|
+
# We need to open /dev/tty so we can read from STDIN without borking
|
160
|
+
# ncurses
|
161
|
+
term = CLib.fopen("/dev/tty", "rb+")
|
162
|
+
screen = newterm(nil, term, term)
|
163
|
+
old_screen = set_term(screen)
|
164
|
+
|
165
|
+
# save original window size
|
166
|
+
orig_rows, orig_cols = getmaxyx(stdscr)
|
167
|
+
if arg_rows.nil?
|
168
|
+
arg_rows = orig_rows
|
169
|
+
end
|
170
|
+
if arg_cols.nil?
|
171
|
+
arg_cols = orig_cols
|
172
|
+
end
|
173
|
+
if arg_rows != orig_rows || arg_cols != orig_cols
|
174
|
+
resize_terminal_window(arg_rows, arg_cols)
|
175
|
+
end
|
176
|
+
|
177
|
+
help_text = <<EOT
|
178
|
+
F1, ? - Help
|
179
|
+
Home - Beginning of file
|
180
|
+
End - End of file
|
181
|
+
PgDn, space - Scroll down one page
|
182
|
+
PgUp - Scroll up one page
|
183
|
+
Down arrow - Scroll down one line
|
184
|
+
Up arrow - Scroll up one line
|
185
|
+
Right arrow - Scroll right one column
|
186
|
+
Left arrow - Scroll left one column
|
187
|
+
+/- - Increase/decrease window size
|
188
|
+
^q, q, Esc - Quit
|
189
|
+
EOT
|
190
|
+
|
191
|
+
saved_exception = nil
|
192
|
+
begin
|
193
|
+
raw
|
194
|
+
noecho
|
195
|
+
curs_set 0
|
196
|
+
pad = newpad(maxy, maxx)
|
197
|
+
if has_colors
|
198
|
+
start_color
|
199
|
+
std_colors
|
200
|
+
end
|
201
|
+
keypad pad, true
|
202
|
+
|
203
|
+
# just a bit of fun - format RDoc headers, comments and org-mode
|
204
|
+
# header lines
|
205
|
+
flag = false
|
206
|
+
bg_flag = false
|
207
|
+
attribute = 0
|
208
|
+
lines.each do |line|
|
209
|
+
if line =~ /^\s*[=*#]+/
|
210
|
+
case line
|
211
|
+
when /^\s*#/
|
212
|
+
attribute = COLOR_PAIR(4)
|
213
|
+
when /^\s*=+/
|
214
|
+
# attribute = A_UNDERLINE
|
215
|
+
wbkgdset(pad, A_REVERSE)
|
216
|
+
wclrtoeol(pad)
|
217
|
+
bg_flag = true
|
218
|
+
when /^\s*\*/
|
219
|
+
attribute = COLOR_PAIR(1)
|
220
|
+
end
|
221
|
+
# wchgat(pad, -1, A_REVERSE, COLOR_WHITE, nil)
|
222
|
+
flag = true
|
223
|
+
# wattron(pad, A_BOLD)
|
224
|
+
wattron(pad, attribute)
|
225
|
+
end
|
226
|
+
rv = waddstr(pad, line)
|
227
|
+
if flag
|
228
|
+
flag = false
|
229
|
+
wattroff(pad, attribute)
|
230
|
+
if bg_flag
|
231
|
+
wbkgdset(pad, A_NORMAL)
|
232
|
+
wattroff(pad, A_BOLD)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
log :adding, line, :rv, rv
|
236
|
+
end
|
237
|
+
|
238
|
+
current_line = 0
|
239
|
+
current_col = 0
|
240
|
+
|
241
|
+
redraw_background_frame
|
242
|
+
update_row_col(@left_border, current_line, current_col)
|
243
|
+
|
244
|
+
rv = pnoutrefresh(pad, current_line, current_col, @top_border, @left_border, page_height, page_width)
|
245
|
+
log :prefresh1, :rv, rv
|
246
|
+
doupdate
|
247
|
+
|
248
|
+
# main loop
|
249
|
+
while ch = wgetch(pad)
|
250
|
+
log :ch, ch
|
251
|
+
case ch
|
252
|
+
when KEY_HOME
|
253
|
+
current_line = 0
|
254
|
+
current_col = 0
|
255
|
+
when KEY_END
|
256
|
+
current_line = maxy - LINES() + total_lr_border
|
257
|
+
current_col = 0
|
258
|
+
when KEY_NPAGE, KEY(' ')
|
259
|
+
current_line = [current_line + page_height, [maxy - page_height, 0].max].min
|
260
|
+
when KEY_PPAGE
|
261
|
+
current_line = [current_line - page_height, 0].max
|
262
|
+
when KEY_DOWN
|
263
|
+
current_line = [current_line + 1, [maxy - page_height, 0].max].min
|
264
|
+
when KEY_UP
|
265
|
+
current_line = [current_line - 1, 0].max
|
266
|
+
when KEY_RIGHT
|
267
|
+
current_col = [current_col + 1, [maxx - page_width, 0].max].min
|
268
|
+
when KEY_LEFT
|
269
|
+
current_col = [current_col - 1, 0].max
|
270
|
+
when KEY_F1, KEY('?')
|
271
|
+
# Help
|
272
|
+
show_text_window stdscr, "Help", help_text
|
273
|
+
redraw_background_frame
|
274
|
+
when KEY('f')
|
275
|
+
toggle_maximize(term)
|
276
|
+
when KEY('+')
|
277
|
+
rows, cols = getmaxyx(stdscr)
|
278
|
+
rows += 1
|
279
|
+
cols += 1
|
280
|
+
resize_terminal_window(rows, cols)
|
281
|
+
redraw_background_frame
|
282
|
+
when KEY('-')
|
283
|
+
rows, cols = getmaxyx(stdscr)
|
284
|
+
rows -= 1
|
285
|
+
cols -= 1
|
286
|
+
resize_terminal_window(rows, cols)
|
287
|
+
redraw_background_frame
|
288
|
+
when KEY_CTRL_Q, 'q'[0].ord, 27 # 27 == Esc
|
289
|
+
# Quit
|
290
|
+
delwin(pad)
|
291
|
+
break
|
292
|
+
when KEY_CTRL_C
|
293
|
+
# for JRuby - Ctrl-C in cbreak mode is not trapped in rescue
|
294
|
+
# below for some reason and so leaves the terminal in raw mode
|
295
|
+
raise Interrupt
|
296
|
+
when KEY_RESIZE, KEY_CTRL_L
|
297
|
+
log :redraw2
|
298
|
+
redraw_background_frame
|
299
|
+
end
|
300
|
+
|
301
|
+
update_row_col(@left_border, current_line, current_col)
|
302
|
+
rv = pnoutrefresh(pad, current_line, current_col, @top_border, @left_border, page_height, page_width)
|
303
|
+
log :prefresh2, :rv, rv, :line, current_line, :col, current_col
|
304
|
+
doupdate
|
305
|
+
flushinp
|
306
|
+
end
|
307
|
+
rescue Object => saved_exception
|
308
|
+
ensure
|
309
|
+
CLib.fclose(term) if !CLib.feof(term)
|
310
|
+
flushinp
|
311
|
+
end_rows, end_cols = getmaxyx(stdscr)
|
312
|
+
endwin
|
313
|
+
delscreen(screen)
|
314
|
+
|
315
|
+
# restore terminal size at launch
|
316
|
+
if end_rows != orig_rows || end_cols != orig_cols
|
317
|
+
resizeterm(orig_rows, orig_cols)
|
318
|
+
File.open("/dev/tty", "wb+") do |file|
|
319
|
+
file.print("\e[8;#{orig_rows};#{orig_cols};t") # restore window
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
if saved_exception
|
324
|
+
raise saved_exception
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
def main
|
329
|
+
require 'ostruct'
|
330
|
+
require 'optparse'
|
331
|
+
|
332
|
+
options = OpenStruct.new
|
333
|
+
|
334
|
+
xopts = OptionParser.new do |opts|
|
335
|
+
opts.on("-c", "--c COLS", Integer, "Resize screen to COLS width") do |cols|
|
336
|
+
options.columns = cols
|
337
|
+
end
|
338
|
+
opts.on("-r", "--r ROWS", Integer, "Resize screen to ROWS height") do |rows|
|
339
|
+
options.rows = rows
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
xopts.parse!(ARGV)
|
344
|
+
view(ARGF.read, options.rows, options.columns) # simplistic but works for demo
|
345
|
+
|
346
|
+
end
|
347
|
+
|
348
|
+
if __FILE__ == $0
|
349
|
+
main
|
350
|
+
end
|