ffi-tk 2009.11.29

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 (189) hide show
  1. data/CHANGELOG +748 -0
  2. data/MANIFEST +188 -0
  3. data/README.md +85 -0
  4. data/Rakefile +47 -0
  5. data/TODO.md +62 -0
  6. data/bin/rwish +33 -0
  7. data/doc/MIT_LICENSE +18 -0
  8. data/doc/TCL_LICENSE +39 -0
  9. data/example/choose_color.rb +22 -0
  10. data/example/choose_directory.rb +22 -0
  11. data/example/dialog.rb +37 -0
  12. data/example/hello.rb +11 -0
  13. data/example/message_box.rb +26 -0
  14. data/example/option_menu.rb +17 -0
  15. data/example/popup.rb +24 -0
  16. data/example/set_palette.rb +32 -0
  17. data/example/text.rb +47 -0
  18. data/example/tile/kroc_demo_small.rb +123 -0
  19. data/example/tile/kroc_rb_demo.rb +135 -0
  20. data/example/tile/notebook.rb +48 -0
  21. data/example/tile/theme_hello.rb +38 -0
  22. data/example/tile/treeview.rb +71 -0
  23. data/example/various.rb +25 -0
  24. data/example/wait.rb +16 -0
  25. data/ffi-tk.gemspec +33 -0
  26. data/lib/ffi-tk.rb +76 -0
  27. data/lib/ffi-tk/command.rb +39 -0
  28. data/lib/ffi-tk/command/after.rb +36 -0
  29. data/lib/ffi-tk/command/bell.rb +34 -0
  30. data/lib/ffi-tk/command/bind.rb +11 -0
  31. data/lib/ffi-tk/command/bindtags.rb +69 -0
  32. data/lib/ffi-tk/command/cget.rb +92 -0
  33. data/lib/ffi-tk/command/choose_color.rb +29 -0
  34. data/lib/ffi-tk/command/choose_directory.rb +45 -0
  35. data/lib/ffi-tk/command/clipboard.rb +102 -0
  36. data/lib/ffi-tk/command/configure.rb +88 -0
  37. data/lib/ffi-tk/command/destroy.rb +12 -0
  38. data/lib/ffi-tk/command/dialog.rb +54 -0
  39. data/lib/ffi-tk/command/event.rb +79 -0
  40. data/lib/ffi-tk/command/focus.rb +70 -0
  41. data/lib/ffi-tk/command/font.rb +124 -0
  42. data/lib/ffi-tk/command/get_open_file.rb +85 -0
  43. data/lib/ffi-tk/command/get_save_file.rb +83 -0
  44. data/lib/ffi-tk/command/grab.rb +141 -0
  45. data/lib/ffi-tk/command/grid.rb +246 -0
  46. data/lib/ffi-tk/command/image.rb +79 -0
  47. data/lib/ffi-tk/command/lower.rb +23 -0
  48. data/lib/ffi-tk/command/message_box.rb +65 -0
  49. data/lib/ffi-tk/command/option_menu.rb +8 -0
  50. data/lib/ffi-tk/command/pack.rb +99 -0
  51. data/lib/ffi-tk/command/place.rb +91 -0
  52. data/lib/ffi-tk/command/popup.rb +14 -0
  53. data/lib/ffi-tk/command/raise.rb +25 -0
  54. data/lib/ffi-tk/command/scrollable.rb +151 -0
  55. data/lib/ffi-tk/command/selection.rb +132 -0
  56. data/lib/ffi-tk/command/set_palette.rb +9 -0
  57. data/lib/ffi-tk/command/tk_cmd.rb +155 -0
  58. data/lib/ffi-tk/command/vars.rb +82 -0
  59. data/lib/ffi-tk/command/wait.rb +39 -0
  60. data/lib/ffi-tk/command/winfo.rb +668 -0
  61. data/lib/ffi-tk/command/wm.rb +1025 -0
  62. data/lib/ffi-tk/core_extensions.rb +154 -0
  63. data/lib/ffi-tk/event/data.rb +60 -0
  64. data/lib/ffi-tk/event/handler.rb +44 -0
  65. data/lib/ffi-tk/ffi/tcl.rb +92 -0
  66. data/lib/ffi-tk/ffi/tcl/cmd_proc.rb +10 -0
  67. data/lib/ffi-tk/ffi/tcl/eval_result.rb +148 -0
  68. data/lib/ffi-tk/ffi/tcl/interp.rb +95 -0
  69. data/lib/ffi-tk/ffi/tcl/obj.rb +89 -0
  70. data/lib/ffi-tk/ffi/tcl/time.rb +36 -0
  71. data/lib/ffi-tk/ffi/tk.rb +35 -0
  72. data/lib/ffi-tk/geometry.rb +32 -0
  73. data/lib/ffi-tk/thread_sender.rb +26 -0
  74. data/lib/ffi-tk/tk.rb +222 -0
  75. data/lib/ffi-tk/variable.rb +46 -0
  76. data/lib/ffi-tk/widget.rb +68 -0
  77. data/lib/ffi-tk/widget/button.rb +41 -0
  78. data/lib/ffi-tk/widget/canvas.rb +806 -0
  79. data/lib/ffi-tk/widget/canvas/arc.rb +18 -0
  80. data/lib/ffi-tk/widget/canvas/bitmap.rb +13 -0
  81. data/lib/ffi-tk/widget/canvas/image.rb +10 -0
  82. data/lib/ffi-tk/widget/canvas/item.rb +170 -0
  83. data/lib/ffi-tk/widget/canvas/line.rb +16 -0
  84. data/lib/ffi-tk/widget/canvas/oval.rb +15 -0
  85. data/lib/ffi-tk/widget/canvas/polygon.rb +16 -0
  86. data/lib/ffi-tk/widget/canvas/rectangle.rb +15 -0
  87. data/lib/ffi-tk/widget/canvas/text.rb +15 -0
  88. data/lib/ffi-tk/widget/canvas/window.rb +11 -0
  89. data/lib/ffi-tk/widget/checkbutton.rb +63 -0
  90. data/lib/ffi-tk/widget/entry.rb +208 -0
  91. data/lib/ffi-tk/widget/frame.rb +12 -0
  92. data/lib/ffi-tk/widget/label.rb +26 -0
  93. data/lib/ffi-tk/widget/labelframe.rb +7 -0
  94. data/lib/ffi-tk/widget/listbox.rb +192 -0
  95. data/lib/ffi-tk/widget/menu.rb +318 -0
  96. data/lib/ffi-tk/widget/menubutton.rb +7 -0
  97. data/lib/ffi-tk/widget/message.rb +36 -0
  98. data/lib/ffi-tk/widget/panedwindow.rb +164 -0
  99. data/lib/ffi-tk/widget/radiobutton.rb +43 -0
  100. data/lib/ffi-tk/widget/root.rb +8 -0
  101. data/lib/ffi-tk/widget/scale.rb +44 -0
  102. data/lib/ffi-tk/widget/scrollbar.rb +114 -0
  103. data/lib/ffi-tk/widget/spinbox.rb +198 -0
  104. data/lib/ffi-tk/widget/text.rb +893 -0
  105. data/lib/ffi-tk/widget/text/peer.rb +10 -0
  106. data/lib/ffi-tk/widget/tile.rb +70 -0
  107. data/lib/ffi-tk/widget/tile/button.rb +8 -0
  108. data/lib/ffi-tk/widget/tile/checkbutton.rb +8 -0
  109. data/lib/ffi-tk/widget/tile/combobox.rb +43 -0
  110. data/lib/ffi-tk/widget/tile/entry.rb +8 -0
  111. data/lib/ffi-tk/widget/tile/frame.rb +13 -0
  112. data/lib/ffi-tk/widget/tile/label.rb +9 -0
  113. data/lib/ffi-tk/widget/tile/labelframe.rb +8 -0
  114. data/lib/ffi-tk/widget/tile/menubutton.rb +8 -0
  115. data/lib/ffi-tk/widget/tile/notebook.rb +93 -0
  116. data/lib/ffi-tk/widget/tile/panedwindow.rb +9 -0
  117. data/lib/ffi-tk/widget/tile/progressbar.rb +59 -0
  118. data/lib/ffi-tk/widget/tile/radiobutton.rb +8 -0
  119. data/lib/ffi-tk/widget/tile/scale.rb +8 -0
  120. data/lib/ffi-tk/widget/tile/scrollbar.rb +41 -0
  121. data/lib/ffi-tk/widget/tile/separator.rb +23 -0
  122. data/lib/ffi-tk/widget/tile/sizegrip.rb +24 -0
  123. data/lib/ffi-tk/widget/tile/style.rb +114 -0
  124. data/lib/ffi-tk/widget/tile/treeview.rb +414 -0
  125. data/lib/ffi-tk/widget/toplevel.rb +14 -0
  126. data/spec/ffi-tk/command/bindtags.rb +18 -0
  127. data/spec/ffi-tk/command/clipboard.rb +18 -0
  128. data/spec/ffi-tk/command/font.rb +67 -0
  129. data/spec/ffi-tk/command/grid.rb +6 -0
  130. data/spec/ffi-tk/command/image.rb +26 -0
  131. data/spec/ffi-tk/command/pack.rb +20 -0
  132. data/spec/ffi-tk/command/place.rb +20 -0
  133. data/spec/ffi-tk/command/selection.rb +13 -0
  134. data/spec/ffi-tk/command/vars.rb +32 -0
  135. data/spec/ffi-tk/command/winfo.rb +233 -0
  136. data/spec/ffi-tk/command/wm.rb +185 -0
  137. data/spec/ffi-tk/event.rb +95 -0
  138. data/spec/ffi-tk/tile/button.rb +51 -0
  139. data/spec/ffi-tk/tile/checkbutton.rb +13 -0
  140. data/spec/ffi-tk/tile/combobox.rb +65 -0
  141. data/spec/ffi-tk/tile/entry.rb +61 -0
  142. data/spec/ffi-tk/tile/frame.rb +65 -0
  143. data/spec/ffi-tk/tile/label.rb +17 -0
  144. data/spec/ffi-tk/tile/labelframe.rb +13 -0
  145. data/spec/ffi-tk/tile/menubutton.rb +13 -0
  146. data/spec/ffi-tk/tile/notebook.rb +103 -0
  147. data/spec/ffi-tk/tile/panedwindow.rb +13 -0
  148. data/spec/ffi-tk/tile/progressbar.rb +78 -0
  149. data/spec/ffi-tk/tile/radiobutton.rb +13 -0
  150. data/spec/ffi-tk/tile/scale.rb +13 -0
  151. data/spec/ffi-tk/tile/scrollbar.rb +43 -0
  152. data/spec/ffi-tk/tile/separator.rb +22 -0
  153. data/spec/ffi-tk/tile/sizegrip.rb +13 -0
  154. data/spec/ffi-tk/tile/style.rb +161 -0
  155. data/spec/ffi-tk/tile/treeview.rb +101 -0
  156. data/spec/ffi-tk/variable.rb +24 -0
  157. data/spec/ffi-tk/widget/button.rb +22 -0
  158. data/spec/ffi-tk/widget/canvas.rb +169 -0
  159. data/spec/ffi-tk/widget/checkbutton.rb +44 -0
  160. data/spec/ffi-tk/widget/entry.rb +155 -0
  161. data/spec/ffi-tk/widget/frame.rb +8 -0
  162. data/spec/ffi-tk/widget/label.rb +16 -0
  163. data/spec/ffi-tk/widget/labelframe.rb +12 -0
  164. data/spec/ffi-tk/widget/listbox.rb +19 -0
  165. data/spec/ffi-tk/widget/menu.rb +12 -0
  166. data/spec/ffi-tk/widget/menubutton.rb +12 -0
  167. data/spec/ffi-tk/widget/message.rb +12 -0
  168. data/spec/ffi-tk/widget/panedwindow.rb +12 -0
  169. data/spec/ffi-tk/widget/radiobutton.rb +12 -0
  170. data/spec/ffi-tk/widget/root.rb +9 -0
  171. data/spec/ffi-tk/widget/scale.rb +12 -0
  172. data/spec/ffi-tk/widget/scrollbar.rb +12 -0
  173. data/spec/ffi-tk/widget/spinbox.rb +12 -0
  174. data/spec/ffi-tk/widget/text.rb +246 -0
  175. data/spec/ffi-tk/widget/toplevel.rb +12 -0
  176. data/spec/helper.rb +3 -0
  177. data/tasks/authors.rake +21 -0
  178. data/tasks/bacon.rake +66 -0
  179. data/tasks/changelog.rake +18 -0
  180. data/tasks/gem.rake +22 -0
  181. data/tasks/gem_setup.rake +113 -0
  182. data/tasks/grancher.rake +12 -0
  183. data/tasks/manifest.rake +4 -0
  184. data/tasks/rcov.rake +17 -0
  185. data/tasks/release.rake +65 -0
  186. data/tasks/reversion.rake +8 -0
  187. data/tasks/setup.rake +12 -0
  188. data/tasks/ycov.rake +84 -0
  189. metadata +261 -0
