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.
Files changed (61) hide show
  1. data/COPYING +22 -0
  2. data/Gemfile +5 -0
  3. data/Gemfile.lock +13 -0
  4. data/History.txt +32 -3
  5. data/README.rdoc +118 -58
  6. data/Rakefile +30 -0
  7. data/examples/acs_chars.rb +53 -0
  8. data/examples/acs_chars.rbc +1502 -0
  9. data/examples/{example-attributes.rb → attributes.rb} +0 -0
  10. data/examples/color.rb +63 -0
  11. data/examples/cursor.rb +27 -0
  12. data/examples/example.rb +17 -17
  13. data/examples/getkey.rb +212 -0
  14. data/examples/{example-getsetsyx.rb → getsetsyx.rb} +2 -2
  15. data/examples/globals.rb +38 -0
  16. data/examples/hello.rb +34 -0
  17. data/examples/hello.rbc +638 -0
  18. data/examples/hellowide.rb +59 -0
  19. data/examples/keys.rb +27 -0
  20. data/examples/{example-mouse.rb → mouse.rb} +2 -2
  21. data/examples/multiterm.rb +120 -0
  22. data/examples/ncurses/LICENSES_for_examples +26 -0
  23. data/examples/{ncurses-example.rb → ncurses/example.rb} +13 -80
  24. data/examples/ncurses/hello_ncurses.rb +57 -0
  25. data/examples/ncurses/rain.rb +220 -0
  26. data/examples/ncurses/read_line.rb +67 -0
  27. data/examples/ncurses/subwin.rb +71 -0
  28. data/examples/ncurses/tclock.rb +227 -0
  29. data/examples/newterm.rb +65 -0
  30. data/examples/panel_simple.rb +82 -0
  31. data/examples/{example-printw-variadic.rb → printw-variadic.rb} +1 -1
  32. data/examples/ripoffline.rb +86 -0
  33. data/examples/run-all.sh +14 -0
  34. data/examples/{example-softkeys.rb → softkeys.rb} +0 -0
  35. data/examples/{example-stdscr.rb → stdscr.rb} +2 -1
  36. data/examples/temp_leave.rb +99 -0
  37. data/examples/viewer.rb +350 -0
  38. data/examples/wacs_chars.rb +64 -0
  39. data/examples/windows.rb +73 -0
  40. data/ffi-ncurses.gemspec +39 -52
  41. data/lib/ffi-ncurses.rb +214 -474
  42. data/lib/ffi-ncurses/acs.rb +150 -0
  43. data/lib/ffi-ncurses/bool_wrappers.rb +66 -0
  44. data/lib/ffi-ncurses/functions.rb +450 -0
  45. data/lib/ffi-ncurses/keydefs.rb +136 -99
  46. data/lib/ffi-ncurses/mouse.rb +106 -106
  47. data/lib/ffi-ncurses/ncurses.rb +176 -0
  48. data/lib/ffi-ncurses/{ord-shim.rb → ord_shim.rb} +0 -0
  49. data/lib/ffi-ncurses/panel.rb +21 -0
  50. data/lib/ffi-ncurses/typedefs.rb +35 -0
  51. data/lib/ffi-ncurses/version.rb +7 -0
  52. data/lib/ffi-ncurses/widechars.rb +137 -0
  53. data/lib/ffi-ncurses/winstruct.rb +55 -33
  54. data/spec/attached_functions_spec.rb +42 -0
  55. metadata +95 -85
  56. data/examples/example-colour.rb +0 -63
  57. data/examples/example-cursor.rb +0 -22
  58. data/examples/example-hello.rb +0 -24
  59. data/examples/example-jruby.rb +0 -14
  60. data/examples/example-keys.rb +0 -25
  61. data/examples/example-windows.rb +0 -24
