gir_ffi 0.0.1
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/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
|