gir_ffi 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/DESIGN.rdoc +54 -0
- data/History.txt +3 -0
- data/README.rdoc +59 -0
- data/Rakefile +21 -0
- data/TODO.rdoc +40 -0
- data/examples/01_empty_window.rb +15 -0
- data/examples/02_hello_world.rb +30 -0
- data/examples/03_upgraded_hello_world.rb +45 -0
- data/examples/demo_ffi_inherited_layout.rb +21 -0
- data/examples/demo_ffi_nested_struct.rb +17 -0
- data/examples/demo_ffi_safe_inherited_layout.rb +43 -0
- data/examples/hard_coded.rb +144 -0
- data/lib/gir_ffi.rb +47 -0
- data/lib/gir_ffi/allocation_helper.rb +12 -0
- data/lib/gir_ffi/arg_helper.rb +77 -0
- data/lib/gir_ffi/base.rb +23 -0
- data/lib/gir_ffi/builder.rb +159 -0
- data/lib/gir_ffi/builder_helper.rb +32 -0
- data/lib/gir_ffi/class_base.rb +11 -0
- data/lib/gir_ffi/class_builder.rb +116 -0
- data/lib/gir_ffi/constructor_definition_builder.rb +20 -0
- data/lib/gir_ffi/function_definition_builder.rb +148 -0
- data/lib/gir_ffi/g_error.rb +8 -0
- data/lib/gir_ffi/g_type.rb +14 -0
- data/lib/gir_ffi/i_arg_info.rb +16 -0
- data/lib/gir_ffi/i_base_info.rb +45 -0
- data/lib/gir_ffi/i_callable_info.rb +18 -0
- data/lib/gir_ffi/i_callback_info.rb +7 -0
- data/lib/gir_ffi/i_constant_info.rb +6 -0
- data/lib/gir_ffi/i_enum_info.rb +13 -0
- data/lib/gir_ffi/i_field_info.rb +10 -0
- data/lib/gir_ffi/i_flags_info.rb +5 -0
- data/lib/gir_ffi/i_function_info.rb +16 -0
- data/lib/gir_ffi/i_interface_info.rb +7 -0
- data/lib/gir_ffi/i_object_info.rb +50 -0
- data/lib/gir_ffi/i_property_info.rb +7 -0
- data/lib/gir_ffi/i_registered_type_info.rb +8 -0
- data/lib/gir_ffi/i_repository.rb +108 -0
- data/lib/gir_ffi/i_signal_info.rb +7 -0
- data/lib/gir_ffi/i_struct_info.rb +22 -0
- data/lib/gir_ffi/i_type_info.rb +25 -0
- data/lib/gir_ffi/i_union_info.rb +7 -0
- data/lib/gir_ffi/i_value_info.rb +8 -0
- data/lib/gir_ffi/i_vfunc_info.rb +7 -0
- data/lib/gir_ffi/lib.rb +174 -0
- data/lib/gir_ffi/lib_c.rb +11 -0
- data/lib/gir_ffi/method_missing_definition_builder.rb +62 -0
- data/lib/gir_ffi/module_builder.rb +66 -0
- data/lib/gir_ffi/overrides/gtk.rb +12 -0
- data/lib/gir_ffi/version.rb +4 -0
- data/tasks/bones.rake +87 -0
- data/tasks/notes.rake +134 -0
- data/tasks/post_load.rake +25 -0
- data/tasks/setup.rb +138 -0
- data/tasks/test.rake +22 -0
- data/test/arg_helper_test.rb +112 -0
- data/test/builder_test.rb +328 -0
- data/test/constructor_definition_builder_test.rb +19 -0
- data/test/function_definition_builder_test.rb +60 -0
- data/test/g_type_test.rb +22 -0
- data/test/girffi_test.rb +11 -0
- data/test/gtk_overrides_test.rb +22 -0
- data/test/i_repository_test.rb +54 -0
- data/test/test_helper.rb +39 -0
- metadata +174 -0
data/DESIGN.rdoc
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
= Design of Gir-FFI
|
2
|
+
|
3
|
+
== Basic Idea
|
4
|
+
|
5
|
+
Gir-FFI uses FFI to read information from the GObject Introspection
|
6
|
+
Repository. Based on that it creates bindings for the information read.
|
7
|
+
|
8
|
+
== Class and method creation
|
9
|
+
|
10
|
+
GirFFI::Builder creates classes and modules at runtime and adds appropriate
|
11
|
+
method_missing methods to them to generate methods and perhaps other
|
12
|
+
classes when required.
|
13
|
+
|
14
|
+
The following options were discarded, at least for now.
|
15
|
+
|
16
|
+
* Create classes and all of their methods at runtime. This would be very
|
17
|
+
similar to the method chosen, but would concentrate all the overhead at
|
18
|
+
start-up, some of which would be used for creating methods that will
|
19
|
+
never get called.
|
20
|
+
|
21
|
+
* Allow offline creation of ruby source generated from the GIR. This is
|
22
|
+
still in interesting idea, but off-line source code generation is not
|
23
|
+
really the Ruby way.
|
24
|
+
|
25
|
+
== Method Naming
|
26
|
+
|
27
|
+
Probably, get_x/set_x pairs should become x and x= to be more Ruby-like.
|
28
|
+
This should be done either by defining them as such directly, or by
|
29
|
+
aliasing. Blindly going by the name leads to weird results thoough, like
|
30
|
+
having x, x= and x_full= as a set. Also, some get_ or set_ methods take
|
31
|
+
extra arguments. These probably shouldn't become accessors either.
|
32
|
+
|
33
|
+
Boolean-valued methods could get a ? at the end.
|
34
|
+
|
35
|
+
This requires a lot more thought. For now, the full method names as
|
36
|
+
defined in the GIR are used.
|
37
|
+
|
38
|
+
== Ruby-GNOME Compatibility
|
39
|
+
|
40
|
+
Full Ruby-GNOME compatibility cannot be achieved automatically, since its
|
41
|
+
object hierarchy differs from that of standard GObject: It puts Object in
|
42
|
+
the GLib namespace, and defines signal_connect and friends as methods of
|
43
|
+
GLib::Instantiable; In standard GObject they are functions.
|
44
|
+
|
45
|
+
Possibly, compatibility enhancing code can be added for these specific
|
46
|
+
exceptions.
|
47
|
+
|
48
|
+
== Bootstrapping Class Design
|
49
|
+
|
50
|
+
The interface to the GObject Introspection Repository itself is also
|
51
|
+
introspectable. The interface is specified in terms of structs and
|
52
|
+
functions rather than objects and methods. For now, the hard-coded Ruby
|
53
|
+
bindings for this don't follow the introspected specification: Gir-FFI
|
54
|
+
cannot bootstrap itself.
|
data/History.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
= GirFFI
|
2
|
+
|
3
|
+
by Matijs van Zuijlen
|
4
|
+
|
5
|
+
http://www.github.com/mvz/ruby-gir-ffi
|
6
|
+
|
7
|
+
== Description
|
8
|
+
|
9
|
+
Ruby-FFI-based binding of the GObject Introspection Repository
|
10
|
+
|
11
|
+
== Features/Problems
|
12
|
+
|
13
|
+
* Create bindings to GObject-based libraries at runtime
|
14
|
+
* Not done yet
|
15
|
+
|
16
|
+
== Synopsis
|
17
|
+
|
18
|
+
require 'gir_ffi'
|
19
|
+
|
20
|
+
GirFFI.setup :Gtk
|
21
|
+
Gtk.init
|
22
|
+
win = Gtk::Window.new :toplevel
|
23
|
+
|
24
|
+
== Requirements
|
25
|
+
|
26
|
+
* Ruby-FFI of course
|
27
|
+
* gobject-introspection installed with some introspection data
|
28
|
+
|
29
|
+
The current implementation needs the actual libraries to be available under
|
30
|
+
the name ending in just `.so`. On Debian and Ubuntu at least, this means
|
31
|
+
you have to install the -dev packages of any library you may want to
|
32
|
+
access.
|
33
|
+
|
34
|
+
On Ubuntu, the following set of packages should do the trick:
|
35
|
+
`libffi-ruby`, `libgirepository1.0-dev`, `libgtk2.0-dev`,
|
36
|
+
`libshoulda-ruby`, `gir1.0-everything-1.0`.
|
37
|
+
|
38
|
+
On Debian it's the same, but the `gir1.0-everything-1.0` package is only
|
39
|
+
available in experimental. You can take one of the patches attached to bug
|
40
|
+
550478 and recompile `gobject-introspection` to get the files GirFFI needs.
|
41
|
+
|
42
|
+
== Hacking
|
43
|
+
|
44
|
+
This is still very much a work in progress. You can start exploring by
|
45
|
+
running the example programs in the examples/ folder. Some illustrate what
|
46
|
+
works, some are a test bed for how things should work. Have a look at
|
47
|
+
+rake+ +-T+.
|
48
|
+
|
49
|
+
== Install
|
50
|
+
|
51
|
+
* sudo gem install gir_ffi
|
52
|
+
|
53
|
+
== License
|
54
|
+
|
55
|
+
Copyright (c) 2009--2010 Matijs van Zuijlen
|
56
|
+
|
57
|
+
GirFFI is free software, distributed under the terms of the GNU Lesser
|
58
|
+
General Public License, version 2.1 or later. See the file COPYING.LIB for
|
59
|
+
more information.
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Look in the tasks/setup.rb file for the various options that can be
|
2
|
+
# configured in this Rakefile. The .rake files in the tasks directory
|
3
|
+
# are where the options are used.
|
4
|
+
|
5
|
+
load 'tasks/setup.rb'
|
6
|
+
|
7
|
+
ensure_in_path 'lib'
|
8
|
+
require 'gir_ffi'
|
9
|
+
|
10
|
+
task :default => 'test:run'
|
11
|
+
|
12
|
+
PROJ.name = 'gir_ffi'
|
13
|
+
PROJ.authors = 'Matijs van Zuijlen'
|
14
|
+
PROJ.email = 'matijs@matijs.net'
|
15
|
+
PROJ.url = 'http://www.github.com/mvz/ruby-gir-ffi'
|
16
|
+
PROJ.version = GirFFI::VERSION
|
17
|
+
PROJ.readme_file = 'README.rdoc'
|
18
|
+
|
19
|
+
PROJ.exclude << ["^tmp/", "\\.swp$", "^\\.gitignore$", "^\\.autotest$"]
|
20
|
+
|
21
|
+
# EOF
|
data/TODO.rdoc
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
= TODO
|
2
|
+
|
3
|
+
== Use GIR to bootstrap the GIRepository namespace
|
4
|
+
|
5
|
+
Currently, all the classes used to read the GIR are hand-coded. It should
|
6
|
+
be possible to hand-code only part of it and use that to generate the rest.
|
7
|
+
This would also integrate that properly with the rest of the GObject type
|
8
|
+
system.
|
9
|
+
|
10
|
+
Update: This has been tried, but the problem is that the GIRepository
|
11
|
+
namespace is not object-oriented: The Info structs are not GObjects, and
|
12
|
+
the methods that act upon them are just functions in the GIRepository
|
13
|
+
namespace. Perhaps some custom method_missing can be implemented to handle
|
14
|
+
this, though.
|
15
|
+
|
16
|
+
== Handle passing of generic pointers
|
17
|
+
|
18
|
+
Many GObject methods take a pointer to 'user data'. This means we should be
|
19
|
+
able to pass any Ruby object. On the other hand, these cases cannot be
|
20
|
+
distinguished, based on the GIR data, from methods that take a pointer to
|
21
|
+
any GObject.
|
22
|
+
|
23
|
+
I'm currently leaning towards passing the object id as the value of the
|
24
|
+
'gpointer'. Special overrided will have to be used for the cases where the
|
25
|
+
'gpointer' actually needs to be a GObject. I consider it an omission in
|
26
|
+
GIRepository that these two cases are not distinguished.
|
27
|
+
|
28
|
+
== Compatibility with all implementations.
|
29
|
+
|
30
|
+
Currently, there are the following incompatibilities:
|
31
|
+
|
32
|
+
* The MRI implementation does not allow invoking layout more than once. In
|
33
|
+
particular, this means you cannot subclass a struct and change (augment)
|
34
|
+
the layout. This *not* a problem in JRuby.
|
35
|
+
|
36
|
+
* JRuby disables ObjectSpace by default, so using _id2ref is not ideal.
|
37
|
+
|
38
|
+
== See Also
|
39
|
+
|
40
|
+
rake notes
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#
|
2
|
+
# Based on the empty window Gtk+ tutorial example at
|
3
|
+
# http://library.gnome.org/devel/gtk-tutorial/2.90/c39.html
|
4
|
+
#
|
5
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
6
|
+
require 'gir_ffi'
|
7
|
+
|
8
|
+
GirFFI.setup :GObject
|
9
|
+
GirFFI.setup :Gtk
|
10
|
+
|
11
|
+
Gtk.init
|
12
|
+
win = Gtk::Window.new :toplevel
|
13
|
+
win.show
|
14
|
+
GObject.signal_connect_data win, "destroy", Proc.new { Gtk.main_quit }, nil, nil, 0
|
15
|
+
Gtk.main
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#
|
2
|
+
# Based on the 'Hello world' Gtk+ tutorial example at
|
3
|
+
# http://library.gnome.org/devel/gtk-tutorial/2.90/c39.html#SEC-HELLOWORLD
|
4
|
+
#
|
5
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
6
|
+
require 'gir_ffi'
|
7
|
+
|
8
|
+
GirFFI.setup :GObject
|
9
|
+
GirFFI.setup :Gtk
|
10
|
+
|
11
|
+
Gtk.init
|
12
|
+
|
13
|
+
win = Gtk::Window.new(:toplevel)
|
14
|
+
GObject.signal_connect_data win, "delete-event", FFI::Function.new(:bool, [:pointer, :pointer]) {
|
15
|
+
puts "delete event occured"
|
16
|
+
true
|
17
|
+
}, nil, nil, 0
|
18
|
+
|
19
|
+
GObject.signal_connect_data win, "destroy", Proc.new { Gtk.main_quit }, nil, nil, 0
|
20
|
+
win.set_border_width 10
|
21
|
+
|
22
|
+
but = Gtk::Button.new_with_label("Hello World")
|
23
|
+
GObject.signal_connect_data but, "clicked", Proc.new { win.destroy }, nil, nil, :swapped
|
24
|
+
|
25
|
+
win.add but
|
26
|
+
|
27
|
+
but.show
|
28
|
+
win.show
|
29
|
+
|
30
|
+
Gtk.main
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#
|
2
|
+
# Based on the 'Upgraded Hello world' Gtk+ tutorial example at
|
3
|
+
# http://library.gnome.org/devel/gtk-tutorial/2.90/x344.html
|
4
|
+
#
|
5
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
6
|
+
require 'gir_ffi'
|
7
|
+
|
8
|
+
GirFFI.setup :GObject
|
9
|
+
GirFFI.setup :Gtk
|
10
|
+
|
11
|
+
callback = FFI::Function.new :void, [:pointer, :pointer],
|
12
|
+
&GirFFI::ArgHelper.mapped_callback_args { |widget, data|
|
13
|
+
puts "Hello again - #{data} was pressed"
|
14
|
+
}
|
15
|
+
|
16
|
+
Gtk.init
|
17
|
+
|
18
|
+
win = Gtk::Window.new(:toplevel)
|
19
|
+
win.set_title "Hello Buttons!"
|
20
|
+
|
21
|
+
GObject.signal_connect_data win, "delete-event", FFI::Function.new(:bool, [:pointer, :pointer]) {
|
22
|
+
Gtk.main_quit
|
23
|
+
false
|
24
|
+
}, nil, nil, 0
|
25
|
+
|
26
|
+
win.set_border_width 10
|
27
|
+
|
28
|
+
box = Gtk::HBox.new(false, 0)
|
29
|
+
win.add box
|
30
|
+
|
31
|
+
button = Gtk::Button.new_with_label("Button 1")
|
32
|
+
GObject.signal_connect_data button, "clicked", callback, "button 1", nil, 0
|
33
|
+
box.pack_start button, true, true, 0
|
34
|
+
button.show
|
35
|
+
|
36
|
+
button = Gtk::Button.new_with_label("Button 2")
|
37
|
+
GObject.signal_connect_data button, "clicked", callback, "button 2", nil, 0
|
38
|
+
box.pack_start button, true, true, 0
|
39
|
+
button.show
|
40
|
+
|
41
|
+
box.show
|
42
|
+
win.show
|
43
|
+
|
44
|
+
Gtk.main
|
45
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Demonstration program for FFI functionality.
|
2
|
+
#
|
3
|
+
# Show what happens if we call layout again in a subclass. This works in
|
4
|
+
# JRuby, but not in MRI (gives warnings with ffi 0.6.3, is explicitely
|
5
|
+
# forbidden later).
|
6
|
+
#
|
7
|
+
require 'ffi'
|
8
|
+
|
9
|
+
class Foo < FFI::Struct
|
10
|
+
layout :a, :int, :b, :int
|
11
|
+
end
|
12
|
+
|
13
|
+
class Bar < Foo
|
14
|
+
layout :p, Foo, :c, :int
|
15
|
+
end
|
16
|
+
|
17
|
+
bar = Bar.new
|
18
|
+
foo = Foo.new(bar.to_ptr)
|
19
|
+
foo[:a] = 20
|
20
|
+
puts "bar[:p][:a] = #{bar[:p][:a]}"
|
21
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Demonstration program for FFI functionality.
|
2
|
+
#
|
3
|
+
# Basic demo of nested struct. Works in MRI, YARV, and JRuby. Does not work
|
4
|
+
# in Rubinius.
|
5
|
+
#
|
6
|
+
require 'ffi'
|
7
|
+
|
8
|
+
module LibTest
|
9
|
+
class Foo < FFI::Struct
|
10
|
+
layout :a, :int, :b, :int
|
11
|
+
end
|
12
|
+
|
13
|
+
class Bar < FFI::Struct
|
14
|
+
layout :f, Foo, :g, :int
|
15
|
+
end
|
16
|
+
end
|
17
|
+
puts LibTest::Bar.members.inspect
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# Demonstrate safe inheritance with layout.
|
2
|
+
#
|
3
|
+
# Uses nested Struct class to separate inheritance from FFI::Struct from
|
4
|
+
# main inheritance structure. Works with MRI and JRuby, without warnings.
|
5
|
+
|
6
|
+
require 'ffi'
|
7
|
+
require 'forwardable'
|
8
|
+
|
9
|
+
class Foo
|
10
|
+
extend Forwardable
|
11
|
+
def_delegators :@struct, :[], :to_ptr
|
12
|
+
|
13
|
+
class Struct < FFI::Struct
|
14
|
+
layout :a, :int, :b, :int
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(ptr=nil)
|
18
|
+
@struct = ptr.nil? ?
|
19
|
+
self.ffi_structure.new :
|
20
|
+
self.ffi_structure.new(ptr)
|
21
|
+
end
|
22
|
+
|
23
|
+
def ffi_structure
|
24
|
+
self.class.ffi_structure
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
def ffi_structure
|
29
|
+
self.const_get(:Struct)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Bar < Foo
|
35
|
+
class Struct < FFI::Struct
|
36
|
+
layout :p, Foo.ffi_structure, :c, :int
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
bar = Bar.new
|
41
|
+
bar[:p][:a] = 20
|
42
|
+
foo = Foo.new(bar.to_ptr)
|
43
|
+
puts foo[:a]
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# Hard-code FFI-based Gtk+ test program. Nothing is generated here.
|
2
|
+
require 'ffi'
|
3
|
+
module GObject
|
4
|
+
|
5
|
+
module Lib
|
6
|
+
extend FFI::Library
|
7
|
+
CALLBACKS = []
|
8
|
+
ffi_lib "gobject-2.0"
|
9
|
+
callback :GCallback, [], :void
|
10
|
+
enum :GConnectFlags, [:AFTER, (1<<0), :SWAPPED, (1<<1)]
|
11
|
+
|
12
|
+
attach_function :g_signal_connect_data, [:pointer, :string, :GCallback,
|
13
|
+
:pointer, :pointer, :GConnectFlags], :ulong
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.signal_connect_data gobject, signal, prc, data, destroy_data, connect_flags
|
17
|
+
Lib::CALLBACKS << prc
|
18
|
+
Lib.g_signal_connect_data gobject.to_ptr, signal, prc, data, destroy_data, connect_flags
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module Gtk
|
23
|
+
module Lib
|
24
|
+
extend FFI::Library
|
25
|
+
|
26
|
+
ffi_lib "gtk-x11-2.0"
|
27
|
+
attach_function :gtk_init, [:pointer, :pointer], :void
|
28
|
+
attach_function :gtk_main, [], :void
|
29
|
+
attach_function :gtk_main_quit, [], :void
|
30
|
+
|
31
|
+
attach_function :gtk_widget_show, [:pointer], :pointer
|
32
|
+
attach_function :gtk_widget_destroy, [:pointer], :void
|
33
|
+
attach_function :gtk_container_add, [:pointer, :pointer], :void
|
34
|
+
|
35
|
+
enum :GtkWindowType, [:GTK_WINDOW_TOPLEVEL, :GTK_WINDOW_POPUP]
|
36
|
+
attach_function :gtk_window_new, [:GtkWindowType], :pointer
|
37
|
+
attach_function :gtk_button_new, [], :pointer
|
38
|
+
attach_function :gtk_button_new_with_label, [:string], :pointer
|
39
|
+
attach_function :gtk_label_new, [:string], :pointer
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.init size, ary
|
43
|
+
argv = self.string_array_to_inoutptr ary
|
44
|
+
argc = self.int_to_inoutptr(size)
|
45
|
+
|
46
|
+
Lib.gtk_init argc, argv
|
47
|
+
|
48
|
+
outsize = self.outptr_to_int argc
|
49
|
+
outary = self.outptr_to_string_array argv, ary.nil? ? 0 : ary.size
|
50
|
+
|
51
|
+
return outsize, outary
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.int_to_inoutptr val
|
55
|
+
ptr = FFI::MemoryPointer.new(:int)
|
56
|
+
ptr.write_int val
|
57
|
+
return ptr
|
58
|
+
end
|
59
|
+
|
60
|
+
# Note: This implementation would dump core if the garbage collector runs
|
61
|
+
# before the contents of the pointers is used.
|
62
|
+
def self.string_array_to_inoutptr ary
|
63
|
+
ptrs = ary.map {|a| FFI::MemoryPointer.from_string(a)}
|
64
|
+
block = FFI::MemoryPointer.new(:pointer, ptrs.length)
|
65
|
+
block.write_array_of_pointer ptrs
|
66
|
+
argv = FFI::MemoryPointer.new(:pointer)
|
67
|
+
argv.write_pointer block
|
68
|
+
argv
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.outptr_to_int ptr
|
72
|
+
return ptr.read_int
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.outptr_to_string_array ptr, size
|
76
|
+
block = ptr.read_pointer
|
77
|
+
ptrs = block.read_array_of_pointer(size)
|
78
|
+
return ptrs.map {|p| p.null? ? nil : p.read_string}
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.main; Lib.gtk_main; end
|
82
|
+
def self.main_quit; Lib.gtk_main_quit; end
|
83
|
+
|
84
|
+
class Widget
|
85
|
+
def show
|
86
|
+
Lib.gtk_widget_show(@gobj)
|
87
|
+
end
|
88
|
+
def destroy
|
89
|
+
Lib.gtk_widget_destroy(@gobj)
|
90
|
+
end
|
91
|
+
def to_ptr
|
92
|
+
@gobj
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class Container < Widget
|
97
|
+
def add widget
|
98
|
+
Lib.gtk_container_add self.to_ptr, widget.to_ptr
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class Window < Container
|
103
|
+
def initialize type
|
104
|
+
@gobj = Lib.gtk_window_new(type)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class Button < Container
|
109
|
+
def initialize ptr
|
110
|
+
@gobj = ptr
|
111
|
+
end
|
112
|
+
class << self
|
113
|
+
alias :real_new :new
|
114
|
+
end
|
115
|
+
def self.new
|
116
|
+
self.real_new Lib.gtk_button_new()
|
117
|
+
end
|
118
|
+
def self.new_with_label text
|
119
|
+
self.real_new Lib.gtk_button_new_with_label(text)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
(my_len, my_args) = Gtk.init ARGV.length + 1, [$0, *ARGV]
|
125
|
+
p my_len, my_args
|
126
|
+
win = Gtk::Window.new(:GTK_WINDOW_TOPLEVEL)
|
127
|
+
btn = Gtk::Button.new_with_label('Hello World')
|
128
|
+
win.add btn
|
129
|
+
|
130
|
+
quit_prc = Proc.new { Gtk.main_quit }
|
131
|
+
|
132
|
+
# We can create callbacks with a different signature by using FFI::Function
|
133
|
+
# directly.
|
134
|
+
del_prc = FFI::Function.new(:bool, [:pointer, :pointer]) {|a, b|
|
135
|
+
puts "delete event occured"
|
136
|
+
true
|
137
|
+
}
|
138
|
+
GObject.signal_connect_data(win, "destroy", quit_prc, nil, nil, 0)
|
139
|
+
GObject.signal_connect_data(win, "delete-event", del_prc, nil, nil, 0)
|
140
|
+
GObject.signal_connect_data(btn, "clicked", Proc.new { win.destroy }, nil, nil, :SWAPPED)
|
141
|
+
|
142
|
+
btn.show
|
143
|
+
win.show
|
144
|
+
Gtk.main
|