@@ -0,0 +1,176 @@
1
+ # Ncurses compatiblity layer.
2
+ require "ffi-ncurses"
3
+ require 'ffi-ncurses/ord_shim' # for 1.8.6 compatibility
4
+
5
+ def log(*a)
6
+ File.open("ncurses.log", "a") do |file|
7
+ file.puts(a.inspect)
8
+ end
9
+ end
10
+
11
+ module FFI
12
+ module NCurses
13
+ # Methods to help wrap FFI::NCurses methods to be compatible with Ncurses.
14
+ module Compatibility
15
+ extend self
16
+
17
+ def lookup_signature(method)
18
+ FFI::NCurses::FUNCTION_SIGNATURES.assoc(method)
19
+ end
20
+
21
+ def unbox_args(signature, args)
22
+ if signature
23
+ signature[1].zip(args).map{ |sig, arg|
24
+ case sig
25
+ when :window_p
26
+ #log :unbox_args, signature, arg, arg.respond_to?(:win)
27
+ if arg.respond_to?(:win)
28
+ #log :unbox_args, :win, arg.win
29
+ arg.win
30
+ else
31
+ #log :unbox_args, :nowin, arg
32
+ arg
33
+ end
34
+ when :chtype
35
+ arg.ord
36
+ else
37
+ arg
38
+ end
39
+ }
40
+ else
41
+ args
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ module Ncurses
49
+ module NCX
50
+ def COLS
51
+ FFI::NCurses.getmaxx(FFI::NCurses.stdscr)
52
+ end
53
+
54
+ def LINES
55
+ FFI::NCurses.getmaxy(FFI::NCurses.stdscr)
56
+ end
57
+
58
+ def has_colors?
59
+ FFI::NCurses.has_colors
60
+ end
61
+ end
62
+ include NCX
63
+ extend NCX
64
+
65
+ class WINDOW
66
+ attr_accessor :win
67
+
68
+ def initialize(*args, &block)
69
+ # SOH: Lifted from Ncurses. One example of how truly horrible
70
+ # that interface is (using the existence of an otherwise useless
71
+ # block as a flag).
72
+ if block_given?
73
+ @win = args.first
74
+ else
75
+ @win = FFI::NCurses.newwin(*args)
76
+ end
77
+ end
78
+
79
+ # Lifted from Ncurses.
80
+ def method_missing(name, *args)
81
+ # log(:mm, 1, name)
82
+ name = name.to_s
83
+ if name[0,2] == "mv"
84
+ # log(:mm, 2, "mv")
85
+ test_name = name.dup
86
+ test_name[2,0] = "w" # insert "w" after"mv"
87
+ if FFI::NCurses.respond_to?(test_name)
88
+ # log(:mm, 2.1, test_name)
89
+ Ncurses.send(test_name, @win, *args)
90
+ else
91
+ super
92
+ end
93
+ else
94
+ # log(:mm, 3, "w")
95
+ test_name = "w" + name
96
+ if FFI::NCurses.respond_to?(test_name)
97
+ # log(:mm, 4, test_name)
98
+ Ncurses.send(test_name, @win, *args)
99
+ elsif FFI::NCurses.respond_to?(name)
100
+ # log(:mm, 5, name)
101
+ Ncurses.send(name, @win, *args)
102
+ else
103
+ super
104
+ end
105
+ end
106
+ end
107
+
108
+ def respond_to?(name)
109
+ if super
110
+ true
111
+ else
112
+ name = name.to_s
113
+ if name[0,2] == "mv" && FFI::NCurses.respond_to?("mvw" + name[2..-1])
114
+ true
115
+ else
116
+ FFI::NCurses.respond_to?("w" + name) || FFI::NCurses.respond_to?(name)
117
+ end
118
+ end
119
+ end
120
+
121
+ def del
122
+ FFI::NCurses.delwin(@win)
123
+ end
124
+ alias delete del
125
+
126
+ end
127
+
128
+ def self.initscr
129
+ @stdscr = Ncurses::WINDOW.new(FFI::NCurses.initscr) { }
130
+ end
131
+
132
+ def initscr
133
+ Ncurses.initscr
134
+ end
135
+
136
+ def self.stdscr
137
+ @stdscr
138
+ end
139
+
140
+ def stdscr
141
+ Ncurses.stdscr
142
+ end
143
+
144
+ module MM
145
+ def method_missing(method, *args, &block)
146
+ if FFI::NCurses.respond_to?(method)
147
+ signature = FFI::NCurses::Compatibility.lookup_signature(method)
148
+ args = FFI::NCurses::Compatibility.unbox_args(signature, args)
149
+ res = FFI::NCurses.send(method, *args, &block)
150
+ # log :MM, signature, res
151
+ if signature && signature.last == :window_p && res.kind_of?(FFI::Pointer)
152
+ Ncurses::WINDOW.new(res) { }
153
+ else
154
+ res
155
+ end
156
+ else
157
+ super
158
+ end
159
+ end
160
+
161
+ def respond_to?(method)
162
+ FFI::NCurses.respond_to?(method)
163
+ end
164
+ end
165
+ include MM
166
+ extend MM
167
+
168
+ TRUE = true
169
+ FALSE = false
170
+
171
+ include FFI::NCurses::Color
172
+ include FFI::NCurses::Attributes
173
+ include FFI::NCurses::KeyDefs
174
+ include FFI::NCurses::Constants
175
+ include FFI::NCurses::Mouse
176
+ end
@@ -0,0 +1,21 @@
1
+ # typedef struct panel
2
+ # {
3
+ # WINDOW *win;
4
+ # struct panel *below;
5
+ # struct panel *above;
6
+ # NCURSES_CONST void *user;
7
+ # } PANEL;
8
+
9
+ module FFI
10
+ module NCurses
11
+ module PanelStruct
12
+ class Panel < FFI::Struct
13
+ layout \
14
+ :win, :pointer, # WINDOW*
15
+ :below, :pointer, # PANEL*
16
+ :above, :pointer, # PANEL*
17
+ :user, :pointer # void*
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,35 @@
1
+ module FFI
2
+ module NCurses
3
+ extend FFI::Library
4
+
5
+ # If I define my own typedefs, a bug in FFI (at least 0.6.3) means
6
+ # I have to redefine the built-in types if they're going to be
7
+ # used in variadic functions
8
+ FFI::TypeDefs.keys.each do |key|
9
+ typedef key, key
10
+ end
11
+
12
+ typedef :ulong, :attr_t
13
+ typedef :pointer, :attr_t_p
14
+ typedef :pointer, :cchar_t_p
15
+ typedef :ulong, :chtype
16
+ typedef :pointer, :chtype_p
17
+ typedef :pointer, :file_p
18
+ typedef :pointer, :int_p
19
+ typedef :pointer, :mevent_p
20
+ typedef :ulong, :mmask_t
21
+ typedef :pointer, :mmask_t_p
22
+ typedef :pointer, :panel_p
23
+ typedef :pointer, :screen_p
24
+ typedef :pointer, :short_p
25
+ typedef :ushort, :wchar_t
26
+ typedef :pointer, :wchar_t_p
27
+ typedef :pointer, :window_p
28
+ typedef :int, :wint_t # An integral type capable of storing any valid value of wchar_t, or WEOF
29
+ typedef :pointer, :wint_t_p
30
+
31
+ end
32
+ end
33
+
34
+ # Refs:
35
+ # http://pubs.opengroup.org/onlinepubs/007908799/xsh/wchar.h.html
@@ -0,0 +1,7 @@
1
+ module FFI
2
+ module NCurses
3
+ NAME = "ffi-ncurses"
4
+ VERSION = "0.4.0"
5
+ end
6
+ end
7
+
@@ -0,0 +1,137 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module WideChars
4
+ def display_width(txt)
5
+ #buffer_size = (FFI::WideChars.mbstowcs(nil, self, 0) + 1) * FFI::WideChars.find_type(:wchar_t).size
6
+ wchar_t = FFI::WideChars.find_type(:wchar_t)
7
+ buffer_size = (txt.size + 1) * wchar_t.size
8
+ buffer = FFI::Buffer.new(wchar_t, buffer_size)
9
+ rv = FFI::WideChars.mbstowcs(buffer, txt, txt.size)
10
+ if rv == -1
11
+ raise ArgumentError, "Invalid multibyte sequence"
12
+ else
13
+ rv = FFI::WideChars.wcswidth(buffer, rv)
14
+ if rv == -1
15
+ raise ArgumentError, "Wide character string contains non-printable characters"
16
+ else
17
+ rv
18
+ end
19
+ end
20
+ end
21
+
22
+ if RUBY_VERSION < '1.9.0'
23
+ def display_slice(txt, start, length = 1)
24
+ count = 0
25
+ str = ""
26
+ if start.kind_of?(Range)
27
+ limit = start.last + (start.exclude_end? ? 0 : 1)
28
+ start = start.first
29
+ else
30
+ limit = start + length
31
+ end
32
+ txt.each_char do |char|
33
+ if count >= start && count < limit
34
+ str << char
35
+ end
36
+ count += 1
37
+ end
38
+ str
39
+ end
40
+ else
41
+ def display_slice(txt, *args, &b)
42
+ txt.slice(*args, &b)
43
+ end
44
+ end
45
+
46
+ extend self
47
+ end
48
+
49
+ module FFI
50
+ module WideChars
51
+ extend FFI::Library
52
+
53
+ FFI::TypeDefs.keys.each do |key|
54
+ typedef key, key
55
+ end
56
+
57
+ typedef :ulong, :attr_t
58
+ typedef :pointer, :attr_t_p
59
+ typedef :pointer, :cchar_t_p
60
+ typedef :ulong, :chtype
61
+ typedef :pointer, :chtype_p
62
+ typedef :pointer, :file_p
63
+ typedef :pointer, :int_p
64
+ typedef :pointer, :mevent_p
65
+ typedef :ulong, :mmask_t
66
+ typedef :pointer, :mmask_t_p
67
+ typedef :pointer, :panel_p
68
+ typedef :pointer, :screen_p
69
+ typedef :pointer, :short_p
70
+ typedef :ushort, :wchar_t
71
+ typedef :pointer, :wchar_t_p
72
+ typedef :pointer, :window_p
73
+ typedef :pointer, :wint_t_p
74
+
75
+ # add some functions for handling widechars
76
+ FUNCTIONS = [
77
+ [:mbstowcs, [:pointer, :string, :size_t], :int],
78
+ [:setlocale, [:int, :string], :string],
79
+ [:wcwidth, [:wchar_t], :int],
80
+ [:wcswidth, [:wchar_t_p, :size_t], :int],
81
+ ]
82
+ FUNCTION_SIGNATURES = Marshal.load(Marshal.dump(FUNCTIONS))
83
+
84
+ LIB_HANDLE = { }
85
+ LIB_HANDLE[:c] = ffi_lib(FFI::Library::LIBC) # for wcwidth, wcswidth
86
+ FUNCTIONS.each do |function|
87
+ attach_function *function
88
+ end
89
+
90
+ #if RUBY_VERSION < '1.9.0'
91
+ lc_all = 0
92
+ setlocale(lc_all, "")
93
+ #end
94
+ end
95
+ end
96
+
97
+ # JRuby 1.6 --1.9 fails when using wcswidth on multichar string
98
+ # but seems to work when you feed it character by character.
99
+ # Also, this seems to be the only safe version.
100
+
101
+ # This won't work in 1.8.7 (chars == bytes)
102
+ module WideCharsJR
103
+ def display_width2
104
+ width = 0
105
+ wchar_t_size = FFI::WideChars.find_type(:wchar_t).size
106
+ # we rely on Ruby to convert a char into a mbs
107
+ each_char do |ch|
108
+ buffer_size = (ch.size + 1) * wchar_t_size
109
+ # p [:size, size]
110
+ buffer = FFI::Buffer.new(FFI::WideChars.find_type(:wchar_t), buffer_size)
111
+ rv = FFI::WideChars.mbstowcs(buffer, ch, ch.size)
112
+ # p [:mbstowcs, rv]
113
+ if rv == -1
114
+ raise ArgumentError, "Invalid multibyte sequence"
115
+ else
116
+ rv = FFI::WideChars.wcswidth(buffer, rv)
117
+ # p [:wcswidth, rv]
118
+ if rv == -1
119
+ raise ArgumentError, "Wide character string contains non-printable characters"
120
+ else
121
+ width += rv
122
+ end
123
+ end
124
+ end
125
+ width
126
+ end
127
+ end
128
+
129
+ class String
130
+ def display_width
131
+ WideChars.display_width(self)
132
+ end
133
+ def display_slice(*args, &b)
134
+ WideChars.display_slice(self, *args, &b)
135
+ end
136
+ end
137
+
@@ -1,23 +1,20 @@
1
1
  module FFI