@@ -0,0 +1,95 @@
1
+ module FFI
2
+ module Tcl
3
+ class Interp < PrettyStruct
4
+ layout(
5
+ :result, :string,
6
+ :freeProc, :pointer,
7
+ :errorLine, :int
8
+ )
9
+
10
+ def inspect
11
+ "Interp"
12
+ end
13
+
14
+ def guess_result
15
+ EvalResult.guess(self, Obj.new(Tcl.get_obj_result(self)))
16
+ end
17
+
18
+ def obj_result
19
+ Obj.new(Tcl.get_obj_result(self))
20
+ end
21
+
22
+ def obj_result=(ruby_obj)
23
+ obj =
24
+ case ruby_obj
25
+ when true
26
+ Tcl.new_boolean_obj(1)
27
+ when false
28
+ Tcl.new_boolean_obj(0)
29
+ when String
30
+ Tcl.new_string_obj(ruby_obj, ruby_obj.bytesize)
31
+ when Fixnum
32
+ Tcl.new_int_obj(ruby_obj)
33
+ else
34
+ if ruby_obj.respond_to?(:to_tcl)
35
+ ruby_obj.to_tcl
36
+ else
37
+ raise ArgumentError, "Don't know how to set %p automatically" % [ruby_obj]
38
+ end
39
+ end
40
+
41
+ Tcl.set_obj_result(self, obj)
42
+ end
43
+
44
+ def wait_for_event(seconds = 0.0)
45
+ if seconds && seconds > 0.0
46
+ seconds, microseconds = (seconds * 1000).divmod(1000)
47
+ time = TclTime.new(seconds, microseconds)
48
+ else
49
+ time = nil
50
+ end
51
+
52
+ Tcl.wait_for_event(time)
53
+ end
54
+
55
+ def do_one_event(flag = 0)
56
+ Tcl.do_one_event(flag)
57
+ end
58
+
59
+ def do_events_until(flag = 0)
60
+ begin
61
+ wait_for_event(0.1)
62
+ Tcl.do_one_event(flag)
63
+ end until yield
64
+ end
65
+
66
+ def do_events_while(flag = 0)
67
+ begin
68
+ wait_for_event(0.1)
69
+ Tcl.do_one_event(flag)
70
+ end while yield
71
+ end
72
+
73
+ def eval(string)
74
+ if $DEBUG
75
+ if string =~ /\n/
76
+ puts "eval: %p" % [string]
77
+ else
78
+ puts "eval: %s" % [string]
79
+ end
80
+ end
81
+
82
+ code = Tcl.eval_ex(self, string, string.bytesize, 0x40000)
83
+ return true if code == 0
84
+
85
+ message = guess_result.to_s
86
+
87
+ if message.empty?
88
+ raise 'Failure during eval of: %p' % [string]
89
+ else
90
+ raise '%s during eval of: %p' % [message, string]
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,89 @@
1
+ module FFI
2
+ module Tcl
3
+ # Nicer introspection and some accessors.
4
+ class PrettyStruct < FFI::Struct
5
+ ACCESSOR_CODE = <<-CODE
6
+ def {name}; self[{sym}]; end
7
+ def {name}=(value) self[{sym}] = value; end
8
+ CODE
9
+
10
+ def self.layout(*kvs)
11
+ kvs.each_slice(2) do |key, value|
12
+ eval ACCESSOR_CODE.gsub(/\{(.*?)\}/, '{name}' => key, '{sym}' => ":#{key}")
13
+ end
14
+
15
+ super
16
+ end
17
+
18
+ def inspect
19
+ kvs = members.zip(values)
20
+ kvs.map!{|key, value| "%s=%s" % [key, value.inspect] }
21
+ "<%s %s>" % [self.class, kvs.join(' ')]
22
+ end
23
+ end
24
+
25
+ # The following structure represents a type of object, which is a particular
26
+ # internal representation for an object plus a set of functions that provide
27
+ # standard operations on objects of that type.
28
+ class ObjType < PrettyStruct
29
+ layout(
30
+ :name, :string,
31
+ :free_internal_rep_proc, :pointer,
32
+ :dup_internal_rep_proc, :pointer,
33
+ :update_string_proc, :pointer,
34
+ :set_from_any_proc, :pointer
35
+ )
36
+
37
+ def to_i
38
+ pointer.get_pointer(0).to_i
39
+ end
40
+
41
+ def inspect
42
+ "#<ObjType name=%p>" % [self[:name]]
43
+ end
44
+ end
45
+
46
+ class Obj < PrettyStruct
47
+ class InternalRep < Union
48
+ class TwoPtrValue < PrettyStruct
49
+ layout(
50
+ :ptr1, :pointer,
51
+ :ptr2, :pointer
52
+ )
53
+ end
54
+
55
+ class PtrAndLongRep < PrettyStruct
56
+ layout(
57
+ :ptr, :pointer,
58
+ :value, :ulong
59
+ )
60
+ end
61
+
62
+ layout(
63
+ :longValue, :long,
64
+ :doubleValue, :double,
65
+ :otherValuePtr, :pointer,
66
+ :wideValue, :int64,
67
+ :twoPtrValue, TwoPtrValue,
68
+ :ptrAndLongRep, PtrAndLongRep
69
+ )
70
+ end
71
+
72
+ layout(
73
+ :refCount, :int,
74
+ :bytes, :string,
75
+ :length, :int,
76
+ :type, ObjType,
77
+ :internalRep, InternalRep
78
+ )
79
+
80
+ def pretty_type
81
+ EvalResult::TYPES[type.to_i]
82
+ end
83
+
84
+ def inspect
85
+ "#<Obj bytes=%p type=%p>" % [self[:bytes], pretty_type]
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,36 @@
1
+ module FFI
2
+ module Tcl
3
+ class TclTime < PrettyStruct
4
+ layout(
5
+ :sec, :long, # Seconds
6
+ :usec, :long # Microseconds
7
+ )
8
+
9
+ def initialize(seconds = nil, microseconds = nil)
10
+ super()
11
+ self[:sec] = seconds.to_i if seconds
12
+ self[:usec] = microseconds.to_i if microseconds
13
+ end
14
+
15
+ def seconds
16
+ self[:sec]
17
+ end
18
+ alias sec seconds
19
+
20
+ def microseconds
21
+ self[:usec]
22
+ end
23
+ alias usec microseconds
24
+
25
+ def seconds=(seconds)
26
+ self[:sec] = seconds
27
+ end
28
+ alias sec= seconds=
29
+
30
+ def microseconds=(microseconds)
31
+ self[:usec] = microseconds
32
+ end
33
+ alias usec= microseconds=
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,35 @@
1
+ module FFI
2
+ module Tk
3
+ extend FFI::Library
4
+ ffi_lib 'libtk8.5.so', 'libtk.so', *::Tk::LIBPATH[:tk]
5
+
6
+ attach_function :Tk_Init, [:pointer], :int
7
+ attach_function :Tk_MainLoop, [], :void
8
+
9
+ module_function
10
+
11
+ def mainloop
12
+ if ::Tk::RUN_EVENTLOOP_ON_MAIN_THREAD
13
+ Tk_MainLoop()
14
+ else
15
+ Tcl.thread_sender.thread_send{ Tk_MainLoop() }
16
+ end
17
+ end
18
+
19
+ def init(interp)
20
+ if ::Tk::RUN_EVENTLOOP_ON_MAIN_THREAD
21
+ if Tk_Init(interp) == 1
22
+ message = Tcl.Tcl_GetStringResult(interp)
23
+ raise RuntimeError, message
24
+ end
25
+ else
26
+ Tcl.thread_sender.thread_send do
27
+ if Tk_Init(interp) == 1
28
+ message = Tcl.Tcl_GetStringResult(interp)
29
+ raise RuntimeError, message
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,32 @@
1
+ module Tk
2
+ class TkGeometry < Struct.new(:original, :width, :height, :x, :y)
3
+ def initialize(tcl_string)
4
+ case tcl_string.to_s
5
+ when /^\=?(?<width>\d+)x(?<height>\d+)(?<x>[+-]\d+)(?<y>[+-]\d+)$/
6
+ md = $~
7
+ self.width, self.height, self.x, self.y =
8
+ md[:width].to_i, md[:height].to_i, md[:x].to_i, md[:y].to_i
9
+ when /^\=?(?<width>\d+)x(?<height>\d+)$/
10
+ md = $~
11
+ self.width, self.height = md[:width].to_i, md[:height].to_i
12
+ when /^\=?(?<x>[+-]\d+)(?<y>[+-]\d+)$/
13
+ md = $~
14
+ self.x, self.y = md[:x].to_i, md[:y].to_i
15
+ else
16
+ raise "Invalid geometry: %p" % [tcl_string]
17
+ end
18
+ end
19
+
20
+ def to_tcl
21
+ if width && height && x && y
22
+ "=%dx%d%+d%+d" % [width, height, x, y]
23
+ elsif width && height
24
+ "=%dx%d%" % [width, height]
25
+ elsif x && y
26
+ "=+d%+d" % [x, y]
27
+ else
28
+ raise "Incomplete geometry: %p" % [self]
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,26 @@
1
+ class ThreadSender
2
+ def initialize
3
+ @queue = Queue.new
4
+ @thread = Thread.new{ reaper }
5
+ end
6
+
7
+ def reaper
8
+ loop do
9
+ block, response_queue = *@queue.pop
10
+ response_queue.push(block.call)
11
+ end
12
+ end
13
+
14
+ # If callbacks are invoked within a thread_send, we process them inside the
15
+ # same thread.
16
+ # Calling pop on the queue would cause deadlocks.
17
+ def thread_send
18
+ if @thread == Thread.current
19
+ yield
20
+ else
21
+ response_queue = Queue.new
22
+ @queue.push([Proc.new, response_queue])
23
+ response_queue.pop
24
+ end
25
+ end
26
+ end
data/lib/ffi-tk/tk.rb ADDED
@@ -0,0 +1,222 @@
1
+ class Object
2
+ undef :type if respond_to?(:type)
3
+ end
4
+
5
+ module Tk
6
+ @register = Hash.new(0)
7
+ @widgets = {}
8
+ @callbacks = {}
9
+ @mutex = Mutex.new
10
+
11
+ class << self
12
+ attr_reader :callbacks, :widgets
13
+ end
14
+
15
+ module_function
16
+
17
+ unless const_defined?(:RUN_EVENTLOOP_ON_MAIN_THREAD)
18
+ # In some cases Tk has trouble running, this seems to happen on windows and
19
+ # OSX/TkAqua mostly.
20
+ # In these cases please use:
21
+ # module Tk; RUN_EVENTLOOP_ON_MAIN_THREAD = true; end
22
+ # before you require 'tk'
23
+ RUN_EVENTLOOP_ON_MAIN_THREAD = false
24
+ end
25
+
26
+ def init
27
+ if RUN_EVENTLOOP_ON_MAIN_THREAD
28
+ @interp = FFI::Tcl.setup_eventloop_on_main_thread
29
+ else
30
+ @interp = FFI::Tcl.setup_eventloop_on_new_thread
31
+ end
32
+
33
+ FFI::Tcl.init(@interp)
34
+ FFI::Tcl::EvalResult.reset_types(@interp)
35
+ FFI::Tk.init(@interp)
36
+
37
+ @root = Root.new
38
+
39
+ @interp.eval('namespace eval RubyFFI {}')
40
+
41
+ FFI::Tcl.create_obj_command(@interp, 'RubyFFI::callback', TCL_CALLBACK, 0, TCL_DELETE)
42
+ FFI::Tcl.create_obj_command(@interp, 'RubyFFI::event', TCL_EVENT, 0, TCL_DELETE)
43
+
44
+ module_eval('class << Tk; attr_reader :interp, :root; end')
45
+
46
+ return @interp
47
+ end
48
+
49
+ # A little something so people don't have to call Tk.init
50
+ def interp
51
+ Tk.init
52
+ @interp
53
+ end
54
+
55
+ # A little something so people don't have to call Tk.init
56
+ def root
57
+ Tk.init
58
+ @root
59
+ end
60
+
61
+ def mainloop
62
+ @running = true
63
+
64
+ while @running && interp.wait_for_event(0.1)
65
+ interp.do_one_event(0)
66
+ end
67
+ end
68
+
69
+ def stop
70
+ @running = false
71
+ end
72
+
73
+ def eval(string)
74
+ interp.eval(string)
75
+ end
76
+
77
+ def execute_only(*args)
78
+ interp.eval(convert_arguments(*args))
79
+ end
80
+
81
+ def execute(*args)
82
+ interp.eval(convert_arguments(*args))
83
+ result
84
+ end
85
+
86
+ def result
87
+ interp.guess_result
88
+ end
89
+
90
+ def exit
91
+ execute('exit')
92
+ end
93
+
94
+ def callback_break
95
+ throw :callback_break
96
+ end
97
+
98
+ def callback_continue
99
+ throw :callback_continue
100
+ end
101
+
102
+ # without our callbacks, nothing goes anymore, abort mission
103
+ def tcl_delete(client_data)
104
+ raise RuntimeError, "tcl function is going to be removed"
105
+ end
106
+ TCL_DELETE = method(:tcl_delete)
107
+
108
+ # TODO: support for break and continue return status (by catch/throw)
109
+ # 1 means true, 0 means false.
110
+ def tcl_callback(client_data, interp, objc, objv)
111
+ cmd, id, *args = tcl_cmd_args(interp, objc, objv)
112
+ id = id.first if id.is_a?(Array)
113
+
114
+ catch :callback_break do
115
+ catch :callback_continue do
116
+ result = handle_callback(id, *args)
117
+ FFI::Tcl::Interp.new(interp).obj_result = result
118
+
119
+ return OK
120
+ end
121
+
122
+ return CONTINUE
123
+ end
124
+
125
+ return BREAK
126
+ rescue => ex
127
+ FFI::Tcl::Interp.new(interp).obj_result = ex.message
128
+ return ERROR
129
+ end
130
+ TCL_CALLBACK = method(:tcl_callback)
131
+
132
+ # TODO: support for break and continue return status (by catch/throw)
133
+ def tcl_event(client_data, interp, objc, objv)
134
+ cmd, id, sequence, *args = tcl_cmd_args(interp, objc, objv)
135
+
136
+ catch :callback_break do
137
+ catch :callback_continue do
138
+ Event::Data.new(id.to_i, sequence.to_s, *args).call
139
+
140
+ return OK
141
+ end
142
+
143
+ return CONTINUE
144
+ end
145
+
146
+ return BREAK
147
+ rescue => ex
148
+ FFI::Tcl::Interp.new(interp).obj_result = ex.message
149
+ return ERROR
150
+ end
151
+ TCL_EVENT = method(:tcl_event)
152
+
153
+ def tcl_cmd_args(interp, objc, objv)
154
+ length = FFI::MemoryPointer.new(0)
155
+ array = objv.read_array_of_pointer(objc)
156
+ array.map{|e|
157
+ obj = FFI::Tcl::EvalResult.guess(interp, e)
158
+ case obj
159
+ when Fixnum, Float
160
+ obj
161
+ else
162
+ obj.respond_to?(:dup) ? obj.dup : obj
163
+ end
164
+ }
165
+ end
166
+
167
+ def handle_callback(id, *args)
168
+ callback = @callbacks.fetch(id.to_i)
169
+ callback.call(*args)
170
+ end
171
+
172
+ def register_object(parent, object)
173
+ parent_name = parent.respond_to?(:tk_pathname) ? parent.tk_pathname : parent
174
+ cmd = object.class.tk_command
175
+
176
+ id = "#{cmd}#{uuid(cmd)}"
177
+ pathname = [parent_name, id].join('.').squeeze('.')
178
+ @widgets[pathname] = object
179
+
180
+ return pathname
181
+ end
182
+
183
+ def unregister_object(object)
184
+ @widgets.delete_if{|path, obj| obj == object }
185
+ end
186
+
187
+ def unregister_objects(*objects)
188
+ @widgets.delete_if{|path, obj| objects.include?(obj) }
189
+ end
190
+
191
+ def register_proc(proc, argument_string = '')
192
+ id = uuid(:proc){|uuid| @callbacks[uuid] = proc }
193
+ return id, %(RubyFFI::callback #{id} #{argument_string})
194
+ end
195
+
196
+ def unregister_proc(id)
197
+ @callbacks.delete(id)
198
+ end
199
+
200
+ def uuid(name)
201
+ @mutex.synchronize do
202
+ id = @register[name]
203
+ @register[name] += 1
204
+ yield id if block_given?
205
+ id
206
+ end
207
+ end
208
+
209
+ def boolean(obj)
210
+ boolean_pointer = FFI::MemoryPointer.new(:int)
211
+ FFI::Tcl.get_boolean(interp, obj.to_s, boolean_pointer)
212
+ boolean_pointer.get_int(0) == 1
213
+ end
214
+
215
+ def convert_arguments(*args)
216
+ args.map(&:to_tcl).compact.join(' ')
217
+ end
218
+
219
+ def pathname_to_widget(pathname)
220
+ @widgets[pathname]
221
+ end
222
+ end