ffi-tk 2009.11.29

Sign up to get free protection for your applications and to get access to all the features.
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