2
2
  module NCurses
3
3
  module WinStruct
4
- NCURSES_SIZE_T = :short
5
- NCURSES_ATTR_T = :int
4
+ NCURSES_SIZE_T = :short
5
+ NCURSES_ATTR_T = :int
6
6
  NCURSES_COLOR_T = :short
7
- NCURSES_CH_T = :short
7
+ NCURSES_CH_T = :short
8
+ BOOLEAN = :uchar # sizeof(bool) == 1
9
+ WCHAR_T = :ushort
10
+ CHTYPE = :ulong
11
+ CCHARW_MAX = 5
8
12
 
9
- CHTYPE = :ulong
10
- CCHARW_MAX = 5
11
-
12
- BOOLEAN = :uchar # sizeof(bool) == 1
13
-
14
- WCHAR_T = :ushort
15
-
16
- # class CCharT < FFI::Struct
17
- # layout \
18
- # :attr, NCURSES_ATTR_T,
19
- # :chars, [WCHAR_T, CCHARW_MAX]
20
- # end
13
+ class CCharT < FFI::Struct
14
+ layout \
15
+ :attr, NCURSES_ATTR_T,
16
+ :chars, [WCHAR_T, CCHARW_MAX]
17
+ end
21
18
 
22
19
  class PDat < FFI::Struct
