ffi-ncurses 0.3.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|