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.
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