23
20
  layout \
@@ -57,57 +54,82 @@ module FFI
57
54
  :_regtop, NCURSES_SIZE_T,
58
55
  :_regbottom, NCURSES_SIZE_T,
59
56
 
60
- # why x,y when everything else is y,x?
57
+ # aside: why x,y when everything else is y,x?
61
58
  :_parx, :int,
62
59
  :_pary, :int,
63
- :_parent, :pointer # WINDOW
60
+ :_parent, :pointer, # WINDOW
64
61
 
65
- # don't know how to include nested Struct yet
66
- # :_pad, PDat,
67
-
68
- # :_yoffset, NCURSES_SIZE_T
69
- # :_bkgrnd, CCharT
62
+ # nested struct (for Pads)
63
+ :_pad, PDat, # NB. Rubinius doesn't like this (but Rubinius FFI is very far behind...)
64
+ #:_pad, :pointer,
65
+ :_yoffset, NCURSES_SIZE_T,
66
+ :_bkgrnd, :pointer #CCharT
70
67
  end
71
68
 
72
69
  def _win(win, member)
70
+ # TODO: raise exception if win.nil?
73
71
  win && WinSt.new(win)[member]
74
72
  end
75
73
  private :_win
76
-
74
+
77
75
  # extensions - not in X/Open
