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