ffi-tk 2009.11.29
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +748 -0
- data/MANIFEST +188 -0
- data/README.md +85 -0
- data/Rakefile +47 -0
- data/TODO.md +62 -0
- data/bin/rwish +33 -0
- data/doc/MIT_LICENSE +18 -0
- data/doc/TCL_LICENSE +39 -0
- data/example/choose_color.rb +22 -0
- data/example/choose_directory.rb +22 -0
- data/example/dialog.rb +37 -0
- data/example/hello.rb +11 -0
- data/example/message_box.rb +26 -0
- data/example/option_menu.rb +17 -0
- data/example/popup.rb +24 -0
- data/example/set_palette.rb +32 -0
- data/example/text.rb +47 -0
- data/example/tile/kroc_demo_small.rb +123 -0
- data/example/tile/kroc_rb_demo.rb +135 -0
- data/example/tile/notebook.rb +48 -0
- data/example/tile/theme_hello.rb +38 -0
- data/example/tile/treeview.rb +71 -0
- data/example/various.rb +25 -0
- data/example/wait.rb +16 -0
- data/ffi-tk.gemspec +33 -0
- data/lib/ffi-tk.rb +76 -0
- data/lib/ffi-tk/command.rb +39 -0
- data/lib/ffi-tk/command/after.rb +36 -0
- data/lib/ffi-tk/command/bell.rb +34 -0
- data/lib/ffi-tk/command/bind.rb +11 -0
- data/lib/ffi-tk/command/bindtags.rb +69 -0
- data/lib/ffi-tk/command/cget.rb +92 -0
- data/lib/ffi-tk/command/choose_color.rb +29 -0
- data/lib/ffi-tk/command/choose_directory.rb +45 -0
- data/lib/ffi-tk/command/clipboard.rb +102 -0
- data/lib/ffi-tk/command/configure.rb +88 -0
- data/lib/ffi-tk/command/destroy.rb +12 -0
- data/lib/ffi-tk/command/dialog.rb +54 -0
- data/lib/ffi-tk/command/event.rb +79 -0
- data/lib/ffi-tk/command/focus.rb +70 -0
- data/lib/ffi-tk/command/font.rb +124 -0
- data/lib/ffi-tk/command/get_open_file.rb +85 -0
- data/lib/ffi-tk/command/get_save_file.rb +83 -0
- data/lib/ffi-tk/command/grab.rb +141 -0
- data/lib/ffi-tk/command/grid.rb +246 -0
- data/lib/ffi-tk/command/image.rb +79 -0
- data/lib/ffi-tk/command/lower.rb +23 -0
- data/lib/ffi-tk/command/message_box.rb +65 -0
- data/lib/ffi-tk/command/option_menu.rb +8 -0
- data/lib/ffi-tk/command/pack.rb +99 -0
- data/lib/ffi-tk/command/place.rb +91 -0
- data/lib/ffi-tk/command/popup.rb +14 -0
- data/lib/ffi-tk/command/raise.rb +25 -0
- data/lib/ffi-tk/command/scrollable.rb +151 -0
- data/lib/ffi-tk/command/selection.rb +132 -0
- data/lib/ffi-tk/command/set_palette.rb +9 -0
- data/lib/ffi-tk/command/tk_cmd.rb +155 -0
- data/lib/ffi-tk/command/vars.rb +82 -0
- data/lib/ffi-tk/command/wait.rb +39 -0
- data/lib/ffi-tk/command/winfo.rb +668 -0
- data/lib/ffi-tk/command/wm.rb +1025 -0
- data/lib/ffi-tk/core_extensions.rb +154 -0
- data/lib/ffi-tk/event/data.rb +60 -0
- data/lib/ffi-tk/event/handler.rb +44 -0
- data/lib/ffi-tk/ffi/tcl.rb +92 -0
- data/lib/ffi-tk/ffi/tcl/cmd_proc.rb +10 -0
- data/lib/ffi-tk/ffi/tcl/eval_result.rb +148 -0
- data/lib/ffi-tk/ffi/tcl/interp.rb +95 -0
- data/lib/ffi-tk/ffi/tcl/obj.rb +89 -0
- data/lib/ffi-tk/ffi/tcl/time.rb +36 -0
- data/lib/ffi-tk/ffi/tk.rb +35 -0
- data/lib/ffi-tk/geometry.rb +32 -0
- data/lib/ffi-tk/thread_sender.rb +26 -0
- data/lib/ffi-tk/tk.rb +222 -0
- data/lib/ffi-tk/variable.rb +46 -0
- data/lib/ffi-tk/widget.rb +68 -0
- data/lib/ffi-tk/widget/button.rb +41 -0
- data/lib/ffi-tk/widget/canvas.rb +806 -0
- data/lib/ffi-tk/widget/canvas/arc.rb +18 -0
- data/lib/ffi-tk/widget/canvas/bitmap.rb +13 -0
- data/lib/ffi-tk/widget/canvas/image.rb +10 -0
- data/lib/ffi-tk/widget/canvas/item.rb +170 -0
- data/lib/ffi-tk/widget/canvas/line.rb +16 -0
- data/lib/ffi-tk/widget/canvas/oval.rb +15 -0
- data/lib/ffi-tk/widget/canvas/polygon.rb +16 -0
- data/lib/ffi-tk/widget/canvas/rectangle.rb +15 -0
- data/lib/ffi-tk/widget/canvas/text.rb +15 -0
- data/lib/ffi-tk/widget/canvas/window.rb +11 -0
- data/lib/ffi-tk/widget/checkbutton.rb +63 -0
- data/lib/ffi-tk/widget/entry.rb +208 -0
- data/lib/ffi-tk/widget/frame.rb +12 -0
- data/lib/ffi-tk/widget/label.rb +26 -0
- data/lib/ffi-tk/widget/labelframe.rb +7 -0
- data/lib/ffi-tk/widget/listbox.rb +192 -0
- data/lib/ffi-tk/widget/menu.rb +318 -0
- data/lib/ffi-tk/widget/menubutton.rb +7 -0
- data/lib/ffi-tk/widget/message.rb +36 -0
- data/lib/ffi-tk/widget/panedwindow.rb +164 -0
- data/lib/ffi-tk/widget/radiobutton.rb +43 -0
- data/lib/ffi-tk/widget/root.rb +8 -0
- data/lib/ffi-tk/widget/scale.rb +44 -0
- data/lib/ffi-tk/widget/scrollbar.rb +114 -0
- data/lib/ffi-tk/widget/spinbox.rb +198 -0
- data/lib/ffi-tk/widget/text.rb +893 -0
- data/lib/ffi-tk/widget/text/peer.rb +10 -0
- data/lib/ffi-tk/widget/tile.rb +70 -0
- data/lib/ffi-tk/widget/tile/button.rb +8 -0
- data/lib/ffi-tk/widget/tile/checkbutton.rb +8 -0
- data/lib/ffi-tk/widget/tile/combobox.rb +43 -0
- data/lib/ffi-tk/widget/tile/entry.rb +8 -0
- data/lib/ffi-tk/widget/tile/frame.rb +13 -0
- data/lib/ffi-tk/widget/tile/label.rb +9 -0
- data/lib/ffi-tk/widget/tile/labelframe.rb +8 -0
- data/lib/ffi-tk/widget/tile/menubutton.rb +8 -0
- data/lib/ffi-tk/widget/tile/notebook.rb +93 -0
- data/lib/ffi-tk/widget/tile/panedwindow.rb +9 -0
- data/lib/ffi-tk/widget/tile/progressbar.rb +59 -0
- data/lib/ffi-tk/widget/tile/radiobutton.rb +8 -0
- data/lib/ffi-tk/widget/tile/scale.rb +8 -0
- data/lib/ffi-tk/widget/tile/scrollbar.rb +41 -0
- data/lib/ffi-tk/widget/tile/separator.rb +23 -0
- data/lib/ffi-tk/widget/tile/sizegrip.rb +24 -0
- data/lib/ffi-tk/widget/tile/style.rb +114 -0
- data/lib/ffi-tk/widget/tile/treeview.rb +414 -0
- data/lib/ffi-tk/widget/toplevel.rb +14 -0
- data/spec/ffi-tk/command/bindtags.rb +18 -0
- data/spec/ffi-tk/command/clipboard.rb +18 -0
- data/spec/ffi-tk/command/font.rb +67 -0
- data/spec/ffi-tk/command/grid.rb +6 -0
- data/spec/ffi-tk/command/image.rb +26 -0
- data/spec/ffi-tk/command/pack.rb +20 -0
- data/spec/ffi-tk/command/place.rb +20 -0
- data/spec/ffi-tk/command/selection.rb +13 -0
- data/spec/ffi-tk/command/vars.rb +32 -0
- data/spec/ffi-tk/command/winfo.rb +233 -0
- data/spec/ffi-tk/command/wm.rb +185 -0
- data/spec/ffi-tk/event.rb +95 -0
- data/spec/ffi-tk/tile/button.rb +51 -0
- data/spec/ffi-tk/tile/checkbutton.rb +13 -0
- data/spec/ffi-tk/tile/combobox.rb +65 -0
- data/spec/ffi-tk/tile/entry.rb +61 -0
- data/spec/ffi-tk/tile/frame.rb +65 -0
- data/spec/ffi-tk/tile/label.rb +17 -0
- data/spec/ffi-tk/tile/labelframe.rb +13 -0
- data/spec/ffi-tk/tile/menubutton.rb +13 -0
- data/spec/ffi-tk/tile/notebook.rb +103 -0
- data/spec/ffi-tk/tile/panedwindow.rb +13 -0
- data/spec/ffi-tk/tile/progressbar.rb +78 -0
- data/spec/ffi-tk/tile/radiobutton.rb +13 -0
- data/spec/ffi-tk/tile/scale.rb +13 -0
- data/spec/ffi-tk/tile/scrollbar.rb +43 -0
- data/spec/ffi-tk/tile/separator.rb +22 -0
- data/spec/ffi-tk/tile/sizegrip.rb +13 -0
- data/spec/ffi-tk/tile/style.rb +161 -0
- data/spec/ffi-tk/tile/treeview.rb +101 -0
- data/spec/ffi-tk/variable.rb +24 -0
- data/spec/ffi-tk/widget/button.rb +22 -0
- data/spec/ffi-tk/widget/canvas.rb +169 -0
- data/spec/ffi-tk/widget/checkbutton.rb +44 -0
- data/spec/ffi-tk/widget/entry.rb +155 -0
- data/spec/ffi-tk/widget/frame.rb +8 -0
- data/spec/ffi-tk/widget/label.rb +16 -0
- data/spec/ffi-tk/widget/labelframe.rb +12 -0
- data/spec/ffi-tk/widget/listbox.rb +19 -0
- data/spec/ffi-tk/widget/menu.rb +12 -0
- data/spec/ffi-tk/widget/menubutton.rb +12 -0
- data/spec/ffi-tk/widget/message.rb +12 -0
- data/spec/ffi-tk/widget/panedwindow.rb +12 -0
- data/spec/ffi-tk/widget/radiobutton.rb +12 -0
- data/spec/ffi-tk/widget/root.rb +9 -0
- data/spec/ffi-tk/widget/scale.rb +12 -0
- data/spec/ffi-tk/widget/scrollbar.rb +12 -0
- data/spec/ffi-tk/widget/spinbox.rb +12 -0
- data/spec/ffi-tk/widget/text.rb +246 -0
- data/spec/ffi-tk/widget/toplevel.rb +12 -0
- data/spec/helper.rb +3 -0
- data/tasks/authors.rake +21 -0
- data/tasks/bacon.rake +66 -0
- data/tasks/changelog.rake +18 -0
- data/tasks/gem.rake +22 -0
- data/tasks/gem_setup.rake +113 -0
- data/tasks/grancher.rake +12 -0
- data/tasks/manifest.rake +4 -0
- data/tasks/rcov.rake +17 -0
- data/tasks/release.rake +65 -0
- data/tasks/reversion.rake +8 -0
- data/tasks/setup.rake +12 -0
- data/tasks/ycov.rake +84 -0
- 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
|