76
+
77
+ # consider all data in the window invalid?
78
78
  def is_cleared(win)
79
- _win(win, :_clear)
79
+ _win(win, :_clear) != 0
80
80
  end
81
+
82
+ # OK to use insert/delete char?
81
83
  def is_idcok(win)
82
- _win(win, :_idcok)
84
+ _win(win, :_idcok) != 0
83
85
  end
86
+
87
+ # OK to use insert/delete line?
84
88
  def is_idlok(win)
85
- _win(:win, :_idlok)
89
+ _win(:win, :_idlok) != 0
86
90
  end
91
+
92
+ # window in immed mode? (not yet used)
87
93
  def is_immedok(win)
88
- _win(win, :_immed)
94
+ _win(win, :_immed) != 0
89
95
  end
96
+
97
+ # process function keys into KEY_ symbols?
90
98
  def is_keypad(win)
91
- _win(win, :_use_keypad)
99
+ _win(win, :_use_keypad) != 0
92
100
  end
101
+
102
+ # OK to not reset cursor on exit?
93
103
  def is_leaveok(win)
94
- _win(win, :_leaveok) || ERR
104
+ # I've changed this to return true or false - ncurses returns ERR if not true
105
+ # (_win(win, :_leaveok) != 0) || ERR # why ERR - why not false?
106
+ _win(win, :_leaveok) != 0
95
107
  end
108
+
109
+ # 0 = nodelay, <0 = blocking, >0 = delay
96
110
  def is_nodelay(win)
97
- _win(win, :_delay) == 0 ? FFI::NCurses::TRUE : FFI::NCurses::FALSE
111
+ _win(win, :_delay) == 0
98
112
  end
113
+
114
+ # no time out on function-key entry?
99
115
  def is_notimeout(win)
100
- _win(win, :_notimeout)
116
+ _win(win, :_notimeout) != 0
101
117
  end
118
+
119
+ # OK to scroll this window?
102
120
  def is_scrollok(win)
103
- _win(win, :_scroll)
121
+ _win(win, :_scroll) != 0
104
122
  end
123
+
124
+ # window in sync mode?
105
125
  def is_syncok(win)
106
- _win(win, :_sync)
126
+ _win(win, :_sync) != 0
107
127
  end
128
+
108
129
  def wgetparent(win)
109
130
  _win(win, :_parent)
110
131
  end
132
+
111
133
  def wgetscrreg(win, t, b)
112
134
  # ((win) ? (*(t) = (win)->_regtop, *(b) = (win)->_regbottom, OK) : ERR)
113
135
  # not entirely satisfactory - no error return