pax_global_header00006660000000000000000000000064131667430730014524gustar00rootroot0000000000000052 comment=0fdcf8c677094d0c109dfb199031fdbc0c9c47ea lgi-0.9.2/000077500000000000000000000000001316674307300123075ustar00rootroot00000000000000lgi-0.9.2/.gitignore000066400000000000000000000001001316674307300142660ustar00rootroot00000000000000*.o *.so *.dll *.stackdump .depcheck *.rockspec cairodemo-*.png lgi-0.9.2/.travis.yml000066400000000000000000000047051316674307300144260ustar00rootroot00000000000000sudo: required dist: trusty language: c env: matrix: - LUA=5.1 LUANAME=luajit-2.0 - LUA=5.1 LUANAME=lua5.1 - LUA=5.2 LUANAME=lua5.2 - LUA=5.3 LUANAME=lua5.3 before_install: - if [ -z $LUAINCLUDE ]; then LUAINCLUDE=/usr/include/${LUANAME}; fi install: # Travis boilerplate: the apt-get cache might be out of date. - travis_retry sudo apt-get update -qq # Do not install recommended packages with apt-get. - echo 'APT::Install-Recommends "false";' | sudo tee /etc/apt/apt.conf.d/no-recommends # Install build dependencies - travis_retry sudo apt-get install -y libgirepository1.0-dev libcairo2-dev gir1.2-gtk-3.0 libffi-dev libglib2.0-dev # And dependencies for running tests - travis_retry sudo apt-get install -y xvfb dbus-x11 # Install Lua (per env). # Note that Lua 5.3 is installed manually, because it is not available in Ubuntu Trusty. # For this we enable LUA_USE_APICHECK to catch errors in Lua API use. # LuaJIT is also installed manually. - | set -ev if [[ "$LUA" == "5.3" ]]; then travis_retry wget http://www.lua.org/ftp/lua-5.3.3.tar.gz -O lua.tar.gz tar -xvzf lua.tar.gz (cd lua-5.3.3/src \ && make SYSCFLAGS="-DLUA_USE_LINUX -ULUA_COMPAT_5_2 -DLUA_USE_APICHECK" SYSLIBS="-Wl,-E -ldl -lreadline" LUA_A=liblua.so MYCFLAGS="-fPIC" RANLIB=: AR="gcc -shared -ldl -o" liblua.so \ && cd .. \ && sudo make INSTALL_TOP=/usr/ INSTALL_INC=${LUAINCLUDE} TO_LIB=liblua.so linux install) elif [[ "$LUANAME" == "luajit-2.0" ]]; then travis_retry git clone http://luajit.org/git/luajit-2.0.git (cd luajit-2.0 && sudo make install PREFIX=/usr) # "Create" /usr/bin/lua if needed (Yup, this is a bad hack) if [ ! -e "/usr/bin/lua" ]; then sudo ln -s /usr/bin/luajit /usr/bin/lua; fi else sudo apt-get install -y lib${LUANAME}-dev ${LUANAME} fi script: # The tests need an enum value that is new in version 2.44. Of course Ubuntu # Ancient (=Trusty) as used by Travis only has version 2.40.0. - sed -e 's/Gio.NetworkConnectivity.LOCAL/1/' -i tests/gobject.lua # Build everything and run the tests - xvfb-run -a make check LUA_CFLAGS="-I$LUAINCLUDE" # Just to also check make install - sudo make install LUA_VERSION="$LUA" - xvfb-run -a lua -e 'Gtk = require("lgi").Gtk c = Gtk.Grid() w = Gtk.Label() c:add(w) assert(w.parent == c) a, b = dofile("lgi/version.lua"), require("lgi.version") assert(a == b, string.format("%s == %s", a, b))' lgi-0.9.2/LICENSE000066400000000000000000000020531316674307300133140ustar00rootroot00000000000000Copyright (c) 2010, 2011 Pavel Holejsovsky Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. lgi-0.9.2/Makefile000066400000000000000000000007601316674307300137520ustar00rootroot00000000000000# # LGI Dynamic GObject introspection binding. # # Author: Pavel Holejsovsky # License: MIT # VERSION = 0.9.2 MAKE ?= make ROCK = lgi-$(VERSION)-1.rockspec .PHONY : rock all clean install check all : $(MAKE) -C lgi rock : $(ROCK) $(ROCK) : rockspec.in Makefile sed 's/%VERSION%/$(VERSION)/' $< >$@ clean : rm -f *.rockspec $(MAKE) -C lgi clean $(MAKE) -C tests clean install : $(MAKE) -C lgi install check : all $(MAKE) -C tests check export VERSION lgi-0.9.2/README.md000066400000000000000000000251571316674307300136000ustar00rootroot00000000000000# LGI LGI is gobject-introspection based dynamic Lua binding to GObject based libraries. It allows using GObject-based libraries directly from Lua. Licensed under [MIT-style](http://www.opensource.org/licenses/mit-license.php) license, see LICENSE file for full text. Home of the project is on [GitHub](http://github.com/pavouk/lgi). LGI is tested and compatible with standard Lua 5.1, Lua 5.2, Lua 5.3 and LuaJIT2. Compatibility with other Lua implementations is not tested yet. If you need to support pre-gobject-introspection GTK (ancient GTK+ 2.x releases), use [Lua-Gnome](http://sourceforge.net/projects/lua-gnome/). ## Installation: In order to be able to compile native part of lgi, gobject-introspection >= 0.10.8 development package must be installed, although preferred version is >= 1.30. The development package is called `libgirepository1.0-dev` on debian-based systems (like Ubuntu) and `gobject-introspection-devel` on RedHat-based systems (like Fedora). Using LuaRocks: luarocks install lgi Alternatively, use make-based installation: make [sudo] make install [PREFIX=] [DESTDIR=] Please note that on BSD-systems you may need to use 'gmake'. ## Usage See examples in samples/ directory. Documentation is available in doc/ directory in markdown format. Process it with your favorite Markdown processor if you want to read it in HTML. ## Credits List of contributors, in no particular order: - Uli Schlachter - Jasper Lievisse Adriaanse - Ildar Mulyukov - Nils Nordman - Ignas Anikevicius - Craig Barnes - Nicola Fontana - Andreas Stührk - Aaron Faanes - Jiří Klimeš - Garrett Regier - Kenneth Zhou - Travis Hoppe - Tobias Jakobs - Heiko Becker - Vincent Bermel - Szunti Many other people contributed to what lgi is today, in many forms - writing patches, reporting bugs, packaging for distributions, providing ideas, spreading a word... *Many thanks to all of you!* ## History ### 0.9.2 (9-Oct-2017) - fix assorted crashes in closure callback invocation code - fix double-free bug caused by incorrect annotation of Gio.DBusProxy.get_interface_info - fix marshaling of arrays of pointers - make objects unusable in __gc metamethod - work around API break in GLib 2.54 - use structured GLib logging when available - add Gio.Async support also for static methods and global functions - better error message when Gtk.init fails - add support for Travis - don't hardcode pkg-config executable - fix URI in GStreamer sample - fix flags for DBus samples ### 0.9.1 (27-May-2016) - marshal NULL strings as nil instead of empty strings. This allows use of e.g. DataInputStream:read_line() APIs. - fix and improve build for OSX and Win-based configurations - add support for arrays with lengths as struct fields - allow GLib.Variant construction for lightuserdata - fix gtop binding (certain structs could not be imported) - adapt to new set of annotations in newer glib - assorted Lua5.3 fixes, lgi is now fully Lua5.3 compatible - fix binding of Gdk.Rectangle from newer GDK ### 0.9.0 (23-Mar-2015) - new feature: allow defining new properties on custom GObject subclasses implemented using lgi - fix: improve compatibility with locales (turkish) - fix: GPtrArray handling - fix: improve behavior when running in assorted multiple-embedded and concurrent scenarios ### 0.8.0 (02-Jul-2014) - new feature: add automatically-generated wrappers for do_async()/do_finish() pairs. Documented as Gio.Async namespace functionality. - new feature: automatically initialize objects implementing Gio.Initable and Gio.AsyncInitable interfaces. - new feature: GLib.Error is now handled properly, functions returning errors return GLib.Error instances instead of error numbers and strings. This also allows overriding virtual methods from Lua which report errors by returning GLib.Error - new feature: GLib.Bytes support, adding # operator and 'data' property allowing easy use of GLib.Bytes object. This leads to deprecation of seldomly used modifiable buffers extensions. - optimization: type attribute caching brings significant speedup of method and property lookups. - fix: Gtk: override for Gtk.Menu.popup() compatible with new GTK annotation. - fix: cairo: assorted small fixes of bad signatures, added some convenience properties. - fix: GLib: add overrides for GLib.MarkupParser - fix: Gio: add overrides for Gio.DBus*Info structures - fix: Pango: add override for Pango.GlyphString.glyphs array - fix: Gdk: Add override inheriting specific Gdk.EventXxx from Gdk.Event - fix: Compile cleanly against Lua5.2 with compatibility mode turned on - fix: avoid leak when adding already cached owned record and for retrieving Variant from Value. ### 0.7.2 (12-Sep-2013) - fix: improper marshalling of certain APIs passing pointers to records. - fix: cairo.PsSurface.create() had incorrect signature, missing filename. - fix: If GTK initialization fails, raise Lua exception instead of hard-crash of calling process. - fix: when running test in devel tree, prefer lgi from devel tree instead of the installed one. - add: cairo.Status.to_string() API - fix: avoid referencing GdkRGBA in GDk override when targetting Gdk2.0, which does not have GdkRGBA. - fix: replace GStaticRecMutex with GRecMutex to avoid compilation warnings. - fix: Gtk.Container.'child' pseudoproperty works even in Gtk2, where it was shadowed by internal field. - fix: add workaround for improperly parsed g_bytes_get_data() annotation. - fix: add workarounf for incorrect annotation on Pango.Layour.set_attributes(), which caused memory leak. - fix: adapt to Gio.InputStream.[read|read_all|read_async] API change, which does not accept buffer length argument any more (due to the newly added annotations). ### 0.7.1 (4-Mar-2013) - Add support for GStreamer 1.0, while still retaining GStreamer 0.10 compatibility. - fix: crash when trying to to access '_class' attribute of class which does not have public classstruct exposed in typelib - fix: crash when passing 'nil' to transfer=full struct (caused crash of Awesome WM during startup). ### 0.7.0 (23-Feb-2013) - New feature - subclassing. Allows creating GObject subclasses and implementing their virtual methods in Lua. - cairo: add support for most 1.12-specific cairo features - cairo: create hierarchy for Pattern subclasses - cairo: assorted small cairo bugfixes - samples: add GDBus client example - samples: add GnomeKeyring example - samples: GTK: offscreen window demos - samples: libsoup simple http server example - platforms: added support for darwin/macosx platform - platforms: additional fixes for OpenBSD - build: Makefiles now respect `CFLAGS` and `LDFLAGS` env vars values - build: Add Lua version option into Makefile - fix: custom ffi enum/flags handling - fix: more exotic callback-to-Lua marshalling scenarios - fix: do not allow GTK+ and gstreamer to call setlocale() - this might break Lua in some locales - fix: small adjustments, fixes and additions in Gtk override - fix: tons of other small fixes ### 0.6.2 (25-Jun-2012) - Avoid unexpected dependency on cairo-devel, cairo-runtime is now enough - Make `set_resident()` more robust and fix stack leak for Lua 5.2 case, avoid useless warning when `set_resident()` fails (to accommodate for static linking case). - Fix small memory leak (mutex) which occured once per opened `lua_State` using lgi. ### 0.6.1 (19-Jun-2012) - objects and structs: actually implement `_type` property as documented - tests: Fix regression tests for less common platforms - Pango: Add a few missing overrides - cairo: Fix `Context:user_to_device()` family of methods. - GStreamer: Add support for transfer!=none for input objects. This is needed to avoid leaks caused by strange usage of transfer annotations of gstreamer-0.10 - GStreamer: Add more missing overrides - GStreamer: Fix and improve samples - Various fixes for usecase when Lua context with loaded lgi is closed and opened again - Gtk: Add missing `Gtk.Builder:connect_signals()` override ### 0.6 (22-May-2012) - Add cairo bindings, cairo sample and finish some gtk-demo parts which were requiring cairo ### 0.5.1 (not officially released) - Fix a few problems on more exotic architectures (s390x, mips, ia64). - Allow passing `byte.buffer` when UTF8 string is requested. ### 0.5 (15-Apr-2012) - Port gtk3-demo to Lua code. Try running 'lua samples/gtk-demo/main.lua' - Finish override set for Gtk - Extend and document features for interfacing LGI with external libraries (exporting and importing objects and structures via lightuserdata pointers). - Fix: a few bugs with resolving bitflags values - Fix: a few bugs in coroutines-as-callbacks feature - Fix: workaround for crashing bug in gobject-introspection 1.32.0 - Fix: don't try to squeeze `GType` into `lua_Number` any more; this could cause crashes on some 64bit arches. ### 0.4 (4-Jan-2012) - Changed handling of enums and bitflags, switched from marshaling them as numbers to prefering strings for enums and tables (sets or lists) for bitflags. Numeric values still work for Lua->C marshalling, but backward compatibility is broken in C->Lua enum and bitflags marshalling. - Compatible with Lua 5.2 and LuaJIT - Added standardized way for overrides to handle constructor argument table array part. - Existing Gtk overrides reworked and improved, there is now a way to describe and create widget hierarchies in Lua-friendly way. See `docs/gtk.lua`, chapter about `Gtk.Container` for overview and samples. - Various bugfixes and portability fixes. ### 0.3 (28-Nov-2011) - Project hosting moved to GitHub. - Build system switched from `waf` to simple Makefile-based one - Added automatic locking of thread-sensitive libraries (Gdk and Clutter). There is no need to add `Gdk.threads_enter()`, `Gdk.threads_leave()` and `Clutter.threads_enter()`, `Clutter.threads_leave()` pairs into application, lgi handles this automatically. - Added new sample `samples/console.lua`, which implements already quite usable Lua console using Gtk widgets. - Fixes for compatibility with older gobject-introspection 0.10.8 package - Testsuite is not built automatically, because building it can be apparently problematic on some systems, causing installation failure even when testsuite is not needed at all. - Remove `setlocale()` initialization, which could break Lua when used with some regional locales. The downside of this change is that marshaling file names containing non-ASCII characters on systems which define `G_BROKEN_FILENAMES` environment variable (probably only Fedora 15) does not work now. ### 0.2 (7-Nov-2011) First public release lgi-0.9.2/docs/000077500000000000000000000000001316674307300132375ustar00rootroot00000000000000lgi-0.9.2/docs/cairo.md000066400000000000000000000157211316674307300146640ustar00rootroot00000000000000# cairo support Cairo library is an important part of any GTK-based setup, because GTK internally uses cairo exclusively for painting. However, cairo itself is not built using GObject technology and thus imposes quite a problem for any GObject Introspection based binding, such as lgi. ## Basic binding description Although internal implementation is a bit different from other fully introspection-enabled libraries, this difference is not visible to lgi user. cairo must be imported in the same way as other libraries, e.g. local lgi = require 'lgi' local cairo = lgi.cairo Cairo library itself is organized using object-oriented style, using C structures as objects (e.g. `cairo_t`, `cairo_surface_t`) and functions as methods acting upon these objects. lgi exports objects as classes in the `cairo` namespace, e.g. `cairo.Context`, `cairo.Surface` etc. To create new object instance, cairo offers assorted `create` methods, e.g. `cairo_create` or `cairo_pattern_create`, which are mapped as expected to `cairo.Context.create` and `cairo.Pattern.create`. It is also possible to invoke them using lgi's 'constructor' syntax, i.e. to create new context on specified surface, it is possible to use either `local cr = cairo.Context.create(surface)` or `local cr = cairo.Context(surface)`. ### Version checking `cairo.version` and `cairo.version_string` fields contain current runtime cairo library version, as returned by their C counterparts `cairo_version()` and `cairo_version_string()`. Original `CAIRO_VERSION_ENCODE` macro is reimplemented as `cairo.version_encode(major, minor, micro)`. For example, following section shows how to guard code which should be run only when cairo version is at least 1.12: if cairo.version >= cairo.version_encode(1, 12, 0) then -- Cairo 1.12-specific code else -- Fallback to older cairo version code end ### Synthetic properties There are many getter and setter functions for assorted cairo objects. lgi exports them in the form of method calls as the native C interface does, and it also provides property-like access, so that it is possible to query or assign named property of the object. Following example demonstrates two identical ways to set and get line width on cairo.Context instance: local cr = cairo.Context(surface) cr:set_line_width(10) print('line width ', cr:get_line_width()) cr.line_width = 10 print('line width ', cr.line_width) In general, any kind of `get_xxx()` method call on any cairo object can be replaced using `xxx` property on the object, and any `set_xxx()` method can be replaced by setting `xxx` property. ### cairo.Surface hierarchy Cairo provides basic rendering surface object `cairo.Surface`, and a bunch of specialized surfaces implementing rendering to assorted targets, e.g. `cairo.ImageSurface`, `cairo.PdfSurface` etc. These surface provide their own class, which is logically inherited from `cairo.Surface`. lgi fully implements this inheritance, so that calling `cairo.ImageSurface()` actually creates an instance of `cairo.ImageSurface` class, which provides all methods abd properties of `cairo.Surface` and and some specialized methods and properties like `width` and `height`. In addition, lgi always assigns the real type of the surface, so that even when `cairo.Context.get_target()` method (or `cairo.Context.target` property) is designated as returning `cairo.Surface` instance, upon the call the type of the surface is queried and proper kind of surface type is really returned. Following example demonstrates that it is possible to query `cairo.ImageSurface`-specific `width` property directly on the `cairo.Context.target` result. -- Assumes the cr is cairo.Context instance with assigned surface print('width of the surface' cr.target.width) It is also possible to use lgi generic typechecking machinery for checking the type of the surface: if cairo.ImageSurface:is_type_of(cr.target) then print('width of the surface' cr.target.width) else print('unsupported type of the surface') end ### cairo.Pattern hierarchy cairo's pattern API actually hides the inheritance of assorted pattern types. lgi binding brings this hierarchy up in the same way as for surfaces described in previous section. Following hierarchy exists: cairo.Pattern cairo.SolidPattern cairo.SurfacePattern cairo.GradientPattern cairo.LinearPattern cairo.RadialPattern cairo.MeshPattern Patterns can be created using static factory methods on `cairo.Pattern` as documented in cairo documentation. In addition, lgi maps creation methods to specific subclass constructors, so following snippets are equivalent: local pattern = cairo.Pattern.create_linear(0, 0, 10, 10) local pattern = cairo.LinearPattern(0, 0, 10, 10) ### cairo.Context path iteration cairo library offers iteration over the drawing path returned via `cairo.Context.copy_path()` method. Resulting path can be iterated using `pairs()` method of `cairo.Path` class. `pairs()` method returns iterator suitable to be used in Lua 'generic for' construct. Iterator returns type of the path element, optionally followed by 0, 1 or 3 points. Following example shows how to iterate the path. local path = cr:copy_path() for kind, points in path:pairs() do io.write(kind .. ':') for pt in ipairs(points) do io.write((' { %g, %g }'):format(pt.x, pt.y)) end end end ## Impact of cairo on other libraries In addition to cairo itself, there is a bunch of cairo-specific methods inside Gtk, Gdk and Pango libraries. lgi wires them up so that they can be called naturally as if they were built in to the cairo core itself. ### Gdk and Gtk `Gdk.Rectangle` is just a link to `cairo.RectangleInt` (similar to C, where `GdkRectangle` is just a typedef of `cairo_rectangle_int_t`). `gdk_rectangle_union` and `gdk_rectangle_intersect` are wired as a methods of `Gdk.Rectangle` as expected. `Gdk.cairo_create()` is aliased as a method `Gdk.Window.cairo_create()`. `Gdk.cairo_region_create_from_surface()` is aliased as `cairo.Region.create_from_surface()`. `cairo.Context.set_source_rgba()` is overriden so that it also accepts `Gdk.RGBA` instance as an argument. Similarly, `cairo.Context.rectangle()` alternatively accepts `Gdk.Rectangle` as an argument. `cairo.Context` has additional methods `get_clip_rectangle()`, `set_source_color()`, `set_source_pixbuf()`, `set_source_window` and `region`, implemented as calls to appropriate `Gdk.cairo_xxx` functions. Since all these extensions are implemented inside Gdk and Gtk libraries, they are present only when `lgi.Gdk` is loaded. When loading just pure `lgi.cairo`, they are not available. ### PangoCairo Pango library contains namespace `PangoCairo` which implements a bunch of cairo-specific helper functions to integrate Pango use with cairo library. It is of course possible to call them as global methods of PangoCairo interface, however lgi override maps the also to methods and attributes of other classes to which they logically belong. lgi-0.9.2/docs/gio.md000066400000000000000000000075641316674307300143530ustar00rootroot00000000000000# Gio support Most of the Gio facilities are supported natively through gobject-introspection layer. However, lgi provides `Gio.Async` which help in using Gio-style asynchronous I/O operations. ## Asynchronous IO support Native Gio asynchronous operations are based on traditional callback scheme, i.e. the operation is started, pushed to be performed on the background and when it finishes, it calls registered callback with operation results as arguments. While this scheme is widely used for asynchronous programming, it leads to spaghetti code full of callbacks. Lua provides coroutines, which can be used to make the code look 'synchronous' again, but still retaining the advantage of non-blocking I/O operations. Gio-style asynchronous functions come as pair of two methods; `name_async` which starts operation and registers callback, and `name_finish`, which should be called in the context of registered callback and allows retrieveing operation results. When `Gio` override is loaded, lgi detects any of these pairs (in any object, not just from Gio namespace) and when found, it synthesizes `async_name` operations, which wraps native methods and uses Lua coroutines to convert callbacks into synchronous code. In order for `async_method` to work, these methods have to be called in the context of functions called through `Gio.Async` spawning facilities; either `Gio.Async.call` for synchronous calls and `Gio.Async.start` for starting routine on background. ### Gio.Async class This helper class implemented by lgi (not originating from introspected Gio module) contains interface for using lgi asynchronous support. This class contains only static methods and attributes, it is not possible to instantiate it. ### Gio.Async.call and Gio.Async.start local call_results = Gio.Async.call(user_function[, cancellable[, io_priority])(user_args) local resume_results = Gio.Async.start(user_function[, cancellable[, io_priority])(user_args) These methods accept user function to be run as argument and return function which starts execution of the user function in async-enabled context. Any `async_name` methods called inside context do not accept `io_priority` and `cancellable` arguments (as their `name_async` original counterparts do), instead global cancellable and io_priority values given as arguments to `Gio.Async.call/start` are used. ### Gio.Async.cancellable and Gio.Async.io_priority Code running inside async-enabled context can query or change value of context-default `cancellable` and `io_priority` attributes by getting or setting them as attributes of `Gio.Async` class. If `cancellable` or `io_priority` arguments are not provided to `Gio.Async.start` or `Gio.Async.call`, they are automatically inherited from currently running async-enabled coroutine, or default values are used (if caller is not running in async-enabled context). ### Simple asynchronous I/O example Following example reacts on the press of button, reads contents of `/etc/passwd` and dumps it to standard output. local window = Gtk.Window { ... Gtk.Button { id = 'button', label = 'Breach' }, ... } function window.child.button:on_clicked() local function dump_file(filename) local file = Gio.File.new_for_path(filename) local info = file:async_query_info('standard::size', 'NONE') local stream = file:async_read() local bytes = stream:async_read_bytes(info:get_size()) print(bytes.data) stream:async_close() end Gio.Async.start(dump_file)('/etc/passwd') end Note that all reading happens running on background, on_clicked() handler finished when the operation is still running on background, so if you have a few gigabytes worth /etc/passwd file, the application will not freeze while dumping it. `samples/giostream.lua` provides far more involved sample illustrating use of asynchronous operations. lgi-0.9.2/docs/gtk.md000066400000000000000000000275241316674307300143600ustar00rootroot00000000000000# Gtk support Lgi Gtk support is based on gobject-introspection support. Some extensions are provided to support non-introspectable features and to provide easier and more Lua-like access to some important Gtk features. ## Basic Widget and Container support ### Style properties access To read style property values of the widget, a `style` attribute is implemented. Following example reads `resize-grip-height` style property from Gtk.Window instance: local window = Gtk.Window() print(window.style.resize_grip_height) ### Gtk.Widget width and height properties lgi adds new `width` and `height` properties to Gtk.Widget. Reading them yields allocated size (`Gtk.Widget.get_allocated_size()`), writing them sets new size request (`Gtk.Widget.set_size_request()`). These usages typically means what an application needs - actual allocated size to draw on when reading, and request for specific size when writing them. ### Child properties Child properties are properties of the relation between a container and child. A Lua-friendly access to these properties is implemented by `property` attribute of `Gtk.Container`. Following example illustrates writing and reading of `width` property of `Gtk.Grid` and child `Gtk.Button`: local grid, button = Gtk.Grid(), Gtk.Button() grid:add(button) grid.property[button].width = 2 print(grid.property[button].width) -- prints 2 ### Adding children to container Basic method for adding child widget into container is `Gtk.Container.add()` method. This method is overloaded by Lgi so that it accepts either widget, or table containing widget at index 1 and the rest `name=value` pairs define child properties. Therefore this method is full replacement of unintrospectable `gtk_container_add_with_properties()` function. Example from previous chapter simplified using this technique follows: local grid, button = Gtk.Grid(), Gtk.Button() grid:add { button, width = 2 } print(grid.property[button].width) -- prints 2 Another important feature of containers is that they have extended constructor, and array part of constructor argument table can contain widgets to be added. Therefore, previous example can be written like this: local button = Gtk.Button() local grid = Gtk.Grid { { button, width = 2 } } print(grid.property[button].width) -- prints 2 ### 'id' property of widgets Another important feature is that all widgets support `id` property, which can hold an arbitrary string which is used to identify the widget. `id` is assigned by caller, defaults to `nil`. To look up widget with specified id in the container's widget tree (i.e. not only in direct container children), query `child` property of the container with requested id. Previous example rewritten with this technique would look like this: local grid = Gtk.Grid { { Gtk.Button { id = 'button' }, width = 2 } } print(grid.property[grid.child.button].width) -- prints 2 The advantage of these features is that they allow using Lua's data-description face for describing widget hierarchies in natural way, instead of human-unfriendly `Gtk.Builder`'s XML. A small example follows: Gtk = lgi.Gtk local window = Gtk.Window { title = 'Application', default_width = 640, default_height = 480, Gtk.Grid { orientation = Gtk.Orientation.VERTICAL, Gtk.Toolbar { Gtk.ToolButton { id = 'about', stock_id = Gtk.STOCK_ABOUT }, Gtk.ToolButton { id = 'quit', stock_id = Gtk.STOCK_QUIT }, }, Gtk.ScrolledWindow { Gtk.TextView { id = 'view', expand = true } }, Gtk.Statusbar { id = 'statusbar' } } } local n = 0 function window.child.about:on_clicked() n = n + 1 window.child.view.buffer.text = 'Clicked ' .. n .. ' times' end function window.child.quit:on_clicked() window:destroy() end window:show_all() Run `samples/console.lua`, paste example into entry view and enjoy. The `samples/console.lua` example itself shows more complex usage of this pattern. ## Gtk.Builder Although Lua's declarative style for creating widget hierarchies (as presented in chapter discussing `Gtk.Container` extensions) is generally preferred to builder's XML authoring by hand, `Gtk.Builder` can still be useful when widget hierarchies are designed in some external tool like `glade`. Original `gtk_builder_add_from_file` and `gtk_builder_add_from_string` return `guint` instead of `gboolean`, which would make direct usage from Lua awkward. Lgi overrides these methods to return `boolean` as the first return value, so that typical `assert(builder:add_from_file(filename))` can be used. A new `objects` attribute provides direct access to loaded objects by their identifier, so that instead of `builder:get_object('id')` it is possible to use `builder.objects.id` `Gtk.Builder.connect_signals(handlers)` tries to connect all signals to handlers which are defined in `handlers` table. Functions from `handlers` table are invoked with target object on which is signal defined as first argument, but it is possible to define `object` attribute, in this case the object instance specified in `object` attribute is used. `after` attribute is honored, but `swapped` is completely ignored, as its semantics for lgi is unclear and not very useful. ## Gtk.Action and Gtk.ActionGroup Lgi provides new method `Gtk.ActionGroup:add()` which generally replaces unintrospectable `gtk_action_group_add_actions()` family of functions. `Gtk.ActionGroup:add()` accepts single argument, which may be one of: - an instance of `Gtk.Action` - this is identical with calling `Gtk.Action.add_action()`. - a table containing instance of `Gtk.Action` at index 1, and optionally having attribute `accelerator`; this is a shorthand for `Gtk.ActionGroup.add_action_with_accel()` - a table with array of `Gtk.RadioAction` instances, and optionally `on_change` attribute containing function to be called when the radio group state is changed. All actions or groups can be added by an array part of `Gtk.ActionGroup` constructor, as demonstrated by following example: local group = Gtk.ActionGroup { Gtk.Action { name = 'new', label = "_New" }, { Gtk.Action { name = 'open', label = "_Open" }, accelerator = 'O' }, { Gtk.RadioAction { name = 'simple', label = "_Simple", value = 1 }, { Gtk.RadioAction { name = 'complex', label = "_Complex", value = 2 }, accelerator = 'C' }, on_change = function(action) print("Changed to: ", action.name) end }, } To access specific action from the group, a read-only attribute `action` is added to the group, which allows to be indexed by action name to retrieve. So continuing the example above, we can implement 'new' action like this: function group.action.new:on_activate() print("Action 'New' invoked") end ## Gtk.TextTagTable It is possible to populate new instance of the tag table with tags during the construction, an array part of constructor argument table is expected to contain `Gtk.TextTag` instances which are then automatically added to the table. A new attribute `tag` is added, provides Lua table which can be indexed by string representing tag name and returns the appropriate tag (so it is essentially a wrapper around `Gtk.TextTagTable:lookup()` method). Following example demonstrates both capabilities: local tag_table = Gtk.TextTagTable { Gtk.TextTag { name = 'plain', color = 'blue' }, Gtk.TextTag { name = 'error', color = 'red' }, } assert(tag_table.tag.plain == tag_table:lookup('plain')) ## TreeView and related classes `Gtk.TreeView` and related classes like `Gtk.TreeModel` are one of the most complicated objects in the whole `Gtk`. Lgi adds some overrides to simplify the work with them. ### Gtk.TreeModel Lgi supports direct indexing of treemodel instances by iterators (i.e. `Gtk.TreeIter` instances). To get value at specified column number, index the resulting value again with column number. Note that although `Gtk` uses 0-based column numbers, Lgi remaps them to 1-based numbers, because working with 1-based arrays is much more natural for Lua. Another extension provided by Lgi is `Gtk.TreeModel:pairs([parent_iter])` method for Lua-native iteration of the model. This method returns 3 values suitable to pass to generic `for`, so that standard Lua iteration protocol can be used. See the example in the next chapter which uses this technique. ### Gtk.ListStore and Gtk.TreeStore Standard `Gtk.TreeModel` implementations, `Gtk.ListStore` and `Gtk.TreeStore` extend the concept of indexing model instance with iterators also to writing values. Indexing resulting value with 1-based column number allows writing individual values, while assigning the table containing column-keyed values allows assigning multiple values at once. Following example illustrates all these techniques: local PersonColumn = { NAME = 1, AGE = 2, EMPLOYEE = 3 } local store = Gtk.ListStore.new { [PersonColumn.NAME] = GObject.Type.STRING, [PersonColumn.AGE] = GObject.Type.INT, [PersonColumn.EMPLOYEE] = GObject.Type.BOOLEAN, } local person = store:append() store[person] = { [PersonColumn.NAME] = "John Doe", [PersonColumn.AGE] = 45, [PersonColumn.EMPLOYEE] = true, } assert(store[person][PersonColumn.AGE] == 45) store[person][PersonColumn.AGE] = 42 assert(store[person][PersonColumn.AGE] == 42) -- Print all persons in the store for i, p in store:pairs() do print(p[PersonColumn.NAME], p[PersonColumn.AGE]) end Note that `append` and `insert` methods are overridden and accept additional parameter containing table with column/value pairs, so creation section of previous example can be simplified to: local person = store:append { [PersonColumn.NAME] = "John Doe", [PersonColumn.AGE] = 45, [PersonColumn.EMPLOYEE] = true, } Note that while the example uses `Gtk.ListStore`, similar overrides are provided also for `Gtk.TreeStore`. ### Gtk.TreeView and Gtk.TreeViewColumn Lgi provides `Gtk.TreeViewColumn:set(cell, data)` method, which allows assigning either a set of `cell` renderer attribute->model column pairs (in case that `data` argument is a table), or assigns custom data function for specified cell renderer (when `data` is a function). Note that column must already have assigned cell renderer. See `gtk_tree_view_column_set_attributes()` and `gtk_tree_view_column_set_cell_data_func()` for precise documentation. The override `Gtk.TreeViewColumn:add(def)` composes both adding new cellrenderer and setting attributes or data function. `def` argument is a table, containing cell renderer instance at index 1 and `data` at index 2. Optionally, it can also contain `expand` attribute (set to `true` or `false`) and `align` (set either to `start` or `end`). This method is basically combination of `gtk_tree_view_column_pack_start()` or `gtk_tree_view_column_pack_end()` and `set()` override method. Array part of `Gtk.TreeViewColumn` constructor call is mapped to call `Gtk.TreeViewColumn:add()` method, and array part of `Gtk.TreeView` constructor call is mapped to call `Gtk.TreeView:append_column()`, and this allows composing the whole initialized treeview in a declarative style like in the example below: -- This example reuses 'store' model created in examples in -- Gtk.TreeModel chapter. local view = Gtk.TreeView { model = store, Gtk.TreeViewColumn { title = "Name and age", expand = true, { Gtk.CellRendererText {}, { text = PersonColumn.NAME } }, { Gtk.CellRendererText {}, { text = PersonColumn.AGE } }, }, Gtk.TreeViewColumn { title = "Employee", { Gtk.CellRendererToggle {}, { active = PersonColumn.EMPLOYEE } } }, } lgi-0.9.2/docs/guide.md000066400000000000000000001007351316674307300146640ustar00rootroot00000000000000# lgi User's Guide All lgi functionality is exported through `lgi` module. To access it, use standard `require` construct, e.g.: local lgi = require 'lgi' Note that lgi does not use `module` function, so it does *not* automatically insert itself into globals, the return value from `require` call has to be used. ## 1. Importing libraries To use any introspection-enabled library, it has to be imported first. Simple way to import it is just referencing its name in `lgi` namespace, like this: local GLib = lgi.GLib local GObject = lgi.GObject local Gtk = lgi.Gtk This imports the latest version of the module which can be found. When exact version is requested, use `lgi.require(modulename, version)`: local Gst = lgi.require('Gst', '0.10') ### 1.1. Repository structure Importing library creates table containing all elements which are present in the library namespace - all classes, structures, global functions, constants etc. All those elements are directly accessible, e.g. assert(GLib.PRIORITY_DEFAULT == 0) Note that all elements in the namespace are lazy-loaded to avoid excessive memory overhead and initial loading time. To force eager-loading, all namespaces (and container elements in them, like classes, structures, enums etc) contains `_resolve(deep)` method, which loads all contents eagerly, possibly recursively if `deep` argument is `true`. So e.g. dump(Gtk.Widget:_resolve(true), 3) prints everything available in Gtk.Widget class, and dump(Gio:_resolve(true), 5) dumps the whole contents of Gio package. Note: the `dump` function used in this manual is part of `cli-debugger` Lua package. Of course, you can use any kind of table-dumping facility you are used to instead. ## 2. Mapping of types between GLib and Lua In order to call methods and access properties and fields from Lua, a mapping between GLib types and Lua types is established. * `void` is ignored, does not produce any Lua value * `gboolean` is mapped to Lua's `boolean` type, with `true` and `false` values * All numeric types are mapped to Lua's `number` type * Enumerations are primarily handled as strings with uppercased GType nicks, optionally the direct numeric values are also accepted. * Bitflags are primarily handled as lists or sets of strings with uppercased GType nicks, optionally the direct numeric values are also accepted. * `gchar*` string is mapped to Lua as `string` type, UTF-8 encoded * C array types and `GArray` is mapped to Lua tables, using array part of the table. Note that although in C the arrays are 0-based, when copied to Lua table, they are 1-based (as Lua uses 1-based arrays). * `GList` and `GSList` is also mapped to Lua array part of tables. * `GHashTable` is mapped to Lua table, fully utilizing key-value and GHashTable's key and value pairs. * C arrays of 1-byte-sized elements (i.e. byte buffers) is mapped to Lua `string` instead of tables, although when going Lua->GLib direction, tables are also accepted for this type of arrays. * GObject class, struct or union is mapped to lgi instances of specific class, struct or union. It is also possible to pass `nil`, in which case the `NULL` is passed to C-side (but only if the annotation `(allow-none)` of the original C method allows passing `NULL`). * `gpointer` are mapped to Lua `lightuserdata` type. In Lua->GLib direction, following values are accepted for `gpointer` type: - Lua `string` instances - Instances of lgi classes, structs or unions - Binary buffers (see below) ### 2.1. Calling functions and methods When calling GLib functions, following conventions apply: * All input arguments are mapped to Lua inputs * Return value is the first Lua return value * All output arguments follow the return value * In/Out arguments are both accepted as input and are also added into Lua returns. * Functions reporting errors through `GError **` as last argument use Lua standard error reporting - they typically return boolean value indicating either success or failure, and if failure occurs, following return values represent error message and error code. #### 2.1.1. Phantom boolean return values GLib based libraries often use boolean return value indicating whether logically-output argument is filled in or not. Typical example is `gboolean gtk_tree_model_get_iter_first(GtkTreeModel *tree_model, GtkTreeIter *iter)`, where `iter` is filled in case of success, and untouched in case of failure. Normal binding of such function feels a bit unnatural in Lua: local ok, iter = model:get_iter_first() -- Even in case of failure, iter contains new 0-initialized -- instance of the iterator, so following line is necessary: if not ok then iter = nil end To ease usage of such method, lgi avoids returning first boolean return. If C function returns `false` in this case, all other output arguments are returned as `nil`. This means that previous example must be instead written simply as: local iter = model:get_iter_first() ### 2.2. Callbacks When some GLib function or method requires callback argument, a Lua function should be provided (or userdata or table implementing `__call` metamethod), and position for callback context (usually called `user_data` in GLib function signature) should be ignored completely. Callbacks are invoked in the context of Lua coroutine which invoked the original call, unless the coroutine is suspended - in this, case a new coroutine is automatically created and callback is invoked in this new context. Callback arguments and return values are governed by the same rules of argument and type conversions as written above. If the Lua callback throws an error, the error is *not* caught by the calling site, instead propagated out (usually terminating unless there is some `pcall` in the call chain). It is also possible to provide coroutine instance as callback argument. In this case, the coroutine is resumed, providing callback arguments as parameters to resume (therefore they are return values of `coroutine.yield()` call which suspended the coroutine passed as callback argument). The callback is in this case considered to return when either coroutine terminates (in this case, callback return value(s) are coroutine final result(s)) or yields again (in this case, callback return value(s) are arguments to `coroutine.yield()` call). This mode of operation is very useful when using Gio-style asynchronous calls. However, it is recommended to use lgi-specific `Gio.Async` facility for this purpose, as described in its own documentation, because it wraps and hides many intricacies which arise with coroutines and mainloop integration. ## 3. Classes Classes are usually derived from `GObject` base class. Classes contain entities like properties, methods and signals and provide inheritance, i.e. entities of ancestor class are also available in all inherited classes. lgi supports Lua-like access to entities using `.` and `:` operators. There is no need to invoke any memory management GObject controls, like `ref` or `unref` methods, because lgi handles reference management transparently underneath. In fact, calling these low-level methods can probably always be considered either as a bug or workaround for possible bug in lgi :-) ### 3.1. Creating instances To create new instance of the class (i.e. new object), call class declaration as if it is a method: local window = Gtk.Window() Optionally, it is possible to pass single argument, table containing entity name mapping to entity value. This way it is possible to initialize properties, fields and even signal handlers in the class construction: local window = Gtk.Window { title = "Title", on_destroy = function() print("Destroyed") end } For some classes, which behave like containers of other things, lgi allows adding also a list of children into the array part of the argument table, which contains children element to be added. A typical example is `Gtk.Container`, which allows adding element in the constructor table, allowing construction of the whole widget hierarchy in Lua-friendly way: local window = Gtk.Window { title = "Title", on_destroy = function() print("Destroyed") end, Gtk.Grid { Gtk.Label { label = "Contents", expand = true }, Gtk.Statusbar {} } } There is also possibility to create instances of classes for which the introspection typelib data is not available, only GType is known. Use `GObject.Object.new()` as illustrated in following sample: local gtype = 'ForeignWidget' local widget = GObject.Object.new(gtype) local window = Gtk.Window { title = 'foreign', widget } ### 3.2. Calling methods Methods are functions grouped inside class (or interface) declarations, accepting pointer to class instance as first argument. Most usual technique to invoke method is using `:` operator, e.g. `window:show_all()`. This is of course identical with `window.show_all(window)`, as is convention in plain Lua. Method declaration itself is also available in the class and it is possible to invoke it without object notation, so previous example can be also rewritten as `Gtk.Window.show_all(window)`. Note that this way of invoking removes dynamic lookup of the method from the object instance type, so it might be marginally faster. However, in case that `window` is actually instance of some `GtkWindow` descendant, lets say `MyWindow`, which also defined `my_window_show_all()` method, there will be a difference: `window:show_all()` will invoke `my_window_show_all(window)`, while `Gtk.Window.show_all(window)` will of course invoke non-specialized `gtk_widget_show_all(window)`. #### 3.2.1. Static methods Static methods (i.e. functions which do not take class instance as first argument) are usually invoked using class namespace, e.g. `Gtk.Window.list_toplevels()`. Very common form of static methods are `new` constructors, e.g. `Gtk.Window.new()`. Note that in most cases, `new` constructors are provided only as convenience for C programmers, in lgi it might be preferable to use `window = Gtk.Window { type = Gtk.WindowType.TOPLEVEL }` instead of `window = Gtk.Window.new(Gtk.WindowType.TOPLEVEL)`. ### 3.3. Accessing properties Object properties are accessed simply by using `.` operator. Continuing previous example, we can write `window.title = window.title .. ' - new'`. Note that in GObject system, property and signal names can contain `-` character. Since this character is illegal in Lua identifiers, it is mapped to `_`, so `can-focus` window property is accessed as `window.can_focus`. ### 3.4. Signals Signals are exposed as `on_signalname` entities on the class instances. #### 3.4.1. Connecting signals Assigning Lua function connects that function to the signal. Signal routine gets object as first argument, followed by other arguments of the signal. Simple example: local window = Gtk.Window() window.on_destroy = function(w) assert(w == window) print("Destroyed", w) end Note that because of Lua's syntactic sugar for object access and function definition, it is possible to use signal connection even in following way: local window = Gtk.Window() function window:on_destroy() assert(self == window) print("Destroyed", self) end Reading signal entity provides temporary table which can be used for connecting signal with specification of the signal detail (see GObject documentation on signal detail explanation). An example of handler which is notified whenever window is activated or deactivated follows: local window = Gtk.Window() window.on_notify['is-active'] = function(self, pspec) assert(self == window) assert(pspec.name == 'is-active') print("Window is active:", self.is_active) end Both forms of signal connection connect handler before default signal handler. If connection after default signal handler is wanted (see `G_CONNECT_AFTER` documentation for details), the most generic connection call has to be used: `object.on_signalname:connect(target, detail, after)`. Previous example rewritten using this connection style follows: local window = Gtk.Window() local function notify_handler(self, pspec) assert(self == window) assert(pspec.name == 'is-active') print("Window is active:", self.is_active) end window.on_notify:connect(notify_handler, 'is-active', false) #### 3.4.2 Emitting signals Emitting existing signals is usually needed only when implementing subclasses of existing classes. Simplest method to emit a signal is to 'call' the signal on the class instance: local treemodel = treemodel:on_row_inserted(path, iter) ### 3.5. Dynamic typing of classes lgi assigns real class types to class instances dynamically, using runtime GObject introspection facilities. When new classes instance is passed from C code into Lua, lgi queries the real type of the object, finds the nearest type in the loaded repository and assigns this type to the Lua-side created proxy for the object. This means that there is no casting needed in lgi (and there is also no casting facility available). Hopefully everything can be explained in following example. Assume that `demo.ui` is GtkBuilder file containing definition of `GtkWindow` labeled `window1` and `GtkAction` called `action1` (among others). local builder = Gtk.Builder() builder:add_from_file('demo.ui') local window = builder:get_object('window1') -- Call Gtk.Window-specific method window:iconify() local action = builder:get_object('action1') -- Set Gtk.Action-specific property action.sensitive = false Although `Gtk.Builder.get_object()` method is marked as returning `GObject*`, lgi actually checks the real type of returned object and assigns proper type to it, so `builder:get_object('window1')` returns instance of `Gtk.Window` and `builder:get_object('action1')` returns instance of `Gtk.Action`. Another mechanism which allows complete lack of casting in lgi is automatic interface discovery. If some class implements some interface, the properties and methods of the interface are directly available on the class instance. ### 3.6. Accessing object's class instance GObject has the notion of object class. There are sometimes useful methods defined on objects class, which are accessible to lgi using object instance pseudo-property `_class`. For example, to list all properties registered for object's class, GObject library provides `g_object_class_list_properties()` function. Following sample lists all properties registered for the given object instance. function dump_props(obj) print("Dumping properties of ", obj) for _, pspec in pairs(obj._class:list_properties()) do print(pspec.name, pspec.value_type) end end Running `dump_props(Gtk.Window())` yields following output: Dumping props of lgi.obj 0xe5c070:Gtk.Window(GtkWindow) name gchararray parent GtkContainer width-request gint height-request gint visible gboolean sensitive gboolean ... (and so on) ### 3.7. Querying the type of the object instances To query whether given Lua value is actually an instance of specified class or subclass, class types define `is_type_of` method. This class-method takes one argument and checks, whether given argument as an instance of specified class (or implements specified interface, when called on interface instances). Following examples demonstrate usage of this construct: local window = Gtk.Window() print(Gtk.Window:is_type_of(window)) -- prints 'true' print(Gtk.Widget:is_type_of(window)) -- prints 'true' print(Gtk.Buildable:is_type_of(window)) -- prints 'true' print(Gtk.Action:is_type_of(window)) -- prints 'false' print(Gtk.Window:is_type_of('string')) -- prints 'false' print(Gtk.Window:is_type_of(nil)) -- prints 'false' There is also possibility to query the type-table from instantiated object, using `_type` property. -- Checks, whether 'unknown' conforms to the type of the 'template' -- object. function same_type(template, unknown) local type = template._type return type:is_type_of(unknown) end ### 3.8. Implementing subclasses It is possible to implement subclass of any existing class in pure Lua. The reason to do so is to implement virtual methods of parent class (and possibly one or more interfaces). In order to create subclass, lgi requires to create `package` first, which is basically namespace where the new classes will live. To create a package, use `lgi.package` function: -- Create MyApp package local MyApp = lgi.package 'MyApp' Once the package is created, it is possible to reference it from `lgi` as any other existing namespace: local Gtk = lgi.Gtk local MyApp = lgi.MyApp To create subclass, use package's method `class(name, parent[, ifacelist])`: MyApp:class('MyWidget', Gtk.Widget) MyApp:class('MyModel', GObject.Object, { Gtk.TreeModel }) After that, newly created class behaves exactly the same as classes picked up from GObjectIntrospection namespaces, like shown in following examples: local widget = MyApp.MyWidget() widget:show() Note that it is important to override virtual methods _before_ any instance of the derived class (see chapter about virtual methods below). ### 3.8.1. Overriding virtual methods To make subclass useful, it is needed to override some of its virtual methods. Existing virtual methods are prefixed with `do_`. In order to call inherited virtual methods, it is needed to use an explicit function reference. There is an automatic property called `priv` which is plain Lua table and allows subclass implementation to store some internal status. All these techniques are illustrated in following sample: function MyApp.MyWidget:do_show() if not self.priv.invisible then -- All three lines perform forwarding to inherited virtual: Gtk.Widget.do_show(self) -- or: MyApp.MyWidget._parent.do_show(self) -- or: self._type._parent.do_show(self) end end -- Convenience method for setting MyWidget 'invisible' function MyApp.MyWidget:set_invisible(invisible) self.priv.invisible = invisible end The important fact is that virtual method overrides are picked up only up to the first instantiation of the class or inheriting new subclass from it. After this point, virtual function overrides are ignored. ### 3.8.2. Installing new properties To add new property for derived class, a new `GObject.ParamSpec` instance describing property must be added into `_property` table of derived class. This must be done before first instantiation of the class. By default, the value of the property is mirrored in `priv` table of the instance. However, it is possible to specify custom getter and setter method in `_property_set` and `_property_get` tables. Both approaches are illustrated in the following example with property called `my_label` MyApp.MyWidget._property.my_label = GObject.ParamSpecString( 'my_label', 'Nick string', 'Blurb string', 'def-value', { 'READABLE', 'WRITABLE', 'CONSTRUCT' }) function MyApp.MyWidget._property_set:my_label(new_value) print(('%s changed my_label from %s to %s'):format( self, self.priv-my_label, new_value)) self.priv.my_label = new_value end local widget = MyApp.MyWidget() -- Access through GObject's property machinery widget.my_label = 'label1' print(widget.my_label) -- Direct access to underlying storage print(widget.priv.my_label) ## 4. Structures and unions Structures and unions are supported in a very similar way to classes. They have only access to methods (in the same way as classes) and fields, which are very similar to the representation of properties on the classes. ### 4.1. Creating instances Structure instances are created by 'calling' structure definition, similar to creating new class: `local color = Gdk.RGBA()`. For simplest structures without constructor methods, the new structure is allocated and zero-initialized. It is also possible to pass table containing fields and values to which the fields should be initialized: `local blue = Gdk.RGBA { blue = 1, alpha = 1 }`. If the structure has defined any constructor named `new`, it is automatically mapped by lgi to the structure creation construct, so calling `local main_loop = GLib.MainLoop(nil, false)` is exactly equivalent with `local main_loop = GLib.MainLoop.new(nil, false)`, and `local color = Clutter.Color(0, 0, 0, 255)` is exactly equivalent with `local color = Clutter.Color.new(0, 0, 0, 255)`. ### 4.2. Calling methods and accessing fields. Structure methods are called in the same way as class methods: `struct:method()`, or `StructType.method()`. For example: local loop = GLib.MainLoop(nil, false) loop:run() -- Following line is equivalent GLib.MainLoop.run(loop) Fields are accessed using `.` operator on structure instance, for example local color = Gdk.RGBA { alpha = 1 } color.green = 0.5 print(color.red, color.green, color.alpha) -- Prints: 0 0.5 1 ## 5. Enums and bitflags, constants lgi primarily maps enumerations to strings containing uppercased nicks of enumeration constant names. Optionally, a direct enumeration value is also accepted. Similarly, bitflags are primarily handled as sets containing uppercased flag nicks, but also lists of these nicks or direct numeric value is accepted. When a numeric value cannot be mapped cleanly to the known set of bitflags, the remaining number is stored in the first array slot of the returned set. Note that this behavior changed in lgi 0.4; up to that alpha release, lgi handled enums and bitmaps exclusively as numbers only. The change is compatible in Lua->C direction, where numbers still can be used, but incompatible in C->Lua direction, where lgi used to return numbers, while now it returns either string with enum value or table with flags. ### 5.1. Accessing numeric values In order to retrieve real enum values from symbolic names, enum and bitflags are loaded into repository as tables mapping symbolic names to numeric constants. Fro example, dumping `Gtk.WindowType` enum yields following output: > dump(Gtk.WindowType) ["table: 0xef9bc0"] = { -- table: 0xef9bc0 TOPLEVEL = 0; POPUP = 1; }; so constants can be referenced using `Gtk.WindowType.TOPLEVEL` construct, or directly using string `'TOPLEVEL'` when a `Gtk.WindowType` is expected. ### 5.2. Backward mapping, getting names from numeric values There is another facility in lgi, which allows backward mapping of numeric constants to symbolic names. Indexing enum table with number actually provides symbolic name to which the specified constant maps: > print(Gtk.WindowType[0]) TOPLEVEL > print(Gtk.WindowType[2]) nil Indexing bitflags table with number provides table containing list of all symbolic names which make up the requested value: > dump(Gtk.RegionFlags) ["table: 0xe5dd10"] = { -- table: 0xe5dd10 ODD = 2; EVEN = 1; SORTED = 32; FIRST = 4; LAST = 8; > dump(Gtk.RegionFlags[34]) ["table: 0xedbba0"] = { -- table: 0xedbba0 SORTED = 32; ODD = 2; }; This way, it is possible to check for presence of specified flag very easily: if Gtk.RegionFlags[flags].ODD then -- Code handling region-odd case endif If the value cannot be cleanly decomposed to known flags, remaining bits are accumulated into number stored at index 1: > dump(Gtk.RegionFlags[51]) ["table: 0x242fb20"] = { -- table: 0x242fb20 EVEN = 1; SORTED = 32; [1] = 16; ODD = 2; }; To construct numeric value which can be passed to a function expecting an enum, it is possible to simply add requested flags. However, there is a danger if some definition contains multiple flags , in which case numeric adding produces incorrect results. Therefore, it is possible to use bitflags pseudoconstructor', which accepts table containing requested flags: > =Gtk.RegionFlags { 'FIRST', 'SORTED' } 36 > =Gtk.RegionFlags { Gtk.RegionFlags.ODD, 16, 'EVEN' } 19 ## 6. Threading and synchronization Lua platform does not allow running real concurrent threads in single Lua state. This rules out any usage of GLib's threading API. However, wrapped libraries can be using threads, and this can lead to situations that callbacks or signals can be invoked from different threads. To avoid corruption which would result from running multiple threads in a single Lua state, lgi implements one internal lock (mutex) which protects access to Lua state. lgi automatically locks (i.e. waits on) this lock when performing C->Lua transition (invoking Lua callback or returning from C call) and unlocks it on Lua->C transition (returning from Lua callback or invoking C call). In a typical GLib-based application, most of the runtime is spent inside mainloop. During this time, lgi lock is unlocked and mainloop can invoke Lua callbacks and signals as needed. This means that lgi-based application does not have to worry about synchronization at all. The only situation which needs intervention is when mainloop is not used or a different form of mainloop is used (e.g. Copas scheduler, Qt UI etc). In this case, lgi lock is locked almost all the time and callbacks and signals are blocked and cannot be delivered. To cope with this situation, a `lgi.yield()` call exists. This call temporarily unlocks the lgi lock, letting other threads to deliver waiting callbacks, and before returning the lock is closed back. This allows code which runs foreign, non-GLib style of mainloop to stick `lgi.yield()` calls to some repeatedly invoked place and thus allowing delivery of callbacks from other threads. ## 7. Logging GLib provides generic logging facility using `g_message` and similar C macros. These utilities are not directly usable in Lua, so lgi provides layer which allows logging messages using GLib logging facilities and controlling behavior of logging methods. All logging is controlled by `lgi.log` table. To allow logging in lgi-enabled code, `lgi.log.domain(name)` method exists. This method returns table containing methods `message`, `warning`, `critical`, `error` and `debug` methods, which take format string optionally followed by inserts and logs specified string. An example of typical usage follows: local lgi = require 'lgi' local log = lgi.log.domain('myapp') -- This is equivalent of C 'g_message("A message %d", 1)' log.message("A message %d", 1) -- This is equivalent to C 'g_warning("Not found")' log.warning("Not found") Note that format string is formatted using Lua's `string.format()`, so the rules for Lua formatting strings apply here. ## 8. Interoperability with native code There might be some scenarios where it is important to either export objects or records created in Lua into C code or vice versa. lgi allows transfers using Lua `lightuserdata` type. To get native pointer to the lgi object, use `_native` attribute of the object. To create lgi object from external pointer, it is possible to pass lightuserdata with object pointer to type constructor. Following example illustrates both techniques: -- Create Lua-side window object. local window = Gtk.Window { title = 'Hello' } -- Get native pointer to this object. local window_ptr = window._native // window_ptr can be now passed to C code, which can use it. GtkWindow *window = lua_touserdata (L, x); char *title; g_object_get (window, "title", &title); g_assert (g_str_equal (title, "Hello")); g_free (title); // Create object on the C side and pass it to Lua GtkButton *button = gtk_button_new_with_label ("Foreign"); lua_pushlightuserdata (L, button); lua_call (L, ...); -- Retrieve button on the Lua side. assert(type(button) == 'userdata') window:add(Gtk.Button(button)) Note that while the example demonstrates objects, the same mechanism works also for structures and unions. ## 9. GObject basic constructs Although GObject library is already covered by gobject-introspection, most of the elements in it are basic object system building blocks and either need or greatly benefit from special handling by lgi. ### 9.1. GObject.Type Contrary to C `GType` representation (which is unsigned number), lgi represents GType by its name, as a string. GType-related constants and methods useful for handling GType are present in `GObject.Type` namespace. Fundamental GType names are imported as constants into GObject.Type namespace, so that it is possible to use for example `GObject.Type.INT` where `G_TYPE_INT` would be used in C code. Following constants are available: > `NONE`, `INTERFACE`, `CHAR`, `UCHAR`, `BOOLEAN`, > `INT`, `UINT`, `LONG`, `ULONG`, `INT64`, `UINT64`, > `ENUM`, `FLAGS`, `FLOAT`, `DOUBLE`, `STRING`, > `POINTER`, `BOXED`, `PARAM`, `OBJECT`, `VARIANT` Moreover, functions operating on `GType` are also present in `GObject.Type` namespace: > `parent`, `depth`, `next_base`, `is_a`, `children`, `interfaces`, > `query`, `fundamental_next`, `fundamental` There is special new method, `Type.type(gtype)` which returns lgi native type representing specified gtype. For example: assert(Gtk.Window == GObject.Type.type('GtkWindow')) assert(Gtk.WidgetPath == GObject.Type.type('GtkWidgetPath')) When transferring `GType` value from Lua to C (e.g. calling function which accepts argument of `GType`), it is possible to use either string with type name, number representing numeric `GType` value, or any loaded component which has its type assigned. Some examples of `GType` usage follow: lgi = require 'lgi' GObject = lgi.GObject Gtk = lgi.Gtk print(GObject.Type.NONE) print(GObject.Type.name(GObject.Type.NONE)) -- prints "void" in both cases print(GObject.Type.name(Gtk.Window)) -- prints "GtkWindow" print(GObject.Type.is_a(Gtk.Window, GObject.Type.OBJECT)) -- prints "true" print(GObject.Type.parent(Gtk.Window)) -- prints "GtkBin" ### 9.2. GObject.Value lgi does not implement any automatic `GValue` boxing or unboxing, because this would involve guessing `GType` from Lua value, which is generally unsafe. Instead, an easy to use and convenient wrappers for accessing `GValue` type and contents are provided. #### 9.2.1. Creation To create new `GObject.Value` instances, use similar method as for creating new structures or classes, i.e. 'call' `GObject.Value` type. The call has two optional arguments, specifying `GType` of newly created `GValue` and optionally also the contents of the value. A few examples for creating new values follow: local lgi = require 'lgi' local GObject = lgi.GObject local Gtk = lgi.Gtk local empty = GObject.Value() local answer = GObject.Value(GObject.Type.INT, 42) local null_window = GObject.Value(Gtk.Window) local window = GObject.Value(Gtk.Window, Gtk.Window()) #### 9.2.2. Boxing and unboxing GObject.Value instances `GObject.Value` adds two new virtual properties, called `gtype` and `value`. `gtype` contains actual type of the value, while `value` provides access to the contents of the value. Both properties are read/write. Reading of them queries current `GObject.Value` state, i.e. reading `value` performs actual `GValue` unboxing. Writing `value` performs value boxing, i.e. the source Lua item is attempted to be stored into the `GObject.Value`. Writing `gtype` attempts to change the type of the value, and in case that value already has a contents, it also converts contents to the new type (using `g_value_transform()`). Examples here continue using the values created in previous section example: assert(empty.gtype == nil) assert(empty.value == nil) assert(answer.gtype == GObject.Type.INT) assert(answer.value == 42) assert(null_window.gtype == 'GtkWindow') assert(null_window.value == nil) empty.gtype = answer.gtype empty.value = 1 assert(empty.gtype == GObject.Type.INT) assert(empty.value == 1) answer.gtype = GObject.Type.STRING) assert(answer.value == '42') Although `GObject.Value` provides most of the GValue documented methods (e.g. `g_value_get_string()` is accessible as `GObject.Value.get_string()` and getting string contents of the value instance can be written as `value:get_string()`), `value` and `gtype` abstract properties are recommended to be used instead. ### 9.3. GObject.Closure Similar to GObject.Value, no automatic GClosure boxing is implemented. To create a new instance of `GClosure`, 'call' closure type and provide Lua function as an argument: closure = GObject.Closure(func) When the closure is emitted, a Lua function is called, getting `GObject.Value` as arguments and expecting to return `GObject.Value` instance. lgi-0.9.2/docs/overview.md000066400000000000000000000105661316674307300154370ustar00rootroot00000000000000# LGI Overview LGI is Lua binding to Gnome platform. It is implemented as dynamic binding using gobject-introspection. This means that all libraries with support for gobject-introspection can be used by LGI without any need to compile/install anything, assuming that proper .typelib file is installed and available. ## Installation ### Dependencies LGI depends on `gobject-introspection >= 1.30` package. To build, gobject-introspection development package must also be installed. Note that required gobject-introspection version is unfortunately rather new, currently mostly available only in unreleased-yet versions of major distributions (part of GNOME-3.2, e.g. Fedora 16). There is planned work to make LGI mostly work also with older gobject-introspection versions, which are part of GNOME-3.0. Pre-3.0 versions are not planned to be supported at all. In order to be able to use assorted gobject-based libraries through LGI, these libraries must have properly installed `.typelib` files. Most, if not all distributions already do this properly. ### Supported platforms LGI is currently tested on Linux (all sane Linux distributions should work fine) and Cygwin. There is no principal obstacle for supporting other platforms, as long as gobject-introspection library (and of course Lua) is ported and working there. ### Installing via LuaRocks The preferred way to install LGI is using luarocks. As of writing this document, LGI is not yet available on public luarocks server, so plain `luarocks install lgi` does not work yet, although it will be preferred way to install LGI in the future. Currently, LGI source must be downloaded, unpacked and installed using `luarocks make`. ### Installing using Makefile Another way to install LGI is using makefiles: make sudo make install [PREFIX=prefix-path] [DESTDIR=destir-path] Default `PREFIX` is `/usr/local` and default `DESTDIR` is empty. ## Quick overview All LGI functionality is available in Lua module lgi, which is loaded by using Lua `require` construct: local lgi = require 'lgi' All gobject-introspection accessible modules are now accessible in lgi table: local Gtk = lgi.Gtk local Gio = lgi.Gio local GLib = lgi.GLib To create instance of the class, simply 'call' the class in the namespace: local window = Gtk.Window() To access object properties and call methods on the object instances, use normal Lua object access notation: window.title = 'I am a window' window:show_all() window.title = window.title .. ' made by Lgi' Note that properties can have `-` (dash) character in them. It is illegal in Lua, so it is translated to `_` (underscore). window.has_resize_grip = true It is also possible to assign properties during object construction: local window = Gtk.Window { title = 'I am a window made by Lgi', has_resize_grip = true } Note that structures and unions are handled similarly to classes, but structure fields are accessed instead of properties. To connect signal to object instance, assign function to be run to `on_signalname` object slot: window.on_destroy = function(object) print('destroying', object) end Note that Lua has nice syntactic sugar for objects, so previous construction can also be written like this: function window:on_destroy() print('destroying', self) end Note that potential dashes in signal names are also translated to underscores to cope well with Lua identifier rules. Enumerations and bitflags are grouped in the enumeration name table, and real names are enumeration nicks uppercased. For example, `GTK_WINDOW_TOPLEVEL` identifier is accessible as `Gtk.WindowType.TOPLEVEL`. There is no need to handle any kind of memory management; LGI handles all reference counting internally in cooperation with Lua's garbage collector. For APIs which use callbacks, provide Lua function which will be called when the callback is invoked. It is also possible to pass coroutine instance as callback argument, in this case, coroutine is resumed and returning `coroutine.yield()` returns all arguments passed to the callback. The callback returns when coroutine yields again or finishes. Arguments passed to `coroutine.yield()` call or exit status of the coroutine are then used as return value from the callback. See examples in `samples` source directory to dive deeper into the LGI features. lgi-0.9.2/docs/reference.md000066400000000000000000000060311316674307300155170ustar00rootroot00000000000000# LGI Core Reference ## Core Core LGI functionality is accessible through `lgi` module, loaded by `require 'lgi'` command. LGI does not install itself into global namespace, caller has to use the return value from `require` call. - `lgi.'module'` - `module` string with module name, e.g.'Gtk' or 'WebKit'. Loads requested module of the latest version found into the repository. - `lgi.require(module, version)` - `module` string with module name, e.g. 'Gtk' or 'WebKit'. - `version` string with exact required version of the module Loads requested module with specified version into the repository. - `lgi.log.domain(name)` - `name` is string denoting logging area name, usually identifying the application or the library - `return` table containing - `message` - `warning` - `critical` - `error` - `debug` methods for logging messages. These methods accept format string and inserts, which are formatted according to Lua's `string.format` conventions. - `lgi.yield()` when called, unlocks LGI state lock, for a while, thus allowing potentially blocked callbacks or signals to enter the Lua state. When using LGI with GLib's MainLoop, this call is not needed at all. ## GObject basic constructs ### GObject.Type - `NONE`, `INTERFACE`, `CHAR`, `UCHAR`, `BOOLEAN`, `INT`, `UINT`, `LONG`, `ULONG`, `INT64`, `UINT64`, `ENUM`, `FLAGS`, `FLOAT`, `DOUBLE`, `STRING`, `POINTER`, `BOXED`, `PARAM`, `OBJECT`, `VARIANT` Constants containing type names of fundamental GObject types. - `parent`, `depth`, `next_base`, `is_a`, `children`, `interfaces`, `query`, `fundamental_next`, `fundamental` Functions for manipulating and querying `GType`. THey are direct mappings of `g_type_xxx()` APIs, e.g. `GObject.Type.parent()` behaves in the same way as `g_type_parent()` in C. ### GObject.Value - `GObject.Value([gtype [, val]])` - `gtype` type of the vlue to create, if not specified, defaults to `GObject.Type.NONE`. - `val` Lua value to initialize GValue with. Creates new GObject.Value of specified type, optionally assigns Lua value to it. For example, `local val = GObject.Value(GObject.Type.INT, 42)` creates GValue of type `G_TYPE_INT` and initializes it to value `42`. - `GObject.Value.gtype` - reading yields the gtype of the value - writing changes the type of the value. Note that if GValue is already initialized with some value, a `g_value_transform` is called to attempt to convert value to target type. - `GObject.Value.value` - reading retrieves Lua-native contents of the referenced Value (i.e. GValue unboxing is performed). - writing stores Lua-native contents to the Value (boxing is performed). ### GObject.Closure - `GObject.Glosure(func)` - `target` is Lua function or anything Lua-callable. Creates new GClosure instance wrapping given Lua callable. When the closure is emitted, `target` function is invoked, getting GObject.Value instances as arguments, and expecting single GObject.Value to be returned. lgi-0.9.2/docs/variant.md000066400000000000000000000136721316674307300152360ustar00rootroot00000000000000# LGI Variant support LGI provides extended overrides for supporting GLib's GVariant type. it supports folloing operations with variants: ## Creation Variants should be created using GLib.Variant(type, value) constructor. Type is either GLib.VariantType or just plain string describing requested type of the variant. Following types are supported: - `b`, `y`, `n`, `q`, `i`, `u`, `q`, `t`, `s`, `d`, `o`, `g` are basic types, see either GVariant documentation or DBus specification for their meaning. `value` argument is expected to contain appropriate string or number for the basic type. - `v` is variant type, `value` should be another GLib.Variant instance. - `m`type is 'maybe' type, `value` should be either `nil` or value acceptable for target type. - `a`type is array of values of specified type, `value` is expected to contain Lua table (array) with values for the array. If the array contains `nil` elements inside, it must contain also `n` field with the real length of the array. - `(typelist)` is tuple of types, `value` is expected to contain Lua table (array) with values for the tuple members. - `{key-value-pair}` is dictionary entry, `value` is expected to contain Lua table (array) with 2 values (key and value) for the entry. There are two convenience exceptions from above rules: - when array of dictionary entries is met (i.e. dictionary), `value` is expected to contain Lua table with keys and values mapping to dictionary keys and values - when array of bytes is met, a bytestring is expected in the form of Lua string, not array of byte numbers. Some examples creating valid variants follow: GLib = require('lgi').Glib local v1 = GLib.Variant('s', 'Hello') local v2 = GLib.Variant('d', 3.14) local v3 = GLib.Variant('ms', nil) local v4 = GLib.Variant('v', v3) local v5 = GLib.Variant('as', { 'Hello', 'world' }) local v6 = GLib.Variant('ami', { 1, nil, 2, n = 3 }) local v7 = GLib.Variant('(is)', { 100, 'title' }) local v8 = GLib.Variant('a{sd}', { pi = 3.14, one = 1 }) local v9 = GLib.Variant('aay', { 'bytetring1', 'bytestring2' }) ## Data access LGI implements following special properties for accessing data stored inside variants - `type` contains read-only string describing type of the variant - `value` unpacks value of the variant. Simple scalar types are unpacked into their corresponding Lua variants, tuples and dictionary entries are unpacked into Lua tables (arrays), child varaints are expanded for `v`-typed variants. Dictionaries return proxy table which can be indexed by dictionary keys to retrieve dictionary values. Generic arrays are __not__ automatically expanded, the source variants are returned are returned instead. - `# operator` Length operator is overriden for GLib.Variants, returning number of child elements. Non-compound variants always return 0, maybe-s return 0 or 1, arrays, tuples and dictionary entries return number of children subvariants. - `[number] operator` Compound variants can be indexed by number, returning n-th subvariant (array entry, n-th field of tuple etc). - `pairs() and ipairs()` Variants support these methods, which behave as standard Lua enumerators. Examples of extracting values from variants created above: assert(v1.type == 's' and v1.value == 'Hello') assert(v2.value == 3.14) assert(v3.value == nil and #v3 = 0) assert(v4.value == nil and #v4 = 1) assert(v5.value == v5 and #v5 == 2 and v5[2] == 'world') assert(#v6 == 3 and v6[2] == nil) assert(v7.value[1] == 100 and v7[1] == 100 and #v7 == 2) assert(v8.value.pi == 3.14 and v8.value['one'] == 1 and #v8 == 2) assert(v9[1] == 'bytestring1') for k, v in v8:pairs() do print(k, v) end ## Serialization To serialize variant into bytestream form, use `data` property, which return Lua string containing serialized variant. Deserialization is done by `Variant.new_from_data` constructor, which is similar to `g_variant_new_from_data`, but it does _not_ accept `destroy_notify` argument. See following serialization example: local v = GLib.Variant('s', 'Hello') local serialized = v.data assert(type(data) == 'string') local newv = GLib.Variant.new_from_data(serialized, true) assert(newv.type == 's' and newv.value == 'Hello') ## Other operations LGI also contains many of the original `g_variant_` APIs, but many of them are not useful because their functionality is covered in more Lua-native way by operations described above. However, there there are still some useful calls, which are enumerated here. All of them can be called using object notation on variant instances, e.g. `local vt = variant:get_type()` See GLib documentation for their closer description. - `print(with_types)` returns textual format of the variant. Note that LGI does not contain opposite operation, i.e. g_variant_parse is not implemented yet - `is_of_type(type)` checks whether variant instance conforms to specified type - `compare(other_variant)` and `equal(other_variant)` allow comparison of variant instances - `byteswap()`, `is_normal_form()` and `get_normal_form()` for affecting the binary representation of variants. - `get_type()` method returns `VariantType` instance representing type of the variant. Seldom useful, `type` property returning type as string is usually better choice. - `GLib.VariantBuilder` although builder is supported, it is seldom useful, because creation of variants using constructors above is usually preferred. The exception may be creating of very large arrays, where creating source Lua table with source array might waste too much memory. Building such array piece-by-piece using builder instance is preferred. Note that VariantBuilder's `end()` method clashes with lua `end` keyword, so it is renamed to `_end()`. - `VARIANT_TYPE_` constants are accessible as `GLib.VariantType.XXX`, e.g. `GLib.VariantType.STRING`. Although there should not be many cases where these constants are needed. lgi-0.9.2/lgi.lua000066400000000000000000000013641316674307300135710ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- LGI Lua-side core. -- -- Copyright (c) 2011 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ -- This is simple forwarder to real package 'lgi/init.lua'. Normally, -- lgi/init.lua could suffice, but this file is needed for two -- reasons: -- 1) Running uninstalled, because Lua unfortunately does not contain -- './?/init.lua' component in its package.path -- 2) Upgrading older installations (<0.2), where lgi.lua was the only -- installed file, it would take precedence over 'lgi/init.lua'. return require 'lgi.init' lgi-0.9.2/lgi/000077500000000000000000000000001316674307300130625ustar00rootroot00000000000000lgi-0.9.2/lgi/.gitignore000066400000000000000000000000141316674307300150450ustar00rootroot00000000000000version.lua lgi-0.9.2/lgi/Makefile000066400000000000000000000045211316674307300145240ustar00rootroot00000000000000# # Makefile for compiling lgi core module in standard-Lua variant # # Author: Pavel Holejsovsky # License: MIT # PREFIX = /usr/local HOST_OS = $(shell uname -s | tr A-Z a-z) LUA_VERSION=5.1 LUA_LIBDIR = $(PREFIX)/lib/lua/$(LUA_VERSION) LUA_SHAREDIR = $(PREFIX)/share/lua/$(LUA_VERSION) PKG_CONFIG = pkg-config GINAME = gobject-introspection-1.0 PKGS = $(GINAME) gmodule-2.0 libffi VERSION_FILE = version.lua ifneq ($(filter cygwin% msys% mingw%, $(HOST_OS)),) CORE = corelgilua51.dll LIBFLAG = -shared LIBS += -llua else ifeq ($(HOST_OS),darwin) CORE = corelgilua51.so LIBFLAG = -bundle -undefined dynamic_lookup CCSHARED = -fno-common GOBJECT_INTROSPECTION_LIBDIR = $(shell $(PKG_CONFIG) --variable=libdir $(GINAME)) else CORE = corelgilua51.so LIBFLAG = -shared CCSHARED = -fPIC endif endif OBJS = buffer.o callable.o core.o gi.o marshal.o object.o record.o ifndef CFLAGS ifndef COPTFLAGS CFLAGS = -Wall -Wextra -O2 -g endif endif ifeq ($(HOST_OS),darwin) CFLAGS += -DGOBJECT_INTROSPECTION_LIBDIR=\"$(GOBJECT_INTROSPECTION_LIBDIR)\" endif ALL_CFLAGS = $(CCSHARED) $(COPTFLAGS) $(LUA_CFLAGS) $(shell $(PKG_CONFIG) --cflags $(PKGS)) $(CFLAGS) LIBS += $(shell $(PKG_CONFIG) --libs $(PKGS)) ALL_LDFLAGS = $(LIBFLAG) $(LDFLAGS) DEPCHECK = .depcheck # Precondition check $(DEPCHECK) : Makefile $(PKG_CONFIG) --exists '$(GINAME) >= 0.10.8' --print-errors touch $@ .PHONY : all clean install all : $(CORE) $(VERSION_FILE) clean : rm -f $(CORE) $(OBJS) %.o : %.c $(CC) $(ALL_CFLAGS) -c -o $@ $< $(CORE) : $(OBJS) $(CC) $(ALL_LDFLAGS) -o $@ $(OBJS) $(LIBS) $(VERSION_FILE) : Makefile ../Makefile echo "return '$(VERSION)'" > $@ buffer.o : buffer.c lgi.h $(DEPCHECK) callable.o : callable.c lgi.h $(DEPCHECK) core.o : core.c lgi.h $(DEPCHECK) gi.o : gi.c lgi.h $(DEPCHECK) marshal.o : marshal.c lgi.h $(DEPCHECK) object.o : object.c lgi.h $(DEPCHECK) record.o : record.c lgi.h $(DEPCHECK) OVERRIDES = $(wildcard override/*.lua) CORESOURCES = $(wildcard *.lua) install : $(CORE) $(VERSION_FILE) mkdir -p $(DESTDIR)$(LUA_LIBDIR)/lgi cp $(CORE) $(DESTDIR)$(LUA_LIBDIR)/lgi mkdir -p $(DESTDIR)$(LUA_SHAREDIR) cp ../lgi.lua $(DESTDIR)$(LUA_SHAREDIR) mkdir -p $(DESTDIR)$(LUA_SHAREDIR)/lgi cp $(CORESOURCES) $(DESTDIR)$(LUA_SHAREDIR)/lgi mkdir -p $(DESTDIR)$(LUA_SHAREDIR)/lgi/override cp $(OVERRIDES) $(DESTDIR)$(LUA_SHAREDIR)/lgi/override lgi-0.9.2/lgi/buffer.c000066400000000000000000000044221316674307300145010ustar00rootroot00000000000000/* * Dynamic Lua binding to GObject using dynamic gobject-introspection. * * Copyright (c) 2010, 2011 Pavel Holejsovsky * Licensed under the MIT license: * http://www.opensource.org/licenses/mit-license.php * * Implementation of writable buffer object. */ #include #include "lgi.h" static int buffer_len (lua_State *L) { luaL_checkudata (L, 1, LGI_BYTES_BUFFER); lua_pushnumber (L, lua_objlen (L, 1)); return 1; } static int buffer_tostring (lua_State *L) { gpointer data = luaL_checkudata (L, 1, LGI_BYTES_BUFFER); lua_pushlstring (L, data, lua_objlen (L, 1)); return 1; } static int buffer_index (lua_State *L) { int index; unsigned char *buffer = luaL_checkudata (L, 1, LGI_BYTES_BUFFER); index = lua_tonumber (L, 2); if (index > 0 && (size_t) index <= lua_objlen (L, 1)) lua_pushnumber (L, buffer[index - 1]); else { luaL_argcheck (L, !lua_isnoneornil (L, 2), 2, "nil index"); lua_pushnil (L); } return 1; } static int buffer_newindex (lua_State *L) { int index; unsigned char *buffer = luaL_checkudata (L, 1, LGI_BYTES_BUFFER); index = luaL_checkint (L, 2); luaL_argcheck (L, index > 0 && (size_t) index <= lua_objlen (L, 1), 2, "bad index"); buffer[index - 1] = luaL_checkint (L, 3) & 0xff; return 0; } static const luaL_Reg buffer_mt_reg[] = { { "__len", buffer_len }, { "__tostring", buffer_tostring }, { "__index", buffer_index }, { "__newindex", buffer_newindex }, { NULL, NULL } }; static int buffer_new (lua_State *L) { size_t size; gpointer *buffer; const char *source = NULL; if (lua_type (L, 1) == LUA_TSTRING) source = lua_tolstring (L, 1, &size); else size = luaL_checknumber (L, 1); buffer = lua_newuserdata (L, size); if (source) memcpy (buffer, source, size); else memset (buffer, 0, size); luaL_getmetatable (L, LGI_BYTES_BUFFER); lua_setmetatable (L, -2); return 1; } static const luaL_Reg buffer_reg[] = { { "new", buffer_new }, { NULL, NULL } }; void lgi_buffer_init (lua_State *L) { /* Register metatables. */ luaL_newmetatable (L, LGI_BYTES_BUFFER); luaL_register (L, NULL, buffer_mt_reg); lua_pop (L, 1); /* Register global API. */ lua_newtable (L); luaL_register (L, NULL, buffer_reg); lua_setfield (L, -2, "bytes"); } lgi-0.9.2/lgi/callable.c000066400000000000000000001255051316674307300147750ustar00rootroot00000000000000/* * Dynamic Lua binding to GObject using dynamic gobject-introspection. * * Copyright (c) 2010, 2011, 2012, 2013 Pavel Holejsovsky * Licensed under the MIT license: * http://www.opensource.org/licenses/mit-license.php * * This code deals with calling from Lua to C and vice versa, using * gobject-introspection information and libffi machinery. */ #include "lgi.h" #include #include /* Kinds or Param structure variation. */ typedef enum _ParamKind { /* Ordinary typeinfo (ti)-based parameter. */ PARAM_KIND_TI = 0, /* Foreign record. ti is unused. */ PARAM_KIND_RECORD, /* Foreign enum/flags. ti contains underlying numeric type. */ PARAM_KIND_ENUM } ParamKind; /* Represents single parameter in callable description. */ typedef struct _Param { GITypeInfo *ti; GIArgInfo ai; /* Indicates whether ai field is valid. */ guint has_arg_info : 1; /* Direction of the argument. */ guint dir : 2; /* Ownership passing rule for output parameters. */ guint transfer : 2; /* Flag indicating whether this parameter is represented by Lua input and/or returned value. Not represented are e.g. callback's user_data, array sizes etc. */ guint internal : 1; /* Flag indicating that this is internal user_data value for the callback. This parameter is supplied automatically, not explicitely from Lua. */ guint internal_user_data : 1; /* Set to nonzero if this argument is user_data for closure which is marked as (scope call). */ guint call_scoped_user_data : 1; /* Number of closures bound to this argument. 0 if this is not user_data for closure. */ guint n_closures : 4; /* Type of the argument, one of ParamKind values. */ guint kind : 2; /* Index into env table attached to the callable, contains repotype table for specified argument. */ guint repotype_index : 4; } Param; /* Structure representing userdata allocated for any callable, i.e. function, method, signal, vtable, callback... */ typedef struct _Callable { /* Stored callable info. */ GICallableInfo *info; /* Address of the function. */ gpointer address; /* Optional, associated 'user_data' context field. */ gpointer user_data; /* Flags with function characteristics. */ guint has_self : 1; guint throws : 1; guint nargs : 6; guint ignore_retval : 1; guint is_closure_marshal : 1; /* Initialized FFI CIF structure. */ ffi_cif cif; /* Param return value and pointer to nargs Param instances. */ Param retval; Param *params; /* ffi_type* array here, contains ffi_type[nargs + 2] entries. */ /* params points here, contains Param[nargs] entries. */ } Callable; /* Address is lightuserdata of Callable metatable in Lua registry. */ static int callable_mt; /* Structure containing basic callback information. */ typedef struct _Callback { /* Thread which created callback and Lua-reference to it (so that it is not GCed). */ lua_State *L; int thread_ref; /* State lock, to be passed to lgi_state_enter() when callback is invoked. */ gpointer state_lock; } Callback; typedef struct _FfiClosureBlock FfiClosureBlock; /* Single element in FFI callbacks block. */ typedef struct _FfiClosure { /* Libffi closure object. */ ffi_closure ffi_closure; /* Pointer to the block to which this closure belongs. */ FfiClosureBlock *block; union { struct { /* Lua reference to associated Callable. */ int callable_ref; /* Callable's target to be invoked (either function, userdata/table with __call metafunction or coroutine (which is resumed instead of called). */ int target_ref; }; /* Closure's entry point, stored only temporarily until closure is created. */ gpointer call_addr; }; /* Flag indicating whether closure should auto-destroy itself after it is called. */ guint autodestroy : 1; /* Flag indicating whether the closure was already created. */ guint created : 1; } FfiClosure; /* Structure containing closure block. This is user_data block for C-side closure arguments. */ struct _FfiClosureBlock { /* 1st closure. */ FfiClosure ffi_closure; /* Target to be invoked. */ Callback callback; /* Number of other closures in the block, excluding the forst one contained already in this header. */ int closures_count; /* Variable-length array of pointers to other closures. Unfortunately libffi does not allow to allocate contiguous block containing more closures, otherwise this array would simply contain FfiClosure instances instead of pointers to dynamically allocated ones. */ FfiClosure *ffi_closures[1]; }; /* lightuserdata key to callable cache table. */ static int callable_cache; /* Gets ffi_type for given tag, returns NULL if it cannot be handled. */ static ffi_type * get_simple_ffi_type (GITypeTag tag) { ffi_type *ffi; switch (tag) { #define HANDLE_TYPE(tag, ffitype) \ case GI_TYPE_TAG_ ## tag: \ ffi = &ffi_type_ ## ffitype; \ break HANDLE_TYPE(VOID, void); HANDLE_TYPE(BOOLEAN, uint); HANDLE_TYPE(INT8, sint8); HANDLE_TYPE(UINT8, uint8); HANDLE_TYPE(INT16, sint16); HANDLE_TYPE(UINT16, uint16); HANDLE_TYPE(INT32, sint32); HANDLE_TYPE(UINT32, uint32); HANDLE_TYPE(INT64, sint64); HANDLE_TYPE(UINT64, uint64); HANDLE_TYPE(FLOAT, float); HANDLE_TYPE(DOUBLE, double); #if GLIB_SIZEOF_SIZE_T == 4 HANDLE_TYPE(GTYPE, uint32); #else HANDLE_TYPE(GTYPE, uint64); #endif #undef HANDLE_TYPE default: ffi = NULL; } return ffi; } /* Gets ffi_type for given Param instance. */ static ffi_type * get_ffi_type(Param *param) { switch (param->kind) { case PARAM_KIND_RECORD: return &ffi_type_pointer; case PARAM_KIND_ENUM: return param->ti ? get_simple_ffi_type (g_type_info_get_tag (param->ti)) : &ffi_type_sint; case PARAM_KIND_TI: break; } /* In case of inout or out parameters, the type is always pointer. */ GITypeTag tag = g_type_info_get_tag (param->ti); ffi_type* ffi = g_type_info_is_pointer(param->ti) ? &ffi_type_pointer : get_simple_ffi_type (tag); if (ffi == NULL) { /* Something more complex. */ if (tag == GI_TYPE_TAG_INTERFACE) { GIBaseInfo *ii = g_type_info_get_interface (param->ti); switch (g_base_info_get_type (ii)) { case GI_INFO_TYPE_ENUM: case GI_INFO_TYPE_FLAGS: ffi = get_simple_ffi_type (g_enum_info_get_storage_type (ii)); break; default: break; } g_base_info_unref (ii); } } return ffi != NULL ? ffi : &ffi_type_pointer; } /* If typeinfo specifies array with length parameter, mark it in specified callable as an internal one. */ static void callable_mark_array_length (Callable *callable, GITypeInfo *ti) { gint arg; if (g_type_info_get_tag (ti) == GI_TYPE_TAG_ARRAY && g_type_info_get_array_type (ti) == GI_ARRAY_TYPE_C) { arg = g_type_info_get_array_length (ti); if (arg >= 0 && arg < callable->nargs) callable->params[arg].internal = TRUE; } } static void callable_param_init (Param *param) { param->ti = NULL; param->has_arg_info =FALSE; param->internal = FALSE; param->internal_user_data = FALSE; param->n_closures = 0; param->call_scoped_user_data = FALSE; param->kind = PARAM_KIND_TI; param->repotype_index = 0; } static Callable * callable_allocate (lua_State *L, int nargs, ffi_type ***ffi_args) { int argi; /* Create userdata structure. */ luaL_checkstack (L, 2, NULL); Callable *callable = lua_newuserdata (L, sizeof (Callable) + sizeof (ffi_type) * (nargs + 2) + sizeof (Param) * nargs); lua_pushlightuserdata (L, &callable_mt); lua_rawget (L, LUA_REGISTRYINDEX); lua_setmetatable (L, -2); /* Inititialize callable contents. */ *ffi_args = (ffi_type **) &callable[1]; callable->params = (Param *) &(*ffi_args)[nargs + 2]; callable->nargs = nargs; callable->user_data = NULL; callable->info = NULL; callable->has_self = 0; callable->throws = 0; callable->ignore_retval = 0; callable->is_closure_marshal = 0; /* Clear all 'internal' flags inside callable parameters, parameters are then marked as internal during processing of their parents. */ callable_param_init (&callable->retval); for (argi = 0; argi < nargs; argi++) callable_param_init (&callable->params[argi]); return callable; } int lgi_callable_create (lua_State *L, GICallableInfo *info, gpointer addr) { Callable *callable; Param *param; ffi_type **ffi_arg, **ffi_args; ffi_type *ffi_retval; gint nargs, argi, arg; /* Allocate Callable userdata. */ nargs = g_callable_info_get_n_args (info); callable = callable_allocate (L, nargs, &ffi_args); callable->info = g_base_info_ref (info); callable->address = addr; if (GI_IS_FUNCTION_INFO (info)) { /* Get FunctionInfo flags. */ const gchar* symbol; gint flags = g_function_info_get_flags (info); if ((flags & GI_FUNCTION_IS_METHOD) != 0 && (flags & GI_FUNCTION_IS_CONSTRUCTOR) == 0) callable->has_self = 1; if ((flags & GI_FUNCTION_THROWS) != 0) callable->throws = 1; /* Resolve symbol (function address). */ symbol = g_function_info_get_symbol (info); if (!g_typelib_symbol (g_base_info_get_typelib (info), symbol, &callable->address)) /* Fail with the error message. */ return luaL_error (L, "could not locate %s(%s): %s", lua_tostring (L, -3), symbol, g_module_error ()); } else if (GI_IS_SIGNAL_INFO (info)) /* Signals always have 'self', i.e. the object on which they are emitted. */ callable->has_self = 1; /* Process return value. */ callable->retval.ti = g_callable_info_get_return_type (callable->info); callable->retval.dir = GI_DIRECTION_OUT; callable->retval.transfer = g_callable_info_get_caller_owns (callable->info); callable->retval.internal = FALSE; callable->retval.repotype_index = 0; ffi_retval = get_ffi_type (&callable->retval); callable_mark_array_length (callable, callable->retval.ti); /* Process 'self' argument, if present. */ ffi_arg = &ffi_args[0]; if (callable->has_self) *ffi_arg++ = &ffi_type_pointer; /* Process the rest of the arguments. */ param = &callable->params[0]; for (argi = 0; argi < nargs; argi++, param++, ffi_arg++) { g_callable_info_load_arg (callable->info, argi, ¶m->ai); param->has_arg_info = TRUE; param->ti = g_arg_info_get_type (¶m->ai); param->dir = g_arg_info_get_direction (¶m->ai); param->transfer = g_arg_info_get_ownership_transfer (¶m->ai); *ffi_arg = (param->dir == GI_DIRECTION_IN) ? get_ffi_type (param) : &ffi_type_pointer; /* Mark closure-related user_data fields and possibly destroy_notify fields as internal. */ arg = g_arg_info_get_closure (¶m->ai); if (arg >= 0 && arg < nargs) { callable->params[arg].internal = TRUE; if (arg == argi) callable->params[arg].internal_user_data = TRUE; callable->params[arg].n_closures++; if (g_arg_info_get_scope (¶m->ai) == GI_SCOPE_TYPE_CALL) callable->params[arg].call_scoped_user_data = TRUE; } arg = g_arg_info_get_destroy (¶m->ai); if (arg > 0 && arg < nargs) callable->params[arg].internal = TRUE; /* Similarly for array length field. */ callable_mark_array_length (callable, param->ti); /* In case that we have an out or inout argument and callable returns boolean, mark it as ignore_retval (because we will signalize failure by returning nil instead of extra value). */ if (param->dir != GI_DIRECTION_IN && g_type_info_get_tag (callable->retval.ti) == GI_TYPE_TAG_BOOLEAN) callable->ignore_retval = 1; } /* Manual adjustment of 'GObject.ClosureMarshal' type, which is crucial for lgi but is missing an array annotation in glib/gobject-introspection < 1.30. */ if (!GLIB_CHECK_VERSION (2, 30, 0) && !strcmp (g_base_info_get_namespace (info), "GObject") && !strcmp (g_base_info_get_name (info), "ClosureMarshal")) { callable->is_closure_marshal = 1; callable->params[2].internal = 1; } /* Add ffi info for 'err' argument. */ if (callable->throws) *ffi_arg++ = &ffi_type_pointer; /* Create ffi_cif. */ if (ffi_prep_cif (&callable->cif, FFI_DEFAULT_ABI, callable->has_self + nargs + callable->throws, ffi_retval, ffi_args) != FFI_OK) { lua_concat (L, lgi_type_get_name (L, callable->info)); return luaL_error (L, "ffi_prep_cif for `%s' failed", lua_tostring (L, -1)); } return 1; } static int callable_param_get_kind (lua_State *L) { int kind = -1, top = lua_gettop (L); if (lgi_udata_test (L, -1, LGI_GI_INFO)) kind = PARAM_KIND_TI; else { luaL_checktype (L, -1, LUA_TTABLE); lua_getmetatable (L, -1); if (!lua_isnil (L, -1)) { lua_getfield (L, -1, "_type"); if (!lua_isnil (L, -1)) { const char *type = lua_tostring (L, -1); if (g_strcmp0 (type, "struct") == 0 || g_strcmp0 (type, "union") == 0) kind = PARAM_KIND_RECORD; else if (g_strcmp0 (type, "enum") == 0 || g_strcmp0 (type, "flags") == 0) kind = PARAM_KIND_ENUM; } } } lua_settop (L, top); return kind; } static const char *dirs[] = { "in", "out", "inout", NULL }; /* Parses single 'Param' structure from the table on the top of the stack. Pops the table from the stack. */ static void callable_param_parse (lua_State *L, Param *param) { int kind = callable_param_get_kind (L); /* Initialize parameters to default values. */ param->transfer = GI_TRANSFER_NOTHING; param->ti = NULL; if (kind == -1) { /* Check the direction. */ lua_getfield (L, -1, "dir"); if (!lua_isnil (L, -1)) param->dir = luaL_checkoption (L, -1, dirs[0], dirs); lua_pop (L, 1); /* Get transfer flag, prepare default according to dir. */ lua_getfield (L, -1, "xfer"); param->transfer = lua_toboolean (L, -1) ? GI_TRANSFER_EVERYTHING : GI_TRANSFER_NOTHING; lua_pop (L, 1); /* Get type, assume record (if not overriden by real giinfo type below). */ lua_getfield (L, -1, "type"); if (!lua_isnil (L, -1)) { /* This is actually an enum, and 'type' field contains numeric type for this enum. Store it into the ti. */ GITypeInfo **ti = luaL_checkudata (L, -1, LGI_GI_INFO); param->ti = g_base_info_ref (*ti); } lua_pop (L, 1); /* Finally get the type from the table (from index 1) and replace the table with the type. */ lua_rawgeti (L, -1, 1); lua_replace (L, -2); } /* Parse the type. */ if (kind == -1) kind = callable_param_get_kind (L); if (kind == PARAM_KIND_TI) { /* Expect typeinfo. */ GITypeInfo **pti = lua_touserdata (L, -1); param->ti = g_base_info_ref (*pti); param->kind = kind; lua_pop (L, 1); } else if (kind == PARAM_KIND_ENUM || kind == PARAM_KIND_RECORD) { /* Add it to the env table. */ int index = lua_objlen (L, -2) + 1; lua_rawseti (L, -2, index); param->repotype_index = index; param->kind = kind; } else luaL_error (L, "bad efn def"); } /* Parses callable from given table. */ int lgi_callable_parse (lua_State *L, int info, gpointer addr) { Callable *callable; int nargs, i; ffi_type **ffi_args; ffi_type *ffi_retval; /* Allocate the raw structure. */ nargs = lua_objlen (L, info); callable = callable_allocate (L, nargs, &ffi_args); /* Create 'env' table. */ lua_newtable (L); /* Add function name to it. */ lua_getfield (L, info, "name"); lua_rawseti (L, -2, 0); /* Get address of the function. */ if (addr == NULL) { lua_getfield (L, info, "addr"); addr = lua_touserdata (L, -1); lua_pop (L, 1); } callable->address = addr; /* Handle 'return' table. */ lua_getfield (L, info, "ret"); /* Get ignore_retval flag. */ lua_getfield (L, -1, "phantom"); callable->ignore_retval = lua_toboolean (L, -1); lua_pop (L, 1); /* Parse return value param. */ callable->retval.dir = GI_DIRECTION_OUT; callable_param_parse (L, &callable->retval); ffi_retval = get_ffi_type (&callable->retval); /* Parse individual arguments. */ for (i = 0; i < nargs; i++) { lua_rawgeti (L, info, i + 1); callable->params[i].dir = GI_DIRECTION_IN; callable_param_parse (L, &callable->params[i]); ffi_args[i] = (callable->params[i].dir == GI_DIRECTION_IN) ? get_ffi_type (&callable->params[i]) : &ffi_type_pointer; } /* Handle 'throws' flag. */ lua_getfield (L, info, "throws"); callable->throws = lua_toboolean (L, -1); lua_pop (L, 1); if (callable->throws) ffi_args[i] = &ffi_type_pointer; /* Create ffi_cif. */ if (ffi_prep_cif (&callable->cif, FFI_DEFAULT_ABI, nargs + callable->throws, ffi_retval, ffi_args) != FFI_OK) return luaL_error (L, "ffi_prep_cif failed for parsed"); /* Attach env table to the returned callable instance. */ lua_setfenv (L, -2); return 1; } /* Checks whether given argument is Callable userdata. */ static Callable * callable_get (lua_State *L, int narg) { luaL_checkstack (L, 3, ""); if (lua_getmetatable (L, narg)) { lua_pushlightuserdata (L, &callable_mt); lua_rawget (L, LUA_REGISTRYINDEX); if (lua_rawequal (L, -1, -2)) { lua_pop (L, 2); return lua_touserdata (L, narg); } } lua_pushfstring (L, "expected lgi.callable, got %s", lua_typename (L, lua_type (L, narg))); luaL_argerror (L, narg, lua_tostring (L, -1)); return NULL; } static void callable_param_destroy (Param *param) { if (param->ti) g_base_info_unref (param->ti); } static int callable_gc (lua_State *L) { int i; /* Unref embedded 'info' field. */ Callable *callable = callable_get (L, 1); if (callable->info) g_base_info_unref (callable->info); /* Destroy all params. */ for (i = 0; i < callable->nargs; i++) callable_param_destroy (&callable->params[i]); callable_param_destroy (&callable->retval ); /* Unset the metatable / make the callable unusable */ lua_pushnil (L); lua_setmetatable (L, 1); return 0; } static void callable_describe (lua_State *L, Callable *callable, FfiClosure *closure) { luaL_checkstack (L, 2, ""); if (closure == NULL) lua_pushfstring (L, "%p", callable->address); else { gconstpointer ptr; lua_rawgeti (L, LUA_REGISTRYINDEX, closure->target_ref); ptr = lua_topointer (L, -1); if (ptr != NULL) lua_pushfstring (L, "%s: %p", luaL_typename (L, -1), lua_topointer (L, -1)); else lua_pushstring (L, luaL_typename (L, -1)); lua_replace (L, -2); } if (callable->info) { lua_pushfstring (L, "lgi.%s (%s): ", (GI_IS_FUNCTION_INFO (callable->info) ? "fun" : (GI_IS_SIGNAL_INFO (callable->info) ? "sig" : (GI_IS_VFUNC_INFO (callable->info) ? "vfn" : "cbk"))), lua_tostring (L, -1)); lua_concat (L, lgi_type_get_name (L, callable->info) + 1); } else { lua_getfenv (L, 1); lua_rawgeti (L, -1, 0); lua_replace (L, -2); lua_pushfstring (L, "lgi.efn (%s): %s", lua_tostring (L, -2), lua_tostring (L, -1)); lua_replace (L, -2); } lua_replace (L, -2); } static int callable_tostring (lua_State *L) { Callable *callable = callable_get (L, 1); callable_describe (L, callable, NULL); return 1; } static int callable_param_2c (lua_State *L, Param *param, int narg, int parent, GIArgument *arg, int callable_index, Callable *callable, void **args) { int nret = 0; if (param->kind == PARAM_KIND_ENUM && lua_type (L, narg) != LUA_TNUMBER) { /* Convert enum symbolic value to numeric one. */ lua_getfenv (L, callable_index); lua_rawgeti (L, -1, param->repotype_index); lua_pushvalue (L, narg); lua_call (L, 1, 1); narg = -1; } if (param->kind != PARAM_KIND_RECORD) { if (param->ti) nret = lgi_marshal_2c (L, param->ti, param->has_arg_info ? ¶m->ai : NULL, param->transfer, arg, narg, parent, callable->info, args + callable->has_self); else { union { GIArgument arg; int i; } *u = (gpointer) arg; u->i = lua_tonumber (L, narg); } /* Stack cleanup from enum value conversion. */ if (narg == -1) lua_pop (L, 2); } else { /* Marshal record according to custom information. */ lua_getfenv (L, callable_index); lua_rawgeti (L, -1, param->repotype_index); lgi_record_2c (L, narg, &arg->v_pointer, FALSE, param->transfer != GI_TRANSFER_NOTHING, TRUE, FALSE); lua_pop (L, 1); } return nret; } static void callable_param_2lua (lua_State *L, Param *param, GIArgument *arg, int parent, int callable_index, Callable *callable, void **args) { if (param->kind != PARAM_KIND_RECORD) { if (param->ti) lgi_marshal_2lua (L, param->ti, callable->info ? ¶m->ai : NULL, param->dir, param->transfer, arg, parent, callable->info, args + callable->has_self); else { union { GIArgument arg; int i; } *u = (gpointer) arg; lua_pushnumber (L, u->i); } } if (param->kind == PARAM_KIND_TI) return; lua_getfenv (L, callable_index); lua_rawgeti (L, -1, param->repotype_index); if (param->kind == PARAM_KIND_RECORD) { /* Marshal record according to custom information. */ lgi_record_2lua (L, arg->v_pointer, param->transfer != GI_TRANSFER_NOTHING, parent); lua_remove (L, -2); } else { /* Convert enum numeric value to symbolic one. */ lua_pushvalue (L, -3); lua_gettable (L, -2); lua_replace (L, -4); lua_pop (L, 2); } } static int callable_call (lua_State *L) { Param *param; int i, lua_argi, nret, caller_allocated = 0, nargs; GIArgument retval, *args; void **ffi_args, **redirect_out; GError *err = NULL; gpointer state_lock = lgi_state_get_lock (L); Callable *callable = callable_get (L, 1); /* Make sure that all unspecified arguments are set as nil; during marshalling we might create temporary values on the stack, which can be confused with input arguments expected but not passed by caller. */ lua_settop(L, callable->has_self + callable->nargs + 1); /* We cannot push more stuff than count of arguments we have. */ luaL_checkstack (L, callable->nargs, ""); /* Prepare data for the call. */ nargs = callable->nargs + callable->has_self; args = g_newa (GIArgument, nargs); redirect_out = g_newa (void *, nargs + callable->throws); ffi_args = g_newa (void *, nargs + callable->throws); /* Prepare 'self', if present. */ lua_argi = 2; nret = 0; if (callable->has_self) { GIBaseInfo *parent = g_base_info_get_container (callable->info); GIInfoType type = g_base_info_get_type (parent); if (type == GI_INFO_TYPE_OBJECT || type == GI_INFO_TYPE_INTERFACE) { args[0].v_pointer = lgi_object_2c (L, 2, g_registered_type_info_get_g_type (parent), FALSE, FALSE, FALSE); nret++; } else { lgi_type_get_repotype (L, G_TYPE_INVALID, parent); lgi_record_2c (L, 2, &args[0].v_pointer, FALSE, FALSE, FALSE, FALSE); nret++; } ffi_args[0] = &args[0]; lua_argi++; } /* Prepare proper call->ffi_args[] pointing to real args (or redirects in case of inout/out parameters). Note that this loop cannot be merged with following marshalling loop, because during marshalling of closure or arrays marshalling code can read/write values ahead of currently marshalled value. */ param = &callable->params[0]; for (i = 0; i < callable->nargs; i++, param++) { /* Prepare ffi_args and redirection for out/inout parameters. */ int argi = i + callable->has_self; if (param->dir == GI_DIRECTION_IN) ffi_args[argi] = &args[argi]; else { ffi_args[argi] = &redirect_out[argi]; redirect_out[argi] = &args[argi]; } if (param->n_closures > 0) { args[argi].v_pointer = lgi_closure_allocate (L, param->n_closures); if (param->call_scoped_user_data) /* Add guard which releases closure block after the call. */ *lgi_guard_create (L, lgi_closure_destroy) = args[argi].v_pointer; } } /* Process input parameters. */ nret = 0; param = &callable->params[0]; for (i = 0; i < callable->nargs; i++, param++) if (!param->internal) { int argi = i + callable->has_self; if (param->dir != GI_DIRECTION_OUT) nret += callable_param_2c (L, param, lua_argi++, 0, &args[argi], 1, callable, ffi_args); /* Special handling for out/caller-alloc structures; we have to manually pre-create them and store them on the stack. */ else if (callable->info && g_arg_info_is_caller_allocates (¶m->ai) && lgi_marshal_2c_caller_alloc (L, param->ti, &args[argi], 0)) { /* Even when marked as OUT, caller-allocates arguments behave as if they are actually IN from libffi POV. */ ffi_args[argi] = &args[argi]; /* Move the value on the stack *below* any already present temporary values. */ lua_insert (L, -nret - 1); caller_allocated++; } else /* Normal OUT parameters. Ideally we don't have to touch them, but see https://github.com/pavouk/lgi/issues/118 */ memset (&args[argi], 0, sizeof (args[argi])); } else if (param->internal_user_data) /* Provide userdata for the callback. */ args[i + callable->has_self].v_pointer = callable->user_data; /* Add error for 'throws' type function. */ if (callable->throws) { redirect_out[nargs] = &err; ffi_args[nargs] = &redirect_out[nargs]; } /* Unlock the state. */ lgi_state_leave (state_lock); /* Call the function. */ ffi_call (&callable->cif, callable->address, &retval, ffi_args); /* Heading back to Lua, lock the state back again. */ lgi_state_enter (state_lock); /* Pop any temporary items from the stack which might be stored there by marshalling code. */ lua_pop (L, nret); /* Handle return value. */ nret = 0; if (!callable->ignore_retval && (callable->retval.ti == NULL || (g_type_info_get_tag (callable->retval.ti) != GI_TYPE_TAG_VOID || g_type_info_is_pointer (callable->retval.ti)))) { callable_param_2lua (L, &callable->retval, &retval, LGI_PARENT_IS_RETVAL, 1, callable, ffi_args); nret++; lua_insert (L, -caller_allocated - 1); } else if (callable->ignore_retval) { /* Make sure that returned boolean is converted according to ffi_call rules. */ union { GIArgument arg; ffi_sarg s; } *ru = (gpointer) &retval; ru->arg.v_boolean = (gboolean) ru->s; } /* Check, whether function threw. */ if (err != NULL) { if (nret == 0) { lua_pushboolean (L, 0); nret = 1; } /* Wrap error instance into GLib.Error record. */ lgi_type_get_repotype (L, G_TYPE_ERROR, NULL); lgi_record_2lua (L, err, TRUE, 0); return nret + 1; } /* Process output parameters. */ param = &callable->params[0]; for (i = 0; i < callable->nargs; i++, param++) if (!param->internal && param->dir != GI_DIRECTION_IN) { if (callable->info && g_arg_info_is_caller_allocates (¶m->ai) && lgi_marshal_2c_caller_alloc (L, param->ti, NULL, -caller_allocated - nret)) /* Caller allocated parameter is already marshalled and lying on the stack. */ caller_allocated--; else { /* Marshal output parameter. */ callable_param_2lua (L, param, &args[i + callable->has_self], 0, 1, callable, ffi_args); lua_insert (L, -caller_allocated - 1); } /* In case that this callable is in ignore-retval mode and function actually returned FALSE, replace the already marshalled return value with NULL. */ if (callable->ignore_retval && !retval.v_boolean) { lua_pushnil (L); lua_replace (L, -caller_allocated - 2); } nret++; } /* When function can throw and we are not returning anything, be sure to return at least 'true', so that caller can check for error in a usual way (i.e. by Lua's assert() call). */ if (nret == 0 && callable->throws) { lua_pushboolean (L, 1); nret = 1; } g_assert (caller_allocated == 0); return nret; } static int callable_index (lua_State *L) { Callable *callable = callable_get (L, 1); const gchar *verb = lua_tostring (L, 2); if (g_strcmp0 (verb, "info") == 0) return lgi_gi_info_new (L, g_base_info_ref (callable->info)); else if (g_strcmp0 (verb, "params") == 0) { int index = 1, i; Param *param; lua_newtable (L); if (callable->has_self) { lua_newtable (L); lua_pushboolean (L, 1); lua_setfield (L, -2, "in"); lua_rawseti (L, -2, index++); } for (i = 0, param = callable->params; i < callable->nargs; i++, param++) if (!param->internal) { lua_newtable (L); /* Add name. */ if (param->has_arg_info) { lua_pushstring (L, g_base_info_get_name (¶m->ai)); lua_setfield (L, -2, "name"); } /* Add typeinfo. */ if (param->ti) { lgi_gi_info_new (L, g_base_info_ref (param->ti)); lua_setfield (L, -2, "typeinfo"); } /* Add in.out info. */ if (param->dir == GI_DIRECTION_IN || param->dir == GI_DIRECTION_INOUT) { lua_pushboolean (L, 1); lua_setfield (L, -2, "in"); } if (param->dir == GI_DIRECTION_OUT || param->dir == GI_DIRECTION_INOUT) { lua_pushboolean (L, 1); lua_setfield (L, -2, "out"); } lua_rawseti (L, -2, index++); } return 1; } else if (g_strcmp0 (verb, "user_data") == 0) { lua_pushlightuserdata (L, callable->user_data); return 1; } return 0; } static int callable_newindex (lua_State *L) { Callable *callable = callable_get (L, 1); if (g_strcmp0 (lua_tostring (L, 2), "user_data") == 0) callable->user_data = lua_touserdata (L, 3); return 0; } static const struct luaL_Reg callable_reg[] = { { "__gc", callable_gc }, { "__tostring", callable_tostring }, { "__call", callable_call }, { "__index", callable_index }, { "__newindex", callable_newindex }, { NULL, NULL } }; /* Closure callback, called by libffi when C code wants to invoke Lua callback. */ static void closure_callback (ffi_cif *cif, void *ret, void **args, void *closure_arg) { Callable *callable; int callable_index; FfiClosure *closure = closure_arg; FfiClosureBlock *block = closure->block; gint res = 0, npos, i, stacktop; gboolean call; Param *param; lua_State *L; (void)cif; /* Get access to proper Lua context. */ lgi_state_enter (block->callback.state_lock); lua_rawgeti (block->callback.L, LUA_REGISTRYINDEX, block->callback.thread_ref); L = lua_tothread (block->callback.L, -1); call = (closure->target_ref != LUA_NOREF); if (call) { /* We will call target method, prepare context/thread to do it. */ if (lua_status (L) != 0) { /* Thread is not in usable state for us, it is suspended, we cannot afford to resume it, because it is possible that the routine we are about to call is actually going to resume it. Create new thread instead and switch closure to its context. */ lua_State *newL = lua_newthread (L); lua_rawseti (L, LUA_REGISTRYINDEX, block->callback.thread_ref); L = newL; } lua_pop (block->callback.L, 1); block->callback.L = L; /* Remember stacktop, this is the position on which we should expect return values (note that callback_prepare_call already might have pushed function to be executed to the stack). */ stacktop = lua_gettop (L); /* Store function to be invoked to the stack. */ lua_rawgeti (L, LUA_REGISTRYINDEX, closure->target_ref); } else { /* Cleanup the stack of the original thread. */ lua_pop (block->callback.L, 1); stacktop = lua_gettop (L); if (lua_status (L) == 0) /* Thread is not suspended yet, so it contains initial function at the top of the stack, so count with it. */ stacktop--; } /* Get access to Callable structure. */ lua_rawgeti (L, LUA_REGISTRYINDEX, closure->callable_ref); callable = lua_touserdata (L, -1); callable_index = lua_gettop (L); /* Marshall 'self' argument, if it is present. */ npos = 0; if (callable->has_self) { GIBaseInfo *parent = g_base_info_get_container (callable->info); GIInfoType type = g_base_info_get_type (parent); gpointer addr = ((GIArgument*) args[0])->v_pointer; npos++; if (type == GI_INFO_TYPE_OBJECT || type == GI_INFO_TYPE_INTERFACE) lgi_object_2lua (L, addr, FALSE, FALSE); else if (type == GI_INFO_TYPE_STRUCT || type == GI_INFO_TYPE_UNION) { lgi_type_get_repotype (L, G_TYPE_INVALID, parent); lgi_record_2lua (L, addr, FALSE, 0); } else g_assert_not_reached (); } /* Marshal input arguments to lua. */ param = callable->params; for (i = 0; i < callable->nargs; ++i, ++param) if (!param->internal && param->dir != GI_DIRECTION_OUT) { if G_LIKELY (i != 3 || !callable->is_closure_marshal) { GIArgument *real_arg = args[i + callable->has_self]; GIArgument arg_value; if (param->dir == GI_DIRECTION_INOUT) { arg_value = *(GIArgument *) real_arg->v_pointer; real_arg = &arg_value; } callable_param_2lua (L, param, real_arg, 0, callable_index, callable, args + callable->has_self); } else { /* Workaround incorrectly annotated but crucial ClosureMarshal callback. Its 3rd argument is actually an array of GValue, not a single GValue as missing annotation suggests. */ guint i, nvals = ((GIArgument *)args[2])->v_uint32; GValue* vals = ((GIArgument *)args[3])->v_pointer; lua_createtable (L, nvals, 0); for (i = 0; i < nvals; ++i) { lua_pushnumber (L, i + 1); lgi_type_get_repotype (L, G_TYPE_VALUE, NULL); lgi_record_2lua (L, &vals[i], FALSE, 0); lua_settable (L, -3); } } npos++; } /* Remove callable userdata from callable_index, otehrwise they mess up carefully prepared stack structure. */ lua_remove (L, callable_index); /* Call it. */ if (call) { if (callable->throws) res = lua_pcall (L, npos, LUA_MULTRET, 0); else if (lua_pcall (L, npos, LUA_MULTRET, 0) != 0) { callable_describe (L, callable, closure); g_warning ("Error raised while calling '%s': %s", lua_tostring (L, -1), lua_tostring (L, -2)); lua_pop (L, 2); } } else { #if LUA_VERSION_NUM >= 502 res = lua_resume (L, NULL, npos); #else res = lua_resume (L, npos); #endif if (res == LUA_YIELD) /* For our purposes is YIELD the same as if the coro really returned. */ res = 0; else if (res == LUA_ERRRUN && !callable->throws) { /* If closure is not allowed to return errors and coroutine finished with error, rethrow the error in the context of the original thread. */ lua_xmove (L, block->callback.L, 1); lua_error (block->callback.L); } /* If coroutine somehow consumed more than expected(?), do not blow up, adjust to the new situation. */ if (stacktop > lua_gettop (L)) stacktop = lua_gettop (L); } /* Reintroduce callable to the stack, we might need it during marshalling of the response. Put it right before all returns. */ lua_rawgeti (L, LUA_REGISTRYINDEX, closure->callable_ref); lua_insert (L, stacktop + 1); callable_index = stacktop + 1; npos = stacktop + 2; /* Check, whether we can report an error here. */ if (res == 0) { int to_pop; GITypeTag tag; /* Make sure that all unspecified returns and outputs are set as nil; during marshalling we might create temporary values on the stack, which can be confused with output values expected but not passed by caller. */ lua_settop(L, lua_gettop (L) + callable->has_self + callable->nargs + 1); /* Marshal return value from Lua. */ tag = g_type_info_get_tag (callable->retval.ti); if (tag != GI_TYPE_TAG_VOID || g_type_info_is_pointer (callable->retval.ti)) { if (callable->ignore_retval) /* Return value should be ignored on Lua side, so we have to synthesize the return value for C side. We should return FALSE if next output argument is nil. */ *(ffi_sarg *) ret = lua_isnoneornil (L, npos) ? FALSE : TRUE; else { to_pop = callable_param_2c (L, &callable->retval, npos, LGI_PARENT_IS_RETVAL, ret, callable_index, callable, args + callable->has_self); if (to_pop != 0) { g_warning ("cbk `%s.%s': return (transfer none) %d, unsafe!", g_base_info_get_namespace (callable->info), g_base_info_get_name (callable->info), to_pop); lua_pop (L, to_pop); } npos++; } } /* Marshal output arguments from Lua. */ param = callable->params; for (i = 0; i < callable->nargs; ++i, ++param) if (!param->internal && param->dir != GI_DIRECTION_IN) { gpointer *arg = args[i + callable->has_self]; gboolean caller_alloc = callable->info && g_arg_info_is_caller_allocates (¶m->ai) && g_type_info_get_tag (param->ti) == GI_TYPE_TAG_INTERFACE; to_pop = callable_param_2c (L, param, npos, caller_alloc ? LGI_PARENT_CALLER_ALLOC : 0, *arg, callable_index, callable, args + callable->has_self); if (to_pop != 0) { g_warning ("cbk %s.%s: arg `%s' (transfer none) %d, unsafe!", g_base_info_get_namespace (callable->info), g_base_info_get_name (callable->info), g_base_info_get_name (¶m->ai), to_pop); lua_pop (L, to_pop); } npos++; } } else { /* If the function is expected to return errors, create proper error. */ GError **err = ((GIArgument *) args[callable->has_self + callable->nargs])->v_pointer; /* Check, whether thrown error is actually GLib.Error instance. */ lgi_type_get_repotype (L, G_TYPE_ERROR, NULL); lgi_record_2c (L, -2, err, FALSE, TRUE, TRUE, TRUE); if (*err == NULL) { /* Nope, so come up with something funny. */ GQuark q = g_quark_from_static_string ("lgi-callback-error-quark"); g_set_error_literal (err, q, 1, lua_tostring (L, -1)); lua_pop (L, 1); } /* Such function should usually return FALSE, so do it. */ if (g_type_info_get_tag (callable->retval.ti) == GI_TYPE_TAG_BOOLEAN) *(gboolean *) ret = FALSE; } /* If the closure is marked as autodestroy, destroy it now. Note that it is unfortunately not possible to destroy it directly here, because we would delete the code under our feet and crash and burn :-(. Instead, we create marshal guard and leave it to GC to destroy the closure later. */ if (closure->autodestroy) *lgi_guard_create (L, lgi_closure_destroy) = block; /* This is NOT called by Lua, so we better leave the Lua stack we used pretty much tidied. */ lua_settop (L, stacktop); /* Going back to C code, release the state synchronization. */ lgi_state_leave (block->callback.state_lock); } /* Destroys specified closure. */ void lgi_closure_destroy (gpointer user_data) { FfiClosureBlock* block = user_data; lua_State *L = block->callback.L; FfiClosure *closure; int i; for (i = block->closures_count - 1; i >= -1; --i) { closure = (i < 0) ? &block->ffi_closure : block->ffi_closures[i]; if (closure->created) { luaL_unref (L, LUA_REGISTRYINDEX, closure->callable_ref); luaL_unref (L, LUA_REGISTRYINDEX, closure->target_ref); } if (i < 0) luaL_unref (L, LUA_REGISTRYINDEX, block->callback.thread_ref); ffi_closure_free (closure); } } /* Creates container block for allocated closures. Returns address of the block, suitable as user_data parameter. */ gpointer lgi_closure_allocate (lua_State *L, int count) { gpointer call_addr; int i; /* Allocate header block. */ FfiClosureBlock *block = ffi_closure_alloc (offsetof (FfiClosureBlock, ffi_closures) + (--count * sizeof (FfiClosure*)), &call_addr); block->ffi_closure.created = 0; block->ffi_closure.call_addr = call_addr; block->ffi_closure.block = block; block->closures_count = count; /* Allocate all additional closures. */ for (i = 0; i < count; ++i) { block->ffi_closures[i] = ffi_closure_alloc (sizeof (FfiClosure), &call_addr); block->ffi_closures[i]->created = 0; block->ffi_closures[i]->call_addr = call_addr; block->ffi_closures[i]->block = block; } /* Store reference to target Lua thread. */ block->callback.L = L; lua_pushthread (L); block->callback.thread_ref = luaL_ref (L, LUA_REGISTRYINDEX); /* Retrieve and remember state lock. */ block->callback.state_lock = lgi_state_get_lock (L); return block; } /* Creates closure from Lua function to be passed to C. */ gpointer lgi_closure_create (lua_State *L, gpointer user_data, int target, gboolean autodestroy) { FfiClosureBlock* block = user_data; FfiClosure *closure; Callable *callable; gpointer call_addr; int i; /* Find pointer to target FfiClosure. */ for (closure = &block->ffi_closure, i = 0; closure->created; ++i) { g_assert (i < block->closures_count); closure = block->ffi_closures[i]; } /* Prepare callable and store reference to it. */ callable = lua_touserdata (L, -1); call_addr = closure->call_addr; closure->created = 1; closure->autodestroy = autodestroy; closure->callable_ref = luaL_ref (L, LUA_REGISTRYINDEX); if (!lua_isthread (L, target)) { lua_pushvalue (L, target); closure->target_ref = luaL_ref (L, LUA_REGISTRYINDEX); } else { /* Switch thread_ref to actual target thread. */ lua_pushvalue (L, target); lua_rawseti (L, LUA_REGISTRYINDEX, block->callback.thread_ref); closure->target_ref = LUA_NOREF; } /* Create closure. */ if (ffi_prep_closure_loc (&closure->ffi_closure, &callable->cif, closure_callback, closure, call_addr) != FFI_OK) { lua_concat (L, lgi_type_get_name (L, callable->info)); luaL_error (L, "failed to prepare closure for `%'", lua_tostring (L, -1)); return NULL; } return call_addr; } /* Creates new Callable instance according to given gi.info. Lua prototype: callable = callable.new(callable_info[, addr]) or callable = callable.new(description_table[, addr]) */ static int callable_new (lua_State *L) { gpointer addr = lua_touserdata (L, 2); if (lua_istable (L, 1)) return lgi_callable_parse (L, 1, addr); else return lgi_callable_create (L, *(GICallableInfo **) luaL_checkudata (L, 1, LGI_GI_INFO), addr); } /* Callable module public API table. */ static const luaL_Reg callable_api_reg[] = { { "new", callable_new }, { NULL, NULL } }; void lgi_callable_init (lua_State *L) { /* Register callable metatable. */ lua_pushlightuserdata (L, &callable_mt); lua_newtable (L); luaL_register (L, NULL, callable_reg); lua_rawset (L, LUA_REGISTRYINDEX); /* Create cache for callables. */ lgi_cache_create (L, &callable_cache, NULL); /* Create public api for callable module. */ lua_newtable (L); luaL_register (L, NULL, callable_api_reg); lua_setfield (L, -2, "callable"); } lgi-0.9.2/lgi/class.lua000066400000000000000000000370701316674307300147010ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- LGI Support for generic GType objects and interfaces -- -- Copyright (c) 2010, 2011, 2012, 2013 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local type, tostring, rawget, rawset, pairs, select, getmetatable, setmetatable, error, assert = type, tostring, rawget, rawset, pairs, select, getmetatable, setmetatable, error, assert local string = require 'string' local core = require 'lgi.core' local gi = core.gi local component = require 'lgi.component' local record = require 'lgi.record' local ffi = require 'lgi.ffi' local ti = ffi.types local GObject = gi.require 'GObject' -- Implementation of class and interface component loading. local class = { class_mt = component.mt:clone( 'class', { '_virtual', '_property', '_signal', '_method', '_constant', '_field' }), interface_mt = component.mt:clone( 'interface', { '_virtual', '_property', '_signal', '_method', '_constant' }), } local type_class_peek = core.callable.new { addr = GObject.resolve.g_type_class_peek, ret = ti.ptr, ti.GType } local type_interface_peek = core.callable.new { addr = GObject.resolve.g_type_interface_peek, ret = ti.ptr, ti.ptr, ti.GType } local type_class = component.create(nil, record.struct_mt) ffi.load_fields(type_class, { { 'g_type', ti.GType } }) local type_instance = component.create(nil, record.struct_mt) ffi.load_fields(type_instance, { { 'g_class', type_class, ptr = true } }) -- Checks whether given argument is type of this class. function class.class_mt:is_type_of(instance) if type(instance) == 'userdata' then local instance_type = core.object.query(instance, 'repo') while instance_type do if instance_type == self or instance_type._implements[self._name] == self then return true end instance_type = rawget(instance_type, '_parent') end end return false end class.interface_mt.is_type_of = class.class_mt.is_type_of local function load_signal_name(name) name = name:match('^on_(.+)$') return name and name:gsub('_', '-') end local function load_signal_name_reverse(name) return 'on_' .. name:gsub('%-', '_') end local function load_vfunc_name(name) return name:match('^do_(.+)$') end local function load_vfunc_name_reverse(name) return 'do_' .. name end local function load_method(mi) local flags = mi.flags if not flags.is_getter and not flags.is_setter then return core.callable.new(mi) end end local function load_properties(info) return component.get_category( info.properties, nil, function(name) return string.gsub(name, '_', '-') end, function(name) return string.gsub(name, '%-', '_') end) end local function find_constructor(info) -- Check that ctor exists and return value conforms to info type. local ctor = gi[info.namespace][core.uncamel(info.name)] if ctor then local ret = ctor.return_type.interface for walk in function(_, c) return c.parent end, nil, info do if ret and walk == ret then ctor = core.callable.new(ctor) return function(self, ...) return ctor(...) end end end end end -- Resolver for classes, recursively resolves also all parents and -- implemented interfaces. function class.class_mt:_resolve(recursive) -- Resolve itself using inherited implementation. component.mt._resolve(self) -- Go to parent and implemented interfaces and resolve them too. if recursive then for _, iface in pairs(self._implements or {}) do iface:_resolve(recursive) end if self._parent then self._parent:_resolve(recursive) end end return self end -- _element implementation for objects, checks parent and implemented -- interfaces if element cannot be found in current typetable. local internals = { _native = true, _type = true, _gtype = true, _class = true, class = true } function class.class_mt:_element(instance, symbol) -- Special handling of internal symbols. if instance and internals[symbol] then return symbol, symbol end -- Check default implementation. local element, category = component.mt._element(self, instance, symbol) if element then return element, category end -- Check parent and all implemented interfaces. local parent = rawget(self, '_parent') if parent then element, category = parent:_element(instance, symbol) if element then return element, category end end local implements = rawget(self, '_implements') or {} for _, implemented in pairs(implements or {}) do element, category = implemented:_element(instance, symbol, self) if element then return element, category end end end -- Implementation of field accessor. Note that compound fields are -- not supported in classes (because they are not seen in the wild and -- I'm lazy). function class.class_mt:_access_field(instance, field, ...) return core.object.field(instance, field, ...) end -- Add accessor '_native' handling. function class.class_mt:_access_native(instance) return core.object.query(instance, 'addr') end -- Add accessor '_type' handling. function class.class_mt:_access_type(instance) return core.object.query(instance, 'repo') end -- Add accessor '_gtype' handling. function class.class_mt:_access_gtype(instance) -- Cast address of the instance to TypeInstance to get to type info. local ti = core.record.new(type_instance, core.object.query(instance, 'addr')) return ti.g_class.g_type end -- Add accessor '_class' handling. function class.class_mt:_access_class(instance) local gtype = class.class_mt._access_gtype(self, instance) return core.record.new(self._class, type_class_peek(gtype)) end class.class_mt._accessclass = class.class_mt._access_class -- Add accessor '_virtual' handling. function class.class_mt:_access_virtual(instance, vfi) local class_struct local container = vfi.container if container.is_interface then local gtype = class.class_mt._access_gtype(self, instance) local ptr = type_interface_peek(type_class_peek(gtype), container.gtype) class_struct = core.record.new(core.index[container.gtype]._class, ptr) else class_struct = class.class_mt._access_class(self, instance) end -- Retrieve proper method from the class struct. return class_struct[vfi.name] end -- Add __index for _virtual handling. Convert vfi baseinfo into real -- callable pointer according to the target type. function class.class_mt:_index_virtual(vfi) -- Get proper class struct, either from class or interface. local ptr, class_struct = type_class_peek(self._gtype) if not ptr then return nil end local container = vfi.container if container.is_interface then local gtype = container._gtype local ptr = type_interface_peek(ptr, gtype) class_struct = core.record.new(core.index[gtype]._class, ptr) else class_struct = core.record.new(self._class, ptr) end -- Retrieve proper method from the class struct. return class_struct[vfi.name] end function class.load_interface(namespace, info) -- Load all components of the interface. local interface = component.create(info, class.interface_mt) interface._property = load_properties(info) interface._method = component.get_category(info.methods, load_method) interface._signal = component.get_category( info.signals, nil, load_signal_name, load_signal_name_reverse) interface._constant = component.get_category(info.constants, core.constant) local type_struct = info.type_struct if type_struct then interface._virtual = component.get_category( info.vfuncs, nil, load_vfunc_name, load_vfunc_name_reverse) interface._class = record.load(type_struct) interface._class._gtype = interface._gtype interface._class._allow = true interface._class._parent = core.repo.GObject.TypeInterface end interface._new = find_constructor(info) return interface end function class.load_class(namespace, info) -- Find parent record, if available. local parent_info, parent = info.parent if parent_info then local ns, name = parent_info.namespace, parent_info.name if ns ~= namespace._name or name ~= info.name then parent = core.repo[ns][name] end end -- Create class instance, copy mt from parent, if parent exists, -- otherwise defaults to class_mt. local class = component.create( info, parent and getmetatable(parent) or class.class_mt) class._parent = parent class._property = load_properties(info) class._method = component.get_category(info.methods, load_method) class._signal = component.get_category( info.signals, nil, load_signal_name, load_signal_name_reverse) class._constant = component.get_category(info.constants, core.constant) class._field = component.get_category(info.fields) local type_struct = info.type_struct if type_struct then class._virtual = component.get_category( info.vfuncs, nil, load_vfunc_name, load_vfunc_name_reverse) class._class = record.load(type_struct) class._class._gtype = class._gtype class._class._allow = true class._class._parent = parent and parent._class or core.repo.GObject.TypeClass end -- Populate inheritation information (_implements and _parent fields). local interfaces, implements = info.interfaces, {} for i = 1, #interfaces do local iface = interfaces[i] implements[iface.fullname] = core.repo[iface.namespace][iface.name] end class._implements = implements class._new = find_constructor(info) return class end local register_static = core.callable.new(GObject.type_register_static) local type_query = core.callable.new(GObject.type_query) local type_add_interface_static = core.callable.new( GObject.type_add_interface_static) function class.class_mt:derive(typename, ifaces) -- Prepare repotable for newly registered class. local new_class = setmetatable( { _parent = self, _override = {}, _guard = {}, _implements = {}, _property = {}, _element = class.derived_mt._element, _property_get = {}, _property_set = {}, _class = self._class, _name = typename }, class.derived_mt) -- Create class-initialization closure, which assigns pointers to -- all known overriden virtual methods. local function class_init(class_addr) -- Create instance of real class. local class_addr = core.record.query(class_addr, "addr") or class_addr local class_struct = core.record.new(new_class._class, class_addr) -- Iterate through all overrides and assign to the virtual callbacks. for name, addr in pairs(new_class._override) do if type(addr) == 'userdata' then class_struct[name] = addr end end -- If type specified _class_init method, invoke it. local _class_init = new_class._class_init if _class_init then _class_init(new_class, class_struct) end end local class_init_guard, class_init_addr = core.marshal.callback( GObject.ClassInitFunc, class_init) new_class._guard._class_init = class_init_guard -- Create instance initialization function. Note that we do not -- pass directly user's method, because user will probably set it -- later after the type is already created, but we need to pass its -- address right now during type initialization. Therefore, a stub -- which looks up the init method of the type dynamically is used -- instead. local function instance_init(instance) local _init = rawget(new_class, '_init') if _init then -- Convert instance to real type and call init with it. _init(core.object.new(core.record.query(instance, 'addr'), false, true)) end end local instance_init_guard, instance_init_addr = core.marshal.callback( GObject.InstanceInitFunc, instance_init) new_class._guard._instance_init = instance_init_guard -- Prepare GTypeInfo with the registration. local parent_info = type_query(self._gtype) local type_info = core.repo.GObject.TypeInfo { class_size = parent_info.class_size, class_init = class_init_addr, instance_size = parent_info.instance_size, instance_init = instance_init_addr, } -- Create the name to register with the GType system. local g_typename = typename:gsub('%.', '') .. core.id -- Register new type with GType system. local gtype = register_static(self._gtype, g_typename, type_info, {}) rawset(new_class, '_gtype', core.gtype(gtype)) if not new_class._gtype then error(("failed to derive `%s' from `%s'"):format(typename, self._name)) end -- Add newly registered type into the lgi type index. core.index[new_class._gtype] = new_class -- Create interface initialization closures. for _, iface in pairs(ifaces or {}) do local override = {} new_class._override[iface._name] = override new_class._implements[iface._name] = iface -- Prepare interface initialization closure. local function iface_init(iface_addr) iface_addr = core.record.query(iface_addr, "addr") or iface_addr local iface_struct = core.record.new(iface._class, iface_addr) -- Iterate through all interface overrides and assign to the -- virtual callbacks. for name, addr in pairs(override) do iface_struct[name] = addr end end local iface_init_guard, iface_init_addr = core.marshal.callback( GObject.InterfaceInitFunc, iface_init) new_class._guard['_iface_init_' .. iface._name] = iface_init_guard -- Hook up interface to the registered class. local iface_info = core.repo.GObject.InterfaceInfo { interface_init = iface_init_addr, } type_add_interface_static(new_class, iface, iface_info) end return new_class end class.derived_mt = class.class_mt:clone('derived', {}) -- Support for 'priv' pseudomember, holding table with user -- implementation data. function class.derived_mt:_element(instance, symbol) -- Special handling of 'priv' attribute. if instance and symbol == 'priv' then return symbol, '_priv' end -- Check default implementation. local element, category = class.class_mt._element(self, instance, symbol) if element then return element, category end end function class.derived_mt:_access_priv(instance, name, ...) if select('#', ...) > 0 then error(("%s: cannot assign `%s'"):format(self._name, name), 5) end return core.object.env(instance) end -- Overload __newindex to catch assignment to virtual - this causes -- installation of new virtual method function class.derived_mt:__newindex(name, target) -- Use _element method to get category to write to. local _element = (rawget(self, '_element') or rawget(getmetatable(self), '_element')) local value, category = _element(self, nil, name) if category == '_virtual' then -- Overriding virtual method. Prepare callback to the target and -- store it to the _override type helper subtable. name = load_vfunc_name(name) local container = value.container local class_struct, override if container.is_interface then class_struct = core.index[container.gtype] override = self._override[class_struct._name] class_struct = class_struct._class else class_struct = self._class override = self._override end local guard, vfunc = core.marshal.callback( class_struct[name].callable, target) override[name] = vfunc self._guard[container.name .. ':' .. name] = guard else -- Simply assign to type. This most probably means adding new -- member function to the class (or some static member). rawset(self, name, target) end end return class lgi-0.9.2/lgi/component.lua000066400000000000000000000255721316674307300156020ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- lgi Basic repo type component implementation -- -- Copyright (c) 2010, 2011, 2012, 2013, 2016 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local assert, pcall, setmetatable, getmetatable, pairs, next, rawget, rawset, type, select, error = assert, pcall, setmetatable, getmetatable, pairs, next, rawget, rawset, type, select, error local table = require 'table' local string = require 'string' local core = require 'lgi.core' -- Generic component metatable. Component is any entity in the repo, -- e.g. record, object, enum, etc. local component = { mt = {} } -- Creates new component table by cloning all contents and setting -- Gets table for category of compound (i.e. _field of struct or _property -- for class etc). Installs metatable which performs on-demand lookup of -- symbols. function component.get_category(children, xform_value, xform_name, xform_name_reverse) -- Either none or both transform methods must be provided. assert(not xform_name or xform_name_reverse) -- Early shortcircuit; no elements, no table needed at all. if #children == 0 then return nil end -- Index contains array of indices which were still not retrieved -- from 'children' table, and table part contains name->index -- mapping. local index, mt = {}, {} for i = 1, #children do index[i] = i end -- Fully resolves the category (i.e. loads everything remaining to -- be loaded in given category) and disconnects on-demand loading -- metatable. local function resolve(category) -- Load al values from unknown indices. local ei, en, val local function xvalue(arg) if not xform_value then return arg end if arg then local ok, res = pcall(xform_value, arg) return ok and res end end while #index > 0 do ei = children[table.remove(index)] val = xvalue(ei) if val then en = ei.name en = not xform_name_reverse and en or xform_name_reverse(en) if en then category[en] = val end end end -- Load all known indices. for en, idx in pairs(index) do val = xvalue(children[idx]) en = not xform_name_reverse and en or xform_name_reverse(en) if en then category[en] = val end end -- Metatable is no longer needed, disconnect it. return setmetatable(category, nil) end function mt:__index(requested_name) -- Check if closure for fully resolving the category is needed. if requested_name == '_resolve' then return resolve end -- Transform name by transform function. local name = not xform_name and requested_name or xform_name(requested_name) if not name then return end -- Check, whether we already know its index. local idx, val = index[name] if idx then -- We know at least the index, so get info directly. val = children[idx] index[name] = nil else -- Not yet, go through unknown indices and try to find the -- name. while #index > 0 do idx = table.remove(index) val = children[idx] local en = val.name if en == name then break end val = nil index[en] = idx end end -- If there is nothing in the index, we can disconnect -- metatable, because everything is already loaded. if not next(index) then setmetatable(self, nil) end -- Transform found value and store it into the category (self) -- table. if not val then return nil end if xform_value then val = xform_value(val) end if not val then return nil end self[requested_name] = val return val end return setmetatable({}, mt) end -- Creates new component table by cloning all contents and setting -- categories table. function component.mt:clone(type, categories) local new_component = {} for key, value in pairs(self) do new_component[key] = value end new_component._type = type if categories then table.insert(categories, 1, '_attribute') new_component._categories = categories end return new_component end -- __call implementation, uses _new method to create new instance of -- component type. function component.mt:__call(...) return self:_new(...) end -- Fully resolves the whole typetable, i.e. load all symbols normally -- loaded on-demand at once. Returns self, so that resolve can be -- easily chained for the caller. function component.mt:_resolve() local categories = self._categories or {} for i = 1, #categories do -- Invoke '_resolve' function for all category tables, if they have it. local category = rawget(self, categories[i]) local resolve = type(category) == 'table' and category._resolve if resolve then resolve(category) end end return self end -- Implementation of _access method, which is called by _core when -- instance of repo-based type is accessed for reading or writing. function component.mt:_access(instance, symbol, ...) -- Invoke _element, which converts symbol to element and category. local element, category = self:_element(instance, symbol) if not element then error(("%s: no `%s'"):format(self._name, tostring(symbol)), 3) end -- Get category handler to be used, and invoke it. if category then local handler = self['_access' .. category] if handler then return handler(self, instance, element, ...) end end -- If specific accessor does not exist, consider the element to be -- 'static const' attribute of the class. This works well for -- methods, constants and assorted other elements added manually -- into the class by overrides. if select('#', ...) > 0 then error(("%s: `%s' is not writable"):format(self._name, symbol), 4) end return element end -- Keyword translation dictionary. Used for translating Lua keywords -- which might appear as symbols in typelibs into Lua-neutral identifiers. local keyword_dictionary = { _end = 'end', _do = 'do', _then = 'then', _elseif = 'elseif', _in = 'in', _local = 'local', _function = 'function', _nil = 'nil', _false = 'false', _true = 'true', _and = 'and', _or = 'or', _not = 'not', } -- Retrieves (element, category) pair from given componenttable and -- instance for given symbol. function component.mt:_element(instance, symbol, origin) -- This generic version can work only with strings. Refuse -- everything other, hoping that some more specialized _element -- implementation will handle it. if type(symbol) ~= 'string' then return end -- Check keyword translation dictionary. If the symbol can be -- found there, try to lookup translated symbol. symbol = keyword_dictionary[symbol] or symbol -- Check whether symbol is directly accessible in the component. local element = rawget(self, symbol) if element then return element end -- Check whether symbol is accessible in cached directory of the -- component, packed as element value and category local cached = rawget(self, '_cached') if cached then element = cached[symbol] if element then return element[1], element[2] end end -- Decompose symbol name, in case that it contains category prefix -- (e.g. '_field_name' when requesting explicitely field called -- name). local category, name = string.match(symbol, '^(_.-)_(.*)$') if category and name and category ~= '_access' then -- Check requested category. local cat = rawget(self, category) element = cat and cat[name] elseif string.sub(symbol, 1, 1) ~= '_' then -- Check all available categories. local categories = self._categories or {} for i = 1, #categories do category = categories[i] local cat = rawget(self, category) element = cat and cat[symbol] if element then break end end end if element then -- Make sure that table-based attributes have symbol name, so -- that potential errors contain the name of referenced -- attribute. if type(element) == 'table' and category == '_attribute' then element._name = element._name or symbol end -- If possible, cache the element in root table. if not category or not (origin or self)['_access' .. category] then -- No category or no special category handler is present, -- store it directly, which results in fastest access. This -- is most typical for methods. self[symbol] = element else -- Store into _cached table, because we have to preserve the -- category. if not cached then cached = {} self._cached = cached end cached[symbol] = { element, category } end return element, category end end -- __index implementation, uses _element method to perform lookup. function component.mt:__index(key) -- First try to invoke our own _element method. local _element, mt = rawget(self, '_element') if not _element then mt = getmetatable(self) _element = rawget(mt, '_element') end local value, category = _element(self, nil, key) if value then if category then -- Mangle the result by type-specific '_index_' -- method, if present. local index_name = '_index' .. category local index = rawget(self, index_name) if not index then if not mt then mt = getmetatable(self) end if mt then index = rawget(mt, index_name) end end if index then value = index(self, value) end end return value end -- If not found as object element, examine the metatable itself. return rawget(mt or getmetatable(self), key) end -- Implementation of attribute accessor. Attribute is either function -- to be directly invoked, or table containing set and get functions. function component.mt:_access_attribute(instance, element, ...) -- If element is a table, assume that this table contains 'get' and -- 'set' methods. Dispatch to them, and error out if they are -- missing. if type(element) == 'table' then local mode = select('#', ...) == 0 and 'get' or 'set' if not element[mode] then error(("%s: cannot %s `%s'"):format( self._name, mode == 'get' and 'read' or 'write', element._name or ''), 5) end element = element[mode] end -- Invoke attribute access function. return element(instance, ...) end -- Pretty prints type name function component.mt:__tostring() return self._name end -- Creates new component and sets up common parts according to given -- info. function component.create(info, mt, name) local gtype if core.gi.isinfo(info) then gtype = info.gtype name = info.fullname else gtype = info and core.gtype(info) end -- Fill in meta of the compound. local component = { _name = name } if gtype then -- Bind component in repo, make the relation using GType. component._gtype = gtype core.index[gtype] = component end return setmetatable(component, mt) end return component lgi-0.9.2/lgi/core.c000066400000000000000000000465631316674307300141740ustar00rootroot00000000000000/* * Dynamic Lua binding to GObject using dynamic gobject-introspection. * * Copyright (c) 2010, 2011, 2012, 2013 Pavel Holejsovsky * Licensed under the MIT license: * http://www.opensource.org/licenses/mit-license.php * * Core C utility API. */ #include #include "lgi.h" /* GLib 2.32 deprecated GStaticRecMutex in favor of GRecMutex. For older GLib versions, use still older version. */ #if !GLIB_CHECK_VERSION(2, 32, 0) #define GRecMutex GStaticRecMutex #define G_REC_MUTEX_INIT = G_STATIC_REC_MUTEX_INIT #define g_rec_mutex_init g_static_rec_mutex_init #define g_rec_mutex_lock g_static_rec_mutex_lock #define g_rec_mutex_unlock g_static_rec_mutex_unlock #define g_rec_mutex_clear g_static_rec_mutex_free #else #define G_REC_MUTEX_INIT #endif /* GLib 2.30 changed g_atomic_int_add() to return the new value. For older GLib versions, use g_atomic_int_exchange_and_add() which did. */ #if !GLIB_CHECK_VERSION(2, 30, 0) #ifdef g_atomic_int_add #undef g_atomic_int_add #endif #define g_atomic_int_add(atomic, val) \ (g_atomic_int_exchange_and_add ((atomic), (val))) #endif volatile gint global_state_id = 0; #ifndef NDEBUG const char *lgi_sd (lua_State *L) { int i; static gchar *msg = 0; g_free (msg); msg = g_strdup (""); int top = lua_gettop (L); for (i = 1; i <= top; i++) { int t = lua_type (L, i); gchar *item, *nmsg; switch (t) { case LUA_TSTRING: item = g_strdup_printf ("`%s'", lua_tostring (L, i)); break; case LUA_TBOOLEAN: item = g_strdup_printf (lua_toboolean (L, i) ? "true" : "false"); break; case LUA_TNUMBER: item = g_strdup_printf ("%g", lua_tonumber (L, i)); break; default: item = g_strdup_printf ("%s(%p)", lua_typename (L, t), lua_topointer (L, i)); break; } nmsg = g_strconcat (msg, " ", item, NULL); g_free (msg); g_free (item); msg = nmsg; } return msg; } #endif /* lightuserdata of this address is a key in LUA_REGISTRYINDEX table to repo table. */ static int repo; /* lightuserdata of this address is a key in LUA_REGISTRYINDEX table to index table mapping lightuserdata-gtype -> repotable. */ static int repo_index; void * lgi_udata_test (lua_State *L, int narg, const char *name) { void *udata = NULL; luaL_checkstack (L, 2, ""); lgi_makeabs (L, narg); if (lua_getmetatable (L, narg)) { luaL_getmetatable (L, name); if (lua_equal (L, -1, -2)) udata = lua_touserdata (L, narg); lua_pop (L, 2); } return udata; } void lgi_cache_create (lua_State *L, gpointer key, const char *mode) { lua_pushlightuserdata (L, key); lua_newtable (L); if (mode) { lua_newtable (L); lua_pushstring (L, mode); lua_setfield (L, -2, "__mode"); lua_setmetatable (L, -2); } lua_rawset (L, LUA_REGISTRYINDEX); } int lgi_type_get_name (lua_State *L, GIBaseInfo *info) { GSList *list = NULL, *i; int n = 1; lua_pushstring (L, g_base_info_get_namespace (info)); if (g_base_info_get_type (info) == GI_INFO_TYPE_CALLBACK) /* Avoid duplicate name for callbacks. */ info = g_base_info_get_container (info); /* Add names on the whole path, but in reverse order. */ for (; info != NULL; info = g_base_info_get_container (info)) if (!GI_IS_TYPE_INFO (info)) list = g_slist_prepend (list, info); for (i = list; i != NULL; i = g_slist_next (i)) { if (g_base_info_get_type (i->data) != GI_INFO_TYPE_TYPE) { lua_pushstring (L, "."); lua_pushstring (L, g_base_info_get_name (i->data)); n += 2; } } g_slist_free (list); return n; } void lgi_type_get_repotype (lua_State *L, GType gtype, GIBaseInfo *info) { luaL_checkstack (L, 4, ""); /* Get repo-index table. */ lua_pushlightuserdata (L, &repo_index); lua_rawget (L, LUA_REGISTRYINDEX); /* Prepare gtype, if not given directly. */ if (gtype == G_TYPE_INVALID && info && GI_IS_REGISTERED_TYPE_INFO (info)) { gtype = g_registered_type_info_get_g_type (info); if (gtype == G_TYPE_NONE) gtype = G_TYPE_INVALID; } /* First of all, check direct indexing of repo-index by gtype, should be fastest. */ if (gtype != G_TYPE_INVALID) { lua_pushlightuserdata (L, (gpointer) gtype); lua_rawget (L, -2); } else lua_pushnil (L); if (lua_isnil (L, -1)) { /* Not indexed yet. Try to lookup by name - this works when lazy-loaded repo tables are not loaded yet. */ if (!info && gtype != G_TYPE_INVALID) { info = g_irepository_find_by_gtype (NULL, gtype); lgi_gi_info_new (L, info); } else /* Keep stack balanced as in the previous 'if' branch. */ lua_pushnil (L); if (info) { lua_pushlightuserdata (L, &repo); lua_rawget (L, LUA_REGISTRYINDEX); lua_getfield (L, -1, g_base_info_get_namespace (info)); lua_getfield (L, -1, g_base_info_get_name (info)); lua_replace (L, -5); lua_pop (L, 3); } else lua_pop (L, 1); } lua_replace (L, -2); } GType lgi_type_get_gtype (lua_State *L, int narg) { /* Handle simple cases natively, forward to Lua implementation for the rest. */ switch (lua_type (L, narg)) { case LUA_TNIL: case LUA_TNONE: return G_TYPE_INVALID; case LUA_TNUMBER: return lua_tonumber (L, narg); case LUA_TLIGHTUSERDATA: return (GType) lua_touserdata (L, narg); case LUA_TSTRING: return g_type_from_name (lua_tostring (L, narg)); case LUA_TTABLE: { GType gtype; lgi_makeabs (L, narg); lua_pushstring (L, "_gtype"); lua_rawget (L, narg); gtype = lgi_type_get_gtype (L, -1); lua_pop (L, 1); return gtype; } default: return luaL_error (L, "GType expected, got %s", lua_typename (L, lua_type (L, narg))); } } typedef struct _Guard { gpointer data; GDestroyNotify destroy; } Guard; #define UD_GUARD "lgi.guard" static int guard_gc (lua_State *L) { Guard *guard = lua_touserdata (L, 1); if (guard->data != NULL) guard->destroy (guard->data); return 0; } gpointer * lgi_guard_create (lua_State *L, GDestroyNotify destroy) { Guard *guard = lua_newuserdata (L, sizeof (Guard)); g_assert (destroy != NULL); luaL_getmetatable (L, UD_GUARD); lua_setmetatable (L, -2); guard->data = NULL; guard->destroy = destroy; return &guard->data; } /* Converts any allowed GType kind to lightuserdata form. */ static int core_gtype (lua_State *L) { lua_pushlightuserdata (L, (gpointer) lgi_type_get_gtype (L, 1)); return 1; } /* Converts either GType or gi.info into repotype table. */ static int core_repotype (lua_State *L) { GType gtype = G_TYPE_INVALID; GIBaseInfo **info = lgi_udata_test (L, 1, LGI_GI_INFO); if (!info) gtype = lgi_type_get_gtype (L, 1); lgi_type_get_repotype (L, gtype, info ? *info : NULL); return 1; } /* Instantiate constant from given gi_info. */ static int core_constant (lua_State *L) { /* Get typeinfo of the constant. */ GIArgument val; GIConstantInfo *ci = *(GIConstantInfo **) luaL_checkudata (L, 1, LGI_GI_INFO); GITypeInfo *ti = g_constant_info_get_type (ci); lgi_gi_info_new (L, ti); g_constant_info_get_value (ci, &val); lgi_marshal_2lua (L, ti, NULL, GI_DIRECTION_IN, GI_TRANSFER_NOTHING, &val, 0, NULL, NULL); return 1; } typedef struct _LgiStateMutex { /* Pointer to either local state lock (next member of this structure) or to global package lock. */ GRecMutex *mutex; GRecMutex state_mutex; } LgiStateMutex; /* Global package lock (the one used for gdk_threads_enter/clutter_threads_enter) */ static GRecMutex package_mutex G_REC_MUTEX_INIT; /* GC method for GRecMutex structure, which lives inside lua_State. */ static int call_mutex_gc (lua_State* L) { LgiStateMutex *mutex = lua_touserdata (L, 1); g_rec_mutex_unlock (mutex->mutex); g_rec_mutex_clear (&mutex->state_mutex); return 0; } /* MT for CallMutex. */ static int call_mutex_mt; /* lightuserdata of address of this member is key to LUA_REGISTRYINDEX where CallMutex instance for this state resides. */ static int call_mutex; gpointer lgi_state_get_lock (lua_State *L) { gpointer state_lock; lua_pushlightuserdata (L, &call_mutex); lua_gettable (L, LUA_REGISTRYINDEX); state_lock = lua_touserdata (L, -1); lua_pop (L, 1); return state_lock; } void lgi_state_enter (gpointer state_lock) { LgiStateMutex *mutex = state_lock; GRecMutex *wait_on; /* There is a complication with lock switching. During the wait for the lock, someone could call core.registerlock() and thus change the lock protecting the state. Accomodate for this situation. */ for (;;) { wait_on = g_atomic_pointer_get (&mutex->mutex); g_rec_mutex_lock (wait_on); if (wait_on == mutex->mutex) break; /* The lock is changed, unlock this one and wait again. */ g_rec_mutex_unlock (wait_on); } } void lgi_state_leave (gpointer state_lock) { /* Get pointer to the call mutex belonging to this state. */ LgiStateMutex *mutex = state_lock; g_rec_mutex_unlock (mutex->mutex); } static const char* log_levels[] = { "ERROR", "CRITICAL", "WARNING", "MESSAGE", "INFO", "DEBUG", "???", NULL }; static int core_log (lua_State *L) { const char *domain = luaL_checkstring (L, 1); int level = 1 << (luaL_checkoption (L, 2, log_levels[5], log_levels) + 2); const char *message = luaL_checkstring (L, 3); #if GLIB_CHECK_VERSION(2, 50, 0) /* TODO: We can include more debug information such as lua line numbers */ g_log_structured (domain, level, "MESSAGE", "%s", message); #else g_log (domain, level, "%s", message); #endif return 0; } static int core_yield (lua_State *L) { /* Perform yield with unlocked mutex; this might force another threads waiting on the mutex to perform what they need to do (i.e. enter Lua with callbacks). */ gpointer state_lock = lgi_state_get_lock (L); lgi_state_leave (state_lock); g_thread_yield (); lgi_state_enter (state_lock); return 0; } static void package_lock_enter (void) { g_rec_mutex_lock (&package_mutex); } static void package_lock_leave (void) { g_rec_mutex_unlock (&package_mutex); } static gpointer package_lock_register[8] = { NULL }; static int core_registerlock (lua_State *L) { void (*set_lock_functions)(GCallback, GCallback); LgiStateMutex *mutex; GRecMutex *wait_on; unsigned i; /* Get registration function. */ luaL_checktype (L, 1, LUA_TLIGHTUSERDATA); set_lock_functions = lua_touserdata (L, 1); luaL_argcheck (L, set_lock_functions != NULL, 1, "NULL function"); /* Check, whether this package was already registered. */ for (i = 0; i < G_N_ELEMENTS (package_lock_register) && package_lock_register[i] != set_lock_functions; i++) { if (package_lock_register[i] == NULL) { /* Register our package lock functions. */ package_lock_register[i] = set_lock_functions; set_lock_functions (package_lock_enter, package_lock_leave); break; } } /* Switch our statelock to actually use packagelock. */ lua_pushlightuserdata (L, &call_mutex); lua_rawget (L, LUA_REGISTRYINDEX); mutex = lua_touserdata (L, -1); wait_on = g_atomic_pointer_get (&mutex->mutex); if (wait_on != &package_mutex) { g_rec_mutex_lock (&package_mutex); g_atomic_pointer_set (&mutex->mutex, &package_mutex); g_rec_mutex_unlock (wait_on); } return 0; } static int core_band (lua_State *L) { lua_pushnumber (L, (unsigned)luaL_checknumber (L, 1) & (unsigned)luaL_checknumber (L, 2)); return 1; } static int core_bor (lua_State *L) { lua_pushnumber (L, (unsigned)luaL_checknumber (L, 1) | (unsigned)luaL_checknumber (L, 2)); return 1; } #define UD_MODULE "lgi.core.module" static int module_gc (lua_State *L) { GModule **module = luaL_checkudata (L, 1, UD_MODULE); g_module_close (*module); /* Unset the metatable / make the module unusable */ lua_pushnil (L); lua_setmetatable (L, 1); return 0; } static int module_index (lua_State *L) { GModule **module = luaL_checkudata (L, 1, UD_MODULE); gpointer address; if (g_module_symbol (*module, luaL_checkstring (L, 2), &address)) { lua_pushlightuserdata (L, address); return 1; } lua_pushnil (L); lua_pushstring (L, g_module_error ()); return 2; } static const struct luaL_Reg module_reg[] = { { "__gc", module_gc }, { "__index", module_index }, { NULL, NULL } }; #if defined(G_WITH_CYGWIN) #define MODULE_NAME_FORMAT_VERSION "cyg%s-%d.dll" #define MODULE_NAME_FORMAT_PLAIN "cyg%s.dll" #elif defined(G_OS_WIN32) #define MODULE_NAME_FORMAT_VERSION "lib%s-%d.dll" #define MODULE_NAME_FORMAT_PLAIN "lib%s.dll" #elif defined(__APPLE__) #define MODULE_NAME_FORMAT_VERSION "lib%s.%d.dylib" #define MODULE_NAME_FORMAT_PLAIN "lib%s.dylib" #else #define MODULE_NAME_FORMAT_VERSION "lib%s.so.%d" #define MODULE_NAME_FORMAT_PLAIN "lib%s.so" #endif /* Creates 'module' object which resolves symbol names to lightuserdata addresses. module, path = core.module(basename[, version]) */ static int core_module (lua_State *L) { char *name; /* If the version is present, combine it with basename. Except on OpenBSD, where libraries are versioned like libfoo.so.0.0 and we will always load the shared object with the highest version number. */ #ifndef __OpenBSD__ if (!lua_isnoneornil (L, 2)) name = g_strdup_printf (MODULE_NAME_FORMAT_VERSION, luaL_checkstring (L, 1), (int) luaL_checkinteger (L, 2)); else #endif name = g_strdup_printf (MODULE_NAME_FORMAT_PLAIN, luaL_checkstring (L, 1)); #if defined(__APPLE__) char *path = g_module_build_path (GOBJECT_INTROSPECTION_LIBDIR, name); g_free(name); name = path; #endif /* Try to load the module. */ GModule *module = g_module_open (name, 0); if (module == NULL) { lua_pushnil (L); goto end; } /* Embed the module in the userdata for the module. */ *(GModule **) lua_newuserdata (L, sizeof (module)) = module; luaL_getmetatable (L, UD_MODULE); lua_setmetatable (L, -2); end: lua_pushstring (L, name); g_free (name); return 2; } static int core_upcase (lua_State *L) { gchar *str = g_ascii_strup (luaL_checkstring (L, 1), -1); lua_pushstring (L, str); g_free (str); return 1; } static int core_downcase (lua_State *L) { gchar *str = g_ascii_strdown (luaL_checkstring (L, 1), -1); lua_pushstring (L, str); g_free (str); return 1; } static const struct luaL_Reg lgi_reg[] = { { "log", core_log }, { "gtype", core_gtype }, { "repotype", core_repotype }, { "constant", core_constant }, { "yield", core_yield }, { "registerlock", core_registerlock }, { "band", core_band }, { "bor", core_bor }, { "module", core_module }, { "upcase", core_upcase }, { "downcase", core_downcase }, { NULL, NULL } }; static void create_repo_table (lua_State *L, const char *name, void *key) { lua_newtable (L); lua_pushlightuserdata (L, key); lua_pushvalue (L, -2); lua_rawset (L, LUA_REGISTRYINDEX); lua_setfield (L, -2, name); } static void set_resident (lua_State *L) { /* Get '_CLIBS' table from the registry (Lua5.2). */ lua_getfield (L, LUA_REGISTRYINDEX, "_CLIBS"); if (!lua_isnil (L, -1)) { /* Remove the very last item in they array part, which is handle to our loaded module used by _CLIBS.gctm to clean modules upon state cleanup. But before removing it, check, that it is really the handle of our module. Our module filename is passed as arg 2. */ lua_pushvalue (L, 2); lua_gettable (L, -2); lua_rawgeti (L, -2, lua_objlen (L, -2)); if (lua_equal (L, -1, -2)) { lua_pushnil (L); lua_rawseti (L, -4, lua_objlen (L, -4)); } lua_pop (L, 3); return; } else { if (lua_gettop(L) == 3) { /* Some Lua versions give us the path to the .so on the stack. Just load & leak it. */ GModule* module = g_module_open(lua_tostring(L, 2), G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); if (module != NULL) return; } /* This hack tries to enumerate the whole registry table and find 'LOADLIB: path' library. When it detects itself, it just removes pointer to the loaded library, disallowing Lua to close it, thus leaving it resident even when the state is closed. */ /* Note: 'nil' is on the stack from lua_getfield() call above. */ while (lua_next (L, LUA_REGISTRYINDEX)) { if (lua_type (L, -2) == LUA_TSTRING) { const char *str = lua_tostring (L, -2); if (g_str_has_prefix (str, "LOADLIB: ") && strstr (str, "corelgilua5")) { /* NULL the pointer to the loaded library. */ if (lua_type (L, -1) == LUA_TUSERDATA) { gpointer *lib = lua_touserdata (L, -1); *lib = NULL; } /* Clean the stack and return. */ lua_pop (L, 2); return; } } lua_pop (L, 1); } } } G_MODULE_EXPORT int luaopen_lgi_corelgilua51 (lua_State* L) { LgiStateMutex *mutex; gint state_id; /* Try to make itself resident. This is needed because this dynamic module is 'statically' linked with glib/gobject, and these libraries are not designed to be unloaded. Once they are unloaded, they cannot be safely loaded again into the same process. To avoid problems when repeatedly opening and closing lua_States and loading lgi into them, we try to make the whole 'core' module resident. */ set_resident (L); #if !GLIB_CHECK_VERSION(2, 36, 0) g_type_init (); #endif /* Early GLib initializations. Make sure that following fundamental G_TYPEs are already initialized. */ volatile GType unused; unused = G_TYPE_DATE; unused = G_TYPE_REGEX; unused = G_TYPE_DATE_TIME; unused = G_TYPE_VARIANT_TYPE; unused = G_TYPE_STRV; unused = unused; /* Register 'guard' metatable. */ luaL_newmetatable (L, UD_GUARD); lua_pushcfunction (L, guard_gc); lua_setfield (L, -2, "__gc"); lua_pop (L, 1); /* Register 'module' metatable. */ luaL_newmetatable (L, UD_MODULE); luaL_register (L, NULL, module_reg); lua_pop (L, 1); /* Register 'call-mutex' metatable. */ lua_pushlightuserdata (L, &call_mutex_mt); lua_newtable (L); lua_pushcfunction (L, call_mutex_gc); lua_setfield (L, -2, "__gc"); lua_rawset (L, LUA_REGISTRYINDEX); /* Create call mutex guard, keep it locked initially (it is unlocked only when we are calling out to GObject-C code) and store it into the registry. */ lua_pushlightuserdata (L, &call_mutex); mutex = lua_newuserdata (L, sizeof (*mutex)); mutex->mutex = &mutex->state_mutex; g_rec_mutex_init (&mutex->state_mutex); g_rec_mutex_lock (&mutex->state_mutex); lua_pushlightuserdata (L, &call_mutex_mt); lua_rawget (L, LUA_REGISTRYINDEX); lua_setmetatable (L, -2); lua_rawset (L, LUA_REGISTRYINDEX); /* Register 'lgi.core' interface. */ lua_newtable (L); luaL_register (L, NULL, lgi_reg); /* Add the state ID */ state_id = g_atomic_int_add (&global_state_id, 1); if (state_id == 0) lua_pushliteral (L, ""); else lua_pushfstring (L, "+L%d", state_id); lua_setfield (L, -2, "id"); /* Add lock and enter/leave locking functions. */ lua_pushlightuserdata (L, lgi_state_get_lock (L)); lua_setfield (L, -2, "lock"); lua_pushlightuserdata (L, lgi_state_enter); lua_setfield (L, -2, "enter"); lua_pushlightuserdata (L, lgi_state_leave); lua_setfield (L, -2, "leave"); /* Create repo and index table. */ create_repo_table (L, "index", &repo_index); create_repo_table (L, "repo", &repo); /* Initialize modules. */ lgi_buffer_init (L); lgi_gi_init (L); lgi_marshal_init (L); lgi_record_init (L); lgi_object_init (L); lgi_callable_init (L); /* Return registration table. */ return 1; } lgi-0.9.2/lgi/core.lua000066400000000000000000000013711316674307300145170ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- lgi Lua-side core module selector -- -- Copyright (c) 2010, 2011, 2015 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ -- This module decides what kind of core routines should be loaded. -- Currently only one implementation exists, standard-Lua C-side -- implementation, LuaJIT-FFI-based one is planned. local core = require 'lgi.corelgilua51' -- Helper methods for converting between CamelCase and uscore_delim -- names. function core.uncamel(name) return core.downcase(name:gsub('([%l%d])([%u])', '%1_%2')) end return core lgi-0.9.2/lgi/enum.lua000066400000000000000000000063671316674307300145450ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- lgi support for enums and bitflags -- -- Copyright (c) 2010, 2011, 2013 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local setmetatable, pairs, type, select = setmetatable, pairs, type, select local core = require 'lgi.core' local gi = core.gi local component = require 'lgi.component' local band, bor = core.band, core.bor local enum = { enum_mt = component.mt:clone('enum', { '_method' }), bitflags_mt = component.mt:clone('flags', { '_method' }), } function enum.load(info, meta) local enum_type = component.create(info, meta) enum_type.error_domain = info.error_domain if info.methods then enum_type._method = component.get_category( info.methods, core.callable.new) else -- Enum.methods was added only in GI1.30; for older gi, simulate -- the access using lookup in the global namespace. local prefix = core.downcase(info.name:gsub('%u+[^%u]+', '%1_')) local namespace = core.repo[info.namespace] enum_type._method = setmetatable( {}, { __index = function(_, name) return namespace[prefix .. name] end }) end -- Load all enum values. local values = info.values for i = 1, #values do local mi = values[i] enum_type[core.upcase(mi.name)] = mi.value end -- Install metatable providing reverse lookup (i.e name(s) by -- value). return enum_type end -- Enum reverse mapping, value->name. function enum.enum_mt:_element(instance, value) if type(value) == 'number' then for name, val in pairs(self) do if val == value then return name end end return value else return component.mt._element(self, instance, value) end end -- Constructs enum number from specified string. Eventually, for -- error-type enums, allows creating error instance. function enum.enum_mt:_new(param, ...) if type(param) == 'string' then param = self[param] end if self.error_domain and select('#', ...) > 0 then return core.repo.GLib.Error(self, param, ...) else return param end end -- Resolving arbitrary number to the table containing symbolic names -- of contained bits. function enum.bitflags_mt:_element(instance, value) if type(value) == 'number' then local result, remainder = {}, value for name, flag in pairs(self) do if type(flag) == 'number' and name:sub(1, 1) ~= '_' and band(value, flag) == flag then result[name] = true remainder = remainder - flag end end if remainder > 0 then result[1] = remainder end return result else return component.mt._element(self, instance, value) end end -- 'Constructs' number from specified flags (or accepts just number). function enum.bitflags_mt:_new(param) if type(param) == 'string' then return self[param] elseif type(param) == 'number' then return param else local num = 0 for key, value in pairs(param) do if type(key) == 'string' then value = key end if type(value) == 'string' then value = self[value] end num = bor(num, value) end return num end end return enum lgi-0.9.2/lgi/ffi.lua000066400000000000000000000134631316674307300143400ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- LGI Helpers for custom FFI definitions -- -- Copyright (c) 2012 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local pairs, ipairs, setmetatable, getmetatable = pairs, ipairs, setmetatable, getmetatable local math = require 'math' local core = require 'lgi.core' local gi = core.gi local component = require 'lgi.component' local enum = require 'lgi.enum' local record = require 'lgi.record' local ffi = {} local gobject = gi.require('GObject') local glib = gi.require('GLib') -- Gather all basic types. We have to 'steal' them from well-known -- declarations, because girepository API does not allow synthesizing -- GIBaseInfo instances from the air. ffi.types = { void = gobject.Callback.return_type, boolean = glib.Variant.methods.get_boolean.return_type, int8 = gobject.ParamSpecChar.fields.minimum.typeinfo, uint8 = gobject.ParamSpecUChar.fields.minimum.typeinfo, int16 = glib.Variant.methods.get_int16.return_type, uint16 = glib.Variant.methods.get_uint16.return_type, int32 = glib.Variant.methods.get_int32.return_type, uint32 = glib.Variant.methods.get_uint32.return_type, int64 = glib.Variant.methods.get_int64.return_type, uint64 = glib.Variant.methods.get_uint64.return_type, int = gobject.ParamSpecEnum.fields.default_value.typeinfo, uint = gobject.ParamSpecFlags.fields.default_value.typeinfo, long = gobject.ParamSpecLong.fields.default_value.typeinfo, ulong = gobject.ParamSpecULong.fields.default_value.typeinfo, float = gobject.ParamSpecFloat.fields.default_value.typeinfo, double = gobject.ParamSpecDouble.fields.default_value.typeinfo, utf8 = gobject.ParamSpecString.fields.default_value.typeinfo, filename = glib.file_get_contents.args[1].typeinfo, GType = gobject.ParamSpecGType.fields.is_a_type.typeinfo, GStrv = glib.KeyFile.methods.get_groups.return_type, GDestroyNotify = glib.Hook.fields.destroy.typeinfo, ptr = glib.CompareDataFunc.args[1].typeinfo, } for name, alias in pairs { char = 'int8', uchar = 'uint8', short = 'int16', ushort = 'uint16' } do ffi.types[name] = ffi.types[alias] end -- Gets gtype from specified resolved and _get_type function name. function ffi.load_gtype(resolver, get_type_name) local addr = resolver[get_type_name] if not addr then return nil, ("`%s' not found"):format(get_type_name) end local get_gtype = core.callable.new( { name = get_type_name, addr = addr, ret = ffi.types.GType }) return get_gtype() end -- Creates new enum/flags table with all values from specified gtype. function ffi.load_enum(gtype, name) local GObject = core.repo.GObject local is_flags = GObject.Type.is_a(gtype, GObject.Type.FLAGS) local enum_component = component.create( gtype, is_flags and enum.bitflags_mt or enum.enum_mt, name) local type_class = GObject.TypeClass.ref(gtype) local enum_class = core.record.cast( type_class, is_flags and GObject.FlagsClass or GObject.EnumClass) for i = 0, enum_class.n_values - 1 do local val = core.record.fromarray(enum_class.values, i) enum_component[core.upcase(val.value_nick):gsub('%-', '_')] = val.value end type_class:unref() return enum_component end -- Aligns offset to specified alignment. local function align(offset, align) return math.modf((offset + align - 1) / align) * align end -- Creates record from the table of the field definitions. function ffi.load_fields(rec, defs) rec._field = {} local offset = 0 local max_align = 1 local max_size = 0 for _, def in ipairs(defs) do local field = {} -- Get size and alignment of this field. local size, alignment if gi.isinfo(def[2]) then field[2] = 0 if def[2].tag == 'interface' then local ii = def[2].interface if ii.type == 'enum' or ii.type == 'flags' then size, alignment = core.marshal.typeinfo(ii.typeinfo) elseif ii.type == 'struct' or ii.type == 'union' then size, alignment = core.marshal.typeinfo(ffi.types.ptr) if not ii.is_pointer then -- Alignment is tricky; ideally we should go through -- the record and find alignment according to the -- largest alignments of the fields, but now we just -- punt and use 'ptr' as alignment. But this might -- be incorrect in case that doubles are used on 32b -- platform. -- Get size from the record descriptor. size = core.repotype(ii)._size end end else -- Basic type. size, alignment = core.marshal.typeinfo(def[2]) end else -- Either record or enum, decide according to repotable. local repotype = getmetatable(def[2])._type if repotype == 'struct' or repotype == 'union' then field[2] = 1 size, alignment = core.marshal.typeinfo(ffi.types.ptr) if not def.ptr then field[2] = 2 size = def[2]._size end elseif repotype == 'enum' or repotype == 'flags' then field[2] = 3 field[4] = def.type or ffi.types.int size, alignment = core.marshal.typeinfo(field[4]) end end -- Adjust offset according to the alignment. offset = align(offset, alignment) max_align = math.max(max_align, alignment) -- Create and add field definition. field[1] = offset field[3] = def[2] rec._field[def[1]] = field if getmetatable(rec)._type == 'union' then -- Remember largest size as the size of the union. max_size = math.max(max_size, align(size, alignment)) else -- Move offset after the field. offset = offset + size end end -- Store the total size of the record. rec._size = ((getmetatable(rec)._type == 'union') and max_size or align(offset, max_align)) end return ffi lgi-0.9.2/lgi/gi.c000066400000000000000000000507251316674307300136360ustar00rootroot00000000000000/* * Dynamic Lua binding to GObject using dynamic gobject-introspection. * * Copyright (c) 2010, 2011 Pavel Holejsovsky * Licensed under the MIT license: * http://www.opensource.org/licenses/mit-license.php * * Native Lua wrappers around GIRepository. */ #include #include "lgi.h" typedef GIBaseInfo *(* InfosItemGet)(GIBaseInfo* info, gint item); /* Creates new instance of info from given GIBaseInfo pointer. */ int lgi_gi_info_new (lua_State *L, GIBaseInfo *info) { if (info) { GIBaseInfo **ud_info; if (g_base_info_get_type (info) == GI_INFO_TYPE_INVALID) { g_base_info_unref (info); lua_pushnil (L); } else { ud_info = lua_newuserdata (L, sizeof (info)); *ud_info = info; luaL_getmetatable (L, LGI_GI_INFO); lua_setmetatable (L, -2); } } else lua_pushnil (L); return 1; } gpointer lgi_gi_load_function (lua_State *L, int typetable, const char *name) { GIBaseInfo **info; gpointer symbol = NULL; luaL_checkstack (L, 3, ""); lua_getfield (L, typetable, name); info = lgi_udata_test (L, -1, LGI_GI_INFO); if (info && GI_IS_FUNCTION_INFO (*info)) g_typelib_symbol (g_base_info_get_typelib (*info), g_function_info_get_symbol (*info), &symbol); else if (lua_islightuserdata (L, -1)) symbol = lua_touserdata (L, -1); lua_pop (L, 1); return symbol; } /* Userdata representing single group of infos (e.g. methods on object, fields of struct etc.). Emulates Lua table for access. */ typedef struct _Infos { GIBaseInfo *info; gint count; InfosItemGet item_get; } Infos; #define LGI_GI_INFOS "lgi.gi.infos" static int infos_len (lua_State *L) { Infos* infos = luaL_checkudata (L, 1, LGI_GI_INFOS); lua_pushnumber (L, infos->count); return 1; } static int infos_index (lua_State *L) { Infos* infos = luaL_checkudata (L, 1, LGI_GI_INFOS); gint n; if (lua_type (L, 2) == LUA_TNUMBER) { n = lua_tonumber (L, 2) - 1; luaL_argcheck (L, n >= 0 && n < infos->count, 2, "out of bounds"); return lgi_gi_info_new (L, infos->item_get (infos->info, n)); } else { const gchar *name = luaL_checkstring (L, 2); for (n = 0; n < infos->count; n++) { GIBaseInfo *info = infos->item_get (infos->info, n); if (strcmp (g_base_info_get_name (info), name) == 0) return lgi_gi_info_new (L, info); g_base_info_unref (info); } lua_pushnil (L); return 1; } } static int infos_gc (lua_State *L) { Infos *infos = luaL_checkudata (L, 1, LGI_GI_INFOS); g_base_info_unref (infos->info); /* Unset the metatable / make the infos unusable */ lua_pushnil (L); lua_setmetatable (L, 1); return 0; } /* Creates new userdata object representing given category of infos. */ static int infos_new (lua_State *L, GIBaseInfo *info, gint count, InfosItemGet item_get) { Infos *infos = lua_newuserdata (L, sizeof (Infos)); luaL_getmetatable (L, LGI_GI_INFOS); lua_setmetatable (L, -2); infos->info = g_base_info_ref (info); infos->count = count; infos->item_get = item_get; return 1; } static const luaL_Reg gi_infos_reg[] = { { "__gc", infos_gc }, { "__len", infos_len }, { "__index", infos_index }, { NULL, NULL } }; static int info_push_transfer (lua_State *L, GITransfer transfer) { if (0); #define H(n1, n2) \ else if (transfer == GI_TRANSFER_ ## n1) \ { \ lua_pushstring (L, #n2); \ return 1; \ } H(NOTHING, none) H(CONTAINER, container) H(EVERYTHING, full) #undef H return 0; } static int info_index (lua_State *L) { GIBaseInfo **info = luaL_checkudata (L, 1, LGI_GI_INFO); const gchar *prop = luaL_checkstring (L, 2); #define INFOS(n1, n2) \ else if (strcmp (prop, #n2 "s") == 0) \ return infos_new (L, *info, \ g_ ## n1 ## _info_get_n_ ## n2 ## s (*info), \ g_ ## n1 ## _info_get_ ## n2); #define INFOS2(n1, n2, n3) \ else if (strcmp (prop, #n3) == 0) \ return infos_new (L, *info, \ g_ ## n1 ## _info_get_n_ ## n3 (*info), \ g_ ## n1 ## _info_get_ ## n2); if (strcmp (prop, "type") == 0) { switch (g_base_info_get_type (*info)) { #define H(n1, n2) \ case GI_INFO_TYPE_ ## n1: \ lua_pushstring (L, #n2); \ return 1; H(FUNCTION, function) H(CALLBACK, callback) H(STRUCT, struct) H(BOXED, boxed) H(ENUM, enum) H(FLAGS, flags) H(OBJECT, object) H(INTERFACE, interface) H(CONSTANT, constant) H(UNION, union) H(VALUE, value) H(SIGNAL, signal) H(VFUNC, vfunc) H(PROPERTY, property) H(FIELD, field) H(ARG, arg) H(TYPE, type) H(UNRESOLVED, unresolved) #undef H default: g_assert_not_reached (); } } #define H(n1, n2) \ if (strcmp (prop, "is_" #n2) == 0) \ { \ lua_pushboolean (L, GI_IS_ ## n1 ## _INFO (*info)); \ return 1; \ } H(ARG, arg) H(CALLABLE, callable) H(FUNCTION, function) H(SIGNAL, signal) H(VFUNC, vfunc) H(CONSTANT, constant) H(FIELD, field) H(PROPERTY, property) H(REGISTERED_TYPE, registered_type) H(ENUM, enum) H(INTERFACE, interface) H(OBJECT, object) H(STRUCT, struct) H(UNION, union) H(TYPE, type) H(VALUE, value); #undef H if (!GI_IS_TYPE_INFO (*info)) { if (strcmp (prop, "name") == 0) { lua_pushstring (L, g_base_info_get_name (*info)); return 1; } else if (strcmp (prop, "namespace") == 0) { lua_pushstring (L, g_base_info_get_namespace (*info)); return 1; } } if (strcmp (prop, "fullname") == 0) { lua_concat (L, lgi_type_get_name (L, *info)); return 1; } if (strcmp (prop, "deprecated") == 0) { lua_pushboolean (L, g_base_info_is_deprecated (*info)); return 1; } else if (strcmp (prop, "container") == 0) { GIBaseInfo *container = g_base_info_get_container (*info); if (container) g_base_info_ref (container); return lgi_gi_info_new (L, container); } else if (strcmp (prop, "typeinfo") == 0) { GITypeInfo *ti = NULL; if (GI_IS_ARG_INFO (*info)) ti = g_arg_info_get_type (*info); else if (GI_IS_CONSTANT_INFO (*info)) ti = g_constant_info_get_type (*info); else if (GI_IS_PROPERTY_INFO (*info)) ti = g_property_info_get_type (*info); else if (GI_IS_FIELD_INFO (*info)) ti = g_field_info_get_type (*info); if (ti) return lgi_gi_info_new (L, ti); } if (GI_IS_REGISTERED_TYPE_INFO (*info)) { if (strcmp (prop, "gtype") == 0) { GType gtype = g_registered_type_info_get_g_type (*info); if (gtype != G_TYPE_NONE) lua_pushlightuserdata (L, (void *) gtype); else lua_pushnil (L); return 1; } else if (GI_IS_STRUCT_INFO (*info)) { if (strcmp (prop, "is_gtype_struct") == 0) { lua_pushboolean (L, g_struct_info_is_gtype_struct (*info)); return 1; } else if (strcmp (prop, "size") == 0) { lua_pushnumber (L, g_struct_info_get_size (*info)); return 1; } INFOS (struct, field) INFOS (struct, method); } else if (GI_IS_UNION_INFO (*info)) { if (strcmp (prop, "size") == 0) { lua_pushnumber (L, g_struct_info_get_size (*info)); return 1; } INFOS (union, field) INFOS (union, method); } else if (GI_IS_INTERFACE_INFO (*info)) { if (strcmp (prop, "type_struct") == 0) return lgi_gi_info_new (L, g_interface_info_get_iface_struct (*info)); INFOS (interface, prerequisite) INFOS (interface, vfunc) INFOS (interface, method) INFOS (interface, constant) INFOS2 (interface, property, properties) INFOS (interface, signal); } else if (GI_IS_OBJECT_INFO (*info)) { if (strcmp (prop, "parent") == 0) return lgi_gi_info_new (L, g_object_info_get_parent (*info)); else if (strcmp (prop, "type_struct") == 0) return lgi_gi_info_new (L, g_object_info_get_class_struct (*info)); INFOS (object, interface) INFOS (object, field) INFOS (object, vfunc) INFOS (object, method) INFOS (object, constant) INFOS2 (object, property, properties) INFOS (object, signal); } } if (GI_IS_CALLABLE_INFO (*info)) { if (strcmp (prop, "return_type") == 0) return lgi_gi_info_new (L, g_callable_info_get_return_type (*info)); else if (strcmp (prop, "return_transfer") == 0) return info_push_transfer (L, g_callable_info_get_caller_owns (*info)); INFOS (callable, arg); if (GI_IS_SIGNAL_INFO (*info)) { if (strcmp (prop, "flags") == 0) { GSignalFlags flags = g_signal_info_get_flags (*info); lua_newtable (L); #define H(n1, n2) \ if ((flags & G_SIGNAL_ ## n1) != 0) \ { \ lua_pushboolean (L, 1); \ lua_setfield (L, -2, #n2); \ } H(RUN_FIRST, run_first) H(RUN_LAST, run_last) H(RUN_CLEANUP, run_cleanup) H(NO_RECURSE, no_recurse) H(DETAILED, detailed) H(ACTION, action) H(NO_HOOKS, no_hooks); #undef H return 1; } } if (GI_IS_FUNCTION_INFO (*info)) { if (strcmp (prop, "flags") == 0) { GIFunctionInfoFlags flags = g_function_info_get_flags (*info); lua_newtable (L); if (0); #define H(n1, n2) \ else if ((flags & GI_FUNCTION_ ## n1) != 0) \ { \ lua_pushboolean (L, 1); \ lua_setfield (L, -2, #n2); \ } H(IS_METHOD, is_method) H(IS_CONSTRUCTOR, is_constructor) H(IS_GETTER, is_getter) H(IS_SETTER, is_setter) H(WRAPS_VFUNC, wraps_vfunc) H(THROWS, throws); #undef H return 1; } } } if (GI_IS_ENUM_INFO (*info)) { if (strcmp (prop, "storage") == 0) { GITypeTag tag = g_enum_info_get_storage_type (*info); lua_pushstring (L, g_type_tag_to_string (tag)); return 1; } #if GLIB_CHECK_VERSION (2, 30, 0) INFOS (enum, method) #endif INFOS (enum, value) else if (strcmp (prop, "error_domain") == 0) { const gchar *domain = g_enum_info_get_error_domain (*info); if (domain != NULL) lua_pushnumber (L, g_quark_from_string (domain)); else lua_pushnil (L); return 1; } } if (GI_IS_VALUE_INFO (*info)) { if (strcmp (prop, "value") == 0) { lua_pushnumber (L, g_value_info_get_value (*info)); return 1; } } if (GI_IS_ARG_INFO (*info)) { if (strcmp (prop, "direction") == 0) { GIDirection dir = g_arg_info_get_direction (*info); if (dir == GI_DIRECTION_OUT) lua_pushstring (L, g_arg_info_is_caller_allocates (*info) ? "out-caller-alloc" : "out"); else lua_pushstring (L, dir == GI_DIRECTION_IN ? "in" : "inout"); return 1; } if (strcmp (prop, "transfer") == 0) return info_push_transfer (L, g_arg_info_get_ownership_transfer (*info)); if (strcmp (prop, "optional") == 0) { lua_pushboolean (L, g_arg_info_is_optional (*info) || g_arg_info_may_be_null (*info)); return 1; } } if (GI_IS_PROPERTY_INFO (*info)) { if (strcmp (prop, "flags") == 0) { lua_pushnumber (L, g_property_info_get_flags (*info)); return 1; } else if (strcmp (prop, "transfer") == 0) return info_push_transfer (L, g_property_info_get_ownership_transfer (*info)); } if (GI_IS_FIELD_INFO (*info)) { if (strcmp (prop, "flags") == 0) { GIFieldInfoFlags flags = g_field_info_get_flags (*info); lua_newtable (L); if (0); #define H(n1, n2) \ else if ((flags & GI_FIELD_ ## n1) != 0) \ { \ lua_pushboolean (L, 1); \ lua_setfield (L, -2, #n2); \ } H(IS_READABLE, is_readable) H(IS_WRITABLE, is_writable) #undef H return 1; } else if (strcmp (prop, "size") == 0) { lua_pushnumber (L, g_field_info_get_size (*info)); return 1; } else if (strcmp (prop, "offset") == 0) { lua_pushnumber (L, g_field_info_get_offset (*info)); return 1; } } if (GI_IS_TYPE_INFO (*info)) { GITypeTag tag = g_type_info_get_tag (*info); if (strcmp (prop, "tag") == 0) { lua_pushstring (L, g_type_tag_to_string (tag)); return 1; } else if (strcmp (prop, "is_basic") == 0) { lua_pushboolean (L, G_TYPE_TAG_IS_BASIC (tag)); return 1; } else if (strcmp (prop, "params") == 0) { if (tag == GI_TYPE_TAG_ARRAY || tag == GI_TYPE_TAG_GLIST || tag == GI_TYPE_TAG_GSLIST || tag == GI_TYPE_TAG_GHASH) { lua_newtable (L); lgi_gi_info_new (L, g_type_info_get_param_type (*info, 0)); lua_rawseti (L, -2, 1); if (tag == GI_TYPE_TAG_GHASH) { lgi_gi_info_new (L, g_type_info_get_param_type (*info, 1)); lua_rawseti (L, -2, 2); } return 1; } } else if (strcmp (prop, "interface") == 0 && tag == GI_TYPE_TAG_INTERFACE) { lgi_gi_info_new (L, g_type_info_get_interface (*info)); return 1; } else if (strcmp (prop, "array_type") == 0 && tag == GI_TYPE_TAG_ARRAY) { switch (g_type_info_get_array_type (*info)) { #define H(n1, n2) \ case GI_ARRAY_TYPE_ ## n1: \ lua_pushstring (L, #n2); \ return 1; H(C, c) H(ARRAY, array) H(PTR_ARRAY, ptr_array) H(BYTE_ARRAY, byte_array) #undef H default: g_assert_not_reached (); } } else if (strcmp (prop, "is_zero_terminated") == 0 && tag == GI_TYPE_TAG_ARRAY) { lua_pushboolean (L, g_type_info_is_zero_terminated (*info)); return 1; } else if (strcmp (prop, "array_length") == 0) { int len = g_type_info_get_array_length (*info); if (len >= 0) { lua_pushnumber (L, len); return 1; } } else if (strcmp (prop, "fixed_size") == 0) { int size = g_type_info_get_array_fixed_size (*info); if (size >= 0) { lua_pushnumber (L, size); return 1; } } else if (strcmp (prop, "is_pointer") == 0) { lua_pushboolean (L, g_type_info_is_pointer (*info)); return 1; } } lua_pushnil (L); return 1; #undef INFOS #undef INFOS2 } static int info_eq (lua_State *L) { GIBaseInfo **i1 = luaL_checkudata (L, 1, LGI_GI_INFO); GIBaseInfo **i2 = luaL_checkudata (L, 2, LGI_GI_INFO); lua_pushboolean (L, g_base_info_equal (*i1, *i2)); return 1; } static int info_gc (lua_State *L) { GIBaseInfo **info = luaL_checkudata (L, 1, LGI_GI_INFO); g_base_info_unref (*info); return 0; } static const luaL_Reg gi_info_reg[] = { { "__gc", info_gc }, { "__index", info_index }, { "__eq", info_eq }, { NULL, NULL } }; /* Userdata representing symbol resolver of the namespace. */ #define LGI_GI_RESOLVER "lgi.gi.resolver" static int resolver_index (lua_State *L) { gpointer address; GITypelib **typelib = luaL_checkudata (L, 1, LGI_GI_RESOLVER); if (g_typelib_symbol (*typelib, luaL_checkstring (L, 2), &address)) { lua_pushlightuserdata (L, address); return 1; } return 0; } static const luaL_Reg gi_resolver_reg[] = { { "__index", resolver_index }, { NULL, NULL } }; /* Userdata representing namespace in girepository. */ #define LGI_GI_NAMESPACE "lgi.gi.namespace" static int namespace_len (lua_State *L) { const gchar *ns = luaL_checkudata (L, 1, LGI_GI_NAMESPACE); lua_pushnumber (L, g_irepository_get_n_infos (NULL, ns)); return 1; } static int namespace_index (lua_State *L) { const gchar *ns = luaL_checkudata (L, 1, LGI_GI_NAMESPACE); const gchar *prop; if (lua_type (L, 2) == LUA_TNUMBER) { GIBaseInfo *info = g_irepository_get_info (NULL, ns, lua_tointeger (L, 2) - 1); return lgi_gi_info_new (L, info); } prop = luaL_checkstring (L, 2); if (strcmp (prop, "dependencies") == 0) { gchar **deps = g_irepository_get_dependencies (NULL, ns); if (deps == NULL) lua_pushnil (L); else { int index; gchar **dep; lua_newtable (L); for (index = 1, dep = deps; *dep; dep++, index++) { const gchar *sep = strchr (*dep, '-'); lua_pushlstring (L, *dep, sep - *dep); lua_pushstring (L, sep + 1); lua_settable (L, -3); } g_strfreev (deps); } return 1; } else if (strcmp (prop, "version") == 0) { lua_pushstring (L, g_irepository_get_version (NULL, ns)); return 1; } else if (strcmp (prop, "name") == 0) { lua_pushstring (L, ns); return 1; } else if (strcmp (prop, "resolve") == 0) { GITypelib **udata = lua_newuserdata (L, sizeof (GITypelib *)); luaL_getmetatable (L, LGI_GI_RESOLVER); lua_setmetatable (L, -2); *udata = g_irepository_require (NULL, ns, NULL, 0, NULL); return 1; } else /* Try to lookup the symbol. */ return lgi_gi_info_new (L, g_irepository_find_by_name (NULL, ns, prop)); } static int namespace_new (lua_State *L, const gchar *namespace) { gchar *ns = lua_newuserdata (L, strlen (namespace) + 1); luaL_getmetatable (L, LGI_GI_NAMESPACE); lua_setmetatable (L, -2); strcpy (ns, namespace); return 1; } static const luaL_Reg gi_namespace_reg[] = { { "__index", namespace_index }, { "__len", namespace_len }, { NULL, NULL } }; /* Lua API: core.gi.require(namespace[, version[, typelib_dir]]) */ static int gi_require (lua_State *L) { GError *err = NULL; const gchar *namespace = luaL_checkstring (L, 1); const gchar *version = luaL_optstring (L, 2, NULL); const gchar *typelib_dir = luaL_optstring (L, 3, NULL); GITypelib *typelib; if (typelib_dir == NULL) typelib = g_irepository_require (NULL, namespace, version, 0, &err); else typelib = g_irepository_require_private (NULL, typelib_dir, namespace, version, 0, &err); if (!typelib) { lua_pushboolean (L, 0); lua_pushstring (L, err->message); lua_pushnumber (L, err->code); g_error_free (err); return 3; } return namespace_new (L, namespace); } /* Lua API: boolean = core.gi.isinfo(info) */ static int gi_isinfo (lua_State *L) { if (lua_getmetatable (L, 1)) { luaL_getmetatable (L, LGI_GI_INFO); lua_pushboolean (L, lua_rawequal (L, -1, -2)); } else lua_pushboolean (L, 0); return 1; } static int gi_index (lua_State *L) { if (lua_type (L, 2) == LUA_TLIGHTUSERDATA) { GType gtype = (GType) lua_touserdata (L, 2); GIBaseInfo *info = (gtype != G_TYPE_INVALID) ? g_irepository_find_by_gtype (NULL, gtype) : NULL; return lgi_gi_info_new (L, info); } else if (lua_type (L, 2) == LUA_TNUMBER) { GQuark domain = (GQuark) lua_tonumber (L, 2); GIBaseInfo *info = g_irepository_find_by_error_domain (NULL, domain); return lgi_gi_info_new (L, info); } else { const gchar *ns = luaL_checkstring (L, 2); if (g_irepository_is_registered (NULL, ns, NULL)) return namespace_new (L, ns); } return 0; } typedef struct _Reg { const gchar *name; const luaL_Reg* reg; } Reg; static const Reg gi_reg[] = { { LGI_GI_INFOS, gi_infos_reg }, { LGI_GI_INFO, gi_info_reg }, { LGI_GI_NAMESPACE, gi_namespace_reg }, { LGI_GI_RESOLVER, gi_resolver_reg }, { NULL, NULL } }; static const luaL_Reg gi_api_reg[] = { { "require", gi_require }, { "isinfo", gi_isinfo }, { NULL, NULL } }; void lgi_gi_init (lua_State *L) { const Reg *reg; /* Register metatables for userdata objects. */ for (reg = gi_reg; reg->name; reg++) { luaL_newmetatable (L, reg->name); luaL_register (L, NULL, reg->reg); lua_pop (L, 1); } /* Register global API. */ lua_newtable (L); luaL_register (L, NULL, gi_api_reg); lua_newtable (L); lua_pushcfunction (L, gi_index); lua_setfield (L, -2, "__index"); lua_setmetatable (L, -2); lua_setfield (L, -2, "gi"); } #if !GLIB_CHECK_VERSION(2, 30, 0) /* Workaround for broken g_struct_info_get_size() for GValue, see https://bugzilla.gnome.org/show_bug.cgi?id=657040 */ static GIStructInfo *parameter_info = NULL; static GIFieldInfo *parameter_value_info = NULL; #undef g_struct_info_get_size gsize lgi_struct_info_get_size (GIStructInfo *info) { if (parameter_info == NULL) parameter_info = g_irepository_find_by_name (NULL, "GObject", "Parameter"); if (g_registered_type_info_get_g_type (info) == G_TYPE_VALUE) return sizeof (GValue); else if (parameter_info && g_base_info_equal (info, parameter_info)) return sizeof (GParameter); return g_struct_info_get_size (info); } #undef g_field_info_get_offset gint lgi_field_info_get_offset (GIFieldInfo *info) { if (parameter_value_info == NULL) { if (parameter_info == NULL) parameter_info = g_irepository_find_by_name (NULL, "GObject", "Parameter"); parameter_value_info = g_struct_info_get_field (parameter_info, 1); } if (parameter_value_info && g_base_info_equal (info, parameter_value_info)) return G_STRUCT_OFFSET (GParameter, value); return g_field_info_get_offset (info); } #endif lgi-0.9.2/lgi/init.lua000066400000000000000000000065131316674307300145350ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- LGI Lua-side core. -- -- Copyright (c) 2010, 2011 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local assert, require, pcall, setmetatable, pairs, type, error, tostring, _VERSION, jit = assert, require, pcall, setmetatable, pairs, type, error, tostring, _VERSION, rawget(_G, 'jit') local package = require 'package' -- Require core lgi utilities, used during bootstrap. local core = require 'lgi.core' -- Create lgi table, containing the module. local lgi = { _NAME = 'lgi', _VERSION = require 'lgi.version' } -- Forward selected core methods into external interface. for _, name in pairs { 'yield', 'lock', 'enter', 'leave' } do lgi[name] = core[name] end -- If global package 'bytes' does not exist (i.e. not provided -- externally), use our internal (although incomplete) implementation. local ok, bytes = pcall(require, 'bytes') if not ok or not bytes then package.loaded.bytes = core.bytes end -- Prepare logging support. 'log' is module-exported table, containing all -- functionality related to logging wrapped around GLib g_log facility. lgi.log = require 'lgi.log' -- For the rest of bootstrap, prepare logging to lgi domain. local log = lgi.log.domain('lgi') -- Repository, table with all loaded namespaces. Its metatable takes care of -- loading on-demand. Created by C-side bootstrap. local repo = core.repo local namespace = require 'lgi.namespace' lgi.require = namespace.require -- Install 'lgi.package' method. lgi.package = require('lgi.package').ensure -- Add assert override, which accepts not only text message but any -- kind of error object. function lgi.assert(cond, ...) if cond then return cond, ... end local err = ... if _VERSION == 'Lua 5.1' and not jit then -- Lua 5.1 does not support displaying message of non-string -- errors on the commandline interpreter, so better stringize -- the error message right now. err = tostring(err) end error(err, 2) end -- Install metatable into repo table, so that on-demand loading works. setmetatable(repo, { __index = function(_, name) return lgi.require(name) end }) -- Create lazy-loading components for base gobject entities. assert(core.gi.require ('GLib', '2.0')) assert(core.gi.require ('GObject', '2.0')) repo.GObject._precondition = {} for _, name in pairs { 'Type', 'Value', 'Closure', 'Object' } do repo.GObject._precondition[name] = 'GObject-' .. name end repo.GObject._precondition.InitiallyUnowned = 'GObject-Object' -- Create lazy-loading components for GLib primitives. repo.GLib._precondition = {} repo.GLib._precondition.Error = 'GLib-Error' repo.GLib._precondition.Bytes = 'GLib-Bytes' repo.GLib._precondition.Timer = 'GLib-Timer' repo.GLib._precondition.MarkupParser = 'GLib-Markup' repo.GLib._precondition.MarkupParseContext = 'GLib-Markup' repo.GLib._precondition.Source = 'GLib-Source' repo.GLib._precondition.SourceFuncs = 'GLib-Source' for _, name in pairs { 'Variant', 'VariantType', 'VariantBuilder' } do repo.GLib._precondition[name] = 'GLib-Variant' end -- Access to module proxies the whole repo, so that lgi.'namespace' -- notation works. return setmetatable(lgi, { __index = repo }) lgi-0.9.2/lgi/lgi.h000066400000000000000000000204261316674307300140120ustar00rootroot00000000000000/* * Dynamic Lua binding to GObject using dynamic gobject-introspection. * * Author: Pavel Holejsovsky (pavel.holejsovsky@gmail.com) * * License: MIT. */ #define G_LOG_DOMAIN "Lgi" #include #include /* Lua 5.2 compatibility stuff. */ #if LUA_VERSION_NUM >= 502 #ifndef luaL_register #define luaL_register(L, null, regs) luaL_setfuncs (L, regs, 0) #endif #ifndef lua_equal #define lua_equal(L, p1, p2) lua_compare (L, p1, p2, LUA_OPEQ) #endif #ifndef lua_objlen #define lua_objlen(L, p) lua_rawlen (L, p) #endif #define lua_setfenv(L, p) lua_setuservalue (L, p) #define lua_getfenv(L, p) lua_getuservalue (L, p) #ifndef luaL_checkint #define luaL_checkint(L, p) luaL_checkinteger (L, p) #endif #endif #include #include #include #include #include /* Makes sure that Lua stack offset is absolute one, not relative. */ #define lgi_makeabs(L, x) do { if (x < 0) x += lua_gettop (L) + 1; } while (0) /* Puts parts of the name to the stack, to be concatenated by lua_concat. Returns number of pushed elements. */ int lgi_type_get_name (lua_State *L, GIBaseInfo *info); /* Stores repo type table associated with specified gtype (or BaseInfo if gtype is invalid). to the stack, or nil if no such table can be found in the repo. */ void lgi_type_get_repotype (lua_State *L, GType gtype, GIBaseInfo *info); /* Gets GType from Lua index narg. Accepts number and when it is other type, invokes Lua helper to convert. */ GType lgi_type_get_gtype (lua_State *L, int narg); /* Allocates guard, a pointer-size userdata with associated destroy handler. Returns pointer to user_data stored inside guard. */ gpointer *lgi_guard_create (lua_State *L, GDestroyNotify destroy); /* Creates cache table (optionally with given table __mode), stores it into registry to specified userdata address. */ void lgi_cache_create (lua_State *L, gpointer key, const char *mode); /* Initialization of modules. */ void lgi_marshal_init (lua_State *L); void lgi_record_init (lua_State *L); void lgi_object_init (lua_State *L); void lgi_callable_init (lua_State *L); void lgi_gi_init (lua_State *L); void lgi_buffer_init (lua_State *L); /* Checks whether given argument is of specified udata - similar to luaL_testudata, which is missing in Lua 5.1 */ void * lgi_udata_test (lua_State *L, int narg, const char *name); /* Metatable name of userdata for 'bytes' extension; see http://permalink.gmane.org/gmane.comp.lang.lua.general/79288 */ #define LGI_BYTES_BUFFER "bytes.bytearray" /* Metatable name of userdata - gi wrapped 'GIBaseInfo*' */ #define LGI_GI_INFO "lgi.gi.info" /* Creates new instance of info from given GIBaseInfo pointer. */ int lgi_gi_info_new (lua_State *L, GIBaseInfo *info); /* Assumes that 'typetable' can hold field 'name' which contains wrapped LGI_GI_INFO of function. Returns address of this function, NULL if table does not contain such field. */ gpointer lgi_gi_load_function(lua_State *L, int typetable, const char *name); /* Retrieve synchronization state, which can be used for entering and leaving the state using lgi_state_enter() and lgi_state_leave(). */ gpointer lgi_state_get_lock (lua_State *L); /* Enters/leaves Lua state. */ void lgi_state_enter (gpointer left_state); void lgi_state_leave (gpointer state_lock); /* Special value for 'parent' argument of marshal_2c/lua. When parent is set to this value, marshalling takes place always into pointer on the C side. This isuseful when marshalling value from/to lists, arrays and hashtables. */ #define LGI_PARENT_FORCE_POINTER G_MAXINT /* Another special value for 'parent' argument, meaning that the value should be handled as return value, according to ffi_call retval requirements. */ #define LGI_PARENT_IS_RETVAL (G_MAXINT - 1) /* Yet another special value for 'parent' argument, meaning that the value already contains address of caller-allocated space into which the result should be marshalled. */ #define LGI_PARENT_CALLER_ALLOC (G_MAXINT - 2) /* Marshalls single value from Lua to GLib/C. Returns number of temporary entries pushed to Lua stack, which should be popped before function call returns. */ int lgi_marshal_2c (lua_State *L, GITypeInfo *ti, GIArgInfo *ai, GITransfer xfer, gpointer target, int narg, int parent, GICallableInfo *ci, void **args); /* If given parameter is out:caller-allocates, tries to perform special 2c marshalling. If not needed, returns FALSE, otherwise stores single value with value prepared to be returned to C. */ gboolean lgi_marshal_2c_caller_alloc (lua_State *L, GITypeInfo *ti, GIArgument *target, int pos); /* Marshalls single value from GLib/C to Lua. If parent is non-0, it is stack index of parent structure/array in which this C value resides. */ void lgi_marshal_2lua (lua_State *L, GITypeInfo *ti, GIArgInfo *ai, GIDirection dir, GITransfer xfer, gpointer source, int parent, GICallableInfo *ci, void *args); /* Marshalls field to/from given memory (struct, union or object). Returns number of results pushed to the stack (0 or 1). */ int lgi_marshal_field (lua_State *L, gpointer object, gboolean getmode, int parent_arg, int field_arg, int val_arg); /* Implementation of object/record _access invocation. */ int lgi_marshal_access (lua_State *L, gboolean getmode, int compound_arg, int element_arg, int val_arg); /* Parses given GICallableInfo, creates new userdata for it and stores it to the stack. */ int lgi_callable_create (lua_State *L, GICallableInfo *ci, gpointer addr); /* Parses callable from table-driven info description. */ int lgi_callable_parse (lua_State *L, int info, gpointer addr); /* Creates container block for allocated closures. Returns address of the block, suitable as user_data parameter. */ gpointer lgi_closure_allocate (lua_State *L, int count); /* Allocates n-th closure in the closure block for specified Lua function (or callable table or userdata). Assumes Callable to be created on the stack, pops it. Returns executable address for the closure. */ gpointer lgi_closure_create (lua_State* L, gpointer user_data, int target, gboolean autodestroy); /* GDestroyNotify-compatible callback for destroying closure. */ void lgi_closure_destroy (gpointer user_data); /* Allocates and creates new record instance. Assumes that repotype table is on the stack, replaces it with newly created proxy. */ gpointer lgi_record_new (lua_State *L, int count, gboolean alloc); /* Creates Lua-side part of given record. Assumes that repotype table is on the stack, replaces it with newly created proxy. If parent not zero, it is stack index of record parent (i.e. record of which the arg record is part of). */ void lgi_record_2lua (lua_State *L, gpointer addr, gboolean own, int parent); /* Gets pointer to C-structure from given Lua-side object, or copies record to specified address. Expects repo typetable of expected argument pushed on the top of the stack, removes it. */ void lgi_record_2c (lua_State *L, gint narg, gpointer target, gboolean by_value, gboolean own, gboolean optional, gboolean nothrow); /* Creates Lua-side part (proxy) of given object. If the object is not owned (own == FALSE), an ownership is automatically acquired. Returns number of elements pushed to the stack, i.e. always 1. */ int lgi_object_2lua (lua_State *L, gpointer obj, gboolean own, gboolean no_sink); /* Gets pointer to C-side object represented by given Lua proxy. If gtype is not G_TYPE_INVALID, the real type is checked to conform to requested type. */ gpointer lgi_object_2c (lua_State *L, int narg, GType gtype, gboolean optional, gboolean nothrow, gboolean transfer); #if !GLIB_CHECK_VERSION(2, 30, 0) /* Workaround for broken g_struct_info_get_size() for GValue, see https://bugzilla.gnome.org/show_bug.cgi?id=657040 */ gsize lgi_struct_info_get_size (GIStructInfo *info); #define g_struct_info_get_size lgi_struct_info_get_size int lgi_field_info_get_offset (GIFieldInfo *info); #define g_field_info_get_offset lgi_field_info_get_offset #endif /* Workaround method for broken g_object_info_get_*_function_pointer() in GI 1.32.0. (see https://bugzilla.gnome.org/show_bug.cgi?id=673282) */ gpointer lgi_object_get_function_ptr (GIObjectInfo *info, const gchar *(*getter)(GIObjectInfo *)); lgi-0.9.2/lgi/log.lua000066400000000000000000000021021316674307300143410ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- LGI support for GLib-based logging. -- -- Copyright (c) 2011 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local pcall, ipairs = pcall, ipairs local string = require 'string' local core = require 'lgi.core' local log = {} -- Creates table containing methods 'message', 'warning', 'critical', 'error', -- 'debug' methods which log to specified domain. function log.domain(name) local domain = log[name] or {} for _, level in ipairs { 'message', 'warning', 'critical', 'error', 'debug' } do if not domain[level] then domain[level] = function(format, ...) local ok, msg = pcall(string.format, format, ...) if not ok then msg = ("BAD FMT: `%s', `%s'"):format(format, msg) end core.log(name, core.upcase(level), msg) end end end log[name] = domain return domain end return log lgi-0.9.2/lgi/marshal.c000066400000000000000000001477101316674307300146670ustar00rootroot00000000000000/* * Dynamic Lua binding to GObject using dynamic gobject-introspection. * * Copyright (c) 2010-2013 Pavel Holejsovsky * Licensed under the MIT license: * http://www.opensource.org/licenses/mit-license.php * * Implements marshalling, i.e. transferring values between Lua and GLib/C. */ #include #include #include "lgi.h" /* Checks whether given argument contains number which fits given constraints. If yes, returns it, otherwise throws Lua error. */ static lua_Number check_number (lua_State *L, int narg, lua_Number val_min, lua_Number val_max) { lua_Number val = luaL_checknumber (L, narg); if (val < val_min || val > val_max) { lua_pushfstring (L, "%f is out of <%f, %f>", val, val_min, val_max); luaL_argerror (L, narg, lua_tostring (L, -1)); } return val; } typedef union { GIArgument arg; ffi_arg u; ffi_sarg s; } ReturnUnion; /* Marshals integral types to C. If requested, makes sure that the value is actually marshalled into val->v_pointer no matter what the input type is. */ static void marshal_2c_int (lua_State *L, GITypeTag tag, GIArgument *val, int narg, gboolean optional, int parent) { (void) optional; switch (tag) { #define HANDLE_INT(nameup, namelow, ptrconv, pct, val_min, val_max, ut) \ case GI_TYPE_TAG_ ## nameup: \ val->v_ ## namelow = check_number (L, narg, val_min, val_max); \ if (parent == LGI_PARENT_FORCE_POINTER) \ val->v_pointer = \ G ## ptrconv ## _TO_POINTER ((pct) val->v_ ## namelow); \ else if (sizeof (g ## namelow) <= sizeof (long) \ && parent == LGI_PARENT_IS_RETVAL) \ { \ ReturnUnion *ru = (ReturnUnion *) val; \ ru->ut = ru->arg.v_ ## namelow; \ } \ break #define HANDLE_INT_NOPTR(nameup, namelow, val_min, val_max, ut) \ case GI_TYPE_TAG_ ## nameup: \ val->v_ ## namelow = check_number (L, narg, val_min, val_max); \ g_assert (parent != LGI_PARENT_FORCE_POINTER); \ if (sizeof (g ## namelow) <= sizeof (long) \ && parent == LGI_PARENT_IS_RETVAL) \ { \ ReturnUnion *ru = (ReturnUnion *) val; \ ru->ut = ru->arg.v_ ## namelow; \ } \ break HANDLE_INT(INT8, int8, INT, gint, -0x80, 0x7f, s); HANDLE_INT(UINT8, uint8, UINT, guint, 0, 0xff, u); HANDLE_INT(INT16, int16, INT, gint, -0x8000, 0x7fff, s); HANDLE_INT(UINT16, uint16, UINT, guint, 0, 0xffff, u); HANDLE_INT(INT32, int32, INT, gint, -0x80000000LL, 0x7fffffffLL, s); HANDLE_INT(UINT32, uint32, UINT, guint, 0, 0xffffffffUL, u); HANDLE_INT(UNICHAR, uint32, UINT, guint, 0, 0x7fffffffLL, u); HANDLE_INT_NOPTR(INT64, int64, ((lua_Number) -0x7f00000000000000LL) - 1, 0x7fffffffffffffffLL, s); HANDLE_INT_NOPTR(UINT64, uint64, 0, 0xffffffffffffffffULL, u); #undef HANDLE_INT #undef HANDLE_INT_NOPTR case GI_TYPE_TAG_GTYPE: { #if GLIB_SIZEOF_SIZE_T == 4 val->v_uint32 = #else val->v_uint64 = #endif lgi_type_get_gtype (L, narg); break; } default: g_assert_not_reached (); } } /* Marshals integral types from C to Lua. */ static void marshal_2lua_int (lua_State *L, GITypeTag tag, GIArgument *val, int parent) { switch (tag) { #define HANDLE_INT(nameupper, namelower, ptrconv, ut) \ case GI_TYPE_TAG_ ## nameupper: \ if (sizeof (g ## namelower) <= sizeof (long) \ && parent == LGI_PARENT_IS_RETVAL) \ { \ ReturnUnion *ru = (ReturnUnion *) val; \ ru->arg.v_ ## namelower = (g ## namelower) ru->ut; \ } \ lua_pushnumber (L, parent == LGI_PARENT_FORCE_POINTER \ ? GPOINTER_TO_ ## ptrconv (val->v_pointer) \ : val->v_ ## namelower); \ break; HANDLE_INT(INT8, int8, INT, s); HANDLE_INT(UINT8, uint8, UINT, u); HANDLE_INT(INT16, int16, INT, s); HANDLE_INT(UINT16, uint16, UINT, u); HANDLE_INT(INT32, int32, INT, s); HANDLE_INT(UINT32, uint32, UINT, u); HANDLE_INT(UNICHAR, uint32, UINT, u); HANDLE_INT(INT64, int64, INT, s); HANDLE_INT(UINT64, uint64, UINT, u); #undef HANDLE_INT case GI_TYPE_TAG_GTYPE: lua_pushstring (L, g_type_name ( #if GLIB_SIZEOF_SIZE_T == 4 val->v_uint32 #else val->v_uint64 #endif )); break; default: g_assert_not_reached (); } } /* Gets or sets the length of the array. */ static void array_get_or_set_length (GITypeInfo *ti, gssize *get_length, gssize set_length, GIBaseInfo *ci, void *args) { gint param = g_type_info_get_array_length (ti); if (param >= 0 && ci != NULL) { GIArgument *val; GITypeInfo *eti; GIInfoType itype = g_base_info_get_type (ci); if (itype == GI_INFO_TYPE_FUNCTION || itype == GI_INFO_TYPE_CALLBACK) { GIArgInfo ai; if (param >= g_callable_info_get_n_args (ci)) return; g_callable_info_load_arg (ci, param, &ai); eti = g_arg_info_get_type (&ai); if (g_arg_info_get_direction (&ai) == GI_DIRECTION_IN) /* For input parameters, value is directly pointed to by args table element. */ val = (GIArgument *) ((void **) args)[param]; else /* For output arguments, args table element points to pointer to value. */ val = *(GIArgument **) ((void **) args)[param]; } else if (itype == GI_INFO_TYPE_STRUCT || itype == GI_INFO_TYPE_UNION) { GIFieldInfo *fi; if (param >= g_struct_info_get_n_fields (ci)) return; fi = g_struct_info_get_field (ci, param); eti = g_field_info_get_type (fi); val = (GIArgument *) ((char *) args + g_field_info_get_offset (fi)); g_base_info_unref (fi); } else return; switch (g_type_info_get_tag (eti)) { #define HANDLE_ELT(tag, field) \ case GI_TYPE_TAG_ ## tag: \ if (get_length != NULL) \ *get_length = val->v_ ## field; \ else \ val->v_ ## field = set_length; \ break HANDLE_ELT(INT8, int8); HANDLE_ELT(UINT8, uint8); HANDLE_ELT(INT16, int16); HANDLE_ELT(UINT16, uint16); HANDLE_ELT(INT32, int32); HANDLE_ELT(UINT32, uint32); HANDLE_ELT(INT64, int64); HANDLE_ELT(UINT64, uint64); #undef HANDLE_ELT default: g_assert_not_reached (); } g_base_info_unref (eti); } } /* Retrieves pointer to GIArgument in given array, given that array contains elements of type ti. */ static gssize array_get_elt_size (GITypeInfo *ti, gboolean force_ptr) { gssize size = sizeof (gpointer); if (!g_type_info_is_pointer (ti) && !force_ptr) { switch (g_type_info_get_tag (ti)) { #define HANDLE_ELT(nameupper, nametype) \ case GI_TYPE_TAG_ ## nameupper: \ return sizeof (nametype); HANDLE_ELT(BOOLEAN, gboolean); HANDLE_ELT(INT8, gint8); HANDLE_ELT(UINT8, guint8); HANDLE_ELT(INT16, gint16); HANDLE_ELT(UINT16, guint16); HANDLE_ELT(INT32, gint32); HANDLE_ELT(UINT32, guint32); HANDLE_ELT(UNICHAR, guint32); HANDLE_ELT(INT64, gint64); HANDLE_ELT(UINT64, guint64); HANDLE_ELT(FLOAT, gfloat); HANDLE_ELT(DOUBLE, gdouble); HANDLE_ELT(GTYPE, GType); #undef HANDLE_ELT case GI_TYPE_TAG_INTERFACE: { GIBaseInfo *info = g_type_info_get_interface (ti); GIInfoType type = g_base_info_get_type (info); if (type == GI_INFO_TYPE_STRUCT) size = g_struct_info_get_size (info); else if (type == GI_INFO_TYPE_UNION) size = g_union_info_get_size (info); g_base_info_unref (info); break; } default: break; } } return size; } static void array_detach (GArray *array) { g_array_free (array, FALSE); } static void ptr_array_detach (GPtrArray *array) { g_ptr_array_free (array, FALSE); } static void byte_array_detach (GByteArray *array) { g_byte_array_free (array, FALSE); } /* Marshalls array from Lua to C. Returns number of temporary elements pushed to the stack. */ static int marshal_2c_array (lua_State *L, GITypeInfo *ti, GIArrayType atype, gpointer *out_array, gssize *out_size, int narg, gboolean optional, GITransfer transfer) { GITypeInfo* eti; gssize objlen, esize; gint index, vals = 0, to_pop, eti_guard; GITransfer exfer = (transfer == GI_TRANSFER_EVERYTHING ? GI_TRANSFER_EVERYTHING : GI_TRANSFER_NOTHING); gboolean zero_terminated; GArray *array = NULL; int parent = 0; /* Represent nil as NULL array. */ if (optional && lua_isnoneornil (L, narg)) { *out_size = 0; *out_array = NULL; } else { /* Get element type info, create guard for it. */ eti = g_type_info_get_param_type (ti, 0); lgi_gi_info_new (L, eti); eti_guard = lua_gettop (L); esize = array_get_elt_size (eti, atype == GI_ARRAY_TYPE_PTR_ARRAY); /* Check the type. If this is C-array of byte-sized elements, we can try special-case and accept strings or buffers. */ *out_array = NULL; if (lua_type (L, narg) != LUA_TTABLE && esize == 1 && atype == GI_ARRAY_TYPE_C) { size_t size = 0; *out_array = lgi_udata_test (L, narg, LGI_BYTES_BUFFER); if (*out_array) size = lua_objlen (L, narg); else *out_array = (gpointer *) lua_tolstring (L, narg, &size); if (transfer != GI_TRANSFER_NOTHING) *out_array = g_memdup (*out_array, size); *out_size = size; } if (!*out_array) { /* Otherwise, we allow only tables. */ luaL_checktype (L, narg, LUA_TTABLE); /* Find out how long array should we allocate. */ zero_terminated = g_type_info_is_zero_terminated (ti); objlen = lua_objlen (L, narg); *out_size = g_type_info_get_array_fixed_size (ti); if (atype != GI_ARRAY_TYPE_C || *out_size < 0) *out_size = objlen; else if (*out_size < objlen) objlen = *out_size; /* Allocate the array and wrap it into the userdata guard, if needed. */ if (*out_size > 0 || zero_terminated) { guint total_size = *out_size + (zero_terminated ? 1 : 0); switch (atype) { case GI_ARRAY_TYPE_C: case GI_ARRAY_TYPE_ARRAY: array = g_array_sized_new (zero_terminated, TRUE, esize, *out_size); g_array_set_size (array, *out_size); *lgi_guard_create (L, (GDestroyNotify) (transfer == GI_TRANSFER_EVERYTHING ? array_detach : g_array_unref)) = array; break; case GI_ARRAY_TYPE_PTR_ARRAY: parent = LGI_PARENT_FORCE_POINTER; array = (GArray *) g_ptr_array_sized_new (total_size); g_ptr_array_set_size ((GPtrArray *) array, total_size); *lgi_guard_create (L, (GDestroyNotify) (transfer == GI_TRANSFER_EVERYTHING ? ptr_array_detach : g_ptr_array_unref)) = array; break; case GI_ARRAY_TYPE_BYTE_ARRAY: array = (GArray *) g_byte_array_sized_new (total_size); g_byte_array_set_size ((GByteArray *) array, *out_size); *lgi_guard_create (L, (GDestroyNotify) (transfer == GI_TRANSFER_EVERYTHING ? byte_array_detach : g_byte_array_unref)) = array; break; } vals = 1; } /* Iterate through Lua array and fill GArray accordingly. */ for (index = 0; index < objlen; index++) { lua_pushnumber (L, index + 1); lua_gettable (L, narg); /* Marshal element retrieved from the table into target array. */ to_pop = lgi_marshal_2c (L, eti, NULL, exfer, array->data + index * esize, -1, parent, NULL, NULL); /* Remove temporary element from the stack. */ lua_remove (L, - to_pop - 1); /* Remember that some more temp elements could be pushed. */ vals += to_pop; } /* Return either GArray or direct pointer to the data, according to the array type. */ if (array == NULL) *out_array = NULL; else switch (atype) { case GI_ARRAY_TYPE_C: *out_array = (void *) array->data; break; case GI_ARRAY_TYPE_ARRAY: case GI_ARRAY_TYPE_PTR_ARRAY: case GI_ARRAY_TYPE_BYTE_ARRAY: *out_array = (void *) array; break; } } lua_remove (L, eti_guard); } return vals; } static void marshal_2lua_array (lua_State *L, GITypeInfo *ti, GIDirection dir, GIArrayType atype, GITransfer transfer, gpointer array, gssize size, int parent) { GITypeInfo *eti; gssize len = 0, esize; gint index, eti_guard; char *data = NULL; /* Avoid propagating return value marshaling flag to array elements. */ if (parent == LGI_PARENT_IS_RETVAL) parent = 0; /* First of all, find out the length of the array. */ if (atype == GI_ARRAY_TYPE_ARRAY) { if (array) { len = ((GArray *) array)->len; data = ((GArray *) array)->data; } } else if (atype == GI_ARRAY_TYPE_BYTE_ARRAY) { if (array) { len = ((GByteArray *) array)->len; data = (char *) ((GByteArray *) array)->data; } } else if (atype == GI_ARRAY_TYPE_PTR_ARRAY) { if (array) { len = ((GPtrArray *) array)->len; data = (char *) ((GPtrArray *) array)->pdata; parent = LGI_PARENT_FORCE_POINTER; } } else { data = array; if (g_type_info_is_zero_terminated (ti)) len = -1; else { len = g_type_info_get_array_fixed_size (ti); if (len == -1) /* Length of the array is dynamic, get it from other argument. */ len = size; } } /* Get array element type info, wrap it in the guard so that we don't leak it. */ eti = g_type_info_get_param_type (ti, 0); lgi_gi_info_new (L, eti); eti_guard = lua_gettop (L); esize = array_get_elt_size (eti, atype == GI_ARRAY_TYPE_PTR_ARRAY); /* Note that we ignore is_pointer check for uint8 type. Although it is not exactly correct, we probably would not handle uint8* correctly anyway, this is strange type to use, and moreover this is workaround for g-ir-scanner bug which might mark elements of uint8 arrays as gconstpointer, thus setting is_pointer=true on it. See https://github.com/pavouk/lgi/issues/57 */ if (g_type_info_get_tag (eti) == GI_TYPE_TAG_UINT8) { /* UINT8 arrays are marshalled as Lua strings. */ if (len < 0) len = data ? strlen(data) : 0; if (data != NULL || len != 0) lua_pushlstring (L, data, len); else lua_pushnil (L); } else { if (array == NULL) { /* NULL array is represented by empty table for C arrays, nil for other types. */ if (atype == GI_ARRAY_TYPE_C) lua_newtable (L); else lua_pushnil (L); lua_remove (L, eti_guard); return; } /* Create Lua table which will hold the array. */ lua_createtable (L, len > 0 ? len : 0, 0); /* Iterate through array elements. */ for (index = 0; len < 0 || index < len; index++) { /* Get value from specified index. */ GIArgument *eval = (GIArgument *) (data + index * esize); /* If the array is zero-terminated, terminate now and don't include NULL entry. */ if (len < 0 && eval->v_pointer == NULL) break; /* Store value into the table. */ lgi_marshal_2lua (L, eti, NULL, dir, (transfer == GI_TRANSFER_EVERYTHING) ? GI_TRANSFER_EVERYTHING : GI_TRANSFER_NOTHING, eval, parent, NULL, NULL); lua_rawseti (L, -2, index + 1); } } /* If needed, free the original array. */ if (transfer != GI_TRANSFER_NOTHING) { if (atype == GI_ARRAY_TYPE_ARRAY) g_array_free (array, TRUE); else if (atype == GI_ARRAY_TYPE_BYTE_ARRAY) g_byte_array_free (array, TRUE); else if (atype == GI_ARRAY_TYPE_PTR_ARRAY) g_ptr_array_free (array, TRUE); else g_free (array); } lua_remove (L, eti_guard); } /* Marshalls GSList or GList from Lua to C. Returns number of temporary elements pushed to the stack. */ static int marshal_2c_list (lua_State *L, GITypeInfo *ti, GITypeTag list_tag, gpointer *list, int narg, GITransfer transfer) { GITypeInfo *eti; GITransfer exfer = (transfer == GI_TRANSFER_EVERYTHING ? GI_TRANSFER_EVERYTHING : GI_TRANSFER_NOTHING); gint index, vals = 0, to_pop, eti_guard; GSList **guard = NULL; /* Allow empty list to be expressed also as 'nil', because in C, there is no difference between NULL and empty list. */ if (lua_isnoneornil (L, narg)) index = 0; else { luaL_checktype (L, narg, LUA_TTABLE); index = lua_objlen (L, narg); } /* Get list element type info, create guard for it so that we don't leak it. */ eti = g_type_info_get_param_type (ti, 0); lgi_gi_info_new (L, eti); eti_guard = lua_gettop (L); /* Go from back and prepend to the list, which is cheaper than appending. */ guard = (GSList **) lgi_guard_create (L, list_tag == GI_TYPE_TAG_GSLIST ? (GDestroyNotify) g_slist_free : (GDestroyNotify) g_list_free); while (index > 0) { /* Retrieve index-th element from the source table and marshall it as pointer to arg. */ GIArgument eval; lua_pushnumber (L, index--); lua_gettable (L, narg); to_pop = lgi_marshal_2c (L, eti, NULL, exfer, &eval, -1, LGI_PARENT_FORCE_POINTER, NULL, NULL); /* Prepend new list element and reassign the guard. */ if (list_tag == GI_TYPE_TAG_GSLIST) *guard = g_slist_prepend (*guard, eval.v_pointer); else *guard = (GSList *) g_list_prepend ((GList *) *guard, eval.v_pointer); lua_remove (L, - to_pop - 1); vals += to_pop; } /* Marshalled value is kept inside the guard. */ *list = *guard; lua_remove (L, eti_guard); return vals; } static int marshal_2lua_list (lua_State *L, GITypeInfo *ti, GIDirection dir, GITypeTag list_tag, GITransfer xfer, gpointer list) { GSList *i; GITypeInfo *eti; gint index, eti_guard; /* Get element type info, guard it so that we don't leak it. */ eti = g_type_info_get_param_type (ti, 0); lgi_gi_info_new (L, eti); eti_guard = lua_gettop (L); /* Create table to which we will deserialize the list. */ lua_newtable (L); /* Go through the list and push elements into the table. */ for (i = list, index = 0; i != NULL; i = g_slist_next (i)) { /* Get access to list item. */ GIArgument *eval = (GIArgument *) &i->data; /* Store it into the table. */ lgi_marshal_2lua (L, eti, NULL, dir, (xfer == GI_TRANSFER_EVERYTHING) ? GI_TRANSFER_EVERYTHING : GI_TRANSFER_NOTHING, eval, LGI_PARENT_FORCE_POINTER, NULL, NULL); lua_rawseti(L, -2, ++index); } /* Free the list, if we got its ownership. */ if (xfer != GI_TRANSFER_NOTHING) { if (list_tag == GI_TYPE_TAG_GSLIST) g_slist_free (list); else g_list_free (list); } lua_remove (L, eti_guard); return 1; } /* Marshalls hashtable from Lua to C. Returns number of temporary elements pushed to the stack. */ static int marshal_2c_hash (lua_State *L, GITypeInfo *ti, GHashTable **table, int narg, gboolean optional, GITransfer transfer) { GITypeInfo *eti[2]; GITransfer exfer = (transfer == GI_TRANSFER_EVERYTHING ? GI_TRANSFER_EVERYTHING : GI_TRANSFER_NOTHING); gint i, vals = 0, guard; GHashTable **guarded_table; GHashFunc hash_func; GEqualFunc equal_func; /* Represent nil as NULL table. */ if (optional && lua_isnoneornil (L, narg)) *table = NULL; else { /* Check the type; we allow tables only. */ luaL_checktype (L, narg, LUA_TTABLE); /* Get element type infos, create guard for it. */ guard = lua_gettop (L) + 1; for (i = 0; i < 2; i++) { eti[i] = g_type_info_get_param_type (ti, i); lgi_gi_info_new (L, eti[i]); } /* Create the hashtable and guard it so that it is destroyed in case something goes wrong during marshalling. */ guarded_table = (GHashTable **) lgi_guard_create (L, (GDestroyNotify) g_hash_table_destroy); vals++; /* Find out which hash_func and equal_func should be used, according to the type of the key. */ switch (g_type_info_get_tag (eti[0])) { case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: hash_func = g_str_hash; equal_func = g_str_equal; break; case GI_TYPE_TAG_INT64: case GI_TYPE_TAG_UINT64: hash_func = g_int64_hash; equal_func = g_int64_equal; break; case GI_TYPE_TAG_FLOAT: case GI_TYPE_TAG_DOUBLE: return luaL_error (L, "hashtable with float or double is not " "supported"); default: /* For everything else, use direct hash of stored pointer. */ hash_func = NULL; equal_func = NULL; } *guarded_table = *table = g_hash_table_new (hash_func, equal_func); /* Iterate through Lua table and fill hashtable. */ lua_pushnil (L); while (lua_next (L, narg)) { GIArgument eval[2]; int key_pos = lua_gettop (L) - 1; /* Marshal key and value from the table. */ for (i = 0; i < 2; i++) vals += lgi_marshal_2c (L, eti[i], NULL, exfer, &eval[i], key_pos + i, LGI_PARENT_FORCE_POINTER, NULL, NULL); /* Insert newly marshalled pointers into the table. */ g_hash_table_insert (*table, eval[0].v_pointer, eval[1].v_pointer); /* The great stack suffle; remove value completely and leave key on the top of the stack. Complicated by the fact that both are burried under key_pop + val_pop elements created by marshalling. */ lua_remove (L, key_pos + 1); lua_pushvalue (L, key_pos); lua_remove (L, key_pos); } /* Remove guards for element types. */ lua_remove (L, guard); lua_remove (L, guard); } return vals; } static void marshal_2lua_hash (lua_State *L, GITypeInfo *ti, GIDirection dir, GITransfer xfer, GHashTable *hash_table) { GHashTableIter iter; GITypeInfo *eti[2]; gint i, guard; GIArgument eval[2]; /* Check for 'NULL' table, represent it simply as nil. */ if (hash_table == NULL) lua_pushnil (L); else { /* Get key and value type infos, guard them so that we don't leak it. */ guard = lua_gettop (L) + 1; for (i = 0; i < 2; i++) { eti[i] = g_type_info_get_param_type (ti, i); lgi_gi_info_new (L, eti[i]); } /* Create table to which we will deserialize the hashtable. */ lua_newtable (L); /* Go through the hashtable and push elements into the table. */ g_hash_table_iter_init (&iter, hash_table); while (g_hash_table_iter_next (&iter, &eval[0].v_pointer, &eval[1].v_pointer)) { /* Marshal key and value to the stack. */ for (i = 0; i < 2; i++) lgi_marshal_2lua (L, eti[i], NULL, dir, GI_TRANSFER_NOTHING, &eval[i], LGI_PARENT_FORCE_POINTER, NULL, NULL); /* Store these two elements to the table. */ lua_settable (L, -3); } /* Free the table, if requested. */ if (xfer != GI_TRANSFER_NOTHING) g_hash_table_unref (hash_table); lua_remove (L, guard); lua_remove (L, guard); } } static void marshal_2lua_error (lua_State *L, GITransfer xfer, GError *err) { if (err == NULL) lua_pushnil (L); else { /* Wrap error instance with GLib.Error record. */ lgi_type_get_repotype (L, G_TYPE_ERROR, NULL); lgi_record_2lua (L, err, xfer != GI_TRANSFER_NOTHING, 0); } } /* Marshalls given callable from Lua to C. */ static int marshal_2c_callable (lua_State *L, GICallableInfo *ci, GIArgInfo *ai, gpointer *callback, int narg, gboolean optional, GICallableInfo *argci, void **args) { int nret = 0; GIScopeType scope; gpointer user_data = NULL; gint nargs = 0; if (argci != NULL) nargs = g_callable_info_get_n_args (argci); /* Check 'nil' in optional case. In this case, return NULL as callback. */ if (lua_isnoneornil (L, narg)) { if (optional) { *callback = NULL; /* Also set associated destroy handler to NULL, because some callees tend to call it when left as garbage even when main callback is NULL (gtk_menu_popup_for_device() case). */ if (ai != NULL) { gint arg = g_arg_info_get_destroy (ai); if (arg >= 0 && arg < nargs) ((GIArgument *) args[arg])->v_pointer = NULL; } return 0; } else return luaL_argerror (L, narg, "nil is not allowed"); } /* Check lightuserdata case; simply use that data if provided. */ if (lua_islightuserdata (L, narg)) { *callback = lua_touserdata (L, narg); return 0; } if (argci != NULL) { gint arg = g_arg_info_get_closure (ai); /* user_data block is already preallocated from function call. */ g_assert (args != NULL); if (arg >= 0 && arg < nargs) { user_data = ((GIArgument *) args[arg])->v_pointer; arg = g_arg_info_get_destroy (ai); if (arg >= 0 && arg < nargs) ((GIArgument *) args[arg])->v_pointer = lgi_closure_destroy; } } scope = g_arg_info_get_scope (ai); if (user_data == NULL) { /* Closure without user_data block. Create new data block, setup destruction according to scope. */ user_data = lgi_closure_allocate (L, 1); if (scope == GI_SCOPE_TYPE_CALL) { *lgi_guard_create (L, lgi_closure_destroy) = user_data; nret++; } else g_assert (scope == GI_SCOPE_TYPE_ASYNC); } /* Create the closure. */ lgi_callable_create (L, ci, NULL); *callback = lgi_closure_create (L, user_data, narg, scope == GI_SCOPE_TYPE_ASYNC); return nret; } /* Marshalls single value from Lua to GLib/C. */ int lgi_marshal_2c (lua_State *L, GITypeInfo *ti, GIArgInfo *ai, GITransfer transfer, gpointer target, int narg, int parent, GICallableInfo *ci, void **args) { int nret = 0; gboolean optional = (parent == LGI_PARENT_CALLER_ALLOC) || (ai == NULL || (g_arg_info_is_optional (ai) || g_arg_info_may_be_null (ai))); GITypeTag tag = g_type_info_get_tag (ti); GIArgument *arg = target; /* Convert narg stack position to absolute one, because during marshalling some temporary items might be pushed to the stack, which would disrupt relative stack addressing of the value. */ lgi_makeabs(L, narg); switch (tag) { case GI_TYPE_TAG_BOOLEAN: { gboolean result; result = lua_toboolean (L, narg) ? TRUE : FALSE; if (parent == LGI_PARENT_FORCE_POINTER) arg->v_pointer = GINT_TO_POINTER (result); else if (parent == LGI_PARENT_IS_RETVAL) { ReturnUnion *ru = (ReturnUnion *) arg; ru->s = result; } else arg->v_boolean = result; break; } case GI_TYPE_TAG_FLOAT: case GI_TYPE_TAG_DOUBLE: { /* Retrieve number from given position. */ lua_Number num = (optional && lua_isnoneornil (L, narg)) ? 0 : luaL_checknumber (L, narg); /* Marshalling float/double into pointer target is not possible. */ g_return_val_if_fail (parent != LGI_PARENT_FORCE_POINTER, 0); /* Store read value into chosen target. */ if (tag == GI_TYPE_TAG_FLOAT) arg->v_float = (float) num; else arg->v_double = (double) num; break; } case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: { gchar *str = NULL; int type = lua_type (L, narg); if (type == LUA_TLIGHTUSERDATA) str = lua_touserdata (L, narg); else if (!optional || (type != LUA_TNIL && type != LUA_TNONE)) { if (type == LUA_TUSERDATA) str = (gchar *) lgi_udata_test (L, narg, LGI_BYTES_BUFFER); if (str == NULL) str = (gchar *) luaL_checkstring (L, narg); } if (tag == GI_TYPE_TAG_FILENAME) { /* Convert from UTF-8 to filename encoding. */ if (str) { str = g_filename_from_utf8 (str, -1, NULL, NULL, NULL); if (transfer != GI_TRANSFER_EVERYTHING) { /* Create temporary object on the stack which will destroy the allocated temporary filename. */ *lgi_guard_create (L, g_free) = (gpointer) str; nret = 1; } } } else if (transfer == GI_TRANSFER_EVERYTHING) str = g_strdup (str); if (parent == LGI_PARENT_FORCE_POINTER) arg->v_pointer = str; else arg->v_string = str; } break; case GI_TYPE_TAG_INTERFACE: { GIBaseInfo *info = g_type_info_get_interface (ti); GIInfoType type = g_base_info_get_type (info); int info_guard; lgi_gi_info_new (L, info); info_guard = lua_gettop (L); switch (type) { case GI_INFO_TYPE_ENUM: case GI_INFO_TYPE_FLAGS: /* If the argument is not numeric, convert to number first. Use enum/flags 'constructor' to do this. */ if (lua_type (L, narg) != LUA_TNUMBER) { lgi_type_get_repotype (L, G_TYPE_INVALID, info); lua_pushvalue (L, narg); lua_call (L, 1, 1); narg = -1; } /* Directly store underlying value. */ marshal_2c_int (L, g_enum_info_get_storage_type (info), arg, narg, optional, parent); /* Remove the temporary value, to keep stack balanced. */ if (narg == -1) lua_pop (L, 1); break; case GI_INFO_TYPE_STRUCT: case GI_INFO_TYPE_UNION: { /* Ideally the g_type_info_is_pointer() should be sufficient here, but there is some gobject-introspection quirk that some struct arguments might not be marked as pointers (e.g. g_variant_equals(), which has ctype of gconstpointer, and thus logic in girparser.c which sets is_pointer attribute fails). Workaround it by checking also argument type - structs as C function arguments are always passed as pointers. */ gboolean by_value = parent != LGI_PARENT_FORCE_POINTER && ((!g_type_info_is_pointer (ti) && ai == NULL) || parent == LGI_PARENT_CALLER_ALLOC); lgi_type_get_repotype (L, G_TYPE_INVALID, info); lgi_record_2c (L, narg, target, by_value, transfer != GI_TRANSFER_NOTHING, optional, FALSE); break; } case GI_INFO_TYPE_OBJECT: case GI_INFO_TYPE_INTERFACE: { arg->v_pointer = lgi_object_2c (L, narg, g_registered_type_info_get_g_type (info), optional, FALSE, transfer != GI_TRANSFER_NOTHING); break; } case GI_INFO_TYPE_CALLBACK: nret = marshal_2c_callable (L, info, ai, &arg->v_pointer, narg, optional, ci, args); break; default: g_assert_not_reached (); } lua_remove (L, info_guard); } break; case GI_TYPE_TAG_ARRAY: { gssize size; GIArrayType atype = g_type_info_get_array_type (ti); nret = marshal_2c_array (L, ti, atype, &arg->v_pointer, &size, narg, optional, transfer); /* Fill in array length argument, if it is specified. */ if (atype == GI_ARRAY_TYPE_C) array_get_or_set_length (ti, NULL, size, ci, args); break; } case GI_TYPE_TAG_GLIST: case GI_TYPE_TAG_GSLIST: nret = marshal_2c_list (L, ti, tag, &arg->v_pointer, narg, transfer); break; case GI_TYPE_TAG_GHASH: nret = marshal_2c_hash (L, ti, (GHashTable **) &arg->v_pointer, narg, optional, transfer); break; case GI_TYPE_TAG_VOID: if (g_type_info_is_pointer (ti)) { /* Check and marshal according to real Lua type. */ if (lua_isnoneornil (L, narg)) /* nil -> NULL. */ arg->v_pointer = NULL; if (lua_type (L, narg) == LUA_TSTRING) /* Use string directly. */ arg->v_pointer = (gpointer) lua_tostring (L, narg); else { int type = lua_type (L, narg); if (type == LUA_TLIGHTUSERDATA) /* Generic pointer. */ arg->v_pointer = lua_touserdata (L, narg); else { /* Check memory buffer. */ arg->v_pointer = lgi_udata_test (L, narg, LGI_BYTES_BUFFER); if (!arg->v_pointer) { /* Check object. */ arg->v_pointer = lgi_object_2c (L, narg, G_TYPE_INVALID, FALSE, TRUE, FALSE); if (!arg->v_pointer) { /* Check any kind of record. */ lua_pushnil (L); lgi_record_2c (L, narg, &arg->v_pointer, FALSE, FALSE, FALSE, TRUE); } } } } } break; default: marshal_2c_int (L, tag, arg, narg, optional, parent); } return nret; } gboolean lgi_marshal_2c_caller_alloc (lua_State *L, GITypeInfo *ti, GIArgument *val, int pos) { gboolean handled = FALSE; switch (g_type_info_get_tag (ti)) { case GI_TYPE_TAG_INTERFACE: { GIBaseInfo *ii = g_type_info_get_interface (ti); GIInfoType type = g_base_info_get_type (ii); if (type == GI_INFO_TYPE_STRUCT || type == GI_INFO_TYPE_UNION) { if (pos == 0) { lgi_type_get_repotype (L, G_TYPE_INVALID, ii); val->v_pointer = lgi_record_new (L, 1, FALSE); } handled = TRUE; } g_base_info_unref (ii); break; } case GI_TYPE_TAG_ARRAY: { if (g_type_info_get_array_type (ti) == GI_ARRAY_TYPE_C) { gpointer *array_guard; if (pos == 0) { gssize elt_size, size; /* Currently only fixed-size arrays are supported. */ elt_size = array_get_elt_size (g_type_info_get_param_type (ti, 0), FALSE); size = g_type_info_get_array_fixed_size (ti); g_assert (size > 0); /* Allocate underlying array. It is temporary, existing only for the duration of the call. */ array_guard = lgi_guard_create (L, (GDestroyNotify) g_array_unref); *array_guard = g_array_sized_new (FALSE, FALSE, elt_size, size); g_array_set_size (*array_guard, size); } else { /* Convert the allocated array into Lua table with contents. We have to do it in-place. */ /* Make sure that pos is absolute, so that stack shuffling below does not change the element it points to. */ if (pos < 0) pos += lua_gettop (L) + 1; /* Get GArray from the guard and unmarshal it as a full GArray into Lua. */ array_guard = lua_touserdata (L, pos); marshal_2lua_array (L, ti, GI_DIRECTION_OUT, GI_ARRAY_TYPE_ARRAY, GI_TRANSFER_EVERYTHING, *array_guard, -1, pos); /* Deactivate old guard, everything was marshalled into the newly created and marshalled table. */ *array_guard = NULL; /* Switch old value with the new data. */ lua_replace (L, pos); } handled = TRUE; } break; } default: break; } return handled; } /* Marshalls single value from GLib/C to Lua. Returns 1 if something was pushed to the stack. */ void lgi_marshal_2lua (lua_State *L, GITypeInfo *ti, GIArgInfo *ai, GIDirection dir, GITransfer transfer, gpointer source, int parent, GICallableInfo *ci, void *args) { gboolean own = (transfer != GI_TRANSFER_NOTHING); GITypeTag tag = g_type_info_get_tag (ti); GIArgument *arg = source; /* Make sure that parent is absolute index so that it is fixed even when we add/remove from the stack. */ lgi_makeabs (L, parent); switch (tag) { case GI_TYPE_TAG_VOID: if (g_type_info_is_pointer (ti)) /* Marshal pointer to simple lightuserdata. */ lua_pushlightuserdata (L, arg->v_pointer); else lua_pushnil (L); break; case GI_TYPE_TAG_BOOLEAN: if (parent == LGI_PARENT_IS_RETVAL) { ReturnUnion *ru = (ReturnUnion *) arg; ru->arg.v_boolean = ru->s; } lua_pushboolean (L, arg->v_boolean); break; case GI_TYPE_TAG_FLOAT: case GI_TYPE_TAG_DOUBLE: g_return_if_fail (parent != LGI_PARENT_FORCE_POINTER); lua_pushnumber (L, (tag == GI_TYPE_TAG_FLOAT) ? arg->v_float : arg->v_double); break; case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: { gchar *str = (parent == LGI_PARENT_FORCE_POINTER) ? arg->v_pointer : arg->v_string; if (tag == GI_TYPE_TAG_FILENAME && str != NULL) { gchar *utf8 = g_filename_to_utf8 (str, -1, NULL, NULL, NULL); lua_pushstring (L, utf8); g_free (utf8); } else lua_pushstring (L, str); if (transfer == GI_TRANSFER_EVERYTHING) g_free (str); break; } case GI_TYPE_TAG_INTERFACE: { GIBaseInfo *info = g_type_info_get_interface (ti); GIInfoType type = g_base_info_get_type (info); int info_guard; lgi_gi_info_new (L, info); info_guard = lua_gettop (L); switch (type) { case GI_INFO_TYPE_ENUM: case GI_INFO_TYPE_FLAGS: /* Prepare repotable of enum/flags on the stack. */ lgi_type_get_repotype (L, G_TYPE_INVALID, info); /* Unmarshal the numeric value. */ marshal_2lua_int (L, g_enum_info_get_storage_type (info), arg, parent); /* Get symbolic value from the table. */ lua_gettable (L, -2); /* Remove the table from the stack. */ lua_remove (L, -2); break; case GI_INFO_TYPE_STRUCT: case GI_INFO_TYPE_UNION: { gboolean by_ref = parent == LGI_PARENT_FORCE_POINTER || g_type_info_is_pointer (ti); if (parent < LGI_PARENT_CALLER_ALLOC && by_ref) parent = 0; lgi_type_get_repotype (L, G_TYPE_INVALID, info); lgi_record_2lua (L, by_ref ? arg->v_pointer : source, own, parent); break; } case GI_INFO_TYPE_OBJECT: case GI_INFO_TYPE_INTERFACE: /* Avoid sinking for input arguments, because it wreaks havoc to input arguments of vfunc callbacks during InitiallyUnowned construction phase. */ lgi_object_2lua (L, arg->v_pointer, own, dir == GI_DIRECTION_IN); break; case GI_INFO_TYPE_CALLBACK: if (arg->v_pointer == NULL) lua_pushnil (L); else { lgi_callable_create (L, info, arg->v_pointer); if (ai != NULL && args != NULL) { gint closure = g_arg_info_get_closure (ai); if (closure >= 0) { /* Store context associated with the callback to the callback object. */ GIArgument *arg = ((void **) args)[closure]; lua_pushlightuserdata (L, arg->v_pointer); lua_setfield (L, -2, "user_data"); } } } break; default: g_assert_not_reached (); } lua_remove (L, info_guard); } break; case GI_TYPE_TAG_ARRAY: { GIArrayType atype = g_type_info_get_array_type (ti); gssize size = -1; gpointer ptr = g_type_info_is_pointer (ti) ? arg->v_pointer : arg; array_get_or_set_length (ti, &size, 0, ci, args); marshal_2lua_array (L, ti, dir, atype, transfer, ptr, size, parent); } break; case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_GLIST: marshal_2lua_list (L, ti, dir, tag, transfer, arg->v_pointer); break; case GI_TYPE_TAG_GHASH: marshal_2lua_hash (L, ti, dir, transfer, arg->v_pointer); break; case GI_TYPE_TAG_ERROR: marshal_2lua_error (L, transfer, arg->v_pointer); break; default: marshal_2lua_int (L, tag, arg, parent); } } int lgi_marshal_field (lua_State *L, gpointer object, gboolean getmode, int parent_arg, int field_arg, int val_arg) { GITypeInfo *ti; int to_remove, nret; GIBaseInfo *pi = NULL; gpointer field_addr; /* Check the type of the field information. */ if (lgi_udata_test (L, field_arg, LGI_GI_INFO)) { GIFieldInfoFlags flags; GIFieldInfo **fi = lua_touserdata (L, field_arg); pi = g_base_info_get_container (*fi); /* Check, whether field is readable/writable. */ flags = g_field_info_get_flags (*fi); if ((flags & (getmode ? GI_FIELD_IS_READABLE : GI_FIELD_IS_WRITABLE)) == 0) { /* Check, whether parent did not disable access checks completely. */ lua_getfield (L, -1, "_allow"); if (!lua_toboolean (L, -1)) { /* Prepare proper error message. */ lua_concat (L, lgi_type_get_name (L, g_base_info_get_container (*fi))); return luaL_error (L, "%s: field `%s' is not %s", lua_tostring (L, -1), g_base_info_get_name (*fi), getmode ? "readable" : "writable"); } lua_pop (L, 1); } /* Map GIArgument to proper memory location, get typeinfo of the field and perform actual marshalling. */ field_addr = (char *) object + g_field_info_get_offset (*fi); ti = g_field_info_get_type (*fi); lgi_gi_info_new (L, ti); to_remove = lua_gettop (L); } else { /* Consult field table, get kind of field and offset. */ int kind; lgi_makeabs (L, field_arg); luaL_checktype (L, field_arg, LUA_TTABLE); lua_rawgeti (L, field_arg, 1); field_addr = (char *) object + lua_tointeger (L, -1); lua_rawgeti (L, field_arg, 2); kind = lua_tonumber (L, -1); lua_pop (L, 2); /* Load type information from the table and decide how to handle it according to 'kind' */ lua_rawgeti (L, field_arg, 3); switch (kind) { case 0: /* field[3] contains typeinfo, load it and fall through. */ ti = *(GITypeInfo **) luaL_checkudata (L, -1, LGI_GI_INFO); to_remove = lua_gettop (L); break; case 1: case 2: { GIArgument *arg = (GIArgument *) field_addr; if (getmode) { if (kind == 1) { field_addr = arg->v_pointer; parent_arg = 0; } lgi_record_2lua (L, field_addr, FALSE, parent_arg); return 1; } else { g_assert (kind == 1); lgi_record_2c (L, val_arg, arg->v_pointer, FALSE, TRUE, FALSE, FALSE); return 0; } break; } case 3: { /* Get the typeinfo for marshalling the numeric enum value. */ lua_rawgeti (L, field_arg, 4); ti = *(GITypeInfo **) luaL_checkudata (L, -1, LGI_GI_INFO); if (getmode) { /* Use typeinfo to unmarshal numeric value. */ lgi_marshal_2lua (L, ti, NULL, GI_DIRECTION_OUT, GI_TRANSFER_NOTHING, field_addr, 0, NULL, NULL); /* Replace numeric field with symbolic value. */ lua_gettable (L, -3); lua_replace (L, -3); lua_pop (L, 1); return 1; } else { /* Convert enum symbol to numeric value. */ if (lua_type (L, val_arg != LUA_TNUMBER)) { lua_pushvalue (L, -1); lua_pushvalue (L, val_arg); lua_call (L, 1, 1); lua_replace (L, val_arg); } /* Use typeinfo to marshal the numeric value. */ lgi_marshal_2c (L, ti, NULL, GI_TRANSFER_NOTHING, field_addr, val_arg, 0, NULL, NULL); lua_pop (L, 2); return 0; } } default: return luaL_error (L, "field has bad kind %d", kind); } } if (getmode) { lgi_marshal_2lua (L, ti, NULL, GI_DIRECTION_OUT, GI_TRANSFER_NOTHING, field_addr, parent_arg, pi, object); nret = 1; } else { lgi_marshal_2c (L, ti, NULL, GI_TRANSFER_EVERYTHING, field_addr, val_arg, 0, NULL, NULL); nret = 0; } lua_remove (L, to_remove); return nret; } int lgi_marshal_access (lua_State *L, gboolean getmode, int compound_arg, int element_arg, int val_arg) { lua_getfield (L, -1, "_access"); lua_pushvalue (L, -2); lua_pushvalue (L, compound_arg); lua_pushvalue (L, element_arg); if (getmode) { lua_call (L, 3, 1); return 1; } else { lua_pushvalue (L, val_arg); lua_call (L, 4, 0); return 0; } } /* Container marshaller function. */ static int marshal_container_marshaller (lua_State *L) { GValue *value; GITypeInfo **ti; GITypeTag tag; GITransfer transfer; gpointer data; int nret = 0; gboolean get_mode = lua_isnone (L, 3); /* Get GValue to operate on. */ lgi_type_get_repotype (L, G_TYPE_VALUE, NULL); lgi_record_2c (L, 1, &value, FALSE, FALSE, FALSE, FALSE); /* Get raw pointer from the value. */ if (get_mode) { if (G_VALUE_TYPE (value) == G_TYPE_POINTER) data = g_value_get_pointer (value); else data = g_value_get_boxed (value); } /* Get info and transfer from upvalue. */ ti = lua_touserdata (L, lua_upvalueindex (1)); tag = g_type_info_get_tag (*ti); transfer = lua_tointeger (L, lua_upvalueindex (2)); switch (tag) { case GI_TYPE_TAG_ARRAY: { GIArrayType atype = g_type_info_get_array_type (*ti); gssize size = -1; if (get_mode) { if (lua_type (L, 2) == LUA_TTABLE) { lua_getfield (L, 2, "length"); size = luaL_optinteger (L, -1, -1); lua_pop (L, 1); } marshal_2lua_array (L, *ti, GI_DIRECTION_OUT, atype, transfer, data, size, 0); } else { nret = marshal_2c_array (L, *ti, atype, &data, &size, 3, FALSE, transfer); if (lua_type (L, 2) == LUA_TTABLE) { lua_pushnumber (L, size); lua_setfield (L, 2, "length"); } } break; } case GI_TYPE_TAG_GSLIST: case GI_TYPE_TAG_GLIST: if (get_mode) marshal_2lua_list (L, *ti, GI_DIRECTION_OUT, tag, transfer, data); else nret = marshal_2c_list (L, *ti, tag, &data, 3, transfer); break; case GI_TYPE_TAG_GHASH: if (get_mode) marshal_2lua_hash (L, *ti, GI_DIRECTION_OUT, transfer, data); else nret = marshal_2c_hash (L, *ti, (GHashTable **) &data, 3, FALSE, transfer); break; default: g_assert_not_reached (); } /* Store result pointer to the value. */ if (!get_mode) { if (G_VALUE_TYPE (value) == G_TYPE_POINTER) g_value_set_pointer (value, data); else g_value_set_boxed (value, data); } /* If there are any temporary objects, try to store them into attrs.keepalive table, if it is present. */ if (!lua_isnoneornil (L, 2)) { lua_getfield (L, 2, "keepalive"); if (!lua_isnil (L, -1)) for (lua_insert (L, -nret - 1); nret > 0; nret--) { lua_pushnumber (L, lua_objlen (L, -nret - 1)); lua_insert (L, -2); lua_settable (L, -nret - 3); lua_pop (L, 1); } else lua_pop (L, nret); lua_pop (L, 1); } else lua_pop (L, nret); return get_mode ? 1 : 0; } static const char* const transfers[] = { "none", "container", "full", NULL }; /* Creates container (array, list, slist, hash) marshaller for specified container typeinfo. Signature is: marshaller = marshal.container(typeinfo, transfer) */ static int marshal_container (lua_State *L) { GIBaseInfo **info = luaL_checkudata (L, 1, LGI_GI_INFO); GITypeTag tag = g_type_info_get_tag (*info); GITransfer transfer = luaL_checkoption (L, 2, transfers[0], transfers); if (tag == GI_TYPE_TAG_ARRAY || tag == GI_TYPE_TAG_GHASH || tag == GI_TYPE_TAG_GSLIST || tag == GI_TYPE_TAG_GLIST) { lua_pushvalue (L, 1); lua_pushnumber (L, transfer); lua_pushcclosure (L, marshal_container_marshaller, 2); } else lua_pushnil (L); return 1; } /* Fundamental marshaller closure. */ static int marshal_fundamental_marshaller (lua_State *L) { gpointer obj; gboolean get_mode = lua_isnone (L, 3); GValue *value; lgi_type_get_repotype (L, G_TYPE_VALUE, NULL); lgi_record_2c (L, 1, &value, FALSE, FALSE, FALSE, FALSE); if (get_mode) { /* Get fundamental from value. */ GIObjectInfoGetValueFunction get_value = lua_touserdata (L, lua_upvalueindex (1)); obj = get_value (value); lgi_object_2lua (L, obj, FALSE, FALSE); return 1; } else { /* Set fundamental to value. */ GIObjectInfoSetValueFunction set_value = lua_touserdata (L, lua_upvalueindex (2)); obj = lgi_object_2c (L, 3, G_TYPE_INVALID, FALSE, FALSE, FALSE); set_value (value, obj); return 0; } } /* Creates marshaller closure for specified fundamental object type. If specified object does not have custom setvalue/getvalue functions registered, returns nil. Signature is: marshaller = marshal.fundamental(gtype) */ static int marshal_fundamental (lua_State *L) { /* Find associated baseinfo. */ GIBaseInfo *info = g_irepository_find_by_gtype (NULL, lgi_type_get_gtype (L, 1)); if (info) { lgi_gi_info_new (L, info); if (GI_IS_OBJECT_INFO (info) && g_object_info_get_fundamental (info)) { GIObjectInfoGetValueFunction get_value = lgi_object_get_function_ptr (info, g_object_info_get_get_value_function); GIObjectInfoSetValueFunction set_value = lgi_object_get_function_ptr (info, g_object_info_get_set_value_function); if (get_value && set_value) { lua_pushlightuserdata (L, get_value); lua_pushlightuserdata (L, set_value); lua_pushcclosure (L, marshal_fundamental_marshaller, 2); return 1; } } } lua_pushnil (L); return 1; } /* Creates or marshalls content of GIArgument to/from lua according to specified typeinfo. arg, ptr = marshal.argument() value = marshal.argument(arg, typeinfo, transfer) marshal.argument(arg, typeinfo, transfer, value) */ static int marshal_argument (lua_State *L) { GITypeInfo **info; GITransfer transfer; GIArgument *arg; if (lua_isnone (L, 1)) { /* Create new argument userdata. */ GIArgument *arg = lua_newuserdata (L, sizeof (*arg)); memset (arg, 0, sizeof (*arg)); lua_pushlightuserdata (L, arg); return 2; } arg = lua_touserdata (L, 1); info = luaL_checkudata (L, 2, LGI_GI_INFO); transfer = luaL_checkoption (L, 3, transfers[0], transfers); if (lua_isnone (L, 4)) { lgi_marshal_2lua (L, *info, NULL, GI_DIRECTION_IN, transfer, arg, 0, NULL, NULL); return 1; } else { lua_pop (L, lgi_marshal_2c (L, *info, NULL, transfer, arg, 4, 0, NULL, NULL)); return 0; } } static int marshal_callback (lua_State *L) { gpointer user_data, addr; GICallableInfo **ci; user_data = lgi_closure_allocate (L, 1); *lgi_guard_create (L, lgi_closure_destroy) = user_data; if (lua_istable (L, 1)) lgi_callable_parse (L, 1, NULL); else { ci = lgi_udata_test (L, 1, LGI_GI_INFO); lgi_callable_create (L, *ci, NULL); } addr = lgi_closure_create (L, user_data, 2, FALSE); lua_pushlightuserdata (L, addr); return 2; } static void gclosure_destroy (gpointer user_data, GClosure *closure) { (void) closure; lgi_closure_destroy (user_data); } /* Workaround for incorrectly annotated g_closure_invoke. Since it is pretty performance-sensitive, it is implemented here in native code instead of creating overlay with custom ffi for it. */ static int marshal_closure_invoke (lua_State *L) { GClosure *closure; GValue *result, *params; gint n_params, i; lgi_type_get_repotype (L, G_TYPE_CLOSURE, NULL); lgi_record_2c (L, 1, &closure, FALSE, FALSE, FALSE, FALSE); lgi_type_get_repotype (L, G_TYPE_VALUE, NULL); lua_pushvalue (L, -1); lgi_record_2c (L, 2, &result, FALSE, FALSE, FALSE, FALSE); luaL_checktype (L, 3, LUA_TTABLE); n_params = lua_objlen (L, 3); params = g_newa (GValue, n_params); memset (params, 0, sizeof (GValue) * n_params); for (i = 0; i < n_params; i++) { lua_pushnumber (L, i + 1); lua_gettable (L, 3); lua_pushvalue (L, -2); lgi_record_2c (L, -2, ¶ms[i], TRUE, FALSE, FALSE, FALSE); lua_pop (L, 1); } g_closure_invoke (closure, result, n_params, params, lua_touserdata (L, 4)); return 0; } /* This is workaround for missing glib function, which should look like this: void g_closure_set_marshal_with_data (GClosure *closure, GClosureMarshal marshal, gpointer user_data, GDestroyNotify destroy_notify); Such method would be introspectable. */ static int marshal_closure_set_marshal (lua_State *L) { GClosure *closure; gpointer user_data; GClosureMarshal marshal; GIBaseInfo *ci; ci = g_irepository_find_by_name (NULL, "GObject", "ClosureMarshal"); lgi_type_get_repotype (L, G_TYPE_CLOSURE, NULL); lgi_record_2c (L, 1, &closure, FALSE, FALSE, FALSE, FALSE); user_data = lgi_closure_allocate (L, 1); lgi_callable_create (L, ci, NULL); marshal = lgi_closure_create (L, user_data, 2, FALSE); g_closure_set_marshal (closure, marshal); g_closure_add_invalidate_notifier (closure, user_data, gclosure_destroy); return 0; } /* Calculates size and alignment of specified type. size, align = marshal.typeinfo(tiinfo) */ static int marshal_typeinfo (lua_State *L) { GIBaseInfo **info = luaL_checkudata (L, 1, LGI_GI_INFO); switch (g_type_info_get_tag (*info)) { #define HANDLE_INT(upper, type) \ case GI_TYPE_TAG_ ## upper: \ { \ struct Test { char offender; type examined; }; \ lua_pushnumber (L, sizeof (type)); \ lua_pushnumber (L, G_STRUCT_OFFSET (struct Test, examined)); \ } \ break HANDLE_INT (VOID, gpointer); HANDLE_INT (BOOLEAN, gboolean); HANDLE_INT (INT8, gint8); HANDLE_INT (UINT8, guint8); HANDLE_INT (INT16, gint16); HANDLE_INT (UINT16, guint16); HANDLE_INT (INT32, gint32); HANDLE_INT (UINT32, guint32); HANDLE_INT (INT64, gint64); HANDLE_INT (UINT64, guint64); HANDLE_INT (FLOAT, gfloat); HANDLE_INT (DOUBLE, gdouble); HANDLE_INT (GTYPE, GType); HANDLE_INT (UTF8, const gchar *); HANDLE_INT (FILENAME, const gchar *); HANDLE_INT (UNICHAR, gunichar); #undef HANDLE_INT default: return luaL_argerror (L, 1, "bad typeinfo"); } return 2; } static const struct luaL_Reg marshal_api_reg[] = { { "container", marshal_container }, { "fundamental", marshal_fundamental }, { "argument", marshal_argument }, { "callback", marshal_callback }, { "closure_set_marshal", marshal_closure_set_marshal }, { "closure_invoke", marshal_closure_invoke }, { "typeinfo", marshal_typeinfo }, { NULL, NULL } }; void lgi_marshal_init (lua_State *L) { /* Create 'marshal' API table in main core API table. */ lua_newtable (L); luaL_register (L, NULL, marshal_api_reg); lua_setfield (L, -2, "marshal"); } lgi-0.9.2/lgi/namespace.lua000066400000000000000000000141111316674307300155170ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- lgi Support for repository namespace -- -- Copyright (c) 2010, 2011,2016 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local type, rawget, next, pairs, require, pcall, setmetatable, assert = type, rawget, next, pairs, require, pcall, setmetatable, assert local package = require 'package' local core = require 'lgi.core' local enum = require 'lgi.enum' local component = require 'lgi.component' local record = require 'lgi.record' local class = require 'lgi.class' -- Table containing loaders for various GI types, indexed by -- gi.InfoType constants. local typeloader = {} typeloader['function'] = function(namespace, info) return core.callable.new(info), '_function' end function typeloader.constant(namespace, info) return core.constant(info), '_constant' end function typeloader.enum(namespace, info) return enum.load(info, enum.enum_mt), '_enum' end function typeloader.flags(namespace, info) return enum.load(info, enum.bitflags_mt), '_enum' end function typeloader.struct(namespace, info) -- Avoid exposing internal structs created for object implementations. if not info.is_gtype_struct then return record.load(info), '_struct' end end function typeloader.union(namespace, info) return record.load(info), '_union' end function typeloader.interface(namespace, info) return class.load_interface(namespace, info), '_interface' end function typeloader.object(namespace, info) return class.load_class(namespace, info), '_class' end -- Repo namespace metatable. local namespace = { mt = { _categories = { '_class', '_interface', '_struct', '_union', '_enum', '_function', '_constant', }, _category_mt = {}, } } -- Gets symbol of the specified namespace, if not present yet, tries to load it -- on-demand. function namespace.mt:__index(symbol) -- Check whether symbol is present in the metatable. local val = namespace.mt[symbol] if val then return val end -- Check, whether there is some precondition in the lazy-loading table. local preconditions = rawget(self, '_precondition') local precondition = preconditions and preconditions[symbol] if precondition then local package = preconditions[symbol] if not preconditions[package] then preconditions[package] = true require('lgi.override.' .. package) preconditions[package] = nil end preconditions[symbol] = nil if not next(preconditions) then self._precondition = nil end end -- Check, whether symbol is already loaded. val = component.mt._element(self, nil, symbol, namespace.mt._categories) if val then return val end -- Lookup baseinfo of requested symbol in the GIRepository. local info = core.gi[self._name][symbol] if not info then return nil end -- Decide according to symbol type what to do. local loader = typeloader[info.type] if loader then local category val, category = loader(self, info) -- Cache the symbol in specified category in the namespace. if val then local cat = rawget(self, category) if not cat then local mt = namespace.mt._category_mt[category] if not mt then mt = {} namespace.mt._category_mt[category] = mt end cat = setmetatable({ _namespace = self }, mt) self[category] = cat end -- Store symbol into the repo, but only if it is not already -- there. It could by added to repo as byproduct of loading -- other symbol. if not cat[symbol] then cat[symbol] = val end elseif info.is_gtype_struct then -- If we have XxxClass name, try to lookup class structure of -- the Xxx object. local class = (symbol:match('^(%w+)Class$') or symbol:match('^(%w+)Iface$') or symbol:match('^(%w+)Interface$')) if class then class = self[class] if class then val = class._class end end end else val = info end return val end -- Resolves everything in the namespace by iterating through it. function namespace.mt:_resolve(recurse) -- Iterate through all items in the namespace and dereference them, -- which causes them to be loaded in and cached inside the namespace -- table. local gi_ns = core.gi[self._name] for i = 1, #gi_ns do local ok, component = pcall(function() return self[gi_ns[i].name] end) if ok and recurse and type(component) == 'table' then local resolve = component._resolve if resolve then resolve(component, recurse) end end end return self end -- Makes sure that the namespace (optionally with requested version) -- is properly loaded. function namespace.require(name, version) -- Load the namespace info for GIRepository. This also verifies -- whether requested version can be loaded. local ns_info = assert(core.gi.require(name, version)) -- If the repository table does not exist yet, create it. local ns = rawget(core.repo, name) if not ns then ns = setmetatable({ _name = name, _version = ns_info.version, _dependencies = ns_info.dependencies }, namespace.mt) core.repo[name] = ns -- Make sure that all dependent namespaces are also loaded. for name, version in pairs(ns._dependencies or {}) do namespace.require(name, version) end -- Try to load override, if it is present. local override_name = 'lgi.override.' .. ns._name local ok, msg = pcall(require, override_name) if not ok then -- Try parsing message; if it is something different than -- "module xxx not found", then attempt to load again and let -- the exception fly out. if not msg:find("module '" .. override_name .. "' not found:", 1, true) then package.loaded[override_name] = nil require(override_name) end end if ok and type(msg) == "string" then -- It's an error message for something that does not want to be -- re-loaded, see e.g. the call to Gtk.init_check() in Gtk.lua. error(msg) end end return ns end return namespace lgi-0.9.2/lgi/object.c000066400000000000000000000407321316674307300145020ustar00rootroot00000000000000/* * Dynamic Lua binding to GObject using dynamic gobject-introspection. * * Copyright (c) 2010, 2011 Pavel Holejsovsky * Licensed under the MIT license: * http://www.opensource.org/licenses/mit-license.php * * GObject and GTypeInstance handling. */ #include #include "lgi.h" /* lightuserdata key to registry, containing table representing weak cache of known objects. */ static int cache; /* lightuserdata key to registry for metatable of objects. */ static int object_mt; /* lightuserdata key to registry, containing 'env' table, which maps lightuserdata(obj-addr) -> obj-env-table. */ static int env; /* Keys in 'env' table containing quark used as object's qdata for env and thread which is used from qdata destroy callback. */ enum { OBJECT_QDATA_ENV = 1, OBJECT_QDATA_THREAD }; /* Structure stored in GObject's qdata at OBJECT_QDATA_ENV. */ typedef struct _ObjectData { gpointer object; gpointer state_lock; lua_State *L; } ObjectData; /* lightuserdata key to registry, containing metatable for object env guard. */ static int env_mt; /* Structure containing object_env_guard userdata. */ typedef struct _ObjectEnvGuard { gpointer object; GQuark id; } ObjectEnvGuard; /* Checks that given narg is object type and returns pointer to type instance representing it. */ static gpointer object_check (lua_State *L, int narg) { gpointer *obj = lua_touserdata (L, narg); luaL_checkstack (L, 3, ""); if (!lua_getmetatable (L, narg)) return NULL; lua_pushlightuserdata (L, &object_mt); lua_rawget (L, LUA_REGISTRYINDEX); if (!lua_equal (L, -1, -2)) obj = NULL; lua_pop (L, 2); g_assert (obj == NULL || *obj != NULL); return obj ? *obj : NULL; } /* Walks given type and tries to find the closest known match of the object present in the repo. If found, leaves found type table on the stack and returns real found gtype, otherwise returns G_TYPE_INVALID. */ static GType object_type (lua_State *L, GType gtype) { for (; gtype != G_TYPE_INVALID; gtype = g_type_parent (gtype)) { /* Get appropriate repo table, if present. */ lgi_type_get_repotype (L, gtype, NULL); if (!lua_isnil (L, -1)) break; lua_pop (L, 1); } return gtype; } /* Throws type error for object at given argument, gtype can optionally contain name of requested type. */ static int object_type_error (lua_State *L, int narg, GType gtype) { GType found_gtype; /* Look up type table and get name from it. */ luaL_checkstack (L, 4, ""); found_gtype = object_type (L, gtype); if (found_gtype != G_TYPE_INVALID) { lua_getfield (L, -1, "_name"); lua_pushfstring (L, gtype == found_gtype ? "%s" : "%s(%s)", lua_tostring (L, -1), g_type_name (gtype)); } else { if (gtype == G_TYPE_INVALID) lua_pushliteral (L, "lgi.object"); else lua_pushstring (L, g_type_name (gtype)); } /* Create error message. */ lua_pushstring (L, lua_typename (L, lua_type (L, narg))); lua_pushfstring (L, "%s expected, got %s", lua_tostring (L, -2), lua_tostring (L, -1)); return luaL_argerror (L, narg, lua_tostring (L, -1)); } static gpointer object_get (lua_State *L, int narg) { gpointer obj = object_check (L, narg); if (G_UNLIKELY (!obj)) object_type_error (L, narg, G_TYPE_INVALID); return obj; } /* This is workaround method for broken g_object_info_get_*_function_pointer() in GI 1.32.0. (see https://bugzilla.gnome.org/show_bug.cgi?id=673282) */ gpointer lgi_object_get_function_ptr (GIObjectInfo *info, const gchar *(*getter)(GIObjectInfo *)) { gpointer func = NULL; g_base_info_ref (info); while (info != NULL) { GIBaseInfo *parent; const gchar *func_name; /* Try to get the name and the symbol. */ func_name = getter (info); if (func_name && g_typelib_symbol (g_base_info_get_typelib (info), func_name, &func)) { g_base_info_unref (info); break; } /* Iterate to the parent info. */ parent = g_object_info_get_parent (info); g_base_info_unref (info); info = parent; } return func; } /* Retrieves requested typetable function for the object. */ static gpointer object_load_function (lua_State *L, GType gtype, const gchar *name) { gpointer func = NULL; if (object_type (L, gtype) != G_TYPE_INVALID) { func = lgi_gi_load_function (L, -1, name); lua_pop (L, 1); } return func; } /* Adds one reference to the object, returns TRUE if succeded. */ static gboolean object_refsink (lua_State *L, gpointer obj, gboolean no_sink) { GType gtype = G_TYPE_FROM_INSTANCE (obj); if (G_TYPE_IS_OBJECT (gtype)) { if (G_UNLIKELY (no_sink)) g_object_ref (obj); else g_object_ref_sink (obj); return TRUE; } /* Check whether object has registered fundamental 'ref' function. */ GIObjectInfo *info = g_irepository_find_by_gtype (NULL, gtype); if (info == NULL) info = g_irepository_find_by_gtype (NULL, G_TYPE_FUNDAMENTAL (gtype)); if (info != NULL && g_object_info_get_fundamental (info)) { GIObjectInfoRefFunction ref = lgi_object_get_function_ptr (info, g_object_info_get_ref_function); g_base_info_unref (info); if (ref != NULL) { ref (obj); return TRUE; } } /* Finally check custom _refsink method in typetable. */ gpointer (*refsink_func)(gpointer) = object_load_function (L, gtype, "_refsink"); if (refsink_func) { refsink_func (obj); return TRUE; } /* There is no known wasy how to ref this kind of object. But this typically appears when handling GParamSpec, and GParamSpec handling generally works fine even without ref/unref, so the warnings produced are generally junk, so disabled until a way to handle ParamSpec properly is found. */ #if 0 g_warning ("no way to ref type `%s'", g_type_name (gtype)); #endif return FALSE; } /* Removes one reference from the object. */ static void object_unref (lua_State *L, gpointer obj) { GType gtype = G_TYPE_FROM_INSTANCE (obj); if (G_TYPE_IS_OBJECT (gtype)) { g_object_unref (obj); return; } /* Some other fundamental type, check, whether it has registered custom unref method. */ GIObjectInfo *info = g_irepository_find_by_gtype (NULL, gtype); if (info == NULL) info = g_irepository_find_by_gtype (NULL, G_TYPE_FUNDAMENTAL (gtype)); if (info != NULL && g_object_info_get_fundamental (info)) { GIObjectInfoUnrefFunction unref = lgi_object_get_function_ptr (info, g_object_info_get_unref_function); g_base_info_unref (info); if (unref != NULL) { unref (obj); return; } } void (*unref_func)(gpointer) = object_load_function (L, gtype, "_unref"); if (unref_func) { unref_func (obj); return; } #if 0 g_warning ("no way to unref type `%s'", g_type_name (gtype)); #endif } static int object_gc (lua_State *L) { object_unref (L, object_get (L, 1)); /* Unset the metatable / make the object unusable */ lua_pushnil (L); lua_setmetatable (L, 1); return 0; } static int object_tostring (lua_State *L) { gpointer obj = object_get (L, 1); GType gtype = G_TYPE_FROM_INSTANCE (obj); lua_getfenv (L, 1); if (lua_isnil (L, -1)) lua_pushliteral (L, ""); else { lua_getfield (L, -1, "_tostring"); if (!lua_isnil (L, -1)) { lua_pushvalue (L, 1); lua_call (L, 1, 1); return 1; } lua_getfield (L, -2, "_name"); } lua_pushfstring (L, "lgi.obj %p:%s(%s)", obj, lua_tostring (L, -1), g_type_name (gtype)); return 1; } gpointer lgi_object_2c (lua_State *L, int narg, GType gtype, gboolean optional, gboolean nothrow, gboolean transfer) { gpointer obj; /* Check for nil. */ if (optional && lua_isnoneornil (L, narg)) return NULL; /* Get instance and perform type check. */ obj = object_check (L, narg); if (!nothrow && (!obj || (gtype != G_TYPE_INVALID && !g_type_is_a (G_TYPE_FROM_INSTANCE (obj), gtype)))) object_type_error (L, narg, gtype); if (transfer) object_refsink (L, obj, FALSE); return obj; } int lgi_object_2lua (lua_State *L, gpointer obj, gboolean own, gboolean no_sink) { /* NULL pointer results in nil. */ if (!obj) { lua_pushnil (L); return 1; } /* Check, whether the object is already created (in the cache). */ luaL_checkstack (L, 6, ""); lua_pushlightuserdata (L, &cache); lua_rawget (L, LUA_REGISTRYINDEX); lua_pushlightuserdata (L, obj); lua_rawget (L, -2); if (!lua_isnil (L, -1)) { /* Use the object from the cache. */ lua_replace (L, -2); /* If the object was already owned, remove one reference, because our proxy always keeps only one reference, which we already have. */ if (own) object_unref (L, obj); return 1; } /* Create new userdata object. */ *(gpointer *) lua_newuserdata (L, sizeof (obj)) = obj; lua_pushlightuserdata (L, &object_mt); lua_rawget (L, LUA_REGISTRYINDEX); lua_setmetatable (L, -2); object_type (L, G_TYPE_FROM_INSTANCE (obj)); lua_setfenv (L, -2); /* Store newly created userdata proxy into cache. */ lua_pushlightuserdata (L, obj); lua_pushvalue (L, -2); lua_rawset (L, -5); /* Stack cleanup, remove unnecessary cache and nil under userdata. */ lua_replace (L, -3); lua_pop (L, 1); /* If we don't own the object, take its ownership (and also remove floating reference if there is any). */ if (!own) object_refsink (L, obj, no_sink); return 1; } /* Worker method for __index and __newindex implementation. */ static int object_access (lua_State *L) { gboolean getmode = lua_isnone (L, 3); /* Check that 1st arg is an object and invoke one of the forms: result = type:_access(objectinstance, name) type:_access(objectinstance, name, val) */ object_get (L, 1); lua_getfenv (L, 1); return lgi_marshal_access (L, getmode, 1, 2, 3); } /* Registration table. */ static const luaL_Reg object_mt_reg[] = { { "__gc", object_gc }, { "__tostring", object_tostring }, { "__index", object_access }, { "__newindex", object_access }, { NULL, NULL } }; static const char *const query_mode[] = { "addr", "repo", NULL }; /* Queries for assorted instance properties. Lua-side prototype: res = object.query(objectinstance, mode [, iface-gtype]) Supported mode strings are: 'repo': returns repotable for this instance. 'addr': returns lightuserdata with pointer to the object. */ static int object_query (lua_State *L) { gpointer object = object_check (L, 1); if (object) { int mode = luaL_checkoption (L, 2, query_mode[0], query_mode); if (mode == 0) lua_pushlightuserdata (L, object); else lua_getfenv (L, 1); return 1; } return 0; } /* Object field accessor. Lua-side prototypes: res = object.field(objectinstance, gi.fieldinfo) object.field(objectinstance, gi.fieldinfo, newvalue) */ static int object_field (lua_State *L) { /* Check, whether we are doing set or get operation. */ gboolean getmode = lua_isnone (L, 3); /* Get object instance. */ gpointer object = object_get (L, 1); /* Call field marshalling worker. */ lua_getfenv (L, 1); return lgi_marshal_field (L, object, getmode, 1, 2, 3); } static void object_data_destroy (gpointer user_data) { ObjectData *data = user_data; lua_State *L = data->L; lgi_state_enter (data->state_lock); luaL_checkstack (L, 4, NULL); /* Release 'obj' entry from 'env' table. */ lua_pushlightuserdata (L, &env); lua_rawget (L, LUA_REGISTRYINDEX); /* Deactivate env_destroy, to avoid double destruction. */ lua_pushlightuserdata (L, data->object); lua_rawget (L, -2); if (!lua_isnil (L, -1)) *(gpointer **) lua_touserdata (L, -1) = NULL; lua_pushlightuserdata (L, data->object); lua_pushnil (L); lua_rawset (L, -4); lua_pop (L, 2); /* Leave the context and destroy data structure. */ lgi_state_leave (data->state_lock); g_free (data); } static int object_env_guard_gc (lua_State *L) { ObjectEnvGuard *guard = lua_touserdata (L, -1); g_free (g_object_steal_qdata (G_OBJECT (guard->object), guard->id)); return 0; } /* Object environment table accessor. Lua-side prototype: env = object.env(objectinstance) */ static int object_env (lua_State *L) { ObjectData *data; gpointer obj = object_get (L, 1); if (!G_IS_OBJECT (obj)) /* Only GObject instances can have environment. */ return 0; /* Lookup 'env' table. */ lua_pushlightuserdata (L, &env); lua_rawget (L, LUA_REGISTRYINDEX); lua_pushlightuserdata (L, obj); lua_rawget (L, -2); if (!lua_isnil (L, -1)) /* Object's env table for the object is attached to the controlling userdata in the 'env' table. */ lua_getfenv (L, -1); else { ObjectEnvGuard *guard; /* Create new table which will serve as an object env table. */ lua_newtable (L); /* Create userdata guard, which disconnects env when the state dies. Attach the actual env table as env table to the guard udata. */ guard = lua_newuserdata (L, sizeof (ObjectEnvGuard)); guard->object = obj; lua_rawgeti (L, -4, OBJECT_QDATA_ENV); guard->id = lua_tonumber (L, -1); lua_pop (L, 1); lua_pushvalue (L, -2); lua_setfenv (L, -2); /* Store it to the 'env' table. */ lua_pushlightuserdata (L, obj); lua_pushvalue (L, -2); lua_rawset (L, -6); /* Create and fill new ObjectData structure, to attach it to object's qdata. */ data = g_new (ObjectData, 1); data->object = obj; lua_rawgeti (L, -4, OBJECT_QDATA_THREAD); data->L = lua_tothread (L, -1); data->state_lock = lgi_state_get_lock (data->L); /* Attach ObjectData to the object. */ g_object_set_qdata_full (G_OBJECT (obj), guard->id, data, object_data_destroy); lua_pop (L, 2); } return 1; } /* Creates new object. Lua-side prototypes: res = object.new(luserdata-ptr[, already_own[, no_sink]]) res = object.new(gtype, { GParameter }) */ static int object_new (lua_State *L) { if (lua_islightuserdata (L, 1)) /* Create object from the given pointer. */ return lgi_object_2lua (L, lua_touserdata (L, 1), lua_toboolean (L, 2), lua_toboolean (L, 3)); else { /* Normally Lua code uses GObject.Object.new(), which maps directly to g_object_newv(), but for some reason GOI < 1.0 does not export this method in the typelib. */ /* Get GType - 1st argument. */ GParameter *params; size_t size, i; GIBaseInfo *gparam_info; GType gtype = lgi_type_get_gtype (L, 1); luaL_checktype (L, 2, LUA_TTABLE); /* Find BaseInfo of GParameter. */ gparam_info = g_irepository_find_by_name (NULL, "GObject", "Parameter"); *lgi_guard_create (L, (GDestroyNotify) g_base_info_unref) = gparam_info; /* Prepare array of GParameter structures. */ size = lua_objlen (L, 2); params = g_newa (GParameter, size); for (i = 0; i < size; ++i) { lua_pushnumber (L, i + 1); lua_gettable (L, 2); lgi_type_get_repotype (L, G_TYPE_INVALID, gparam_info); lgi_record_2c (L, -2, ¶ms[i], TRUE, FALSE, FALSE, FALSE); lua_pop (L, 1); } /* Create the object and return it. */ return lgi_object_2lua (L, g_object_newv (gtype, size, params), TRUE, FALSE); } } /* Object API table. */ static const luaL_Reg object_api_reg[] = { { "query", object_query }, { "field", object_field }, { "new", object_new }, { "env", object_env }, { NULL, NULL } }; void lgi_object_init (lua_State *L) { char *id; /* Register metatable. */ lua_pushlightuserdata (L, &object_mt); lua_newtable (L); luaL_register (L, NULL, object_mt_reg); lua_rawset (L, LUA_REGISTRYINDEX); /* Initialize object cache. */ lgi_cache_create (L, &cache, "v"); /* Create table for 'env' tables. */ lua_pushlightuserdata (L, &env); lua_newtable (L); /* Add OBJECT_QDATA_ENV quark to env table. */ id = g_strdup_printf ("lgi:%p", L); lua_pushnumber (L, g_quark_from_string (id)); g_free (id); lua_rawseti (L, -2, OBJECT_QDATA_ENV); /* Add OBJECT_QDATA_THREAD to env table. */ lua_newthread (L); lua_rawseti (L, -2, OBJECT_QDATA_THREAD); /* Add 'env' table to the registry. */ lua_rawset (L, LUA_REGISTRYINDEX); /* Register env_mt table. */ lua_pushlightuserdata (L, &env_mt); lua_newtable (L); lua_pushcfunction (L, object_env_guard_gc); lua_setfield (L, -2, "__gc"); lua_rawset (L, LUA_REGISTRYINDEX); /* Create object API table and set it to the parent. */ lua_newtable (L); luaL_register (L, NULL, object_api_reg); lua_setfield (L, -2, "object"); } lgi-0.9.2/lgi/override/000077500000000000000000000000001316674307300147015ustar00rootroot00000000000000lgi-0.9.2/lgi/override/Clutter.lua000066400000000000000000000030621316674307300170270ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- LGI Clutter override module. -- -- Copyright (c) 2010, 2011 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local select, type, pairs, setmetatable, error = select, type, pairs, setmetatable, error local lgi = require 'lgi' local core = require 'lgi.core' local Clutter = lgi.Clutter Clutter.Container._attribute = {} function Clutter.Container:add(...) local args = { ... } for i = 1, #args do Clutter.Container.add_actor(self, args[i]) end end -- Allow ctor to add widgets from the array part Clutter.Container._container_add = Clutter.Container.add_actor -- Provides pseudo-attribute 'meta' for accessing container's -- child-meta elements. local container_child_meta_mt = {} function container_child_meta_mt:__index(child) return self._container:get_child_meta(child) end Clutter.Container._attribute.meta = {} function Clutter.Container._attribute.meta:get() return setmetatable({ _container = self }, container_child_meta_mt) end -- Take over internal Clutter synchronization lock and initialize -- Clutter's threading. core.registerlock(core.gi.Clutter.resolve.clutter_threads_set_lock_functions) Clutter.threads_init() -- Automatically initialize clutter, avoid continuing if -- initialization fails. local status = Clutter.init() if status ~= 'SUCCESS' then error(("Clutter initialization failed: %s"):format(status)) end lgi-0.9.2/lgi/override/GLib-Bytes.lua000066400000000000000000000012701316674307300173050ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- lgi GLib Bytes support -- -- Copyright (c) 2013 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local select, type, pairs, tostring, setmetatable, error, assert = select, type, pairs, tostring, setmetatable, error, assert local lgi = require 'lgi' local GLib = lgi.GLib local Bytes = GLib.Bytes -- Define length querying operation. Bytes._len = Bytes.get_size -- Add support for querying bytes attribute Bytes._attribute = { data = { get = Bytes.get_data } } lgi-0.9.2/lgi/override/GLib-Error.lua000066400000000000000000000050231316674307300173100ustar00rootroot00000000000000------------------------------------------------------------------------------- -- -- lgi GLib Error support implementation. -- -- Copyright (c) 2013 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------- local pairs, type, select = pairs, type, select local lgi = require 'lgi' local core = require 'lgi.core' local record = require 'lgi.record' local GLib = lgi.GLib local Error = lgi.GLib.Error Error._attribute = {} -- Add attribute which looks up associated enum type. Error._attribute.domain = { get = function(object) local info = core.gi[core.record.field(object, Error._field.domain)] return core.repotype(info) end, } -- Override code attribute to return symbolic code from enum. Error._attribute.code = { get = function(object) local code = core.record.field(object, Error._field.code) local enum = object.domain return enum and enum[code] or code end, } -- Converts domain to quark local function domain_to_quark(domain) if type(domain) == 'table' then return domain.error_domain elseif type(domain) == 'string' then return GLib.quark_from_string(domain) else return domain end end -- Converts code to numeric code, assumes already quark-form domain local function code_to_number(code, domain) if type(code) == 'string' then return core.repotype(core.gi[domain])(code) else return code end end -- Create new error instance. function Error.new(domain, code, message, ...) domain = domain_to_quark(domain) code = code_to_number(code, domain) if select('#', ...) > 0 then message = message:format(...) end return Error.new_literal(domain, code, message) end function Error:_new(...) return Error.new(...) end -- Override _tostring, in order to automatically convert errors to -- error messages. function Error:_tostring() return self.message end -- Override matches() method, so that it can use different methods of -- entering domain and/or code. function Error:matches(domain, code) if GLib.Error:is_type_of(domain) then -- This actually means match against another error instance. local err = domain domain = core.record.field(err, GLib.Error._field.domain) code = core.record.field(err, GLib.Error._field.code) else domain = domain_to_quark(domain) code = code_to_number(code, domain) end return Error._method.matches(self, domain, code) end lgi-0.9.2/lgi/override/GLib-Markup.lua000066400000000000000000000072361316674307300174660ustar00rootroot00000000000000------------------------------------------------------------------------------- -- -- LGI GLib MarkupParser support implementation. -- -- Copyright (c) 2013 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------- local pairs, ipairs, setmetatable, select = pairs, ipairs, setmetatable, select local lgi = require 'lgi' local core = require 'lgi.core' local record = require 'lgi.record' local component = require 'lgi.component' local ffi = require 'lgi.ffi' local ti = ffi.types local MarkupParser = lgi.GLib.MarkupParser local MarkupParseContext = lgi.GLib.MarkupParseContext local parser_guard = setmetatable({}, { __mode = 'k' }) local parser_field = MarkupParser._field MarkupParser._field = nil MarkupParser._attribute = {} MarkupParser._allow = true -- Replace fields with function pointers with attributes which actually -- convert Lua target into C callback. for name, def in pairs { start_element = { signature = { name = 'start_element', throws = true, ret = ti.void, MarkupParseContext, ti.utf8, ti.GStrv, ti.GStrv, ti.ptr }, override = function(start_element) return function(context, element, attr_names, attr_values, user_data) -- Convert attribute lists to table. local attrs = {} for i = 1, #attr_names do attrs[attr_names[i]] = attr_values[i] end start_element(context, element, attrs, user_data) end end, }, end_element = {}, passthrough = {}, text = {}, error = {}, } do MarkupParser._attribute[name] = { set = function(parser, target) -- Prepare guards table for this parser local guards = parser_guard[parser] if not guards then guards = {} parser_guard[parser] = guards end -- Generate real function pointer and guard for the target local cbk_type = def.signature or parser_field[name].typeinfo.interface if def.override then target = def.override(target) end local guard, funcptr = core.marshal.callback(cbk_type, target) guards[name] = guard core.record.field(parser, parser_field[name], funcptr) end } end -- ParseContext helper overrides. if not MarkupParseContext._gtype then -- Note that older glib/gobject-introspection combos did not mark -- MarkupParseContext as boxed. This means that we have to teach -- lgi how to free context and also new(), because in this case -- new() is marked as introspectable="0". MarkupParseContext._free = core.gi.GLib.resolve.g_markup_parse_context_free MarkupParseContext._method.new = core.callable.new { name = 'GLib.MarkupParseContext.new', addr = core.gi.GLib.resolve.g_markup_parse_context_new, ret = { MarkupParseContext, xfer = true }, MarkupParser, lgi.GLib.MarkupParseFlags, ti.ptr, ti.ptr, ti.GDestroyNotify, } end function MarkupParseContext.new(parser, flags, user_data) -- DestroyNotify is required (allow-none) annotation is missing, so -- provide dummy one. return MarkupParseContext._method.new(parser, flags, user_data, function() end) end function MarkupParseContext._new(typetable, parser, flags) return MarkupParseContext.new(parser, flags) end function MarkupParseContext:parse(text, len) return MarkupParseContext._method.parse(self, text, len or -1) end MarkupParseContext._method.pop = core.callable.new { name = 'GLib.MarkupParseContext.pop', addr = core.gi.GLib.resolve.g_markup_parse_context_pop, ret = ti.ptr, MarkupParseContext } local escape_text = lgi.GLib.markup_escape_text function lgi.GLib.markup_escape_text(text) return escape_text(text, -1) end lgi-0.9.2/lgi/override/GLib-Source.lua000066400000000000000000000020721316674307300174600ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- lgi GLib Source support -- -- Copyright (c) 2015 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local type, setmetatable, pairs = type, setmetatable, pairs local lgi = require 'lgi' local core = require 'lgi.core' local gi = core.gi local component = require 'lgi.component' local record = require 'lgi.record' local ffi = require 'lgi.ffi' local ti = ffi.types local GLib = lgi.GLib local Source = GLib.Source local SourceFuncs = GLib.SourceFuncs SourceFuncs._field.prepare = { name = 'prepare', offset = SourceFuncs._field.prepare.offset, ret = ti.boolean, Source, { ti.int, dir = 'out' } } local source_new = Source._new function Source:_new(funcs) if type(funcs) == 'table' then funcs = SourceFuncs(funcs) end function funcs.finalize(source) funcs = nil end return source_new(self, funcs, Source._size) end lgi-0.9.2/lgi/override/GLib-Timer.lua000066400000000000000000000017661316674307300173110ustar00rootroot00000000000000------------------------------------------------------------------------------- -- -- LGI GLib Timer support implementation. -- -- Copyright (c) 2013 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------- local pairs = pairs local lgi = require 'lgi' local core = require 'lgi.core' local record = require 'lgi.record' local ffi = require 'lgi.ffi' local ti = ffi.types local Timer = lgi.GLib.Timer:_resolve(true) local module = core.gi.GLib.resolve for name, def in pairs { new = { ret = { Timer, xfer = true } }, elapsed = { ret = ti.double, { Timer }, { ti.ulong, dir = 'out' } }, } do local _ = Timer[name] def.addr = module['g_timer_' .. name] def.name = 'GLib.Timer.' .. name Timer[name] = core.callable.new(def) end Timer._free = core.gi.GLib.Timer.methods.destroy Timer._method.destroy = nil Timer._new = function(_, ...) return Timer.new(...) end lgi-0.9.2/lgi/override/GLib-Variant.lua000066400000000000000000000254501316674307300176310ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- LGI GLib Variant support implementation. -- -- Copyright (c) 2011 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local select, type, pairs, tostring, setmetatable, error, assert = select, type, pairs, tostring, setmetatable, error, assert local lgi = require 'lgi' local core = require 'lgi.core' local bytes = require 'bytes' local gi = core.gi local GLib = lgi.GLib local Variant = GLib.Variant local variant_info = gi.GLib.Variant -- Add custom methods for variant handling. Variant._refsink = variant_info.methods.ref_sink Variant._free = variant_info.methods.unref -- Add 'type' property to variant, an alias to get_type(). Variant._attribute = { type = { get = Variant.get_type_string } } local VariantBuilder = GLib.VariantBuilder local VariantType = GLib.VariantType -- VariantBuilder and VariantType are boxed only in glib-2.29 and -- newer, add custom _free recipe for older glibs. if not VariantBuilder._gtype then VariantBuilder._free = gi.GLib.VariantBuilder.methods.unref end if not VariantType._gtype then VariantBuilder._free = gi.GLib.VariantType.methods.free end -- Add constants containing basic variant types. for k, v in pairs { BOOLEAN = 'b', BYTE = 'y', INT16 = 'n', UINT16 = 'q', INT32 = 'i', UINT32 = 'u', INT64 = 'x', UINT64 = 't', DOUBLE = 'd', STRING = 's', OBJECT_PATH = 'o', SIGNATURE = 'g', VARIANT = 'v', ANY = '*', BASIC = '?', MAYBE = 'm*', ARRAY = 'a*', TUPLE = 'r', UNIT = '()', DICT_ENTRY = '{?*}', DICTIONARY = 'a{?*}', STRING_ARRAY = 'as', BYTESTRING = 'ay', BYTESTRING_ARRAY = 'aay', } do VariantType[k] = VariantType.new(v) end -- g_variant_get_type() is hidden by g-i (because scanner thinks that -- this is GType getter), so provide manual override. function Variant:get_type() return VariantType.new(Variant.get_type_string(self)) end -- Implementation of Variant.new() from type and value. local variant_new local variant_basic_typemap = { b = 'boolean', y = 'byte', n = 'int16', q = 'uint16', i = 'int32', u = 'uint32', x = 'int64', t = 'uint64', d = 'double', s = 'string', o = 'object_path', g = 'signature', } -- Checks validity of variant type format beginning at pos, return -- position in format string after end of valid part. Returns nil -- when format is not valid. local function read_format(format, pos, basic) local t = format:sub(pos, pos) pos = pos + 1 if variant_basic_typemap[t] then return pos elseif basic then return nil elseif t =='v' then return pos elseif t == 'a' or t == 'm' then return read_format(format, pos) elseif t == '{' then pos = read_format(format, pos, true) if pos then pos = read_format(format, pos) end if not pos or format:sub(pos, pos) ~= '}' then return nil end return pos + 1 elseif t == '(' then while format:sub(pos, pos) ~= ')' do pos = read_format(format, pos) if not pos then return nil end end return pos + 1 end end local function variant_new_basic(format, val) local func = variant_basic_typemap[format] if not func then return end local v = Variant['new_' .. func](val) if not v then error(("Variant.new(`%s') - invalid source value"):format(format)) end return v end function variant_new(format, pos, val) local t = format:sub(pos, pos) pos = pos + 1 local variant = variant_new_basic(t, val) if variant then return variant, pos elseif t == 'v' then return Variant.new_variant(val), pos elseif t == 'm' then local epos if val then variant, epos = variant_new(format, pos, val) else epos = read_format(format, pos) if not epos then return nil end end return Variant.new_maybe(VariantType.new(format:sub(pos, epos - 1)), variant), epos elseif t == 'a' then if format:sub(pos, pos) == 'y' then -- Bytestring is just simple Lua string. return Variant.new_bytestring(val), pos + 1 end local epos = read_format(format, pos) if not epos then return nil end local et = VariantType.new(format:sub(pos, epos - 1)) local builder = VariantBuilder.new(VariantType.new_array(et)) if et:is_subtype_of(VariantType.DICT_ENTRY) then -- Map dictionary to Lua table directly. for k, v in pairs(val) do builder:add_value(Variant.new_dict_entry( variant_new(format, pos + 1, k), variant_new(format, pos + 2, v))) end else -- We have an issue with 'array with holes'. An attempt is -- made here to work around it with 'n' field, if present. for i = 1, val.n or #val do builder:add_value(variant_new(format, pos, val[i])) end end return builder:_end(), epos elseif t == '(' or t == '{' then -- Extract and check whole tuple or entry format. local epos = read_format(format, pos -1) if not epos then return nil end -- Prepare builder with specified format. local builder = VariantBuilder.new( VariantType.new(format:sub(pos - 1, epos - 1))) -- Loop through provided value array and build variant using -- prepared builder. local i = 1 while not format:sub(pos, pos):match('^[%)}]') do local v, epos = variant_new(format, pos, val[i]) if not v then return nil end builder:add_value(v) pos = epos i = i + 1 end return builder:_end(), pos + 1 end end -- Variant.new() is just a facade over variant_new backend. function Variant.new(vt, val) if type(vt) == 'userdata' then -- Wrap existing pointer to variant. return core.record.new(Variant, vt, val) end if type(vt) ~= 'string' then vt = vt:dup_string() end local v, epos = variant_new(vt, 1, val) if not v or epos ~= #vt + 1 then error(("Variant.new(`%s') - invalid type"):format(vt)) end return v end function Variant:_new(...) return Variant.new(...) end -- Implement VariantBuilder:add() using the same facade. function VariantBuilder:add(type, val) VariantBuilder.add_value(Variant.new(type, val)) end -- Converts variant to nearest possible Lua value, but leaves arrays -- intact (use indexing and/or iterators for handling arrays). local simple_unpack_map = { b = 'boolean', y = 'byte', n = 'int16', q = 'uint16', i = 'int32', u = 'uint32', x = 'int64', t = 'uint64', d = 'double', s = 'string', o = 'string', g = 'string', v = 'variant' } local function variant_get(v) local type = v:get_type_string() local func = simple_unpack_map[type] if func then return Variant['get_' .. func](v) elseif type:match('^m') then return v:n_children() == 1 and variant_get(v:get_child_value(0)) or nil elseif type:match('^[{(r]') then -- Unpack dictionary entry or tuple into array. local array = { n = v:n_children() } for i = 1, array.n do array[i] = variant_get(v:get_child_value(i - 1)) end return array elseif Variant.is_of_type(v, VariantType.BYTESTRING) then return tostring(Variant.get_bytestring(v)) elseif Variant.is_of_type(v, VariantType.DICTIONARY) then -- Return proxy table which dynamically looks up items in the -- target variant. local meta = {} if Variant.is_of_type(v, VariantType.new('a{s*}')) then -- Use g_variant_lookup_value. function meta:__index(key) local found = Variant.lookup_value(v, key) return found and variant_get(found) end else -- Custom search, walk key-by-key. Cache key positions in -- the meta table. function meta:__index(key) for i = 0, Variant.n_children(v) - 1 do local entry = Variant.get_child_value(v, i) local vkey = variant_get(Variant.get_child_value(entry, 0)) if vkey == key then local found = Variant.get_child_value(entry, 1) return found and variant_get(found) end end end end return setmetatable({}, meta) end -- No simple unpacking is possible, return just self. Complex -- compound types are meant to be accessed by indexing or -- iteration, implemented below. return v end -- Map simple unpacking to reading 'value' property. Variant._attribute.value = { get = variant_get } -- Define meaning of # and number-indexing to children access. Note -- that GVariant g_asserts when these methods are invoked on variants -- of inappropriate type, so we have to check manually before. function Variant:_len() return self:is_container() and self:n_children() or 0 end local variant_element = Variant._element function Variant:_element(variant, name) -- If number is requested, consider it a special operation, -- indexing a variant. if type(name) == 'number' then return name, '_index' end return variant_element(self, variant, name) end function Variant:_access_index(variant, index, ...) assert(select('#', ...) == 0, 'GLib.Variant is not writable') if (Variant.is_container(variant) and Variant.n_children(variant) >= index) then return Variant.get_child_value(variant, index - 1).value end end -- Implementation of iterators over compound variant (simulating -- standard Lua pairs() and ipairs() methods). local function variant_inext(parent, index) index = index + 1 if index <= #parent then return index, parent[index] end end function Variant:ipairs() return variant_inext, self, 0 end function Variant:pairs() if self:is_of_type(VariantType.DICTIONARY) then -- For dictionaries, provide closure iterator which goes through -- all key-value pairs of the parent. local index = 0 return function() index = index + 1 if index <= #self then local child = self[index] return child[1], child[2] end end end -- For non-dictionaries, pairs() is the same as ipairs(). return self:ipairs() end -- Serialization support. Override Variant:get_data() with safer -- method which fills size to the resulting ref. Variant._attribute.data = {} function Variant._attribute.data:get() local buffer = bytes.new(Variant.get_size(self)) Variant.store(self, buffer) return buffer end -- Map get_data to read-only 'data' property. -- Override for new_from_data. Takes care mainly about tricky -- DestroyNotify handling. local variant_new_from_data = Variant.new_from_data function Variant.new_from_data(vt, data, trusted) if type(vt) == 'string' then vt = VariantType.new(vt) end if trusted == nil then trusted = true end return variant_new_from_data( vt, data, trusted, -- DestroyNotify implemented as closure which holds 'data' value -- as an upvalue. The 'notify' argument is scope-async, which -- means that closure together with its upvalue will be -- destroyed after called. Up to that time 'data' is safely -- held in upvalue for this closure. function() data = nil end) end lgi-0.9.2/lgi/override/GLib.lua000066400000000000000000000010551316674307300162220ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- LGI GLib root override module. -- -- Copyright (c) 2012 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local select, type, pairs = select, type, pairs local core = require 'lgi.core' local GLib = core.repo.GLib GLib._constant = GLib._constant or {} GLib._constant.SOURCE_CONTINUE = true GLib._constant.SOURCE_REMOVE = false lgi-0.9.2/lgi/override/GObject-Closure.lua000066400000000000000000000227001316674307300203340ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- LGI GClosure handling and marshalling of callables in GValues -- arrays as arguments. -- -- Copyright (c) 2010, 2011 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local type, pairs, ipairs, unpack = type, pairs, ipairs, unpack or table.unpack local core = require 'lgi.core' local gi = core.gi local repo = core.repo local Type, Value = repo.GObject.Type, repo.GObject.Value -- Implementation of closure support, together with marshalling. local Closure = repo.GObject.Closure local closure_info = gi.GObject.Closure -- CallInfo is object based on GICallable, allowing marshalling -- from/to GValue arrays. local CallInfo = {} CallInfo.__index = CallInfo -- Compile callable_info into table which allows fast marshalling function CallInfo.new(callable_info, to_lua) local self = setmetatable( { has_self = (callable_info.is_signal or callable_info.is_virtual) }, CallInfo) local argc, gtype = 0 -- If this is a C array with explicit length argument, mark it. local function mark_array_length(cell, ti) local len = ti.array_length if len then cell.len_index = 1 + len + (self.has_self and 1 or 0) if not self[cell.len_index] then self[cell.len_index] = {} end self[cell.len_index].internal = true end end -- Fill in 'self' argument. if self.has_self then argc = 1 gtype = callable_info.container.gtype self[1] = { dir = 'in', gtype = gtype, [to_lua and 'to_lua' or 'to_value'] = Value.find_marshaller(gtype) } end -- Go through arguments. local phantom_return for i = 1, #callable_info.args do local ai = callable_info.args[i] local ti = ai.typeinfo -- Prepare parameter cell in self array. argc = argc + 1 if not self[argc] then self[argc] = {} end local cell = self[argc] -- Fill in marshaller(s) for the cell. cell.dir = ai.direction if cell.dir == 'in' then -- Direct marshalling into value. cell.gtype = Type.from_typeinfo(ti) local marshaller = Value.find_marshaller(cell.gtype, ti, ti.transfer) cell[to_lua and 'to_lua' or 'to_value'] = marshaller else -- Indirect marshalling, value contains just pointer to the -- real storage pointer. Used for inout and out arguments. cell.gtype = Type.POINTER local marshaller = Value.find_marshaller(cell.gtype) if to_lua then if cell.dir == 'inout' then function cell.to_lua(value, params) local arg = marshaller(value, nil) return core.marshal.argument(arg, ti, ti.transfer) end end function cell.to_value(value, params, val) local arg = marshaller(value, nil) core.marshal.argument(arg, ti, ti.transfer, val) end else function cell.to_value(value, params, val) local arg, ptr = core.marshal.argument() params[cell] = arg marshaller(value, nil, ptr) if cell.dir == 'inout' then -- Marshal input to the argument. core.marshal.argument(arg, ti, ti.transfer, val) else -- Returning true indicates that input argument should -- actually not be consumed, because we are just -- preparing slot for the output value. return true end end function cell.to_lua(value, params) local arg = params[cell] return core.marshal.argument(arg, ti, ti.transfer) end end end mark_array_length(cell, ti) -- Check for output parameters; if present, enable -- phantom-return heuristics. phantom_return = phantom_return or cell.dir == 'out' end -- Prepare retval marshalling. local ti = callable_info.return_type if ti.tag ~= 'void' or ti.is_pointer then gtype = Type.from_typeinfo(ti) local ret = { dir = 'out', gtype = gtype, to_value = Value.find_marshaller( gtype, ti, callable_info.return_transfer) } mark_array_length(ret, ti) if phantom_return and ti.tag == 'gboolean' then self.phantom = ret else self.ret = ret end end return self end -- Marshal single call_info cell (either input or output). local function marshal_cell( call_info, cell, direction, args, argc, marshalling_params, value, params, retval) local marshaller = cell[direction] if not marshaller or cell.internal then return argc end argc = argc + 1 local length_marshaller if cell.len_index then -- Prepare length argument marshaller. length_marshaller = call_info[cell.len_index][direction] if direction == 'to_lua' then marshalling_params.length = length_marshaller( params[cell.len_index], {}) end end if direction == 'to_lua' then -- Marshal from GValue to Lua args[argc] = marshaller(value, marshalling_params) else -- Marshal from Lua to GValue if marshaller(value, marshalling_params, args[argc]) then -- When true is returned, we were just preparing slot for the -- output value, so do not consume the argument. argc = argc - 1 end -- Marshal array length output, if applicable. if length_marshaller then length_marshaller(params[cell.len_index], {}, marshalling_params.length) end -- Marshal phantom return, if applicable. if retval and call_info.phantom and args[argc] == nil then call_info.phantom.to_value(retval, marshalling_params, false) end end return argc end -- Creates GClosure marshaller based on compiled CallInfo. function CallInfo:get_closure_marshaller(target) return function(closure, retval, params) local marshalling_params = { keepalive = {} } local args, argc = {}, 0 -- Marshal input arguments. for i = 1, #self do argc = marshal_cell( self, self[i], 'to_lua', args, argc, marshalling_params, params[i], params) end -- Do the call. args = { target(unpack(args, 1, argc)) } argc = 0 marshalling_params.keepalive = {} -- Marshall the return value. if self.ret and retval then argc = marshal_cell( self, self.ret, 'to_value', args, argc, marshalling_params, retval, params) end -- Prepare 'true' into phantom return, will be reset to 'false' -- when some output argument is returned as 'nil'. if self.phantom and retval then self.phantom.to_value(retval, marshalling_params, true) end -- Marshal output arguments. for i = 1, #self do argc = marshal_cell( self, self[i], 'to_value', args, argc, marshalling_params, params[i], params, retval) end end end -- Marshalls Lua arguments into Values suitable for invoking closures -- and signals. Returns Value (for retval), array of Value (for -- params) and keepalive value (which must be kept alive during the -- call) function CallInfo:pre_call(...) -- Prepare array of param values and initialize them with correct type. local params = {} for i = 1, #self do params[#params + 1] = Value(self[i].gtype) end local marshalling_params = { keepalive = {} } -- Marshal input values. local args, argc = { ... }, 0 for i = 1, #self do argc = marshal_cell( self, self[i], 'to_value', args, argc, marshalling_params, params[i], params) end -- Prepare return value. local retval = Value() if self.ret then retval.type = self.ret.gtype end if self.phantom then retval.type = self.phantom.gtype end return retval, params, marshalling_params end -- Unmarshalls Lua restuls from Values after invoking closure or -- signal. Returns all unmarshalled Lua values. function CallInfo:post_call(params, retval, marshalling_params) marshalling_params.keepalive = {} local args, argc = {}, 0 -- Check, whether phantom return exists and returned 'false'. If -- yes, return just nil. if (self.phantom and not self.phantom.to_lua(retval, marshalling_params)) then return nil end -- Unmarshal return value. if self.ret and retval then argc = marshal_cell( self, self.ret, 'to_lua', args, argc, marshalling_params, retval, params) end -- Unmarshal output arguments. for i = 1, #self do argc = marshal_cell( self, self[i], 'to_lua', args, argc, marshalling_params, params[i], params) end -- Return all created Lua values. return unpack(args, 1, argc) end -- Create new closure invoking Lua target function (or anything else -- that can be called). Optionally callback_info specifies detailed -- information about how to marshal signals. function Closure:_new(target, callback_info) local closure = Closure._method.new_simple(closure_info.size, nil) if target then local marshaller if callback_info then -- Create marshaller based on callinfo. local call_info = CallInfo.new(callback_info, true) marshaller = call_info:get_closure_marshaller(target) else -- Create marshaller based only on Value types. function marshaller(closure, retval, params) local args = {} for i, val in ipairs(params) do args[i] = val.value end local ret = target(unpack(args, 1, #params)) if retval then retval.value = ret end end end core.marshal.closure_set_marshal(closure, marshaller) end Closure.ref(closure) Closure.sink(closure) return closure end -- Use native marshalling for g_closure_invoke Closure.invoke = core.marshal.closure_invoke -- Export CallInfo as field of Closure. Closure.CallInfo = CallInfo lgi-0.9.2/lgi/override/GObject-Object.lua000066400000000000000000000305771316674307300201410ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- lgi GObject.Object handling. -- -- Copyright (c) 2010-2014 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local pairs, select, setmetatable, error, type = pairs, select, setmetatable, error, type local core = require 'lgi.core' local gi = core.gi local repo = core.repo local ffi = require 'lgi.ffi' local ti = ffi.types local Value = repo.GObject.Value local Type = repo.GObject.Type local Closure = repo.GObject.Closure local TypeClass = repo.GObject.TypeClass local Object = repo.GObject.Object -- Add overrides for GObject.TypeClass TypeClass._free = gi.GObject.resolve.g_type_class_unref local type_class_ref = core.callable.new { addr = gi.GObject.resolve.g_type_class_ref, ret = ti.ptr, ti.GType } function TypeClass:_new() local ptr = type_class_ref(self._gtype) return core.record.new(self, ptr, true) end -- Object constructor, 'param' contains table _with properties/signals -- to initialize. local parameter_repo = repo.GObject.Parameter -- Before GLib 2.54, g_object_newv() was annotated with [rename-to g_object_new]. -- Starting from GLib 2.54, g_object_new_with_properties() has this annotation. -- We always want g_object_newv(). local object_new = gi.GObject.Object.methods.newv or gi.GObject.Object.methods.new if object_new then object_new = core.callable.new(object_new) else -- Unfortunately, older GI (<1.30) does not export g_object_newv() -- in the typelib, so we have to workaround here with manually -- implemented C version. object_new = core.object.new end -- Generic construction method. function Object:_construct(gtype, param, owns) local object if type(param) == 'userdata' then -- Wrap existing GObject instance in the lgi proxy. object = core.object.new(param, owns) gtype = object._gtype end -- Check that gtype fits. if not Type.is_a(gtype, self._gtype) then error(("`%s' is not subtype of `%s'"):format( Type.name(gtype), self._name), 3) end -- In 'wrap' mode, just return the created object. if object then return object end -- Process 'args' table, separate properties from other fields. local parameters, others, safe = {}, {}, {} for name, arg in pairs(param or {}) do if type(name) == 'string' then local argtype = self[name] if gi.isinfo(argtype) and argtype.is_property then local parameter = core.record.new(parameter_repo) name = argtype.name local value = parameter.value -- Store the name string in some safe Lua place ('safe' -- table), because param is GParameter, which contains -- only non-owning pointer to the string, and it could be -- Lua-GC'ed while still referenced by GParameter -- instance. safe[#safe + 1] = name safe[#safe + 1] = value parameter.name = name local gtype = Type.from_typeinfo(argtype.typeinfo) Value.init(value, gtype) local marshaller = Value.find_marshaller(gtype, argtype.typeinfo) marshaller(value, nil, arg) parameters[#parameters + 1] = parameter else others[name] = arg end end end -- Create the object. object = object_new(gtype, parameters) -- Perform initialization on interfaces. if next(self._implements) then local inited for _, initname in ipairs { '_init1', '_init2' } do for _, interface in pairs(self._implements) do local init = interface[initname] if init then local ok, err = init(object) if not ok then return nil, err end if ok == '_initskip' then -- This initializer does not apply, continue looking -- for others. else inited = true break; end end end if inited then break end end end -- Attach arguments previously filtered out from creation. for name, value in pairs(others) do if type(name) == 'string' then object[name] = value end end -- In case that type has _container_add() method, use it to process -- array part of the args. local add = self._container_add if add and param then for i = 1, #param do add(object, param[i]) end end return object end function Object:_new(...) -- Invoke object's construct method which does the work. return self:_construct(self._gtype, ...) end -- Override normal 'new' constructor, to allow creating objects with -- specified GType. function Object.new(gtype, params, owns) -- Find proper repo instance for gtype. local gtype_walker = gtype while true do local self = core.repotype(gtype_walker) if self then -- We have repo instance, use it to construct the object. return self:_construct(gtype, params, owns) end gtype_walker = Type.parent(gtype_walker) if not gtype_walker then error(("`%s': cannot create object, type not found"):format(gtype), 2) end end end -- Prepare callbacks for get_property and set_property local get_property_guard, get_property_addr = core.marshal.callback( gi.GObject.ObjectClass.fields.get_property.typeinfo.interface, function(self, prop_id, value, pspec) local name = pspec.name:gsub('%-', '_') local prop_get = core.object.query(self, 'repo')._property_get[name] if prop_get then value.value = prop_get(self) else value.value = self.priv[name] end end) local set_property_guard, set_property_addr = core.marshal.callback( gi.GObject.ObjectClass.fields.get_property.typeinfo.interface, function(self, prop_id, value, pspec) local name = pspec.name:gsub('%-', '_') local prop_set = core.object.query(self, 'repo')._property_set[name] if prop_set then prop_set(self, value.value) else self.priv[name] = value.value end end) if not core.guards then core.guards = {} end core.guards.get_property = get_property_guard core.guards.set_property = set_property_guard -- _class_init method on the Object will install all properties -- accumulated in _property table. It will be called automatically on -- derived classes during class initialization routine. function Object:_class_init(class) if next(self._property) then -- First install get/set_property overrides, unless already present. if not self._override.get_property then class.get_property = get_property_addr end if not self._override.set_property then class.set_property = set_property_addr end -- Install properties. local prop_id = 0 for name, pspec in pairs(self._property) do prop_id = prop_id + 1 class:install_property(prop_id, pspec) end end end -- Initially unowned creation is similar to normal GObject creation, -- but we have to ref_sink newly created object. local InitiallyUnowned = repo.GObject.InitiallyUnowned function InitiallyUnowned:_construct(...) local object = Object._construct(self, ...) return Object.ref_sink(object) end -- Reading 'class' yields real instance of the object class. Object._attribute = { _type = {} } function Object._attribute._type:get() return core.object.query(self, 'repo') end -- Custom _element implementation, checks dynamically inherited -- interfaces and dynamic properties. local inherited_element = Object._element function Object:_element(object, name) local element, category = inherited_element(self, object, name) if element then return element, category end -- Everything else works only if we have object instance. if not object then return nil end -- List all interfaces implemented by this object and try whether -- they can handle specified _element request. local interfaces = Type.interfaces(object._gtype) for i = 1, #interfaces do local info = gi[core.gtype(interfaces[i])] local iface = info and repo[info.namespace][info.name] if iface then element, category = iface:_element(object, name, self) end if element then return element, category end end -- Element not found in the repo (typelib), try whether dynamic -- property of the specified name exists. local property = Object._class.find_property( object._class, name:gsub('_', '-')) if property then return property, '_property' end end -- Sets/gets property using specified marshaller attributes. local function marshal_property(obj, name, flags, gtype, marshaller, ...) -- Check access rights of the property. local mode = select('#', ...) > 0 and 'WRITABLE' or 'READABLE' if not flags[mode] then error(("%s: `%s' not %s"):format(core.object.query(obj, 'repo')._name, name, core.downcase(mode))) end local value = Value(gtype) if mode == 'WRITABLE' then marshaller(value, nil, ...) Object.set_property(obj, name, value) else Object.get_property(obj, name, value) return marshaller(value) end end -- Property accessor. function Object:_access_property(object, prop, ...) if gi.isinfo(prop) then -- GI-based property local typeinfo = prop.typeinfo local gtype = Type.from_typeinfo(typeinfo) local marshaller = Value.find_marshaller(gtype, typeinfo, prop.transfer) return marshal_property(object, prop.name, repo.GObject.ParamFlags[prop.flags], gtype, marshaller, ...) else -- pspec-based property return marshal_property(object, prop.name, prop.flags, prop.value_type, Value.find_marshaller(prop.value_type), ...) end end local quark_from_string = repo.GLib.quark_from_string local signal_lookup = repo.GObject.signal_lookup local signal_connect_closure_by_id = repo.GObject.signal_connect_closure_by_id local signal_emitv = repo.GObject.signal_emitv -- Connects signal to specified object instance. local function connect_signal(obj, gtype, name, closure, detail, after) return signal_connect_closure_by_id( obj, signal_lookup(name, gtype), detail and quark_from_string(detail) or 0, closure, after or false) end -- Emits signal on specified object instance. local function emit_signal(obj, gtype, info, detail, ...) -- Compile callable info. local call_info = Closure.CallInfo.new(info) -- Marshal input arguments. local retval, params, marshalling_params = call_info:pre_call(obj, ...) -- Invoke the signal. signal_emitv(params, signal_lookup(info.name, gtype), detail and quark_from_string(detail) or 0, retval) -- Unmarshal results. return call_info:post_call(params, retval, marshalling_params) end -- Signal accessor. function Object:_access_signal(object, info, ...) local gtype = self._gtype if select('#', ...) > 0 then -- Assignment means 'connect signal without detail'. connect_signal(object, gtype, info.name, Closure((...), info)) else -- Reading yields table with signal operations. local mt = {} local pad = setmetatable({}, mt) function pad:connect(target, detail, after) return connect_signal(object, gtype, info.name, Closure(target, info), detail, after) end function pad:emit(...) return emit_signal(object, gtype, info, nil, ...) end function mt:__call(_, ...) return emit_signal(object, gtype, info, nil, ...) end -- If signal supports details, add metatable implementing -- __newindex for connecting in the 'on_signal['detail'] = -- handler' form. if not info.is_signal or info.flags.detailed then function pad:emit(detail, ...) return emit_signal(object, gtype, info, detail, ...) end function mt:__newindex(detail, target) connect_signal(object, gtype, info.name, Closure(target, info), detail) end end -- Return created signal pad. return pad end end -- GOI<1.30 does not export 'Object.on_notify' signal from the -- typelib. Work-around this problem by implementing custom on_notify -- attribute. if not gi.GObject.Object.signals.notify then local notify_info = gi.GObject.ObjectClass.fields.notify.typeinfo.interface function Object._attribute.on_notify(object, ...) local repotable = core.object.query(object, 'repo') return Object._access_signal(repotable, object, notify_info, ...) end end -- Bind property implementation. For some strange reason, GoI<1.30 -- exports it only on GInitiallyUnowned and not on GObject. Oh -- well... for _, name in pairs { 'bind_property', 'bind_property_full' } do if not Object[name] then Object._method[name] = InitiallyUnowned[name] end end lgi-0.9.2/lgi/override/GObject-Type.lua000066400000000000000000000047571316674307300176550ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- LGI GObject.Type facilities. -- -- Copyright (c) 2010, 2011 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local assert, pairs, ipairs = assert, pairs, ipairs local core = require 'lgi.core' local repo = core.repo -- Add synthetic GObject.Type, containing well-known GType constants -- and grouping some type_xxx methods. local Type = { STRV = 'GStrv', ARRAY = 'GArray', BYTE_ARRAY = 'GByteArray', PTR_ARRAY = 'GPtrArray', HASH_TABLE = 'GHashTable', ERROR = 'GError', GTYPE = 'GType' } repo.GObject._struct = { Type = Type } for _, name in pairs { 'name', 'qname', 'from_name', 'parent', 'depth', 'next_base', 'is_a', 'children', 'interfaces', 'query', 'fundamental_next', 'fundamental'} do Type[name] = repo.GObject['type_' .. name] end for num, name in ipairs { 'NONE', 'INTERFACE', 'CHAR', 'UCHAR', 'BOOLEAN', 'INT', 'UINT', 'LONG', 'ULONG', 'INT64', 'UINT64', 'ENUM', 'FLAGS', 'FLOAT', 'DOUBLE', 'STRING', 'POINTER', 'BOXED', 'PARAM', 'OBJECT', 'VARIANT' } do Type[name] = Type.name(num * 4) end -- Map of basic typeinfo tags to GType. local type_tag_map = { gboolean = Type.BOOLEAN, gint8 = Type.CHAR, guint8 = Type.UCHAR, gint16 = Type.INT, guint16 = Type.UINT, gint32 = Type.INT, guint32 = Type.UINT, gint64 = Type.INT64, guint64 = Type.UINT64, gunichar = Type.UINT, gfloat = Type.FLOAT, gdouble = Type.DOUBLE, GType = Type.GTYPE, utf8 = Type.STRING, filename = Type.STRING, ghash = Type.HASH_TABLE, glist = Type.POINTER, gslist = Type.POINTER, error = Type.ERROR } -- Gets GType corresponding to specified typeinfo. function Type.from_typeinfo(ti) local gtype = type_tag_map[ti.tag] if not gtype then if ti.tag == 'interface' then gtype = Type.name(ti.interface.gtype) elseif ti.tag == 'array' then local atype = ti.array_type if atype == 'c' then gtype = Type.POINTER -- Check for Strv. local etag = ti.params[1].tag if ((etag == 'utf8' or etag == 'filename') and ti.is_zero_terminated) then gtype = Type.STRV end else gtype = ({ array = Type.ARRAY, byte_array = Type.BYTE_ARRAY, ptr_array = Type.PTR_ARRAY })[atype] end end end return gtype end -- Gets lgi type from gtype. function Type.type(ti) return core.repotype(ti) end lgi-0.9.2/lgi/override/GObject-Value.lua000066400000000000000000000162211316674307300177750ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- LGI GObject.Value support. -- -- Copyright (c) 2010, 2011 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local assert, pairs, select, type, tostring, error = assert, pairs, select, type, tostring, error local lgi = require 'lgi' local core = require 'lgi.core' local repo = core.repo local ffi = require 'lgi.ffi' local gi = core.gi local Type = repo.GObject.Type -- Value is constructible from any kind of source Lua value, and the -- type of the value can be hinted by type name. local Value = repo.GObject.Value local log = lgi.log.domain('Lgi') -- Workaround for incorrect annotations - g_value_set_xxx are missing -- (allow-none) annotations in glib < 2.30. for _, name in pairs { 'set_object', 'set_variant', 'set_string' } do if not gi.GObject.Value.methods[name].args[1].optional then log.message("g_value_%s() is missing (allow-none)", name) local setter = Value[name] Value._method[name] = function(value, val) if not val then Value.reset(value) else setter(value, val) end end end end -- Workaround for incorrect annotations - g_value_get_variant is missing -- (transfer-none). local _ = Value.get_variant Value.get_variant = core.callable.new { addr = gi.GObject.resolve.g_value_get_variant, name = 'GObject.Value.get_variant', ret = repo.GLib.Variant, Value, } -- Do not allow direct access to fields. local value_field_gtype = Value._field.g_type Value._field = nil -- Register _uninit function, to avoid memory leaks from values which -- are inline-allocated by core.record.new. Value._uninit = core.record.value_unset Value._copy = core.record.value_copy -- 'type' property controls gtype of the property. Value._attribute = { gtype = {} } function Value._attribute.gtype.get(value) return core.record.field(value, value_field_gtype) end function Value._attribute.gtype.set(value, newtype) local gtype = core.record.field(value, value_field_gtype) if gtype then if newtype then -- Try converting old value to new one. local dest = core.record.new(Value) Value.init(dest, newtype) if not Value.transform(value, dest) then error(("GObject.Value: cannot convert `%s' to `%s'"):format( gtype, core.record.field(dest, value_field_gtype))) end Value.unset(value) Value.init(value, newtype) Value.copy(dest, value) else Value.unset(value) end elseif newtype then -- No value was set and some is requested, so set it. Value.init(value, newtype) end end local value_marshallers = {} for name, gtype in pairs(Type) do name = core.downcase(name) local get = Value['get_' .. name] local set = Value['set_' .. name] if get and set then value_marshallers[gtype] = function(value, params, ...) return (select('#', ...) > 0 and set or get)(value, ...) end end end -- Interface marshaller is the same as object marshaller. value_marshallers[Type.INTERFACE] = value_marshallers[Type.OBJECT] -- Override 'boxed' marshaller, default one marshalls to gpointer -- instead of target boxed type. value_marshallers[Type.BOXED] = function(value, params, ...) local repotype = core.repotype(core.record.field(value, value_field_gtype)) if select('#', ...) > 0 then Value.set_boxed(value, core.record.query((...), 'addr', repotype)) else return core.record.new(repotype, Value.get_boxed(value)) end end -- Override marshallers for enums and bitmaps, marshal them as strings -- or sets of string flags. for name, gtype in pairs { ENUM = Type.ENUM, FLAGS = Type.FLAGS } do name = core.downcase(name) local get = Value._method['get_' .. name] local set = Value._method['set_' .. name] value_marshallers[gtype] = function(value, params, ...) local rtype if select('#', ...) > 0 then local param = ... if type(param) ~= 'number' then rtype = core.repotype(core.record.field(value, value_field_gtype)) param = rtype(param) end set(value, param) else rtype = core.repotype(core.record.field(value, value_field_gtype)) return rtype[get(value)] end end end -- Create GStrv marshaller, implement it using typeinfo marshaller -- with proper null-terminated-array-of-utf8 typeinfo 'stolen' from -- g_shell_parse_argv(). value_marshallers[Type.STRV] = core.marshal.container( gi.GLib.shell_parse_argv.args[3].typeinfo) -- Finds marshaller closure which can marshal type described either by -- gtype or typeinfo/transfer combo. function Value._method.find_marshaller(gtype, typeinfo, transfer) -- Check whether we can have marshaller for typeinfo, if the -- typeinfo is container. local marshaller if typeinfo then marshaller = core.marshal.container(typeinfo, transfer) if marshaller then return marshaller end end -- Special case for non-gtype records. if not gtype and typeinfo and typeinfo.tag == 'interface' then -- Workaround for GoI < 1.30; it does not know that GLib structs are -- boxed, so it does not assign them GType; moreover it incorrectly -- considers GParamSpec as GType-less struct instead of the class. local function marshal_record_no_gtype(value, params, ...) -- Check actual gtype of the real value. local gtype = core.record.field(value, value_field_gtype) if Type.is_a(gtype, Type.PARAM) then return value_marshallers[Type.PARAM](value, ...) end -- Find out proper getter/setter method for the value. local get, set if Type.is_a(gtype, Type.BOXED) then get, set = Value.get_boxed, Value.set_boxed else get, set = Value.get_pointer, Value.set_pointer end -- Do GValue<->record transfer. local record_repo = core.repotype(typeinfo.interface) if select('#', ...) > 0 then set(value, core.record.query((...), 'addr', record_repo)) else return core.record.new(record_repo, get(value)) end end return marshal_record_no_gtype end local gt = gtype if type(gt) == 'userdata' then gt = Type.name(gt) end -- Special marshaller, allowing only 'nil'. if not gt then return function() end end -- Find marshaller according to gtype of the value. while gt do -- Check simple and/or fundamental marshallers. marshaller = value_marshallers[gt] or core.marshal.fundamental(gt) if marshaller then return marshaller end gt = Type.parent(gt) end error(("GValue marshaller for `%s' not found"):format(tostring(gtype))) end -- Value 'value' property provides access to GValue's embedded data. function Value._attribute:value(...) local marshaller = Value._method.find_marshaller( core.record.field(self, value_field_gtype)) return marshaller(self, nil, ...) end -- Implement custom 'constructor', taking optionally two values (type -- and value). The reason why it is overriden is that the order of -- initialization is important, and standard record intializer cannot -- enforce the order. function Value:_new(gtype, value) local v = core.record.new(Value) if gtype then v.gtype = gtype end if value then v.value = value end return v end lgi-0.9.2/lgi/override/Gdk.lua000066400000000000000000000102331316674307300161100ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- LGI Gdk3 override module. -- -- Copyright (c) 2011, 2014 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local select, type, pairs, unpack, rawget = select, type, pairs, unpack, rawget local lgi = require 'lgi' local core = require 'lgi.core' local ffi = require 'lgi.ffi' local ti = ffi.types local Gdk = lgi.Gdk local cairo = lgi.cairo -- Take over internal GDK synchronization lock. core.registerlock(core.gi.Gdk.resolve.gdk_threads_set_lock_functions) Gdk.threads_init() -- Gdk.Rectangle does not exist at all in older GOI, because it is -- aliased to cairo.RectangleInt. Make sure that we have it exists, -- because it is very commonly used in API documentation. if not Gdk.Rectangle then Gdk.Rectangle = lgi.cairo.RectangleInt Gdk.Rectangle._method = rawget(Gdk.Rectangle, '_method') or {} Gdk.Rectangle._method.intersect = Gdk.rectangle_intersect Gdk.Rectangle._method.union = Gdk.rectangle_union end -- Declare GdkAtoms which are #define'd in Gdk sources and not -- introspected in gir. local _ = Gdk.KEY_0 for name, val in pairs { SELECTION_PRIMARY = 1, SELECTION_SECONDARY = 2, SELECTION_CLIPBOARD = 69, TARGET_BITMAP = 5, TARGET_COLORMAP = 7, TARGET_DRAWABLE = 17, TARGET_PIXMAP = 20, TARGET_STRING = 31, SELECTION_TYPE_ATOM = 4, SELECTION_TYPE_BITMAP = 5, SELECTION_TYPE_COLORMAP = 7, SELECTION_TYPE_DRAWABLE = 17, SELECTION_TYPE_INTEGER = 19, SELECTION_TYPE_PIXMAP = 20, SELECTION_TYPE_WINDOW = 33, SELECTION_TYPE_STRING = 31, } do Gdk._constant[name] = Gdk.Atom(val) end -- Easier-to-use Gdk.RGBA.parse() override. if Gdk.RGBA then local parse = Gdk.RGBA.parse function Gdk.RGBA.parse(arg1, arg2) if Gdk.RGBA:is_type_of(arg1) then -- Standard member method. return parse(arg1, arg2) else -- Static constructor variant. local rgba = Gdk.RGBA() return parse(rgba, arg1) and rgba or nil end end end -- Gdk.Window.destroy() actually consumes 'self'. Prepare workaround -- with override doing ref on input arg first. local destroy = Gdk.Window.destroy local ref = core.callable.new { addr = core.gi.GObject.resolve.g_object_ref, ret = ti.ptr, ti.ptr } function Gdk.Window:destroy() ref(self._native) destroy(self) end -- Better integrate Gdk cairo helpers. Gdk.Window.cairo_create = Gdk.cairo_create cairo.Region.create_from_surface = Gdk.cairo_region_create_from_surface local cairo_set_source_rgba = cairo.Context.set_source_rgba function cairo.Context:set_source_rgba(...) if select('#', ...) == 1 then return Gdk.cairo_set_source_rgba(self, ...) else return cairo_set_source_rgba(self, ...) end end local cairo_rectangle = cairo.Context.rectangle function cairo.Context:rectangle(...) if select('#', ...) == 1 then return Gdk.cairo_rectangle(self, ...) else return cairo_rectangle(self, ...) end end for _, name in pairs { 'get_clip_rectangle', 'set_source_color', 'set_source_pixbuf', 'set_source_window', 'region' } do cairo.Context._method[name] = Gdk['cairo_' .. name] end for _, name in pairs { 'clip_rectangle', 'source_color', 'source_pixbuf', 'source_window' } do cairo.Context._attribute[name] = { get = cairo.Context._method['get_' .. name], set = cairo.Context._method['set_' .. name], } end -- Gdk events have strange hierarchy; GdkEvent is union of all known -- GdkEventXxx specific types. This means that generic gdk_event_xxx -- methods are not available on GdkEventXxxx specific types. Work -- around this by setting GdkEvent as parent for GdkEventXxxx specific -- types. for _, event_type in pairs { 'Any', 'Expose', 'Visibility', 'Motion', 'Button', 'Touch', 'Scroll', 'Key', 'Crossing', 'Focus', 'Configure', 'Property', 'Selection', 'OwnerChange', 'Proximity', 'DND', 'WindowState', 'Setting', 'GrabBroken' } do local event = Gdk['Event' .. event_type] if event then event._parent = Gdk.Event end end lgi-0.9.2/lgi/override/Gio-DBus.lua000066400000000000000000000052401316674307300167560ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- lgi Gio DBus override module. -- -- Copyright (c) 2013 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local pairs, ipairs = pairs, ipairs local lgi = require 'lgi' local core = require 'lgi.core' local ffi = require 'lgi.ffi' local ti = ffi.types local Gio = lgi.Gio local GLib = lgi.GLib -- DBus introspection support. -- All introspection structures are boxed, but they lack proper C-side -- introspectable constructors. Work around this limitation by adding -- custom lgi constructors which create allocated variants of these -- records. for _, name in pairs { 'Annotation', 'Arg' , 'Method', 'Signal', 'Property', 'Interface', 'Node', } do name = 'DBus' .. name .. 'Info' local infotype = Gio[name] -- Add constructor which properly creates and initializes info. function infotype:_new(params) -- Create allocated variant of the record, because when -- destroyed by g_boxed_free, Info instances automatically sweep -- away also all fields which belong to them. local struct = core.record.new(self, nil, 1, true) -- Initialize ref_count on the instance. struct.ref_count = 1 -- Assign all constructor parameters. for name, value in pairs(params or {}) do struct[name] = value end return struct end -- Assign proper refsink method. infotype._refsink = core.gi.Gio[name].methods.ref end -- g_dbus_node_gemerate_xml is busted, has incorrect [out] annotation -- of its GString argument (which is in fact [in] one). Fix it by -- redeclaring it. Gio.DBusNodeInfo.generate_xml = core.callable.new { name = 'Gio.DBusNodeInfo.generate_xml', addr = core.gi.Gio.resolve.g_dbus_node_info_generate_xml, ret = ti.void, Gio.DBusNodeInfo, ti.uint, GLib.String, } -- Add simple 'xml' attribute as facade over a bit hard-to-use -- generate_xml() method. Gio.DBusNodeInfo._attribute = {} function Gio.DBusNodeInfo._attribute:xml() local xml = GLib.String('') self:generate_xml(0, xml) return xml.str end -- g_dbus_proxy_get_interface_info() is documented as "Do not unref the returned -- object", but has transfer=full. Fix this. if core.gi.Gio.DBusProxy.methods.get_interface_info.return_transfer ~= 'none' then Gio.DBusProxy.get_interface_info = core.callable.new { addr = core.gi.Gio.resolve.g_dbus_proxy_get_interface_info, name = 'DBusProxy.get_interface_info', ret = Gio.DBusInterfaceInfo, core.gi.Gio.DBusProxy.methods.new_sync.return_type, } end lgi-0.9.2/lgi/override/Gio.lua000066400000000000000000000213501316674307300161230ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- lgi Gio override module. -- -- Copyright (c) 2010, 2011, 2013, 2016 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local select, type, pairs, setmetatable, rawset, unpack = select, type, pairs, setmetatable, rawset, unpack or table.unpack local coroutine = require 'coroutine' local lgi = require 'lgi' local GLib = lgi.GLib local GObject = lgi.GObject local Gio = lgi.Gio local component = require 'lgi.component' local class = require 'lgi.class' local namespace = require 'lgi.namespace' local core = require 'lgi.core' local gi = core.gi -- Create completely new 'Async' pseudoclass, wrapping lgi-specific -- async helpers. local async_context = setmetatable({}, { __mode = 'k' }) Gio.Async = setmetatable( {}, { __index = function(self, key) if key == 'io_priority' or key == 'cancellable' then return (async_context[coroutine.running()] or {})[key] end end, __newindex = function(self, key, value) if key == 'io_priority' or key == 'cancellable' then (async_context[coroutine.running()])[key] = value else rawset(self, key, value) end end, }) -- Creates new context for new coroutine and stores it into async_context. local function register_async(coro, cancellable, io_priority) local current = async_context[coroutine.running()] or {} async_context[coro] = { io_priority = io_priority or current.io_priority or GLib.PRIORITY_DEFAULT, cancellable = cancellable or current.cancellable, } end function Gio.Async.start(func, cancellable, io_priority) -- Create coroutine and store context for it. local coro = coroutine.create(func) register_async(coro, cancellable, io_priority) -- Return coroutine starter. return function(...) return coroutine.resume(coro, ...) end end function Gio.Async.call(func, cancellable, io_priority) -- Create mainloop which will create modality. local loop = GLib.MainLoop() local results -- Create coroutine around function wrapper which will invoke -- target and then terminate the loop. local coro = coroutine.create(function(...) (function(...) results = { n = select('#', ...), ... } loop:quit() end)(func(...)) end) -- Register coroutine. register_async(coro, cancellable, io_priority) -- Return starter closure. return function(...) -- Spawn it inside idle handler, to avoid hang in case that -- coroutine finishes during its first resuming. local args = { n = select('#', ...), ... } GLib.idle_add( GLib.PRIORITY_DEFAULT, function() coroutine.resume(coro, unpack(args, 1, args.n)) end) -- Spin the loop. loop:run() -- Unpack results. return unpack(results, 1, results.n) end end -- Add 'async_' method handling. Dynamically generates wrapper around -- xxx_async()/xxx_finish() sequence using currently running -- coroutine. local tag_int = gi.GObject.ParamSpecEnum.fields.default_value.typeinfo.tag local function async_element(name, accessor, param_self, param_object) -- Check, whether we have async_xxx request. local name_root = type(name) == 'string' and name:match('^async_(.+)$') if name_root then local async = accessor(param_self, param_object, name_root .. '_async') local finish = accessor(param_self, param_object, name_root .. '_finish') -- Some clients name async method just 'name' and use -- 'name_sync' for synchronous variant. if finish and not async and accessor(param_self, param_object, name_root .. '_sync') then async = accessor(param_self, param_object, name_root) end -- We have async/finish pair, create element table containing -- information how to synthesize calling function. if async and finish then element = { name = name_root, in_args = 0, async = async, finish = finish, } -- Go through arguments of async method and find indices of -- io_priority, cancellable and callback args. for _, param in ipairs(async.params) do if param['in'] then element.in_args = element.in_args + 1 if not param['out'] and param.typeinfo then if param.name == 'io_priority' and param.typeinfo.tag == tag_int then element.io_priority = element.in_args elseif param.name == 'cancellable' and param.typeinfo.tag == 'interface' then element.cancellable = element.in_args end end end end -- Returns accumulated async call data. return element, '_async' end end end local function async_access(element, process_yield) -- Generate wrapper method calling _async/_finish pair automatically. return function(...) -- Check that we are running inside context. local context = async_context[coroutine.running()] if not context then error(("%s.async_%s: called out of async context"):format( self._name, element.name), 4) end -- Create input args, intersperse them with automatic ones. local args, i, index = {}, 1, 1 for i = 1, element.in_args - 1 do if i == element.io_priority then args[i] = context.io_priority elseif i == element.cancellable then args[i] = context.cancellable else args[i] = select(index, ...) index = index + 1 end end args[element.in_args] = coroutine.running() element.async(unpack(args, 1, element.in_args)) return element.finish(process_yield(coroutine.yield())) end end local inherited_class_element = class.class_mt._element function class.class_mt:_element(object, name) local element, category = inherited_class_element(self, object, name) if element then return element, category end return async_element(name, inherited_class_element, self, object) end function class.class_mt:_index_async(element) return async_access(element, function(_, ...) return ... end) end local inherited_gobject_element = GObject.Object._element function GObject.Object:_element(object, name) local element, category = inherited_gobject_element(self, object, name) if element then return element, category end return async_element(name, inherited_gobject_element, self, object) end function GObject.Object:_access_async(object, element, ...) if select('#', ...) > 0 then error(("%s: `%s' not writable"):format( core.object.query(object, 'repo')._name, name)) end return async_access(element, function(...) return ... end) end -- Enforce that Gio._function category is already created local _ = Gio.bus_get function namespace.mt._category_mt._function:__index(key) local element = async_element(key, function(_, _, name) return self._namespace[name] end) return element and async_access(element, function(_, ...) return ... end) end function Gio.Initable._init2(object) -- Avoid passing cancellable, because it might cause init() to -- fail, even if we could retrieve cancellable from async context. return object:init() end function Gio.AsyncInitable._init1(object) -- Check, whether we are running in async context. If not, skip -- initializer (do not fail it). if not async_context[coroutine.running()] then return '_initskip' end -- Invoke async initializer. return object:async_init() end -- GOI < 1.30 did not map static factory method into interface -- namespace. The prominent example of this fault was that -- Gio.File.new_for_path() had to be accessed as -- Gio.file_new_for_path(). Create a compatibility layer to mask this -- flaw. for _, name in pairs { 'path', 'uri', 'commandline_arg' } do if not Gio.File['new_for_' .. name] then Gio.File['new_for_' .. name] = Gio['file_new_for_' .. name] end end -- Older versions of gio did not annotate input stream methods as -- taking an array. Apply workaround. -- https://github.com/pavouk/lgi/issues/59 for _, name in pairs { 'read', 'read_all', 'read_async' } do local raw_read = Gio.InputStream[name] if gi.Gio.InputStream.methods[name].args[1].typeinfo.tag ~= 'array' then Gio.InputStream[name] = function(self, buffer, ...) return raw_read(self, buffer, #buffer, ...) end if name == 'read_async' then raw_finish = Gio.InputStream.read_finish function Gio.InputStream.async_read(stream, buffer) raw_read(stream, buffer, Gio.Async.io_priority, Gio.Async.cancellable, coroutine.running()) return raw_finish(coroutine.yield()) end end end end -- Add preconditions for auto-loading DBus overrides. Gio._precondition = {} for _, name in pairs { 'AnnotationInfo', 'ArgInfo', 'MethodInfo', 'SignalInfo', 'PropertyInfo', 'InterfaceInfo', 'NodeInfo', } do Gio._precondition['DBus' .. name] = 'Gio-DBus' end lgi-0.9.2/lgi/override/GooCanvas.lua000066400000000000000000000022271316674307300172670ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- lgi GooCanvas override module. -- -- Copyright (c) 2017 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local select, type, pairs, setmetatable, error = select, type, pairs, setmetatable, error local lgi = require 'lgi' local Goo = lgi.GooCanvas Goo.Canvas._attribute = { root_item = Goo.Canvas.get_root_item, root_item_model = Goo.Canvas.get_root_item_model, static_root_item = Goo.Canvas.get_static_root_item, static_root_item_model = Goo.Canvas.get_static_root_item_model, } -- Remove 'parent' field from implementation classes because it -- clashes with 'parent' field defined in implementations of -- CanvasItem interface. for _, class in pairs { Goo.CanvasEllipse, Goo.CanvasGrid, Goo.CanvasGroup, Goo.CanvasImage, Goo.CanvasItemSimple, Goo.CanvasPath, Goo.CanvasPolyline, Goo.CanvasRect, Goo.CanvasTable, Goo.CanvasText, Goo.CanvasWidget, } do local _ = class._field.parent class._field.parent = nil end lgi-0.9.2/lgi/override/Gst.lua000066400000000000000000000044611316674307300161460ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- LGI Gst override module. -- -- Copyright (c) 2010-2013 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local ipairs, tonumber = ipairs, tonumber local os = require 'os' local lgi = require 'lgi' local gi = require('lgi.core').gi local GLib = lgi.GLib local Gst = lgi.Gst if tonumber(Gst._version) < 1.0 then -- GstObject has special ref_sink mechanism, make sure that lgi -- core is aware of it, otherwise refcounting is screwed. Gst.Object._refsink = gi.Gst.Object.methods.ref_sink end -- Gst.Element; GstElement uses ugly macro accessors instead of proper -- GObject properties, so add attributes for assorted Gst.Element -- properties. Gst.Element._attribute = {} for _, name in ipairs { 'name', 'parent', 'bus', 'clock', 'base_time', 'start_time', 'factory', 'index', 'state' } do Gst.Element._attribute[name] = { get = Gst.Element['get_' .. name], set = Gst.Element['set_' .. name], } end function Gst.Element:link_many(...) local target = self for _, source in ipairs {...} do if not target:link(source) then return false end target = source end return true end -- Gst.Bin adjustments if tonumber(Gst._version) < 1.0 then function Gst.Bus:add_watch(priority, callback) if not callback then callback = priority priority = GLib.PRIORITY_DEFAULT end return self:add_watch_full(priority, callback) end end function Gst.Bin:add_many(...) local args = {...} for i = 1, #args do self:add(args[i]) end end -- Gst.TagList adjustments if not Gst.TagList.copy_value then Gst.TagList._method.copy_value = Gst.tag_list_copy_value end function Gst.TagList:get(tag) local gvalue = self:copy_value(tag) return gvalue and gvalue.value end -- Load additional Gst modules. if tonumber(Gst._version) < 1.0 then local GstInterfaces = lgi.require('GstInterfaces', Gst._version) end -- Initialize gstreamer. Gst.init() -- Undo unfortunate gstreamer's setlocale(LC_ALL, ""), which breaks -- Lua's tonumber() implementation for some locales (e.g. pl_PL, pt_BR -- and probably many others). os.setlocale ('C', 'numeric') lgi-0.9.2/lgi/override/Gtk.lua000066400000000000000000000474511316674307300161440ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- LGI Gtk3 override module. -- -- Copyright (c) 2010, 2011 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local select, type, pairs, ipairs, unpack, setmetatable, error, next, rawget = select, type, pairs, ipairs, unpack, setmetatable, error, next, rawget local lgi = require 'lgi' local core = require 'lgi.core' local Gtk = lgi.Gtk local Gdk = lgi.Gdk local GObject = lgi.GObject local cairo = lgi.cairo local log = lgi.log.domain('lgi.Gtk') -- Initialize GTK. Gtk.disable_setlocale() if not Gtk.init_check() then return "gtk_init_check() failed" end -- Gtk.Allocation is just an alias to Gdk.Rectangle. Gtk.Allocation = Gdk.Rectangle -------------------------------- Gtk.Widget overrides. Gtk.Widget._attribute = { width = { get = Gtk.Widget.get_allocated_width }, height = { get = Gtk.Widget.get_allocated_height }, events = {}, style = {}, } -- Add widget attributes for some get/set method combinations not covered by -- native widget properties. for _, name in pairs { 'allocation', 'direction', 'settings', 'realized', 'mapped', 'display', 'screen', 'window', 'root_window', 'has_window', 'style_context' } do if not Gtk.Widget._property[name] then local attr = { get = Gtk.Widget['get_' .. name], set = Gtk.Widget['set_' .. name] } if next(attr) then Gtk.Widget._attribute[name] = attr end end end function Gtk.Widget._attribute.width:set(width) self.width_request = width end function Gtk.Widget._attribute.height:set(height) self.height_request = height end -- gtk_widget_intersect is missing an (out caller-allocates) annotation if core.gi.Gtk.Widget.methods.intersect.args[2].direction == 'in' then local real_intersect = Gtk.Widget.intersect function Gtk.Widget:intersect(area) local intersection = Gdk.Rectangle() local notempty = real_intersect(self, area, intersection) return notempty and intersection or nil end end function Gtk.Widget._attribute.events:get() return Gdk.EventMask[self:get_events()] end function Gtk.Widget._attribute.events:set(events) self:set_events(Gdk.EventMask(events)) end -- Accessing style properties is preferrably done by accessing 'style' -- property. In case that caller wants deprecated 'style' property, it -- must be accessed by '_property_style' name. local widget_style_mt = {} function widget_style_mt:__index(name) name = name:gsub('_', '-') local pspec = self._widget._class:find_style_property(name) if not pspec then error(("%s: no style property `%s'"):format( self._widget.type._name, name:gsub('%-', '_')), 2) end local value = GObject.Value(pspec.value_type) self._widget:style_get_property(name, value) return value.value end function Gtk.Widget._attribute.style:get() return setmetatable({ _widget = self }, widget_style_mt) end -- Get/Set widget from Gdk.Window function Gdk.Window:get_widget() return core.object.new(self:get_user_data(), false) end function Gdk.Window:set_widget(widget) self:set_user_data(widget) end Gdk.Window._attribute = rawget(Gdk.Window, '_attribute') or {} Gdk.Window._attribute.widget = { set = Gdk.Window.set_widget, get = Gdk.Window.get_widget, } -------------------------------- Gtk.Buildable overrides. Gtk.Buildable._attribute = { id = {}, child = {} } -- Create custom 'id' property, mapped to buildable name. function Gtk.Buildable._attribute.id:set(id) core.object.env(self).id = id end function Gtk.Buildable._attribute.id:get() return core.object.env(self).id end -- Custom 'child' property, which returns widget in the subhierarchy -- with specified id. local buildable_child_mt = {} function buildable_child_mt:__index(id) return self._buildable.id == id and self._buildable or nil end function Gtk.Buildable._attribute.child:get() return setmetatable({ _buildable = self }, buildable_child_mt) end -------------------------------- Gtk.Container overrides. Gtk.Container._attribute = {} -- Extand add() functionality to allow adding child properties. function Gtk.Container:add(widget, props) if type(widget) == 'table' then props = widget widget = widget[1] end Gtk.Container._method.add(self, widget) if props then local properties = self.property[widget] for name, value in pairs(props) do if type(name) == 'string' then properties[name] = value end end end end -- Map 'add' method also to '_container_add', so that ctor can use it -- for adding widgets from the array part. Gtk.Container._container_add = Gtk.Container.add -- Accessing child properties is preferrably done by accessing -- 'property' attribute. Gtk.Container._attribute.property = {} local container_property_item_mt = {} function container_property_item_mt:__index(name) name = name:gsub('_', '-') local pspec = self._container._class:find_child_property(name) if not pspec then error(("%s: no child property `%s'"):format( self._container.type._name, name:gsub('%-', '_')), 2) end local value = GObject.Value(pspec.value_type) self._container:child_get_property(self._child, name, value) return value.value end function container_property_item_mt:__newindex(name, val) name = name:gsub('_', '-') local pspec = self._container._class:find_child_property(name) if not pspec then error(("%s: no child property `%s'"):format( self._container.type._name, name:gsub('%-', '_')), 2) end local value = GObject.Value(pspec.value_type, val) self._container:child_set_property(self._child, name, value) end local container_property_mt = {} function container_property_mt:__index(child) if type(child) == 'string' then child = self._container.child[child] end return setmetatable({ _container = self._container, _child = child }, container_property_item_mt) end function Gtk.Container._attribute.property:get() return setmetatable({ _container = self }, container_property_mt) end -- Override 'child' attribute; writing allows adding child with -- children properties (similarly as add() override), reading provides -- table which can lookup subchildren by Gtk.Buildable.id. Gtk.Container._attribute.child = {} local container_child_mt = {} function container_child_mt:__index(id) if type(id) ~= 'string' then return nil end local found = (core.object.env(self._container).id == id and self._container) if not found then for _, child in ipairs(self) do found = child.child[id] if found then break end end end return found or nil end function Gtk.Container._attribute.child:get() local children = self:get_children() children._container = self return setmetatable(children, container_child_mt) end function Gtk.Container._attribute.child:set(widget) self:add(widget) end -------------------------------- Gtk.Builder overrides. Gtk.Builder._attribute = {} -- Override add_from_ family of functions, their C-signatures are -- completely braindead. local function builder_fix_return(res, e1, e2) if res and res ~= 0 then return true end return false, e1, e2 end function Gtk.Builder:add_from_file(filename) return builder_fix_return(Gtk.Builder._method.add_from_file(self, filename)) end function Gtk.Builder:add_objects_from_file(filename, object_ids) return builder_fix_return(Gtk.Builder._method.add_objects_from_file( self, filename, object_ids)) end function Gtk.Builder:add_from_string(string, len) if not len or len == -1 then len = #string end return builder_fix_return(Gtk.Builder._method.add_from_string( self, string, len)) end function Gtk.Builder:add_objects_from_string(string, len, object_ids) if not len or len == -1 then len = #string end return builder_fix_return(Gtk.Builder._method.add_objects_from_string( self, string, len, object_ids)) end -- Wrapping get_object() using 'objects' attribute. Gtk.Builder._attribute.objects = {} local builder_objects_mt = {} function builder_objects_mt:__index(name) return self._builder:get_object(name) end function Gtk.Builder._attribute.objects:get() return setmetatable({ _builder = self }, builder_objects_mt) end -- Implementation of connect_signals() method. function Gtk.Builder._method:connect_signals(handlers) local unconnected self:connect_signals_full( function(builder, object, signal, handler, connect_object, flags) signal = 'on_' .. signal:gsub('%-', '_') local target = handlers[handler] if not target then unconnected = unconnected or {} unconnected[#unconnected + 1] = handler log.warning("%s: failed to connect to `%s' handler", signal, handler) return end local fun if connect_object then fun = function(_, ...) return target(connect_object, ...) end else fun = target end object[signal]:connect(fun, nil, flags.AFTER) end) return unconnected end -------------------------------- Gtk.TextTagTable overrides. Gtk.TextTagTable._attribute = { tag = {} } local text_tag_table_tag_mt = {} function text_tag_table_tag_mt:__index(id) return self._table:lookup(id) end function Gtk.TextTagTable._attribute.tag:get() return setmetatable({ _table = self }, text_tag_table_tag_mt) end -- Map adding of tags in constructor array part to add() method. Gtk.TextTagTable._container_add = Gtk.TextTagTable.add -------------------------------- Gtk.TreeModel and relatives. Gtk.TreeModel._attribute = {} local tree_model_item_mt = {} function tree_model_item_mt:__index(column) return self._model:get_value(self._iter, column - 1).value end function tree_model_item_mt:__newindex(column, val) column = column - 1 local value = GObject.Value(self._model:get_column_type(column), val) self._model:set_value(self._iter, column, value) end -- Map access of lines to iterators by directly indexing treemodel -- instance values with iterators. local tree_model_element = Gtk.TreeModel._element function Gtk.TreeModel:_element(model, key, origin) if Gtk.TreeIter:is_type_of(key) then return key, '_iter' elseif Gtk.TreePath:is_type_of(key) then return model:get_iter(key), '_iter' end return tree_model_element(self, model, key, origin) end function Gtk.TreeModel:_access_iter(model, iter, ...) if select('#', ...) > 0 then model:set(iter, ...) else -- Return proxy table allowing getting/setting individual columns. return setmetatable({ _model = model, _iter = iter }, tree_model_item_mt) end end local function treemodel_prepare_values(model, values) local cols, vals = {}, {} for column, value in pairs(values) do column = column - 1 cols[#cols + 1] = column vals[#vals + 1] = GObject.Value(model:get_column_type(column), value) end return cols, vals end function Gtk.TreeModel:set(iter, values) -- Set all values provided by the table if Gtk.TreePath:is_type_of(iter) then iter = self:get_iter(iter) end self:set_values(iter, treemodel_prepare_values(self, values)) end -- Implement iteration protocol for model. function Gtk.TreeModel:next(iter) if not iter or type(iter) == 'table' then -- Start iteration. iter = self:iter_children(iter and iter[1]) else -- Continue to the next child. if not self:iter_next(iter) then iter = nil end end return iter, iter and Gtk.TreeModel:_access_iter(self, iter) end function Gtk.TreeModel:pairs(parent) return Gtk.TreeModel.next, self, parent and { parent } end -- Redirect 'set' method to our one inherited from TreeModel, it is -- the preferred one. Rename the original to set_values(). Gtk.ListStore._method.set_values = Gtk.ListStore.set Gtk.ListStore._method.set = nil Gtk.ListStore.set = nil -- Allow insert() and append() to handle also 'with_values' case. function Gtk.ListStore:insert(position, values) local iter if not values then iter = Gtk.ListStore._method.insert(self, position) else iter = Gtk.ListStore._method.insert_with_valuesv( self, position, treemodel_prepare_values(self, values)) end return iter end if not Gtk.ListStore._method.insert_with_values then Gtk.ListStore._method.insert_with_values = Gtk.ListStore._method.insert_with_valuesv end function Gtk.ListStore:append(values) local iter if not values then iter = Gtk.ListStore._method.append(self) else iter = Gtk.ListStore._method.insert_with_values( self, -1, treemodel_prepare_values(self, values)) end return iter end -- Similar treatment for treestore. Gtk.TreeStore._method.set_values = Gtk.TreeStore.set Gtk.TreeStore._method.set = nil Gtk.TreeStore.set = nil function Gtk.TreeStore:insert(parent, position, values) local iter if not values then iter = Gtk.TreeStore._method.insert(self, parent, position) else iter = Gtk.TreeStore._method.insert_with_values( self, parent, position, treemodel_prepare_values(self, values)) end return iter end function Gtk.TreeStore:append(parent, values) local iter if not values then iter = Gtk.TreeStore._method.append(self, parent) else iter = Gtk.TreeStore._method.insert_with_values( self, parent, -1, treemodel_prepare_values(self, values)) end return iter end -- Add missing constants, defined as anonymous enums in C headers, which -- is not supported by GIR yet. Gtk.TreeSortable.DEFAULT_SORT_COLUMN_ID = -1 Gtk.TreeSortable.UNSORTED_SORT_COLUMN_ID = -2 -------------------------------- Gtk.TreeView and support. -- Array part in constructor specifies columns to add. Gtk.TreeView._container_add = Gtk.TreeView.append_column -- Allow looking up tree column as child of the tree. Gtk.TreeView._attribute = { child = { set = Gtk.TreeView._parent._attribute.child.set } } local treeview_child_mt = {} function treeview_child_mt:__index(id) if self._view.id == id then return self._view end for _, column in ipairs(self._view:get_columns()) do local child = column.child[id] if child then return child end end end function Gtk.TreeView._attribute.child:get() return setmetatable({ _view = self }, treeview_child_mt) end -- Sets attributes for specified cell. function Gtk.CellLayout:set(cell, data) if type(data) == 'table' then for attr, column in pairs(data) do self:add_attribute(cell, attr, column - 1) end else self:set_cell_data_func(cell, data) end end -- Adds new cellrenderer with full definition into the column. function Gtk.CellLayout:add(def) if def.align == 'start' then self:pack_start(def[1], def.expand) else self:pack_end(def[1], def.expand) end -- Set attributes. self:set(def[1], def[2]) if def.data_func then self:set_cell_data_func(def[1], def.data_func) end end -- Unfortunately, CellView is interface often implemented by descendants -- of Gtk.Container, so we cannot reuse generic _container_add here, -- because it is already occupied by implementing container's ctor. So -- instead add attribute 'cells' which can be assigned the list of cell -- data definitions. Gtk.CellLayout._attribute = { cells = {}, child = {} } function Gtk.CellLayout._attribute.cells:set(cells) for _, data in ipairs(cells) do Gtk.CellLayout.add(self, data) end end -- Allow lookuing up rendereres by assigned id. Gtk.CellRenderer._attribute = { id = Gtk.Buildable._attribute.id } local celllayout_child_mt = {} function celllayout_child_mt:__index(id) if id == self._layout.id then return self._layout end for _, renderer in ipairs(self._layout:get_cells()) do if renderer.id == id then return renderer end end end function Gtk.CellLayout._attribute.child:get() return setmetatable({ _layout = self }, celllayout_child_mt) end Gtk.TreeViewColumn._container_add = Gtk.TreeViewColumn.add -------------------------------- Gtk.Action and relatives function Gtk.ActionGroup:add(action) if type(action) == 'table' then if action.accelerator then -- Add with an accelerator. self:add_action_with_accel(action[1], action.accelerator) return action[1] end -- Go through all actions in the table and add them. local first_radio for i = 1, #action do local added = self:add(action[i]) if Gtk.RadioAction:is_type_of(added) then if not first_radio then first_radio = added else added:join_group(first_radio) end end end -- Install callback for on_activate. if first_radio and action.on_change then local on_change = action.on_change function first_radio:on_changed(current) on_change(current) end end else -- Add plain action. self:add_action(action) return action end end Gtk.ActionGroup._container_add = Gtk.ActionGroup.add Gtk.ActionGroup._attribute = { action = {} } local action_group_mt = {} function action_group_mt:__index(name) return self._group:get_action(name) end function Gtk.ActionGroup._attribute.action:get() return setmetatable({ _group = self }, action_group_mt) end -------------------------------- Gtk.Assistant Gtk.Assistant._attribute = { property = {} } function Gtk.Assistant:add(child) if type(child) == 'table' then local widget = child[1] self:append_page(widget) for name, value in pairs(child) do if type(name) == 'string' then self['set_page_' .. name](self, widget, value) end end else self:append_page(widget) end end Gtk.Assistant._container_add = Gtk.Assistant.add local assistant_property_mt = {} function assistant_property_mt:__newindex(property_name, value) self._assistant['set_page_' .. property_name]( self._assistant, self._page, value) end function assistant_property_mt:__index(property_name) return self._assistant['get_page_' .. property_name]( self._assistant, self._page) end local assistant_properties_mt = {} function assistant_properties_mt:__index(page) if type(page) == 'string' then page = self._assistant.child[page] end return setmetatable({ _assistant = self._assistant, _page = page }, assistant_property_mt) end function Gtk.Assistant._attribute.property:get() return setmetatable({ _assistant = self }, assistant_properties_mt) end -------------------------------- Gtk.Dialog Gtk.Dialog._attribute = { buttons = {} } function Gtk.Dialog._attribute.buttons:set(buttons) for _, button in ipairs(buttons) do self:add_button(button[1], button[2]) end end -------------------------------- Gtk.InfoBar Gtk.InfoBar._attribute = { buttons = Gtk.Dialog._attribute.buttons } -------------------------------- Gtk.Menu function Gtk.Menu:popup(a1, a2, a3, a4, a5, ...) if select('#', ...) > 0 then Gtk.Menu.popup_for_device(self, a1, a2, a3, a4, a5, ...) else Gtk.Menu.popup_for_device(self, nil, a1, a2, a3, a4, a5) end end -------------------------------- Gtk.MenuItem Gtk.MenuItem._attribute = { child = {} } function Gtk.MenuItem._attribute.child:get() local children = Gtk.Container._attribute.child.get(self) children[#children + 1] = Gtk.MenuItem.get_submenu(self) return children end -------------------------------- Gtk.EntryCompletion -- Workaround for bug in GTK+; text_column accessors don't do an extra -- needed work which is done properly in -- gtk_entry_completion_{set/get}_text_column Gtk.EntryCompletion._attribute = { text_column = { get = Gtk.EntryCompletion.get_text_column, set = Gtk.EntryCompletion.set_text_column } } -------------------------------- Gtk.PrintSettings Gtk._constant = Gtk._constant or {} Gtk._constant.PRINT_OUTPUT_FILE_FORMAT = 'output-file-format' Gtk._constant.PRINT_OUTPUT_URI = 'output-uri' -- Gtk-cairo integration helpers. cairo.Context._method.should_draw_window = Gtk.cairo_should_draw_window --------------------------------- Gtk-2 workarounds if Gtk._version == '2.0' then -- Get rid of Gtk.Bin internal 'child' field, which gets in the way -- of 'child' attribute mechanism introduced in this override. local _ = Gtk.Bin.child Gtk.Bin._field.child = nil end lgi-0.9.2/lgi/override/Pango.lua000066400000000000000000000147231316674307300164570ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- LGI Pango override module. -- -- Copyright (c) 2012 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local pairs, rawget = pairs, rawget local table = require 'table' local lgi = require 'lgi' local Pango = lgi.Pango local core = require 'lgi.core' local gi = core.gi local ffi = require 'lgi.ffi' local ti = ffi.types local record = require 'lgi.record' local component = require 'lgi.component' -- Provide defines which are not present in the GIR local _ = Pango.SCALE for name, value in pairs { SCALE_XX_SMALL = 1 / (1.2 * 1.2 * 1.2), SCALE_X_SMALL = 1 / (1.2 * 1.2), SCALE_SMALL = 1 / 1.2, SCALE_MEDIUM = 1, SCALE_LARGE = 1.2, SCALE_X_LARGE = 1.2 * 1.2, SCALE_XX_LARGE = 1.2 * 1.2 * 1.2, } do if not Pango[name] then Pango._constant[name] = value end end -- Because of https://bugzilla.gnome.org/show_bug.cgi?id=672133 Pango -- enums are not gtype based on some platforms. If we detect non-gtype -- enum, reload it using ffi and GEnumInfo machinery. for _, enum in pairs { 'AttrType', 'Underline', 'BidiType', 'Direction', 'CoverageLevel', 'Style', 'Variant', 'Weight', 'Stretch', 'FontMask', 'Gravity', 'GravityHint', 'Alignment', 'WrapMode', 'EllipsizeMode', 'RenderPart', 'Script', 'TabAlign', } do if not Pango[enum]._gtype then local gtype = ffi.load_gtype( core.gi.Pango.resolve, 'pango_' .. core.uncamel(enum) .. '_get_type') Pango._enum[enum] = ffi.load_enum(gtype, 'Pango.' .. enum) end end local pango_layout_set_text = Pango.Layout.set_text function Pango.Layout:set_text(text, len) pango_layout_set_text(self, text, len or -1) end local pango_layout_set_markup = Pango.Layout.set_markup function Pango.Layout:set_markup(text, len) pango_layout_set_markup(self, text, len or -1) end -- Pango.Layout:set_attributes() has incorrect transfer-full -- annotation on its attrs argument. Workaround for -- https://github.com/pavouk/lgi/issues/60 if gi.Pango.Layout.methods.set_attributes.args[1].transfer ~= 'none' then local _ = Pango.Layout.set_attributes Pango.Layout.set_attributes = core.callable.new { addr = core.gi.Pango.resolve.pango_layout_set_attributes, name = 'Pango.Layout.set_attributes', ret = ti.void, gi.Pango.Layout.methods.new.return_type, gi.Pango.Layout.methods.set_attributes.args[1].typeinfo, } end -- Add attributes simulating logically missing properties in Pango classes. for compound, attrs in pairs { [Pango.Layout] = { 'attributes', 'font_description', 'width', 'height', 'wrap', 'context', 'is_wrapped', 'ellipsize', 'is_ellipsized', 'indent', 'spacing', 'justify', 'auto_dir', 'alignment', 'tabs', 'single_paragraph_mode', 'baseline', 'line_count', 'lines', 'log_attrs', 'character_count', 'text', 'markup', }, [Pango.Context] = { 'base_dir', 'base_gravity', 'font_description', 'font_map', 'gravity', 'gravity_hint', 'language', 'matrix', }, [Pango.FontMetrics] = { 'ascent', 'descent', 'approximate_char_width', 'approximate_digit_width', 'underline_thickness', 'underline_position', 'strikethrough_thinkess', 'strikethrough_position', }, } do compound._attribute = rawget(compound, '_attribute') or {} for _, name in pairs(attrs) do if not compound._property or not compound._property[name] then compound._attribute[name] = { get = compound['get_' .. name] or compound[name], set = compound['set_' .. name], } end end end -- Handling of poor-man's OO invented by Pango.Attribute related -- pseudoclasses. Pango.Attribute._free = core.gi.Pango.Attribute.methods.destroy for name, def in pairs { language_new = { Pango.Language }, family_new = { ti.utf8 }, style_new = { Pango.Style }, variant_new = { Pango.Variant }, stretch_new = { Pango.Stretch }, weight_new = { Pango.Weight }, size_new = { ti.int }, size_new_absolute = { ti.int }, desc_new = { Pango.FontDescription }, foreground_new = { ti.uint16, ti.uint16, ti.uint16 }, background_new = { ti.uint16, ti.uint16, ti.uint16 }, strikethrough_new = { ti.boolean }, strikethrough_color_new = { ti.uint16, ti.uint16, ti.uint16 }, underline_new = { Pango.Underline }, underline_color_new = { ti.uint16, ti.uint16, ti.uint16 }, shape_new = { Pango.Rectangle, Pango.Rectangle }, scale_new = { ti.double }, rise_new = { ti.int }, letter_spacing_new = { ti.int }, fallback_new = { ti.boolean }, gravity_new = { Pango.Gravity }, gravity_hint_new = { Pango.GravityHint }, } do def.addr = core.gi.Pango.resolve['pango_attr_' .. name] def.name = 'Pango.Attribute.' .. name def.ret = { Pango.Attribute, xfer = true } Pango.Attribute[name] = core.callable.new(def) end -- Adding Pango.Attribute into the Pango.AttrList takes ownership of -- the record. Pfft, crappy API, wrap and handle. for _, name in pairs { 'insert', 'insert_before', 'change' } do local raw_method = Pango.AttrList[name] Pango.AttrList[name] = function(list, attr) -- Invoke original method first, then unown the attribute. raw_method(list, attr) core.record.set(attr, false) end end -- Pango.Layout:move_cursor_visually() is missing an (out) annotation -- in older Pango releases. Work around this limitation by creating -- custom ffi definition for this method. if gi.Pango.Layout.methods.move_cursor_visually.args[6].direction ~= 'out' then local _ = Pango.Layout.move_cursor_visually Pango.Layout.move_cursor_visually = core.callable.new { addr = core.gi.Pango.resolve.pango_layout_move_cursor_visually, name = 'Pango.Layout.move_cursor_visually', ret = ti.void, gi.Pango.Layout.methods.new.return_type, ti.boolean, ti.int, ti.int, ti.int, { ti.int, dir = 'out' }, { ti.int, dir = 'out' }, } end -- Pango.GlyphString is struct with counted array inside, until -- Pango-1.38 which has fixed annotation. Fix for previous versions. if gi.Pango.GlyphString.fields.glyphs.typeinfo.tag ~= 'array' then Pango.GlyphString._attribute = { glyphs = {} } function Pango.GlyphString._attribute.glyphs:get() local array = core.record.field(self, Pango.GlyphString._field.glyphs) local glyphs = {} for i = 0, self.num_glyphs - 1 do glyphs[i + 1] = core.record.fromarray(array, i) end return glyphs end end lgi-0.9.2/lgi/override/PangoCairo.lua000066400000000000000000000030021316674307300174210ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- LGI PangoCairo override module. -- -- Copyright (c) 2012 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local pairs = pairs local lgi = require 'lgi' local PangoCairo = lgi.PangoCairo local cairo = lgi.cairo local Pango = lgi.Pango -- Make assorted PangoCairo global methods taking cairo.Context as first -- argument methods of the cairo.Context (where they logically belong). for _, name in pairs { 'update_context', 'update_layout', 'show_glyph_string', 'show_glyph_item', 'show_layout_line', 'show_layout', 'show_error_underline', 'glyph_string_path', 'layout_line_path', 'layout_path', 'error_underline_path', } do cairo.Context._method[name] = PangoCairo[name] end Pango.Layout.create = PangoCairo.create_layout -- Extend Pango.Context with additional methods and attributes coming from -- PangoCairo package. Pango.Context.create = PangoCairo.context_create for _, name in pairs { 'get_font_options', 'set_font_options', 'get_resolution', 'set_resolution', 'set_shape_renderer' } do Pango.Context[name] = PangoCairo['context_' .. name] end for _, name in pairs { 'font_options', 'resolution', 'shape_renderer' } do Pango.Context._attribute[name] = { get = Pango.Context['get_' .. name] or Pango.Context[name], set = Pango.Context['set_' .. name], } end lgi-0.9.2/lgi/override/cairo.lua000066400000000000000000000721531316674307300165110ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- LGI Cairo override module. -- -- Copyright (c) 2012 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local assert, pairs, ipairs, setmetatable, table, rawget, type = assert, pairs, ipairs, setmetatable, table, rawget, type local lgi = require 'lgi' local cairo = lgi.cairo local core = require 'lgi.core' local ffi = require 'lgi.ffi' local component = require 'lgi.component' local record = require 'lgi.record' local enum = require 'lgi.enum' local ti = ffi.types cairo._module = core.module('cairo', 2) local module_gobject = core.gi.cairo.resolve -- Versioning support. function cairo.version_encode(major, minor, micro) return 10000 * major + 100 * minor + micro end cairo.version = core.callable.new { addr = cairo._module.cairo_version, ret = ti.int } () cairo.version_string = core.callable.new { addr = cairo._module.cairo_version_string, ret = ti.utf8 } () -- Load some constants. cairo._constant = { MIME_TYPE_JP2 = 'image/jp2', MIME_TYPE_JPEG = 'image/jpeg', MIME_TYPE_PNG = 'image/png', MIME_TYPE_URI = 'text/x-uri', } -- Load definitions of all enums. cairo._enum = cairo._enum or {} for _, name in pairs { 'Status', 'Content', 'Operator', 'Antialias', 'FillRule', 'LineCap', 'LineJoin', 'TextClusterFlags', 'FontSlant', 'FontWeight', 'SubpixelOrder', 'HintStyle', 'HintMetrics', 'FontType', 'PathDataType', 'DeviceType', 'SurfaceType', 'Format', 'PatternType', 'Extend', 'Filter', 'RegionOverlap', 'PdfVersion', 'PsLevel', 'SvgVersion', } do local gtype = ffi.load_gtype( module_gobject, 'cairo_gobject_' .. core.uncamel(name) .. '_get_type') if gtype then cairo._enum[name] = ffi.load_enum(gtype, 'cairo.' .. name) else cairo._enum[name] = component.create(nil, enum.enum_mt, 'cairo.' .. name) end end -- Load definitions of all boxed records. cairo._struct = cairo._struct or {} for index, struct in pairs { 'Context', 'Device', 'Surface', 'Rectangle', 'ScaledFont', 'FontFace', 'FontOptions', 'Region', 'RectangleInt', 'Path', 'TextExtents', 'FontExtents', 'Matrix', 'ImageSurface', 'PdfSurface', 'PsSurface', 'RecordingSurface', 'SvgSurface', 'ToyFontFace', 'RectangleList', 'SolidPattern', 'SurfacePattern', 'GradientPattern', 'LinearPattern', 'RadialPattern', MeshPattern = cairo.version_encode(1, 12, 0), } do local since = 0 if type(index) == 'string' then since = struct struct = index end if cairo.version >= since then local gtype = ffi.load_gtype( module_gobject, 'cairo_gobject_' .. core.uncamel(struct) .. '_get_type') local obj = component.create(gtype, record.struct_mt, 'cairo.' .. struct) cairo._struct[struct] = obj end end local path_data_header = component.create(nil, record.struct_mt) ffi.load_fields(path_data_header, { { 'type', cairo.PathDataType }, { 'length', ti.int } }) local path_data_point = component.create(nil, record.struct_mt) ffi.load_fields(path_data_point, { { 'x', ti.double }, { 'y', ti.double } }) cairo._union = rawget(cairo, '_union') or {} cairo._union.PathData = component.create(nil, record.union_mt, 'cairo.PathData') -- Populate methods into records. for _, info in ipairs { { 'Status', methods = { to_string = { static = true, ret = ti.utf8, cairo.Status } }, }, { 'Rectangle', fields = { { 'x', ti.double }, { 'y', ti.double }, { 'width', ti.double }, { 'height', ti.double }, }, }, { 'RectangleList', fields = { { 'status', cairo.Status }, { 'rectangles', cairo.Rectangle, ptr = true }, { 'num_rectangles', ti.int }, }, }, { 'RectangleInt', fields = { { 'x', ti.int }, { 'y', ti.int }, { 'width', ti.int }, { 'height', ti.int }, }, }, { 'Matrix', fields = { { 'xx', ti.double }, { 'yx', ti.double }, { 'xy', ti.double }, { 'yy', ti.double }, { 'x0', ti.double }, { 'y0', ti.double }, }, methods = { init = { ti.double, ti.double, ti.double, ti.double, ti.double, ti.double }, init_identity = {}, init_translate = { ti.double, ti.double }, init_scale = { ti.double, ti.double }, init_rotate = { ti.double }, translate = { ti.double, ti.double }, scale = { ti.double, ti.double }, rotate = { ti.double }, invert = { ret = cairo.Status }, multiply = { cairo.Matrix, cairo.Matrix }, transform_distance = { { ti.double, dir = 'inout' }, { ti.double, dir = 'inout' } }, transform_point = { { ti.double, dir = 'inout' }, { ti.double, dir = 'inout' } }, }, }, { 'PathData', fields = { { 'header', path_data_header }, { 'point', path_data_point }, }, }, { 'Path', fields = { { 'status', cairo.Status }, { 'data', cairo.PathData, ptr = true }, { 'num_data', ti.int }, }, }, { 'FontExtents', fields = { { 'ascent', ti.double }, { 'descent', ti.double }, { 'height', ti.double }, { 'max_x_advance', ti.double }, { 'max_y_advance', ti.double }, }, }, { 'TextExtents', fields = { { 'x_bearing', ti.double }, { 'y_bearing', ti.double }, { 'width', ti.double }, { 'height', ti.double }, { 'x_advance', ti.double }, { 'y_advance', ti.double }, }, }, { 'Context', cprefix = '', methods = { create = { static = true, ret = { cairo.Context, xfer = true }, cairo.Surface }, status = { ret = cairo.Status }, save = {}, restore = {}, get_target = { ret = cairo.Surface}, push_group = {}, push_group_with_content = { cairo.Content }, pop_group = { ret = { cairo.Pattern, xfer = true } }, pop_group_to_source = {}, get_group_target = { ret = cairo.Surface }, set_source_rgb = { ti.double, ti.double, ti.double }, set_source_rgba = { ti.double, ti.double, ti.double, ti.double }, set_source = { cairo.Pattern }, set_source_surface = { cairo.Surface, ti.double, ti.double }, get_source = { ret = cairo.Pattern }, set_antialias = { cairo.Antialias }, get_antialias = { ret = cairo.Antialias }, get_dash_count = { ret = ti.int }, set_fill_rule = { cairo.FillRule }, get_fill_rule = { ret = cairo.FillRule }, set_line_cap = { cairo.LineCap }, get_line_cap = { ret = cairo.LineCap }, set_line_join = { cairo.LineJoin }, get_line_join = { ret = cairo.LineJoin }, set_line_width = { ti.double }, get_line_width = { ret = ti.double }, set_miter_limit = { ti.double }, get_miter_limit = { ret = ti.double }, set_operator = { cairo.Operator }, get_operator = { ret = cairo.Operator }, set_tolerance = { ti.double }, get_tolerance = { ret = ti.double }, clip = {}, clip_preserve = {}, clip_extents = { { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' } }, in_clip = { ret = ti.boolean, ti.double, ti.double }, reset_clip = {}, copy_clip_rectangle_list = { ret = { cairo.RectangleList, xfer = true } }, fill = {}, fill_preserve = {}, fill_extents = { { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' } }, in_fill = { ret = ti.boolean, ti.double, ti.double }, mask = { cairo.Pattern }, mask_surface = { cairo.Surface, ti.double, ti.double }, paint = {}, paint_with_alpha = { ti.double }, stroke = {}, stroke_preserve = {}, stroke_extents = { { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' } }, in_stroke = { ret = ti.boolean, ti.double, ti.double }, copy_page = {}, show_page = {}, copy_path = { ret = { cairo.Path, xfer = true } }, copy_path_flat = { ret = { cairo.Path, xfer = true } }, append_path = { cairo.Path }, has_current_point = { ret = ti.boolean }, get_current_point = { { ti.double, dir = 'out' }, { ti.double, dir = 'out' } }, new_path = {}, new_sub_path = {}, close_path = {}, arc = { ti.double, ti.double, ti.double, ti.double, ti.double }, arc_negative = { ti.double, ti.double, ti.double, ti.double, ti.double }, curve_to = { ti.double, ti.double, ti.double, ti.double, ti.double, ti.double }, line_to = { ti.double, ti.double }, move_to = { ti.double, ti.double }, rectangle = { ti.double, ti.double, ti.double, ti.double }, -- glyph_path, text_path = { ti.utf8 }, rel_curve_to = { ti.double, ti.double, ti.double, ti.double, ti.double, ti.double }, rel_line_to = { ti.double, ti.double }, rel_move_to = { ti.double, ti.double }, path_extents = { { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' } }, translate = { ti.double, ti.double }, scale = { ti.double, ti.double }, rotate = { ti.double }, transform = { cairo.Matrix }, set_matrix = { cairo.Matrix }, get_matrix = { cairo.Matrix }, identity_matrix = {}, user_to_device = { { ti.double, dir = 'inout' }, { ti.double, dir = 'inout' } }, user_to_device_distance = { { ti.double, dir = 'inout' }, { ti.double, dir = 'inout' } }, device_to_user = { { ti.double, dir = 'inout' }, { ti.double, dir = 'inout' } }, device_to_user_distance = { { ti.double, dir = 'inout' }, { ti.double, dir = 'inout' } }, select_font_face = { ti.utf8, cairo.FontSlant, cairo.FontWeight }, set_font_size = { ti.double }, set_font_matrix = { cairo.Matrix }, get_font_matrix = { cairo.Matrix }, set_font_options = { cairo.FontOptions }, get_font_options = { cairo.FontOptions }, set_font_face = { cairo.FontFace }, get_font_face = { ret = cairo.FontFace }, set_scaled_font = { cairo.ScaledFont }, get_scaled_font = { ret = cairo.ScaledFont }, show_text = { ti.utf8 }, -- show_glyphs, show_text_glyphs font_extents = { cairo.FontExtents }, text_extents = { ti.utf8, cairo.TextExtents }, -- glyph extents }, properties = { 'status', 'target', 'group_target', 'source', 'antialias', 'fill_rule', 'line_cap', 'line_join', 'line_width', 'miter_limit', 'operator', 'tolerance', 'font_size', 'font_face', 'font_matrix', 'scaled_font', 'matrix', }, }, { 'Pattern', methods = { status = { ret = cairo.Status }, set_extend = { cairo.Extend }, get_extend = { ret = cairo.Extend }, set_filter = { cairo.Filter }, get_filter = { ret = cairo.Filter }, set_matrix = { cairo.Matrix }, get_matrix = { cairo.Matrix }, get_type = { ret = cairo.PatternType }, }, properties = { 'status', 'extend', 'filter', 'type' }, }, { 'SolidPattern', parent = cairo.Pattern, cprefix = 'pattern_', methods = { create_rgb = { static = true, ret = { cairo.Pattern, xfer = true }, ti.double, ti.double, ti.double }, create_rgba = { static = true, ret = { cairo.Pattern, xfer = true }, ti.double, ti.double, ti.double, ti.double }, get_rgba = { ret = cairo.Status, { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' } }, }, }, { 'SurfacePattern', parent = cairo.Pattern, cprefix = 'pattern_', methods = { create = { cname = 'cairo_pattern_create_for_surface', static = true, ret = { cairo.Pattern, xfer = true }, cairo.Surface }, get_surface = { ret = cairo.Status, { cairo.Surface, dir = 'out' } }, }, }, { 'GradientPattern', parent = cairo.Pattern, cprefix = 'pattern_', methods = { add_color_stop_rgb = { ti.double, ti.double, ti.double, ti.double }, add_color_stop_rgba = { ti.double, ti.double, ti.double, ti.double, ti.double }, get_color_stop_count = { ret = cairo.Status, { ti.int, dir = 'out' } }, get_color_stop_rgba = { ret = cairo.Status, ti.int, { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' } }, }, }, { 'LinearPattern', parent = cairo.GradientPattern, cprefix = 'pattern_', methods = { create = { cname = 'cairo_pattern_create_linear', static = true, ret = { cairo.Pattern, xfer = true }, ti.double, ti.double, ti.double, ti.double }, get_linear_points = { ret = cairo.Status, { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' } }, } }, { 'RadialPattern', parent = cairo.GradientPattern, cprefix = 'pattern_', methods = { create = { cname = 'cairo_pattern_create_radial', static = true, ret = { cairo.Pattern, xfer = true }, ti.double, ti.double, ti.double, ti.double, ti.double, ti.double }, get_radial_circles = { ret = cairo.Status, { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' } }, }, }, { 'MeshPattern', parent = cairo.Pattern, since = cairo.version_encode(1, 12, 0), methods = { create = { cname = 'cairo_pattern_create_mesh', static = true, ret = { cairo.Pattern, xfer = true } }, begin_patch = {}, end_patch = {}, move_to = { ti.double, ti.double }, line_to = { ti.double, ti.double }, curve_to = { ti.double, ti.double, ti.double, ti.double, ti.double, ti.double }, set_control_point = { ti.int, ti.double, ti.double }, set_corner_color_rgb = { ti.int, ti.double, ti.double, ti.double }, set_corner_color_rgba = { ti.int, ti.double, ti.double, ti.double, ti.double }, get_patch_count = { ret = cairo.Status, { ti.int, dir = 'out' } }, get_path = { ret = { cairo.Path, xfer = true }, ti.int }, get_control_point = { ret = cairo.Status, ti.int, ti.int, { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' } }, get_corner_color_rgba = { ret = cairo.Status, ti.int, ti.int, { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' } }, }, }, { 'Device', methods = { status = { ret = cairo.Status }, finish = {}, flush = {}, get_type = { ret = cairo.DeviceType }, acquire = { ret = cairo.Status }, release = {}, }, properties = { 'status' }, }, { 'Format', methods = { stride_for_width = { ret = ti.int, ti.int }, }, }, { 'Surface', methods = { create_similar = { ret = { cairo.Surface, xfer = true }, cairo.Content, ti.int, ti.int }, create_for_rectangle = { ret = { cairo.Surface, xfer = true }, ti.double, ti.double, ti.double, ti.double }, status = { ret = cairo.Status }, finish = {}, flush = {}, get_device = { ret = cairo.Device }, get_font_options = { cairo.FontOptions }, get_content = { ret = cairo.Content }, mark_dirty = {}, mark_dirty_rectangle = { ti.int, ti.int, ti.int, ti.int }, set_device_offset = { ti.double, ti.double }, get_device_offset = { { ti.double, dir = 'out' }, { ti.double, dir = 'out' } }, set_fallback_resolution = { ti.double, ti.double }, get_fallback_resolution = { { ti.double, dir = 'out' }, { ti.double, dir = 'out' } }, get_type = { ret = cairo.SurfaceType }, copy_page = {}, show_page = {}, has_show_text_glyphs = { ret = ti.boolean }, -- set_mime_data, get_mime_data write_to_png = { ret = cairo.Status, ti.filename }, }, properties = { 'status', 'device', 'content', 'type' }, }, { 'ImageSurface', parent = cairo.Surface, methods = { create = { static = true, ret = { cairo.Surface, xfer = true }, cairo.Format, ti.int, ti.int }, create_for_data = { static = true, ret = { cairo.Surface, xfer = true }, ti.ptr, cairo.Format, ti.int, ti.int, ti.int }, create_from_png = { static = true, ret = { cairo.Surface, xfer = true }, ti.filename }, get_data = { ret = ti.ptr }, get_format = { ret = cairo.Format }, get_width = { ret = ti.int }, get_height = { ret = ti.int }, get_stride = { ret = ti.int }, }, properties = { 'data', 'format', 'width', 'height', 'stride' }, }, { 'PdfVersion', values = { ['1_4'] = 0, ['1_5'] = 1, }, }, { 'PdfSurface', parent = cairo.Surface, methods = { create = { static = true, ret = { cairo.Surface, xfer = true }, ti.filename, ti.double, ti.double }, restrict_to_version = { cairo.PdfVersion }, set_size = { ti.double, ti.double }, }, }, { 'PsLevel', values = { ['2'] = 0, ['3'] = 1, }, }, { 'PsSurface', parent = cairo.Surface, methods = { create = { static = true, ret = { cairo.Surface, xfer = true }, ti.filename, ti.double, ti.double }, restrict_to_level = { cairo.PsLevel }, set_eps = { ti.boolean }, get_eps = { ret = ti.boolean }, set_size = { ti.double, ti.double }, dsc_begin_setup = {}, dsc_begin_page_setup = {}, dsc_comment = { ti.utf8 }, }, properties = { 'eps' }, }, { 'RecordingSurface', parent = cairo.Surface, methods = { create = { static = true, ret = { cairo.Surface, xfer = true }, cairo.Content, cairo.Rectangle }, ink_extents = { { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' }, { ti.double, dir = 'out' } }, get_extents = { since = cairo.version_encode(1, 12, 0), ret = ti.boolean, cairo.Rectangle }, }, }, { 'SvgVersion', values = { ['1_1'] = 0, ['1_2'] = 1, }, }, { 'SvgSurface', parent = cairo.Surface, methods = { create = { static = true, ret = { cairo.Surface, xfer = true }, ti.filename, ti.double, ti.double }, restrict_to_version = { cairo.SvgVersion }, }, }, { 'FontFace', methods = { status = { ret = cairo.Status }, get_type = { ret = cairo.FontType }, }, properties = { 'status', 'type' }, }, { 'ToyFontFace', parent = cairo.FontFace, methods = { create = { static = true, ret = { cairo.ToyFontFace, xfer = true }, ti.utf8, cairo.FontSlant, cairo.FontWeight }, get_family = { ret = ti.utf8 }, get_slant = { ret = cairo.Slant }, get_weight = { ret = cairo.Weight }, }, properties = { 'family', 'slant', 'weight' }, }, { 'FontOptions', methods = { create = { static = true, ret = { cairo.FontOptions, xfer = true } }, copy = { ret = { cairo.FontOptions, xfer = true } }, status = { ret = cairo.Status }, merge = { cairo.FontOptions }, hash = { ret = ti.ulong }, equal = { ret = ti.boolean, cairo.FontOptions }, set_antialias = { cairo.Antialias }, get_antialias = { ret = cairo.Antialias }, set_subpixel_order = { cairo.SubpixelOrder }, get_subpixel_order = { ret = cairo.SubpixelOrder }, set_hint_style = { cairo.HintStyle }, get_hint_style = { ret = cairo.HintStyle }, set_hint_metrics = { cairo.HintMetrics }, get_hint_metrics = { ret = cairo.HintMetrics }, }, properties = { 'status', 'antialias', 'subpixel_order', 'hint_style', 'hint_metrics' }, }, { 'ScaledFont', methods = { create = { static = true, ret = { cairo.ScaledFont, xfer = true }, cairo.FontFace, cairo.Matrix, cairo.Matrix, cairo.FontOptions }, status = { ret = cairo.Status }, extents = { cairo.FontExtents }, text_extents = { ti.utf8, cairo.TextExtents }, -- glyph_extents, text_to_glyphs get_font_face = { ret = cairo.FontFace }, get_font_options = { cairo.FontOptions }, get_font_matrix = { cairo.Matrix }, get_ctm = { cairo.Matrix }, get_scale_matrix = { cairo.Matrix }, get_type = { ret = cairo.FontType }, }, properties = { 'status', 'font_face' }, }, { 'Region', methods = { create = { static = true, ret = { cairo.Region, xfer = true } }, create_rectangle = { static = true, ret = { cairo.Region, xfer = true }, cairo.RectangleInt }, copy = { ret = cairo.Region }, status = { ret = cairo.Status }, get_extents = { cairo.RectangleInt }, num_rectangles = { ret = ti.int }, get_rectangle = { ti.int, cairo.RectangleInt }, is_empty = { ret = ti.boolean }, contains_point = { ret = ti.boolean, ti.int, ti.int }, contains_rectangle = { ret = cairo.RegionOverlap, cairo.RectangleInt }, equal = { ret = ti.boolean, cairo.Region }, translate = { ti.int, ti.int }, intersect = { ret = cairo.Status, cairo.Region }, intersect_rectangle = { ret = cairo.Status, cairo.RectangleInt }, subtract = { ret = cairo.Status, cairo.Region }, subtract_rectangle = { ret = cairo.Status, cairo.RectangleInt }, union = { ret = cairo.Status, cairo.Region }, union_rectangle = { ret = cairo.Status, cairo.RectangleInt }, xor = { ret = cairo.Status, cairo.Region }, xor_rectangle = { ret = cairo.Status, cairo.RectangleInt }, }, properties = { 'status', 'extents', }, }, } do if cairo.version >= (info.since or 0) then local name = info[1] local obj = assert(cairo[name], name) obj._parent = info.parent if info.methods then -- Go through description of the methods and create functions -- from them. obj._method = {} local cprefix = 'cairo_' .. (info.cprefix or core.uncamel(name) .. '_') local self_arg = { obj } for method_name, method_info in pairs(info.methods) do if cairo.version >= (method_info.since or 0) then method_info.name = 'cairo.' .. name .. '.' .. method_name method_info.addr = assert( cairo._module[ method_info.cname or (cprefix .. method_name)]) if not method_info.static then table.insert(method_info, 1, self_arg) end method_info.ret = method_info.ret or ti.void obj._method[method_name] = core.callable.new(method_info) end end end if info.values then -- Fill in addition enum/bitflag values. for n, v in pairs(info.values) do obj[n] = v end end if info.properties then -- Aggregate getters/setters into pseudoproperties -- implemented as attributes. obj._attribute = {} for _, attr in pairs(info.properties) do obj._attribute[attr] = { get = obj._method['get_' .. attr] or obj._method[attr], set = obj._method['set_' .. attr], } end end if info.fields then -- Load record fields definition ffi.load_fields(obj, info.fields) end end end -- Teach non-boxed structs how to destroy itself. cairo.RectangleList._free = cairo._module.cairo_rectangle_list_destroy cairo.Path._free = cairo._module.cairo_path_destroy cairo.FontOptions._free = cairo._module.cairo_font_options_destroy -- Add Matrix creation routines. for _, name in pairs { 'identity', 'scale', 'rotate', 'translate' } do local init = cairo.Matrix._method['init_' .. name] cairo.Matrix._method['create_' .. name] = function(...) local matrix = core.record.new(cairo.Matrix) init(matrix, ...) return matrix end end -- FontOptions can be created only by 'create' method. function cairo.FontOptions._method:_new(props) local font_options = self.create() for k, v in pairs(props or {}) do font_options[k] = v end return font_options end -- Fix all methods using caller-alloc attribute, which is not -- supported by ffi. Emulate it 'by-hand', creating Lua wrapper for -- them. for _, info in pairs { { cairo.Context, 'matrix', cairo.Matrix }, { cairo.Context, 'font_matrix', cairo.Matrix }, { cairo.Context, 'font_options', cairo.FontOptions }, { cairo.Context, 'font_extents', cairo.FontExtents }, { cairo.Context, 'text_extents', cairo.TextExtents, args = 1 }, { cairo.Pattern, 'matrix', cairo.Matrix }, { cairo.Surface, 'font_options', cairo.FontOptions }, { cairo.ScaledFont, 'font_matrix', cairo.Matrix }, { cairo.ScaledFont, 'ctm', cairo.Matrix }, { cairo.ScaledFont, 'scale_matrix', cairo.Matrix }, { cairo.ScaledFont, 'font_options', cairo.FontOptions }, { cairo.ScaledFont, 'extents', cairo.FontExtents }, { cairo.ScaledFont, 'text_extents', cairo.TextExtents, args = 1 }, { cairo.RecordingSurface, 'extents', cairo.Rectangle }, { cairo.Region, 'extents', cairo.RectangleInt }, { cairo.Region, 'get_rectangle', cairo.RectangleInt, args = 1 }, } do local getter_name = 'get_' ..info[2] local raw_getter = info[1]._method[getter_name] if not raw_getter then getter_name = info[2] raw_getter = info[1]._method[getter_name] end if raw_getter then if info.args == 1 then info[1]._method[getter_name] = function(self, arg1) local ret = info[3]() local res = raw_getter(self, arg1, ret) return (res == nil or res) and ret or nil end else info[1]._method[getter_name] = function(self) local ret = info[3]() local res = raw_getter(self, ret) return (res == nil or res) and ret or nil end info[1]._attribute[info[2]] = { get = info[1][getter_name], set = info[1]['set_' .. info[2]], } end end end -- Choose correct 'subclass' of surface on attaching to surface instances. local surface_type_map = { IMAGE = cairo.ImageSurface, PDF = cairo.PdfSurface, PS = cairo.PsSurface, SVG = cairo.SvgSurface, RECORDING = cairo.RecordingSurface, } function cairo.Surface:_attach(surface) local type = cairo.Surface._method.get_type(surface) local repotype = surface_type_map[type] if repotype then core.record.set(surface, repotype) end end -- Also choose correct 'subclass' for patterns. local pattern_type_map = { SOLID = cairo.SolidPattern, SURFACE = cairo.SurfacePattern, LINEAR = cairo.LinearPattern, RADIAL = cairo.RadialPattern, MESH = cairo.MeshPattern } function cairo.Pattern:_attach(pattern) local type = cairo.Pattern._method.get_type(pattern) local repotype = pattern_type_map[type] if repotype then core.record.set(pattern, repotype) end end -- Solid pattern constructor, calling either rgb or rgba variant -- according to the number of arguments. function cairo.SolidPattern._method.create(red, green, blue, alpha) if alpha then return cairo.SolidPattern._method.create_rgba(red, green, blue, alpha) else return cairo.SolidPattern._method.create_rgb(red, green, blue) end end -- For backward compatibility, propagate pattern creation methods from -- specific subpattern types into base cairo.Pattern class. cairo.Pattern._method.create_rgb = cairo.SolidPattern.create_rgb cairo.Pattern._method.create_rgba = cairo.SolidPattern.create_rgba cairo.Pattern._method.create_for_surface = cairo.SurfacePattern.create cairo.Pattern._method.create_linear = cairo.LinearPattern.create cairo.Pattern._method.create_radial = cairo.RadialPattern.create if cairo.MeshPattern then cairo.Pattern._method.create_mesh = cairo.MeshPattern.create end -- Map all 'create' methods to constructors. for _, struct in pairs(cairo._struct) do local create = struct._method and struct._method.create if create then function struct._new(typetable, ...) return create(...) end end end -- Implementation of Context.dash operations. Since ffi does not -- support arrays of doubles, we cheat here and use array of structs -- containing only single 'double' field. local wrapped_double = component.create(nil, record.struct_mt) ffi.load_fields(wrapped_double, { { 'v', ti.double } }) local raw_set_dash = core.callable.new { addr = cairo._module.cairo_set_dash, ret = ti.void, cairo.Context, wrapped_double, ti.int, ti.double } local raw_get_dash = core.callable.new { addr = cairo._module.cairo_get_dash, ret = ti.void, cairo.Context, wrapped_double, { ti.double, dir = 'out' } } function cairo.Context:set_dash(dashes, offset) local count, array = 0 if dashes and #dashes > 0 then -- Convert 'dashes' array into the native array of wrapped_double -- records. count = #dashes array = core.record.new(wrapped_double, nil, count) for i = 1, count do core.record.fromarray(array, i - 1).v = dashes[i] end end raw_set_dash(self, array, count, offset) end function cairo.Context:get_dash() local dashes, offset = {}, 0 local count = self:get_dash_count() if count > 0 then -- Prepare native array of wrapped doubles of specified size. local array = core.record.new(wrapped_double, nil, count) -- Get the dashes. offset = raw_get_dash(self, array) -- Convert output to the table. for i = 1, count do dashes[i] = core.record.fromarray(array, i - 1).v end end return dashes, offset end -- Implementation of iteration protocol over the cairo.Path function cairo.Path:pairs() local index = 0 return function() -- Bounds check, and get appropriate header PathData element. if index >= self.num_data then return nil end local path_data = core.record.fromarray(self.data, index) local type, length = path_data.header.type, path_data.header.length -- Create 'points' table. local points = {} if type ~= 'CLOSE_PATH' then points[1] = core.record.fromarray(self.data, index + 1).point if type == 'CURVE_TO' then points[2] = core.record.fromarray(self.data, index + 2).point points[3] = core.record.fromarray(self.data, index + 3).point end end -- Skip this logical item. index = index + length return type, points end end lgi-0.9.2/lgi/package.lua000066400000000000000000000030671316674307300151660ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- LGI Support for repository packages (namespaces with classes -- overriden in lgi) -- -- Copyright (c) 2012 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local rawget, setmetatable, assert, error = rawget, setmetatable, assert, error local core = require 'lgi.core' -- Repo package metatable. local package = { mt = {} } package.mt.__index = package.mt -- There is no lazy-loading, but define _resolve to do nothing to -- achieve API compatibility with GI-based namespaces. function package.mt:_resolve(recurse) return self end -- Defines new class, deriving from existing one. If the class -- already exists, does nothing and returns nil, otherwise returns -- newly created class type. function package.mt:class(name, parent, ...) if self[name] then return nil end local class = parent:derive(self._name .. '.' .. name, ...) self[name] = class return class end -- Makes sure that given package exists, creates it if it does not. function package.ensure(name, version) local ns = rawget(core.repo, name) if not ns then ns = setmetatable({ _name = name, _version = version }, package.mt) core.repo[name] = ns else if version and ns._version and version ~= nv._version then error(("%s-%s: required version %s "):format( ns._name, ns._version, version)) end end return ns end return package lgi-0.9.2/lgi/record.c000066400000000000000000000457151316674307300145200ustar00rootroot00000000000000/* * Dynamic Lua binding to GObject using dynamic gobject-introspection. * * Copyright (c) 2010, 2011, 2012, 2013 Pavel Holejsovsky * Licensed under the MIT license: * http://www.opensource.org/licenses/mit-license.php * * Management of structures and unions (i.e. records). */ #include #include "lgi.h" /* Available record store modes. */ typedef enum _RecordStore { /* We do not have ownership of the record. */ RECORD_STORE_EXTERNAL, /* Record is stored in data section of Record proxy itself. */ RECORD_STORE_EMBEDDED, /* Record is placed inside some other (parent) record. In order to keep parent record alive, parent record is stored in parent_cache table. */ RECORD_STORE_NESTED, /* Record is allocated by its GLib means and must be freed (by g_boxed_free). */ RECORD_STORE_ALLOCATED, } RecordStore; /* Userdata containing record reference. Table with record type is attached as userdata environment. */ typedef struct _Record { /* Address of the record memory data. */ gpointer addr; /* Store mode of the record. */ RecordStore store; /* If the record is allocated 'on the stack', its data is here. Anonymous union makes sure that data is properly aligned to hold (hopefully) any structure. */ union { gchar data[1]; double align_double; long align_long; gpointer align_ptr; }; } Record; /* lightuserdata key to LUA_REGISTRYINDEX containing metatable for record. */ static int record_mt; /* lightuserdata key to cache table containing lightuserdata(record->addr) -> weak(record) */ static int record_cache; /* lightuserdata key to cache table containing recordproxy(weak) -> parent */ static int parent_cache; gpointer lgi_record_new (lua_State *L, int count, gboolean alloc) { Record *record; size_t size; luaL_checkstack (L, 4, ""); /* Calculate size of the record to allocate. */ lua_getfield (L, -1, "_size"); size = lua_tonumber (L, -1) * count; lua_pop (L, 1); /* Allocate new userdata for record object, attach proper metatable. */ record = lua_newuserdata (L, G_STRUCT_OFFSET (Record, data) + (alloc ? 0 : size)); lua_pushlightuserdata (L, &record_mt); lua_rawget (L, LUA_REGISTRYINDEX); lua_setmetatable (L, -2); if (G_LIKELY (!alloc)) { record->addr = record->data; memset (record->addr, 0, size); record->store = RECORD_STORE_EMBEDDED; } else { record->addr = g_malloc0 (size); record->store = RECORD_STORE_ALLOCATED; } /* Get ref_repo table, attach it as an environment. */ lua_pushvalue (L, -2); lua_setfenv (L, -2); /* Store newly created record into the cache. */ lua_pushlightuserdata (L, &record_cache); lua_rawget (L, LUA_REGISTRYINDEX); lua_pushlightuserdata (L, record->addr); lua_pushvalue (L, -3); lua_rawset (L, -3); lua_pop (L, 1); /* Invoke '_attach' method if present on the typetable. */ lua_getfield (L, -2, "_attach"); if (!lua_isnil (L, -1)) { lua_pushvalue (L, -3); lua_pushvalue (L, -3); lua_call (L, 2, 0); } else lua_pop (L, 1); /* Remove refrepo table from the stack. */ lua_remove (L, -2); return record->addr; } static void record_free (lua_State *L, Record *record, int narg) { GType gtype; g_assert (record->store == RECORD_STORE_ALLOCATED); lua_getfenv (L, narg); for (;;) { lua_getfield (L, -1, "_gtype"); gtype = (GType) lua_touserdata (L, -1); lua_pop (L, 1); if (G_TYPE_IS_BOXED (gtype)) { g_boxed_free (gtype, record->addr); break; } else { /* Use custom _free function. */ void (*free_func)(gpointer) = lgi_gi_load_function (L, -1, "_free"); if (free_func) { free_func (record->addr); break; } } /* Try to get parent of the type. */ lua_getfield (L, -1, "_parent"); lua_replace (L, -2); if (lua_isnil (L, -1)) { lua_getfenv (L, 1); lua_getfield (L, -1, "_name"); g_warning ("unable to free record %s, leaking it", lua_tostring (L, -1)); lua_pop (L, 2); break; } } lua_pop (L, 1); } void lgi_record_2lua (lua_State *L, gpointer addr, gboolean own, int parent) { Record *record; luaL_checkstack (L, 5, ""); /* NULL pointer results in 'nil'. */ if (addr == NULL) { lua_pop (L, 1); lua_pushnil (L); return; } /* Convert 'parent' index to an absolute one. */ if (parent == LGI_PARENT_IS_RETVAL || parent == LGI_PARENT_FORCE_POINTER) parent = 0; else lgi_makeabs (L, parent); /* Prepare access to cache. */ lua_pushlightuserdata (L, &record_cache); lua_rawget (L, LUA_REGISTRYINDEX); /* Check whether the record is already cached. */ lua_pushlightuserdata (L, addr); lua_rawget (L, -2); if (!lua_isnil (L, -1) && parent == 0) { /* Remove unneeded tables under our requested object. */ lua_replace (L, -3); lua_pop (L, 1); /* In case that we want to own the record, make sure that the ownership is properly updated. */ record = lua_touserdata (L, -1); g_assert (record->addr == addr); if (own) { if (record->store == RECORD_STORE_EXTERNAL) record->store = RECORD_STORE_ALLOCATED; else if (record->store == RECORD_STORE_ALLOCATED) record_free (L, record, -1); } return; } /* Allocate new userdata for record object, attach proper metatable. */ record = lua_newuserdata (L, G_STRUCT_OFFSET (Record, data)); lua_pushlightuserdata (L, &record_mt); lua_rawget (L, LUA_REGISTRYINDEX); lua_setmetatable (L, -2); record->addr = addr; if (parent != 0) { /* Store reference to the parent argument into parent reference cache. */ lua_pushlightuserdata (L, &parent_cache); lua_rawget (L, LUA_REGISTRYINDEX); lua_pushvalue (L, -2); lua_pushvalue (L, parent); lua_rawset (L, -3); lua_pop (L, 1); record->store = RECORD_STORE_NESTED; } else { if (!own) { /* Check, whether refrepo table specifies custom _refsink function. */ void (*refsink_func)(gpointer) = lgi_gi_load_function (L, -4, "_refsink"); if (refsink_func) { refsink_func(addr); own = TRUE; } } record->store = own ? RECORD_STORE_ALLOCATED : RECORD_STORE_EXTERNAL; } /* Assign refrepo table (on the stack when we are called) as environment for our proxy. */ lua_pushvalue (L, -4); lua_setfenv (L, -2); /* Store newly created record into the cache. */ if (parent == 0 && own) { lua_pushlightuserdata (L, addr); lua_pushvalue (L, -2); lua_rawset (L, -5); } /* Invoke '_attach' method if present on the typetable. */ lua_getfield (L, -4, "_attach"); if (!lua_isnil (L, -1)) { lua_pushvalue (L, -5); lua_pushvalue (L, -3); lua_call (L, 2, 0); } else lua_pop (L, 1); /* Clean up the stack; remove cache table from under our result, and remove also typetable which was present when we were called. */ lua_replace (L, -4); lua_pop (L, 2); } /* Checks that given argument is Record userdata and returns pointer to it. Returns NULL if narg has bad type. */ static Record * record_check (lua_State *L, int narg) { /* Check using metatable that narg is really Record type. */ Record *record = lua_touserdata (L, narg); luaL_checkstack (L, 3, ""); if (!lua_getmetatable (L, narg)) return NULL; lua_pushlightuserdata (L, &record_mt); lua_rawget (L, LUA_REGISTRYINDEX); if (!lua_equal (L, -1, -2)) record = NULL; lua_pop (L, 2); return record; } /* Throws error that narg is not of expected type. */ static int record_error (lua_State *L, int narg, const gchar *expected_name) { luaL_checkstack (L, 2, ""); lua_pushstring (L, lua_typename (L, lua_type (L, narg))); lua_pushfstring (L, "%s expected, got %s", expected_name ? expected_name : "lgi.record", lua_tostring (L, -1)); return luaL_argerror (L, narg, lua_tostring (L, -1)); } /* Similar to record_check, but throws in case of failure. */ static Record * record_get (lua_State *L, int narg) { Record *record = record_check (L, narg); if (record == NULL) record_error (L, narg, NULL); return record; } void lgi_record_2c (lua_State *L, int narg, gpointer target, gboolean by_value, gboolean own, gboolean optional, gboolean nothrow) { Record *record = NULL; /* Check for nil. */ if (!optional || !lua_isnoneornil (L, narg)) { /* Get record and check its type. */ lgi_makeabs (L, narg); luaL_checkstack (L, 4, ""); record = record_check (L, narg); if (record) { /* Check, whether type fits. Also take into account possible inheritance. */ lua_getfenv (L, narg); for (;;) { if (lua_equal (L, -1, -2)) break; /* Try to get parent of the real type. */ lua_getfield (L, -1, "_parent"); lua_replace (L, -2); if (lua_isnil (L, -1)) { record = NULL; break; } } lua_pop (L, 1); } if (!nothrow && !record) { const gchar *name = NULL; if (!lua_isnil (L, -1)) { lua_getfield (L, -1, "_name"); name = lua_tostring (L, -1); } record_error (L, narg, name); } } if (G_LIKELY (!by_value)) { *(gpointer *) target = record ? record->addr : NULL; if (record && own) { /* Caller wants to steal ownership from us. */ if (G_LIKELY (record->store == RECORD_STORE_ALLOCATED)) { /* Check, whether refrepo table specifies custom _refsink function. */ void (*refsink_func)(gpointer) = lgi_gi_load_function (L, narg, "_refsink"); if (refsink_func) refsink_func(record->addr); else record->store = RECORD_STORE_EXTERNAL; } else g_critical ("attempt to steal record ownership from unowned rec"); } } else { gsize size; lua_getfield (L, -1, "_size"); size = lua_tonumber (L, -1); lua_pop (L, 1); if (record) { /* Check, whether custom _copy is registered. */ void (*copy_func)(gpointer, gpointer) = lgi_gi_load_function (L, -1, "_copy"); if (copy_func) copy_func (record->addr, target); else memcpy (target, record->addr, size); } else /* Transferring NULL ptr, simply NULL target. */ memset (target, 0, size); } lua_pop (L, 1); } static int record_gc (lua_State *L) { Record *record = record_get (L, 1); if (record->store == RECORD_STORE_EMBEDDED || record->store == RECORD_STORE_NESTED) { /* Check whether record has registered '_uninit' function, and invoke it if yes. */ lua_getfenv (L, 1); void (*uninit)(gpointer) = lgi_gi_load_function (L, -1, "_uninit"); if (uninit != NULL) uninit (record->addr); } else if (record->store == RECORD_STORE_ALLOCATED) /* Free the owned record. */ record_free (L, record, 1); if (record->store == RECORD_STORE_NESTED) { /* Free the reference to the parent. */ lua_pushlightuserdata (L, record); lua_pushnil (L); lua_rawset (L, LUA_REGISTRYINDEX); } /* Unset the metatable / make the record unusable */ lua_pushnil (L); lua_setmetatable (L, 1); return 0; } static int record_tostring (lua_State *L) { Record *record = record_get (L, 1); lua_getfenv (L, 1); lua_getfield (L, -1, "_tostring"); if (lua_isnil (L, -1)) { lua_pop (L, 1); lua_pushfstring (L, "lgi.rec %p:", record->addr); lua_getfield (L, -2, "_name"); if (!lua_isnil (L, -1)) lua_concat (L, 2); else lua_pop (L, 1); } else { lua_pushvalue (L, 1); lua_call (L, 1, 1); } return 1; } /* Worker method for __index and __newindex implementation. */ static int record_access (lua_State *L) { gboolean getmode = lua_isnone (L, 3); /* Check that 1st arg is a record and invoke one of the forms: result = type:_access(recordinstance, name) type:_access(recordinstance, name, val) */ record_get (L, 1); lua_getfenv (L, 1); return lgi_marshal_access (L, getmode, 1, 2, 3); } /* Worker method for __len implementation. */ static int record_len (lua_State *L) { /* Check record, get its typetable and try to invoke _len method. */ record_get (L, 1); lua_getfenv (L, 1); lua_getfield (L, -1, "_len"); if (lua_isnil (L, -1)) { lua_getfield (L, -2, "_name"); return luaL_error (L, "`%s': attempt to get length", lua_tostring (L, -1)); } lua_pushvalue (L, 1); lua_call (L, 1, 1); return 1; } static const struct luaL_Reg record_meta_reg[] = { { "__gc", record_gc }, { "__tostring", record_tostring }, { "__index", record_access }, { "__newindex", record_access }, { "__len", record_len }, { NULL, NULL } }; /* Implements generic record creation. Creates new record instance, unless 'addr' argument (lightuserdata or integer) is specified, in which case wraps specified address as record. Lua prototype: recordinstance = core.record.new(repotable[, nil[, count[, alloc]]]) recordinstance = core.record.new(repotable[, addr[, own]]) own (default false) means whether Lua takes record ownership (i.e. if it tries to deallocate the record when created Lua proxy dies). */ static int record_new (lua_State *L) { if (lua_isnoneornil (L, 2)) { /* Create new record instance. */ gboolean alloc = lua_toboolean (L, 4); luaL_checktype (L, 1, LUA_TTABLE); lua_pushvalue (L, 1); lgi_record_new (L, luaL_optinteger (L, 3, 1), alloc); } else { /* Wrap record at existing address. */ gpointer addr = (lua_type (L, 2) == LUA_TLIGHTUSERDATA) ? addr = lua_touserdata (L, 2) : (gpointer) luaL_checkinteger (L, 2); gboolean own = lua_toboolean (L, 3); lua_pushvalue (L, 1); lgi_record_2lua (L, addr, own, 0); } return 1; } static const char* const query_modes[] = { "gtype", "repo", "addr", NULL }; /* Returns specific information mode about given record. Lua prototype: res = record.query(instance, mode) Supported 'mode' strings are: 'gtype': returns real gtype of this instance, nil when it is not boxed. 'repo': returns repotable of this instance. 'addr': returns address of the object. If 3rd argument is either repotable, checks, whether record conforms to the specs and if not, throws an error. */ static int record_query (lua_State *L) { Record *record; int mode = luaL_checkoption (L, 2, query_modes[0], query_modes); if (mode < 2) { record = record_check (L, 1); if (!record) return 0; lua_getfenv (L, 1); if (mode == 0) { GType gtype; if (lua_isnil (L, -1)) return 0; lua_getfield (L, -1, "_gtype"); gtype = luaL_optinteger (L, -1, G_TYPE_INVALID); lua_pushstring (L, g_type_name (gtype)); } return 1; } else { if (lua_isnoneornil (L, 3)) { record = record_check (L, 1); if (!record) return 0; lua_pushlightuserdata (L, record->addr); } else { gpointer addr; lua_pushvalue (L, 3); lgi_record_2c (L, 1, &addr, FALSE, FALSE, TRUE, FALSE); lua_pushlightuserdata (L, addr); } return 1; } } /* Implements set/get field operation. Lua prototypes: res = core.record.field(recordinstance, fieldinfo) core.record.field(recordinstance, fieldinfo, newval) */ static int record_field (lua_State *L) { gboolean getmode; Record *record; /* Check, whether we are doing set or get operation. */ getmode = lua_isnone (L, 3); /* Get record instance. */ record = record_get (L, 1); /* Call field marshalling worker. */ lua_getfenv (L, 1); return lgi_marshal_field (L, record->addr, getmode, 1, 2, 3); } /* Casts given record to another record type. Lua prototype: res = core.record.cast(recordinstance, targettypetable) */ static int record_cast (lua_State *L) { Record *record = record_get (L, 1); luaL_checktype (L, 2, LUA_TTABLE); lgi_record_2lua (L, record->addr, FALSE, 1); return 1; } /* Assumes that given record is the first of the array of records and fetches record with specified index. Negative indices are allowed, but no boundschecking is made. res = core.record.fromarray(recordinstance, index) */ static int record_fromarray (lua_State *L) { Record *record = record_get (L, 1); int index = luaL_checkinteger (L, 2); int parent = 0; gboolean own = FALSE; int size; /* Find out the size of this record. */ lua_getfenv (L, 1); lua_getfield (L, -1, "_size"); size = lua_tonumber (L, -1); if (record->store == RECORD_STORE_EMBEDDED) /* Parent is actually our embedded record. */ parent = 1; else if (record->store == RECORD_STORE_NESTED) { /* Share parent with the original record. */ lua_pushlightuserdata (L, &parent_cache); lua_rawget (L, LUA_REGISTRYINDEX); lua_pushvalue (L, 1); lua_rawget (L, -2); parent = -2; } lua_getfenv (L, 1); lgi_record_2lua (L, ((guint8 *) record->addr) + size * index, own, parent); return 1; } /* Changes ownership mode or repotable of the record. record.set(recordinstance, true|false) - 'own' if true, changing ownership to owned, otherwise to unowned. record.set(recordinstance, repotable) - 'repotable' record repotable to which this instance should be reassigned. */ static int record_set (lua_State *L) { Record *record = record_get (L, 1); if (lua_type (L, 2) == LUA_TTABLE) { /* Assign new typeinfo to the record instance. */ lua_pushvalue (L, 2); lua_setfenv (L, 1); } else { /* Change ownership type of the record. */ if (lua_toboolean (L, 2)) { if (record->store == RECORD_STORE_EXTERNAL) record->store = RECORD_STORE_ALLOCATED; } else { if (record->store == RECORD_STORE_ALLOCATED) record->store = RECORD_STORE_EXTERNAL; } } return 0; } static const struct luaL_Reg record_api_reg[] = { { "new", record_new }, { "query", record_query }, { "field", record_field }, { "cast", record_cast }, { "fromarray", record_fromarray }, { "set", record_set }, { NULL, NULL } }; /* Workaround for g_value_unset(), which complains when invoked on uninitialized GValue instance. */ static void record_value_unset (GValue *value) { if (G_IS_VALUE (value)) g_value_unset (value); } /* Similar stuff for g_value_copy(), requires target argument to be preinitialized with proper type. */ static void record_value_copy (const GValue *src, GValue *dest) { g_value_init (dest, G_VALUE_TYPE (src)); g_value_copy (src, dest); } void lgi_record_init (lua_State *L) { /* Register record metatable. */ lua_pushlightuserdata (L, &record_mt); lua_newtable (L); luaL_register (L, NULL, record_meta_reg); lua_rawset (L, LUA_REGISTRYINDEX); /* Create caches. */ lgi_cache_create (L, &record_cache, "v"); lgi_cache_create (L, &parent_cache, "k"); /* Create 'record' API table in main core API table. */ lua_newtable (L); luaL_register (L, NULL, record_api_reg); lua_pushlightuserdata (L, record_value_unset); lua_setfield (L, -2, "value_unset"); lua_pushlightuserdata (L, record_value_copy); lua_setfield (L, -2, "value_copy"); lua_setfield (L, -2, "record"); } lgi-0.9.2/lgi/record.lua000066400000000000000000000156601316674307300150530ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- lgi - handling of structs and unions -- -- Copyright (c) 2010, 2011,2013 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local rawget, assert, select, pairs, type, error, setmetatable = rawget, assert, select, pairs, type, error, setmetatable -- Require core lgi utilities, used during bootstrap. local core = require 'lgi.core' local gi = core.gi local component = require 'lgi.component' -- Implementation of record_mt, which is inherited from component -- and provides customizations for structures and unions. local record = { struct_mt = component.mt:clone('struct', { '_method', '_field' }), } -- Checks whether given argument is type of this class. function record.struct_mt:is_type_of(instance) if type(instance) == 'userdata' then local instance_type = core.record.query(instance, 'repo') while instance_type do if instance_type == self then return true end instance_type = rawget(instance_type, '_parent') end end return false end -- Resolver for records, recursively resolves also all parents. function record.struct_mt:_resolve(recursive) -- Resolve itself using inherited implementation. component.mt._resolve(self) -- Go to parent and resolve it too. if recursive and self._parent then self._parent:_resolve(recursive) end return self end function record.struct_mt:_element(instance, symbol) -- First of all, try normal inherited functionality. local element, category = component.mt._element(self, instance, symbol) if element then if category == '_field' then if type(element) == 'table' and element.ret then category = '_cbkfield' local ffi = require 'lgi.ffi' element = { name = element.name, ptrfield = { element.offset, 0, ffi.types.ptr }, callable = element } elseif gi.isinfo(element) and element.is_field then local ii = element.typeinfo.interface if ii and ii.type == 'callback' then category = '_cbkfield' local ffi = require 'lgi.ffi' element = { name = element.name, ptrfield = { element.offset, 0, ffi.types.ptr }, callable = ii } end end end return element, category end -- Special handling of '_native' attribute. if symbol == '_native' then return symbol, '_internal' elseif symbol == '_type' then return symbol, '_internal' elseif symbol == '_refsink' then return symbol, '_internal' end -- If the record has parent struct, try it there. local parent = rawget(self, '_parent') if parent then return parent:_element(instance, symbol) end end -- Add accessor for handling fields. function record.struct_mt:_access_field(instance, element, ...) -- Check whether we are marshalling subrecord local subrecord if select('#', ...) > 0 then if gi.isinfo(element) and element.is_field then local ii = element.typeinfo.interface if ii and (ii.type == 'struct' or ii.type == 'union') then subrecord = true end else if type(element) == 'table' and (element[2] == 1 or element[2] == 2) then subrecord = true end end end if subrecord then -- Write to nested structure, handle assignment to it by -- assigning separate fields. subrecord = core.record.field(instance, element) for name, value in pairs(...) do subrecord[name] = value end else -- In other cases, just access the instance using given info. return core.record.field(instance, element, ...) end end -- Add accessor for handling fields containing callbacks local guards_station = setmetatable({}, { __mode = 'k' }) function record.struct_mt:_access_cbkfield(instance, element, ...) if select('#', ...) == 0 then -- Reading callback field, get pointer and wrap it in proper -- callable, so that caller can actually call it. local addr = core.record.field(instance, element.ptrfield) return core.callable.new(element.callable, addr) else local target = ... if type(target) ~= 'userdata' then -- Create closure over Lua target, keep guard stored. local guard guard, target = core.marshal.callback(element.callable, target) local guards = guards_station[instance] if not guards then guards = {} guards_station[instance] = guards end guards[element.name] = guard end core.record.field(instance, element.ptrfield, target) end end -- Add accessor for 'internal' fields handling. function record.struct_mt:_access_internal(instance, element, ...) if select('#', ...) ~= 0 then return end if element == '_native' then return core.record.query(instance, 'addr') elseif element == '_type' then return core.record.query(instance, 'repo') end end function record.struct_mt:_index_internal(element) return nil end -- Create structure instance and initialize it with given fields. function record.struct_mt:_new(param, owns) local struct if type(param) == 'userdata' or type(param) == 'number' then -- Wrap existing pointer. struct = core.record.new(self, param, owns) else -- Check that we are allowed to create the record. if not self._size then error(("%s: not directly instantiable"):format(self._name), 2) end -- Create the structure instance. struct = core.record.new(self) -- Set values of fields. for name, value in pairs(param or {}) do struct[name] = value end end return struct end -- Loads structure information into table representing the structure function record.load(info) local record = component.create( info, info.is_struct and record.struct_mt or record.union_mt) record._size = info.size record._method = component.get_category(info.methods, core.callable.new) record._field = component.get_category(info.fields) -- Check, whether global namespace contains 'constructor' method, -- i.e. method which has the same name as our record type (except -- that type is in CamelCase, while method is -- under_score_delimited). If not found, check for 'new' method. local func = core.downcase(info.name:gsub('([%l%d])([%u])', '%1_%2')) local ctor = gi[info.namespace][func] or gi[info.namespace][func .. '_new'] if not ctor then ctor = info.methods.new end -- Check, whether ctor is valid. In order to be valid, it must -- return instance of this record. if (ctor and ctor.type == 'function' and ctor.return_type.tag =='interface' and ctor.return_type.interface == info) then ctor = core.callable.new(ctor) record._new = function(typetable, ...) return ctor(...) end end return record end -- Union metatable is the same as struct one, but has different name -- to differentiate unions. record.union_mt = record.struct_mt:clone('union') return record lgi-0.9.2/rockspec.in000066400000000000000000000014011316674307300144440ustar00rootroot00000000000000package = 'lgi' version = '%VERSION%-1' description = { summary = "Lua bindings to GObject libraries", detailed = [[ Dynamic Lua binding to any library which is introspectable using gobject-introspection. Allows using GObject-based libraries directly from Lua. ]], license = 'MIT/X11', homepage = 'http://github.com/pavouk/lgi' } supported_platforms = { 'unix' } source = { url = 'git://github.com/pavouk/lgi.git', tag = '%VERSION%' } dependencies = { 'lua >= 5.1' } build = { type = 'make', variables = { PREFIX = '$(PREFIX)', LUA_LIBDIR = '$(LIBDIR)', LUA_SHAREDIR = '$(LUADIR)', LUA_CFLAGS = '$(CFLAGS) -I$(LUA_INCDIR)', LIBFLAG = '$(LIBFLAG)', }, copy_directories = { 'docs', 'samples' } } lgi-0.9.2/samples/000077500000000000000000000000001316674307300137535ustar00rootroot00000000000000lgi-0.9.2/samples/GDbus/000077500000000000000000000000001316674307300147575ustar00rootroot00000000000000lgi-0.9.2/samples/GDbus/list-system-services.lua000066400000000000000000000064511316674307300216060ustar00rootroot00000000000000#! /usr/bin/env lua local help = [[ Usage: lua list-system-services.lua [--session|--system] List services on the system bus (default) or the session bus. ]] --[[ This example is the rewrite of list-system-services.py in Lua. Copyleft (C) 2012 Ildar Mulyukov Original copyright follows: # Copyright (C) 2004-2006 Red Hat Inc. # Copyright (C) 2005-2007 Collabora Ltd. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation # files (the "Software"), to deal in the Software without # restriction, including without limitation the rights to use, copy, # modify, merge, publish, distribute, sublicense, and/or sell copies # of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. ]] local lgi = require 'lgi' local Gio = lgi.require 'Gio' -- main(argv): local argv = {...} local factory = function() return Gio.bus_get_sync(Gio.BusType.SYSTEM) end if #argv > 1 then print(help) return -1 else if #argv == 1 then if argv[1] == '--session' then factory = function() return Gio.bus_get_sync(Gio.BusType.SESSION) end else if argv[1] ~= '--system' then print(help) return -1 end end end end -- Get a connection to the system or session bus as appropriate -- We're only using blocking calls, so don't actually need a main loop here local bus = factory() -- This could be done by calling bus.list_names(), but here's -- more or less what that means: -- Get a reference to the desktop bus' standard object, denoted -- by the path /org/freedesktop/DBus. -- dbus_object = bus.get_object('org.freedesktop.DBus', -- '/org/freedesktop/DBus') -- The object /org/freedesktop/DBus -- implements the 'org.freedesktop.DBus' interface -- dbus_iface = dbus.Interface(dbus_object, 'org.freedesktop.DBus') -- One of the member functions in the org.freedesktop.DBus interface -- is ListNames(), which provides a list of all the other services -- registered on this bus. Call it, and print the list. -- services = dbus_iface.ListNames() local var, err = bus:call_sync('org.freedesktop.DBus', '/org/freedesktop/DBus', 'org.freedesktop.DBus', 'ListNames', nil, nil, Gio.DBusCallFlags.NONE, -1) -- We know that ListNames returns '(as)' local services = var[1].value -- services.sort() - is incorrect as GVariant is immutable for i = 1, #services do print (services[i]) end lgi-0.9.2/samples/GnomeKeyring/000077500000000000000000000000001316674307300163515ustar00rootroot00000000000000lgi-0.9.2/samples/GnomeKeyring/list-keyrings-passwords.lua000066400000000000000000000016561316674307300237130ustar00rootroot00000000000000#! /usr/bin/env lua --[[ This example is the rewrite of Michael Schurter's sample in Lua. Copyleft (C) 2012 Ildar Mulyukov Original python program is [here](http://blog.schmichael.com/2008/10/30/listing-all-passwords-stored-in-gnome-keyring/) ]]-- local lgi = require 'lgi' local GnomeKeyring = lgi.require 'GnomeKeyring' -- main(argv): local _, keyring_names = GnomeKeyring.list_keyring_names_sync() for _, keyring in ipairs(keyring_names) do local _, item_ids = GnomeKeyring.list_item_ids_sync(keyring) for _, id in ipairs(item_ids) do local err, item = GnomeKeyring.item_get_info_sync(keyring, id) if err == 'IO_ERROR' then print ('[' .. keyring .. '] --locked--') break end print ('[' .. keyring .. '] ' .. item:get_display_name() .. ' = ' .. string.gsub(item:get_secret(), ".", "*") ) end if #item_ids == 0 then print ('[' .. keyring .. '] --empty--') end end lgi-0.9.2/samples/cairo.lua000077500000000000000000000142761316674307300155700ustar00rootroot00000000000000#! /usr/bin/env lua -- -- Sample cairo application, based on http://cairographics.org/samples/ -- -- Renders all samples into separate PNG images -- local math = require 'math' local lgi = require 'lgi' local cairo = lgi.cairo local dir = arg[0]:sub(1, arg[0]:find('[^%/\\]+$') - 1):gsub('[/\\]$', '') local imagename = dir .. '/gtk-demo/apple-red.png' local samples = {} function samples.arc(cr) local xc, yc = 128, 128 local radius = 100 local angle1, angle2 = math.rad(45), math.rad(180) cr.line_width = 10 cr:arc(xc, yc, radius, angle1, angle2) cr:stroke() -- draw helping lines cr:set_source_rgba(1, 0.2, 0.2, 0.6) cr.line_width = 6 cr:arc(xc, yc, 10, 0, math.rad(360)) cr:fill() cr:arc(xc, yc, radius, angle1, angle1) cr:line_to(xc, yc) cr:arc(xc, yc, radius, angle2, angle2) cr:line_to(xc, yc) cr:stroke() end function samples.arc_negative(cr) local xc, yc = 128, 128 local radius = 100 local angle1, angle2 = math.rad(45), math.rad(180) cr.line_width = 10 cr:arc_negative(xc, yc, radius, angle1, angle2) cr:stroke() -- draw helping lines cr:set_source_rgba(1, 0.2, 0.2, 0.6) cr.line_width = 6 cr:arc(xc, yc, 10, 0, math.rad(360)) cr:fill() cr:arc(xc, yc, radius, angle1, angle1) cr:line_to(xc, yc) cr:arc(xc, yc, radius, angle2, angle2) cr:line_to(xc, yc) cr:stroke() end function samples.clip(cr) cr:arc(128, 128, 76.8, 0, math.rad(360)) cr:clip() -- current path is not consumed by cairo.Context.clip() cr:new_path() cr:rectangle(0, 0, 256, 256) cr:fill() cr:set_source_rgb(0, 1, 0) cr:move_to(0, 0) cr:line_to(256, 256) cr:move_to(256, 0) cr:line_to(0, 256) cr.line_width = 10 cr:stroke() end function samples.clip_image(cr) cr:arc(128, 128, 76.8, 0, math.rad(360)) cr:clip() cr:new_path() local image = cairo.ImageSurface.create_from_png(imagename) cr:scale(256 / image.width, 256 / image.height) cr:set_source_surface(image, 0, 0) cr:paint() end function samples.dash(cr) cr:set_dash({ 50, 10, 10, 10 }, -50) cr.line_width = 10 cr:move_to(128, 25.6) cr:line_to(230.4, 230.4) cr:rel_line_to(-102.4, 0) cr:curve_to(51.2, 230.4, 51.2, 128, 128, 128) cr:stroke() end function samples.curve_to(cr) local x, y = 25.6, 128 local x1, y1 = 102.4, 230.4 local x2, y2 = 153.6, 25.6 local x3, y3 = 230.4, 128 cr:move_to(x, y) cr:curve_to(x1, y1, x2, y2, x3, y3) cr.line_width = 10 cr:stroke() cr:set_source_rgba(1, 0.2, 0.2, 0.6) cr.line_width = 6 cr:move_to(x, y) cr:line_to(x1, y1) cr:move_to(x2, y2) cr:line_to(x3, y3) cr:stroke() end function samples.fill_and_stroke(cr) cr:move_to(128, 25.6) cr:line_to(230.4, 230.4) cr:rel_line_to(-102.4, 0) cr:curve_to(51.2, 230.4, 51.2, 128, 128, 128) cr:close_path() cr:move_to(64, 25.6) cr:rel_line_to(51.2, 51.2) cr:rel_line_to(-51.2, 51.2) cr:rel_line_to(-51.2, -51.2) cr:close_path() cr.line_width = 10 cr:set_source_rgb(0, 0, 1) cr:fill_preserve() cr:set_source_rgb(0, 0, 0) cr:stroke() end function samples.fill_style(cr) cr.line_width = 6 cr:rectangle(12, 12, 232, 70) cr:new_sub_path() cr:arc(64, 64, 40, 0, math.rad(360)) cr:new_sub_path() cr:arc_negative(192, 64, 40, 0, math.rad(-360)) cr.fill_rule = 'EVEN_ODD' cr:set_source_rgb(0, 0.7, 0) cr:fill_preserve() cr:set_source_rgb(0, 0, 0) cr:stroke() cr:translate(0, 128) cr:rectangle(12, 12, 232, 70) cr:new_sub_path() cr:arc(64, 64, 40, 0, math.rad(360)) cr:new_sub_path() cr:arc_negative(192, 64, 40, 0, math.rad(-360)) cr.fill_rule = 'WINDING' cr:set_source_rgb(0, 0, 0.9) cr:fill_preserve() cr:set_source_rgb(0, 0, 0) cr:stroke() end function samples.gradient(cr) local pat = cairo.Pattern.create_linear(0, 0, 0, 256) pat:add_color_stop_rgba(1, 0, 0, 0, 1) pat:add_color_stop_rgba(0, 1, 1, 1, 1) cr:rectangle(0, 0, 256, 256) cr.source = pat cr:fill() pat = cairo.Pattern.create_radial(115.2, 102.4, 25.6, 102.4, 102.4, 128) pat:add_color_stop_rgba(0, 1, 1, 1, 1); pat:add_color_stop_rgba(1, 0, 0, 0, 0); cr.source = pat cr:arc(128, 128, 76.8, 0, math.rad(360)) cr:fill() end function samples.image(cr) local image = cairo.ImageSurface.create_from_png(imagename) cr:translate(128, 128) cr:rotate(math.rad(45)) cr:scale(256 / image.width, 256 / image.height) cr:translate(-image.width / 2, -image.height / 2) cr:set_source_surface(image, 0, 0) cr:paint() end function samples.imagepattern(cr) local image = cairo.ImageSurface.create_from_png(imagename) local pattern = cairo.Pattern.create_for_surface(image) pattern.extend = 'REPEAT' cr:translate(128, 128) cr:rotate(math.rad(45)) cr:scale(1 / math.sqrt(2), 1 / math.sqrt(2)) cr:translate(-128, -128) pattern.matrix = cairo.Matrix.create_scale(image.width / 256 * 5, image.height / 256 * 5) cr.source = pattern cr:rectangle(0, 0, 256, 256) cr:fill() end function samples.multisegment_caps(cr) cr:move_to(50, 75) cr:line_to(200, 75) cr:move_to(50, 125) cr:line_to(200, 125) cr:move_to(50, 175) cr:line_to(200, 175) cr.line_width = 30 cr.line_cap = 'ROUND' cr:stroke() end function samples.rounded_rectangle(cr) local x, y, width, height = 25.6, 25.6, 204.8, 204.8 local aspect = 1 local corner_radius = height / 10 local radius = corner_radius / aspect cr:new_sub_path() cr:arc(x + width - radius, y + radius, radius, math.rad(-90), math.rad(0)) cr:arc(x + width - radius, y + height - radius, radius, math.rad(0), math.rad(90)) cr:arc(x + radius, y + height - radius, radius, math.rad(90), math.rad(180)) cr:arc(x + radius, y + radius, radius, math.rad(180), math.rad(270)) cr:close_path() cr:set_source_rgb(0.5, 0.5, 1) cr:fill_preserve() cr:set_source_rgba(0.5, 0, 0, 0.5) cr.line_width = 10 cr:stroke() end -- Iterate through all samples and create .png files from them for name, sample in pairs(samples) do local surface = cairo.ImageSurface.create('ARGB32', 256, 256) local cr = cairo.Context.create(surface) sample(cr) surface:write_to_png('cairodemo-' .. name .. '.png') end lgi-0.9.2/samples/clutterdemo.lua000077500000000000000000000032661316674307300170170ustar00rootroot00000000000000#! /usr/bin/env lua -- -- Basic clutter demo. -- local lgi = require('lgi') local Clutter = lgi.require('Clutter', '1.0') local GObject = lgi.require('GObject', '2.0') local Gio = lgi.require('Gio', '2.0') local app = Gio.Application { application_id = 'org.lgi.samples.Clutter' } local stage = Clutter.Stage.get_default() stage.color = Clutter.Color(0, 0, 0, 255) stage.width = 512 stage.height = 512 stage.title = 'LGI Clutter Demo' local rects = {} for i = 1, 6 do rects[i] = Clutter.Rectangle { color = Clutter.Color( 256 / 6 * ((i - 1) % 6), 256 / 6 * ((i + 3) % 6), 256 / 6 * ((-i + 8) % 6), 128), width = 100, height = 100, fixed_x = 200, fixed_y = 200, anchor_x = 128, anchor_y = 64, reactive = true, on_button_press_event = function(rect) rect:raise_top() return true end, } stage:add_actor(rects[i]) rects[i]:show() end local timeline = Clutter.Timeline { duration = 60, loop = true } local rotation, rotation_delta = 0, 0.01 local scale, scale_delta = 1, 0.001 function timeline:on_new_frame(frame_num) rotation = rotation + rotation_delta scale = scale + scale_delta if scale > 2 or scale < 1 then scale_delta = -scale_delta end for i = 1, #rects do rects[i]:set_rotation(Clutter.RotateAxis.Z_AXIS, rotation * (#rects - i), 0, 0, 0) rects[i]:set_scale(scale, 3 - scale) end -- A bug in clutter? If following line is not present, stage stops -- redrawing itself after a while... stage:queue_redraw() end function stage:on_button_press_event(event) app:release() return true end function app:on_activate() self:hold() stage:show() timeline:start() end return app:run { arg[0], ... } lgi-0.9.2/samples/console.lua000077500000000000000000000261671316674307300161370ustar00rootroot00000000000000#! /usr/bin/env lua -- Lua console implemented using Gtk widgets. local lgi = require 'lgi' local Gio = lgi.Gio local Gtk = lgi.require('Gtk', '3.0') local Gdk = lgi.require('Gdk', '3.0') local Pango = lgi.Pango local GtkSource = lgi.GtkSource -- Creates new console instance. local function Console() -- Define console object actions. local actions = { execute = Gtk.Action { name = 'execute', stock_id = Gtk.STOCK_OK, is_important = true, label = "_Execute", tooltip = "Execute", }, multiline = Gtk.ToggleAction { name = 'multiline', stock_id = Gtk.STOCK_JUSTIFY_LEFT, is_important = true, label = "_Multiline", tooltip = "Switches command entry to multiline mode", }, up = Gtk.Action { name = 'up', stock_id = Gtk.STOCK_GO_UP, label = "_Previous", tooltip = "Previous command in history", sensitive = false, }, down = Gtk.Action { name = 'down', stock_id = Gtk.STOCK_GO_DOWN, label = "_Next", tooltip = "Next command in history", sensitive = false, }, clear = Gtk.Action { name = 'clear', stock_id = Gtk.STOCK_CLEAR, label = "_Clear", tooltip = "Clear output window", }, about = Gtk.Action { name = 'about', stock_id = Gtk.STOCK_ABOUT, label = "_About", tooltip = "About", }, quit = Gtk.Action { name = 'quit', stock_id = Gtk.STOCK_QUIT, label = "_Quit", tooltip = "Quit", }, } -- Define the widget tree. local widget = Gtk.Grid { orientation = Gtk.Orientation.VERTICAL, Gtk.Toolbar { Gtk.ToolButton { related_action = actions.clear }, Gtk.SeparatorToolItem {}, Gtk.ToolItem { Gtk.FontButton { id = 'font_button' } }, Gtk.SeparatorToolItem {}, Gtk.ToolButton { related_action = actions.about }, Gtk.ToolButton { related_action = actions.quit }, }, Gtk.Paned { orientation = Gtk.Orientation.VERTICAL, { Gtk.ScrolledWindow { shadow_type = Gtk.ShadowType.IN, Gtk.TextView { id = 'output', expand = true, buffer = Gtk.TextBuffer { tag_table = Gtk.TextTagTable { Gtk.TextTag { name = 'command', foreground = 'blue' }, Gtk.TextTag { name = 'log' }, Gtk.TextTag { name = 'result', style = Pango.Style.ITALIC }, Gtk.TextTag { name = 'error', weight = Pango.Weight.BOLD, foreground = 'red' }, } } } }, resize = true }, { Gtk.Grid { orientation = Gtk.Orientation.HORIZONTAL, { Gtk.ScrolledWindow { shadow_type = Gtk.ShadowType.IN, GtkSource.View { id = 'entry', hexpand = true, wrap_mode = Gtk.WrapMode.CHAR, auto_indent = true, tab_width = 4, buffer = GtkSource.Buffer { language = GtkSource.LanguageManager.get_default(): get_language('lua'), }, } }, height = 2 }, Gtk.Toolbar { orientation = Gtk.Orientation.VERTICAL, toolbar_style = Gtk.ToolbarStyle.ICONS, vexpand = true, Gtk.ToolButton { related_action = actions.execute }, Gtk.ToggleToolButton { related_action = actions.multiline }, Gtk.SeparatorToolItem {}, Gtk.ToolButton { related_action = actions.up }, Gtk.ToolButton { related_action = actions.down }, }, { Gtk.Label { id = 'indicator', label = '1:1', single_line_mode = true, justify = Gtk.Justification.RIGHT, }, left_attach = 1, top_attach = 1 }, }, resize = true } } } -- Cache important widgets in local variables local entry = widget.child.entry local output = widget.child.output local indicator = widget.child.indicator local font_button = widget.child.font_button -- When font changes, apply it to both views. font_button.on_notify['font-name'] = function(button) local desc = Pango.FontDescription.from_string(button.font_name) output:override_font(desc) entry:override_font(desc) end -- Initialize font. Get preferred font from system settings, if -- they are installed. GSettings crash the application if the -- schema is not found, so better check first if we can use it. font_button.font_name = 'Monospace' for _, schema in pairs(Gio.Settings.list_schemas()) do if schema == 'org.gnome.desktop.interface' then font_button.font_name = Gio.Settings( { schema = schema }):get_string('monospace-font-name') break end end -- Change indicator text when position in the entry changes. entry.buffer.on_notify['cursor-position'] = function(buffer) local iter = buffer:get_iter_at_mark(buffer:get_insert()) indicator.label = iter:get_line() + 1 .. ':' .. iter:get_line_offset() + 1 end local output_end_mark = output.buffer:create_mark( nil, output.buffer:get_end_iter(), false) -- Appends text to the output window, optionally with specified tag. local function append_output(text, tag) -- Append the text. local end_iter = output.buffer:get_end_iter() local offset = end_iter:get_offset() output.buffer:insert(end_iter, text, -1) end_iter = output.buffer:get_end_iter() -- Apply proper tag. tag = output.buffer.tag_table.tag[tag] if tag then output.buffer:apply_tag(tag, output.buffer:get_iter_at_offset(offset), end_iter) end -- Scroll so that the end of the buffer is visible, but only in -- case that cursor is at the very end of the view. This avoids -- autoscroll when user tries to select something in the output -- view. local cursor = output.buffer:get_iter_at_mark(output.buffer:get_insert()) if end_iter:get_offset() == cursor:get_offset() then output:scroll_mark_onscreen(output_end_mark) end end -- Define history buffer and operations with it. local history = { '', position = 1 } local function history_select(new_position) history[history.position] = entry.buffer.text history.position = new_position entry.buffer.text = history[history.position] entry.buffer:place_cursor(entry.buffer:get_end_iter()) actions.up.sensitive = history.position > 1 actions.down.sensitive = history.position < #history end -- History navigation actions. function actions.up:on_activate() history_select(history.position - 1) end function actions.down:on_activate() history_select(history.position + 1) end -- Execute Lua command from entry and log result into output. function actions.execute:on_activate() -- Get contents of the entry. local text = entry.buffer.text:gsub('^%s?(=)%s*', 'return ') if text == '' then return end -- Add command to the output view. append_output(text:gsub('\n*$', '\n', 1), 'command') -- Try to execute the command. local chunk, answer = (loadstring or load)(text, '=stdin') local tag = 'error' if not chunk then answer = answer:gsub('\n*$', '\n', 1) else (function(ok, ...) if not ok then answer = tostring(...):gsub('\n*$', '\n', 1) else -- Stringize the results. answer = {} for i = 1, select('#', ...) do answer[#answer + 1] = tostring(select(i, ...)) end answer = #answer > 0 and table.concat(answer, '\t') .. '\n' tag = 'result' end end)(pcall(chunk)) end -- Add answer to the output pane. if answer then append_output(answer, tag) end if tag == 'error' then -- Try to parse the error and find line to place the cursor local line = answer:match('^stdin:(%d+):') if line then entry.buffer:place_cursor(entry.buffer:get_iter_at_line_offset( line - 1, 0)) end else -- Store current text as the last item in the history, but -- avoid duplicating items. history[#history] = (history[#history - 1] ~= text) and text or nil -- Add new empty item to the history, point position to it. history.position = #history + 1 history[history.position] = '' -- Enable/disable history navigation actions. actions.up.sensitive = history.position > 1 actions.down.sensitive = false -- Clear contents of the entry buffer. entry.buffer.text = '' end end -- Intercept assorted keys in order to implement history -- navigation. Ideally, this should be implemented using -- Gtk.BindingKey mechanism, but lgi still lacks possibility to -- derive classes and install new signals, which is needed in order -- to implement this. local keytable = { [Gdk.KEY_Return] = actions.execute, [Gdk.KEY_Up] = actions.up, [Gdk.KEY_Down] = actions.down, } function entry:on_key_press_event(event) -- Lookup action to be activated for specified key combination. local action = keytable[event.keyval] local state = event.state local without_control = not state.CONTROL_MASK if not action or state.SHIFT_MASK or actions.multiline.active == without_control then return false end -- Ask textview whether it still wants to consume the key. if self:im_context_filter_keypress(event) then return true end -- Activate specified action. action:activate() -- Do not continue distributing the signal to the view. return true end function actions.about:on_activate() local about = Gtk.AboutDialog { program_name = 'LGI Lua Console', copyright = '(C) Copyright 2011 Pavel Holejšovský', authors = { 'Pavel Holejšovský' }, } about.license_type = Gtk.License.MIT_X11 about:run() about:hide() end function actions.clear:on_activate() output.buffer.text = '' end -- Return public object. return { widget = widget, output = output, entry = entry, actions = actions, append_output = append_output } end local old_print = print local old_write = io.write -- On activation, create and wire the whole widget hierarchy. local app = Gtk.Application { application_id = 'org.lgi.gtkconsole' } function app:on_activate() -- Create console. local console = Console() -- Create application window with console widget in it. console.window = Gtk.Window { application = app, title = "LGI Lua Console", default_width = 800, default_height = 600, console.widget } -- Make everything visible console.entry.has_focus = true console.window:show_all() -- Map quit action to window destroy. function console.actions.quit:on_activate() console.window:destroy() end -- Inject 'lgi' symbol into global namespace, for convenience. -- Also add 'console' table containing important elements of this -- console, so that it can be manipulated live. _G.lgi = lgi _G.console = console -- Override global 'print' and 'io.write' handlers, so that output -- goes to our output window (with special text style). function _G.print(...) local outs = {} for i = 1, select('#', ...) do outs[#outs + 1] = tostring(select(i, ...)) end console.append_output(table.concat(outs, '\t') .. '\n', 'log') end function _G.io.write(...) for i = 1, select('#', ...) do console.append_output(select(i, ...), 'log') end end end -- Run the whole application app:run { arg[0], ... } -- Revert to old printing routines. print = old_print io.write = old_write lgi-0.9.2/samples/giostream.lua000077500000000000000000000067431316674307300164650ustar00rootroot00000000000000#! /usr/bin/env lua -- -- Sample lgi application for Gio files, streams and usage of -- Gio.Async It also serves as very crude timing 'benchmark' showing -- overhead of assorted types operations. -- local lgi = require 'lgi' local GLib = lgi.GLib local Gio = lgi.Gio local assert = lgi.assert local app = Gio.Application { application_id = 'org.lgi.samples.giostream' } local function read_lua(file) local stream = io.open(file:get_path(), 'r') local contents = stream:read('*a') stream:close() return contents end local function write_lua(file, contents) local stream = io.open(file:get_path(), 'w') stream:write(contents) stream:close() end local function read_sync(file) local info = assert(file:query_info('standard::size', 0)) local stream = assert(file:read()) local read_buffers = {} local remaining = info:get_size() while remaining > 0 do local buffer = assert(stream:read_bytes(remaining)) table.insert(read_buffers, buffer.data) remaining = remaining - #buffer end assert(stream:close()) return table.concat(read_buffers) end local function read_async(file) local info = assert(file:async_query_info('standard::size', 'NONE')) local stream = assert(file:async_read()) local read_buffers = {} local remaining = info:get_size() while remaining > 0 do local buffer = assert(stream:async_read_bytes(remaining)) table.insert(read_buffers, buffer.data) remaining = remaining - #buffer end stream:async_close() return table.concat(read_buffers) end local function write_sync(file, contents) local stream = assert(file:create('NONE')) local pos = 1 while pos <= #contents do local wrote, err = stream:write_bytes(GLib.Bytes(contents:sub(pos))) assert(wrote >= 0, err) pos = pos + wrote end end local function write_async(file, contents) local stream = assert(file:async_create('NONE')) local pos = 1 while pos <= #contents do local wrote, err = stream:async_write_bytes(GLib.Bytes(contents:sub(pos))) assert(wrote >= 0, err) pos = pos + wrote end end local source_file = Gio.File.new_for_commandline_arg(arg[0]) local function perform(read_op, write_op, target_file) app:hold() io.write('+') local contents = read_op(source_file) target_file:delete() write_op(target_file, contents) local contents_copied = read_op(target_file) assert(contents == contents_copied) assert(target_file:delete()) io.write('.') app:release() end local count = 100 local timer = GLib.Timer() -- Perform sync standard-lua variant of the test. timer:reset() for i = 1, count do perform(read_lua, write_lua, Gio.File.new_for_path('test-lua-' .. i)) end print((("\n Lua %0.2f secs"):format(timer:elapsed()))) -- Perform sync variant of the test. timer:reset() for i = 1, count do perform(read_sync, write_sync, Gio.File.new_for_path('test-sync-' .. i)) end print((("\n sync %0.2f secs"):format(timer:elapsed()))) -- Perform async variant of the test. timer:reset() for i = 1, count do Gio.Async.call(perform)(read_async, write_async, Gio.File.new_for_path('test-async-' .. i)) end print((("\n async %0.2f secs"):format(timer:elapsed()))) -- Perform parallel variant of the test. function app:on_activate() for i = 1, count do Gio.Async.start(perform)(read_async, write_async, Gio.File.new_for_path('test-parallel-' .. i)) end end timer:reset() app:run(...) print((("\n parallel %0.2f secs"):format(timer:elapsed()))) lgi-0.9.2/samples/goocanvas.lua000066400000000000000000000016721316674307300164440ustar00rootroot00000000000000local lgi = require 'lgi' local Gtk = lgi.Gtk local Goo = lgi.GooCanvas local window = Gtk.Window { on_delete_event = Gtk.main_quit, Gtk.ScrolledWindow { shadow_type = 'IN', Goo.Canvas { id = 'canvas', width = 600, height = 450, } }, } window:set_default_size(640, 600) window:show_all() window.child.canvas:set_bounds(0, 0, 1000, 1000) local root = window.child.canvas.root_item local rect_item = Goo.CanvasRect { parent = root, x = 100, y = 100, width = 400, height = 400, line_width = 10, stroke_color = 'yellow', fill_color = 'red', radius_x = 20, radius_y = 10, on_button_press_event = function () print("rect item received button press event") end } local text_item = Goo.CanvasText { parent = root, x = 300, y = 300, width = -1, text = "Hello World", anchor = 'CENTER', font = 'Sans 24', } text_item:rotate(45, 300, 300) Gtk.main() lgi-0.9.2/samples/gstplaystream.lua000077500000000000000000000022261316674307300173620ustar00rootroot00000000000000#! /usr/bin/env lua -- -- Sample GStreamer application, port of public Vala GStreamer Audio -- Stream Example (http://live.gnome.org/Vala/GStreamerSample) -- local lgi = require 'lgi' local GLib = lgi.GLib local Gst = lgi.Gst local main_loop = GLib.MainLoop() local function bus_callback(bus, message) if message.type.ERROR then print('Error:', message:parse_error().message) main_loop:quit() elseif message.type.EOS then print 'end of stream' main_loop:quit() elseif message.type.STATE_CHANGED then local old, new, pending = message:parse_state_changed() print(string.format('state changed: %s->%s:%s', old, new, pending)) elseif message.type.TAG then message:parse_tag():foreach( function(list, tag) print(('tag: %s = %s'):format(tag, tostring(list:get(tag)))) end) end return true end local play = Gst.ElementFactory.make('playbin', 'play') play.uri = 'http://ice1.somafm.com/dronezone-128-mp3' --play.uri = 'http://www.cybertechmedia.com/samples/raycharles.mov' play.bus:add_watch(GLib.PRIORITY_DEFAULT, bus_callback) play.state = 'PLAYING' -- Run the loop. main_loop:run() play.state = 'NULL' lgi-0.9.2/samples/gstvideo.lua000077500000000000000000000062231316674307300163100ustar00rootroot00000000000000#! /usr/bin/env lua -- -- Sample GStreamer application, based on public Vala GStreamer Video -- Example (http://live.gnome.org/Vala/GStreamerSample) -- local lgi = require 'lgi' local GLib = lgi.GLib local Gtk = lgi.Gtk local GdkX11 = lgi.GdkX11 local Gst = lgi.Gst if tonumber(Gst._version) >= 1.0 then local GstVideo = lgi.GstVideo end local app = Gtk.Application { application_id = 'org.lgi.samples.gstvideo' } local window = Gtk.Window { title = "LGI Based Video Player", Gtk.Box { orientation = 'VERTICAL', Gtk.DrawingArea { id = 'video', expand = true, width = 300, height = 150, }, Gtk.ButtonBox { orientation = 'HORIZONTAL', Gtk.Button { id = 'play', use_stock = true, label = Gtk.STOCK_MEDIA_PLAY, }, Gtk.Button { id = 'stop', use_stock = true, sensitive = false, label = Gtk.STOCK_MEDIA_STOP, }, Gtk.Button { id = 'quit', use_stock = true, label = Gtk.STOCK_QUIT, }, }, } } function window.child.quit:on_clicked() window:destroy() end local pipeline = Gst.Pipeline.new('mypipeline') local src = Gst.ElementFactory.make('autovideosrc', 'videosrc') local colorspace = Gst.ElementFactory.make('videoconvert', 'colorspace') or Gst.ElementFactory.make('ffmpegcolorspace', 'colorspace') local scale = Gst.ElementFactory.make('videoscale', 'scale') local rate = Gst.ElementFactory.make('videorate', 'rate') local sink = Gst.ElementFactory.make('xvimagesink', 'sink') pipeline:add_many(src, colorspace, scale, rate, sink) src:link_many(colorspace, scale, rate, sink) function window.child.play:on_clicked() pipeline.state = 'PLAYING' end function window.child.stop:on_clicked() pipeline.state = 'PAUSED' end local function bus_callback(bus, message) if message.type.ERROR then print('Error:', message:parse_error().message) Gtk.main_quit() end if message.type.EOS then print 'end of stream' end if message.type.STATE_CHANGED then local old, new, pending = message:parse_state_changed() print(string.format('state changed: %s->%s:%s', old, new, pending)) -- Set up sensitive state on buttons according to current state. -- Note that this is forwarded to mainloop, because bus callback -- can be called in some side thread and Gtk might not like to -- be controlled from other than main thread on some platforms. GLib.idle_add(GLib.PRIORITY_DEFAULT, function() window.child.play.sensitive = (new ~= 'PLAYING') window.child.stop.sensitive = (new == 'PLAYING') return GLib.SOURCE_REMOVE end) end if message.type.TAG then message:parse_tag():foreach( function(list, tag) print(('tag: %s = %s'):format(tag, tostring(list:get(tag)))) end) end return true end function window.child.video:on_realize() -- Retarget video output to the drawingarea. sink:set_window_handle(self.window:get_xid()) end function app:on_activate() window.application = app pipeline.bus:add_watch(GLib.PRIORITY_DEFAULT, bus_callback) window:show_all() end app:run { arg[0], ... } -- Must always set the pipeline to NULL before disposing it pipeline.state = 'NULL' lgi-0.9.2/samples/gtk-demo/000077500000000000000000000000001316674307300154625ustar00rootroot00000000000000lgi-0.9.2/samples/gtk-demo/alphatest.png000066400000000000000000000636411316674307300201670ustar00rootroot00000000000000PNG  IHDR|B}>gAMA|Q cHRMo?r$m_j<Wmg,IDATxb?(`i0 F(> F Q0 F(! F Q0 F(! F Q0 F(! F Q0 F(! F Q0 F(! F Q0 9]:= h`3Z G[`Q02@G(`-G(`-G(`-G(`-G(`-G(`-G(`-G(`-G(`-G(`-G(`-G(`-G(`-G(`-G(`-G(`-G(`-G(`-G(`-G(`-G(R@,Q0 F~8.h Q0 FQ0 FQ0 FQ0 FQ0 FQ0 FQ0 FQ0 FQ0 FQ0 FQ0 FQ0 FQ0 FQ0 FQ0 FQ0 FQ0 FQ0 FQ0 FQ0 FQ0 FQ0 FQ0 FQ0 FQ0 FQ0 FQ0 FQ0 F v(C D,R @Z``d"ڭ` b?,SLWRdh$7|4CH4 UPvfdd3 gm0$riUR=7 (Y^`8FXrpIgt(( Obm(F5@ qE7B+:`eD(ʠN< $uG"=X!܋@\?`|kn  c$Fp@'FF \SxIg*ZcR@{  ȿHhr uj45cac1#iUQ4}E;D-T2?,H͋U^$&,#, ta|HwTgF(  |X!0F.W"ˀ@c)!L6 aAFF

J˙{Ie`*z .opf N{ e\0Vy02BZxh y/ƚ:$-8* t? S<a![&^(@9k(u09 u)#*F{hnhR9@+Ѐ_?D1 ,5i0N!#\?:3AP\u`<A!5^VDi&j024M j-у]'`U`a&D@]࣏`zlCȒm"v`a [tz5#R,CSϸd@z+0@Y[K¿PQ0 h h#NXvB}vQKA10i.J=G--|:nh+a $pCw"nGŶ,;؊ {>WVOv3V|VON˙ , >.[=6#7 F4PC:-{I3t`p?Rw|ZctC[H4,n',Z7xҎNdufѕ@Jh?M'7!$Q]RҢ@,7sj7踂l ѥ!L1`+`L ޾mrژ3hn!db5qNiO!=,98-'@;>Ⱦ]ٟX{ft< &]hA9]ƭ< ^(p 931q@MPy NrynX̤ Y]eyjD"4#v84`sK.#@ |X|=0g"ҁ.M74T@LjÍ "K PnX9 WdL#t <&$ԁydElYv_Ԍ!Cr;VOpW02, K U@75ő)fA갫$[eO)ӌqotg|FYHe#RflQgD3tUB!R<@X]0w .dZ+i t%aK*#}HQLzU?HsYL%5B##rL!@Fd7 TXoNxt2]A X0v ]ЍrK|2J^r z vhl4<2@W댈$= $0‰.9L`Y Np1@/Jeu,ldY*\v|Q7= {P z0(Ԟ t;l?tfEtd+Z`aT4<@:ms2*D! Hص 7@F8#Al+!U2x !U Ԇ< RCVTbP ^ π-SoB3tP*PqJ 6`퟼RBFB6Vy;l? ~C4Y9v*J1Q.Q!ɦ;v2XXAѥgN+Y :,C[J[u/~~q! QRP2IjvoMgҁD9 N'ygdI0-z7lB,z|!}i8i| ['D!sf}n{SA7q0R-l i!=Ǚz"ub e52,FsE݃p?;bv+$|o'6<ȳׄ+i v/YA0wTЍ $`de@]*H ;+ƉKrJ Djι/tXe{X;(;o>N4U4p;Z(̀m[#҆3/mbwSD|<n^08B-EDܝjq,/_i [XkB\K\!;nSD.az}&qG\&@"pu{D&bsFXfd?8%Ī|/.ˀgF"nS Ԥ`f2,c?.z$'K'~ +naJ?(HI2/IW` tձr FD9$5i@QPWx?c8d?R ^f`VΩ].n?:r!l 8'P֩٠Bco!4aSx\ئgFjdf6 Zӟp`@ HόyZ&0Tk fϻE-̜Z|'t"-eRP4QM;p{aY"` @SGRPCBÚ)ᣨЋi` -/ 3YX>*Izn=0 #p$F3,HV& (,&X=$q˯`` [D y _.sXI[Old?]ʓe{4o~_[G܈ .ݔ)杓.%%hClD=ӂƫ֫ _m (u dd~N7{sQ3->s m7S  XVQ+`Xʎ}SWDI!P"1I` hl#]OFAXà,ǘL\ @0Uևn}m4#Ĵ~BVmId~.6`n?de @޵ ,l$vXXG`D ,RQ@4ugύ?w1ŭ=פ-!*Gr]O̽Q.XN}1* D= LdjP+cHsCHr; {ܯ Ur5(Vtv$[&;䟝<2%uM;Y*1|ɲ| ]"ISf}5Pp0nTdNPA:wG"3Iji܏K*NxH o"=TXgH:| = qah3p`gg\pǪ&yi+$$jIdzjIo{{6l}t41I/Z[/ې)-,F}~m5 K)8 19P@{eYKw$I0E @"+T]q<]oV=dzeCLp67TIJʎ2xgbӟ G߅}!35tgnko0L}oRę1,qJ'^mh')! $"%;)د,s$9(N5Ԥ.bC uYemb쵴`ox %ygo0 Ca# 4l =;P2 0+q+zO wGsX'?}I ER#܎!*K^|AUY6$գ[ٜIBi /$٣{͵(ѥBh*&iӴNݲo~SƘ7 _Q&` v.o]S}kfHSC-@;J*W0C6p%A2p-@C:Jՠ+y|ڮO&jq@^ѼV|M;}/Js`2JlOЦA2m@ tH,0=s9g;' hD}';_,O)x_J)`m琿gZQS}=IS;x*!1k@'-I`|(Fh2N,& 3X-Wtp;'}e'<)[TӨ架9]jaU E2Ĭ4hWUB-.$sϬ39!}x'v@;iȴ(O4 J$Z{{eK+Gy< qa(lo +`c [!SЩ?RN{qv?r ߃݂_Q ZBՁfm"u6x-{z{gJFUL<~q"d%)mFPg-:d>f2Foy'T;UosEn@_/|e?Ø٠m<>21V$Pܳ)Ti]vӱ cۤ qFw 2$Wʝh9{4$):pVY|31unWȻ#bǫkj% lw<,fFo ax< }F7_^熧 'ڂ04<_?*ȍpC8,Z´KМ-W)|FeB%wfEVm!Aչ3Q4zhKv<ڍah4+0ð @T4,Ai#K iqPdYz/:r|X&Rz3^/Dډ䃇A*z XZhh(Nêp9'6=RXa& drѣ*Ct{=}2; {B`2UāWR I{m)lGKR\E7lulw̱* @Q&B҉uԕxS1wC[2=kTDTSapZi>Ydҧ]{#o]Wa&.FCtD=[ZI&8a1!1!>},Q KN?. u{iDF+A;=lˣ{4_C^ P1Skdx/<9L.I0=eyyN)[3 k 'PY_em#|P\ Ң~VDP0iIp+:C,Z{/4Ts1av)f PI@ap#R0N>*E1]#e:< na`bLX“ 0JDWu&9/v~?s2*H}6=/ %ɦ"3ԨGf BuDLk;. s|'ج 0pB"l`Z+)xeӢi~}vވz)@2,02q;0kg;Z fG3'SQTL{e&vIвLEcr8^a?G6zXT&BISg=0 B0%&L9 c{ @!A҄ ``ÿU@#3;2,fN ӋW:vsRHbldiHV4{( @Xa\jYd}jG<{ %=> eMg땓 m]E29.ckGaaXNfũf)9rS]1+ZA,tG&# k(L fvl+L)\PGa 6=QTdp $6LvyWP>~?w70oa /FFVٗ6*UN2@g*NNLw _H9TAKexa4%a}ߘrH)$ID"ؽZD_7=7ucI>TE"+G,7IG+{$o";?ӛ"1Ϡ\1^)S$>NԴG^b F+7]!k6(Y2.1>oGrea5$H>R*ie[bv;znW4CN@JyEo+ -2&MilǁMOW:}[{ QQOL0N DXB %h+J@Y&8tyno@ $l >b5d8>}Ԋ'2N@uO#q9بjUS6S):OF@ y raX &_E#LIL 4֬M~#k܉< b" ,H )OSMceaYCDktLmup [D):*7'B'%iBE<^E% g)؄jJ;9ֻFd; Eh}4zT%1+f CESwGo,;gFL=/钱*z(6F4.*+& !"ѣ19 @ d3 gԾD FT&״>_!Qq {F,`_ OL_ϥ_n;tfUՠ !KFJ˧jlCH2w&~yJ!.WJFJ9YLcsWrfjӗ6g LX4͹Z)cĻCn9`@ExbDxs t htgZZF蟀8Ja]RJI ).I&PI"#f#a>Wڵ\lf ye|>QUYd"U]8|a}</fp?*'zx)7>k+yV(rrKzɒ=2Ç1u½jTn:᬴sR'p*T/KYTŒgBߔ"x Mn*$TO p=9k@ @l,l? J /QKG[UmN/\5XvU|+[>r\@] $R/c]Nj QEp`fYA'ja<6+Ʈocƭ U, K( &h:#(wU%">*rKylI!iؖ\_s4LNxɍάh[&3:Ia* 0[ |ҨABDO+g{v;Ql9 IJS͆`uhJ’Նb8:}YWdoulArdWb5wWV_pVW XdґR?Lm)CZďyg$tux)OAr:dV^ g(XRҰ-R'T4ц\iǟXx%6y!diϰ18GsV3?oȻa(/:@b ŗ1`(}ֲsb=0oZE{۝|Vy03X9j_)sy~viBesG)oe =ssA#TI !s2nn9Iy^]Zd=]z$}3+2ۈ]"Ɠ+k@ `*:%[{N#{8)[rҳG*ߙvE54*=̆[[N* W?k4o0SV -12JE#j Xg,Tº<3GHӏ.3WTzatM.[Ł0lYK0xž$ ;]H.@k̇] 0 t`>$@Gk?/Ikg g>ˈ~]EpoWbpdAui,=/MlzJS tEAd=""2 19ak *!`D:lf,>T1GFc6 : 70#+ZD8d-hMO?x2!fY{BտLgZP|z |Uu#D٣RҐ*mW'Jr\<7]0 Q( 蘀)-2%Cc%t.DZ-)/A;6lrрg5'']^Q$D gcnj}*oxH;ū<,K&kg}7cd1{~,_eO(دN;-EZ$cYŞaG|Àm I&dJ1dRj!.r&$|/s|szR.؄C=0Ke6PhƠ'!w_58S$':E1st}a B;c V`h)bKq]:p"qȎ[YvjݏYf̳$=kX)MެH,4u[~%srЈU{W˦퐺0I+$,+zy{#%w{|`[3Ɛi%+nx"2i:hM @VJuGX`%i/3H4P!a >5ZSr{4tA1}5Ii,_K";OA4>Pw80 LG>03&2&NQ DBU!MrGr}1eYa\wvqd6̞9OYW{D5J/qfRK߬nRo'W])8*Y*8N.(q9on]ʳ6 kWdԥP5U>]M  z$t?R&obDO|(y#vR X׃Z@& 6Z0psH,gfu@|g@/TC#Z(/80zN!ۜ4 LaW .$?ZIO\guЂO@wM[ 1`q sn(<[V*sa);T?LئFDs/#-ȇaڅȆ5OEp#Q?\@a t T@KAܱ=ŽD.q ?-?}5c4+iA(]i@aH?lv͊4t XnC;$,p@7KUlj{;wpv4iRh;q /I'3k݌z -D@@&ʷG5(BjyhΜNʢҜa@tr~ YhEJn6 $#EUJ?8JH(^}?5k+8uЯΒk% Z"]0 sF`؅-x cp&X#m'G>IDqDnq 33+9XtA f-to.c w=7r4-j9kNz+HGCIx$=HrqEoxI=EԊKZu JiF(~Hi(j_r:SEZ񰝩 VƱw|B:z@:J[G*I^C>!{.{ӒOrFrnj1pPsޜ\OBwqSK^QB >s,L8hFf+ Tgw':/ra{M$ ,`UI5!P.)Z*o׷1ȝ'5Q`ݐ4>V\?ŭ6o mnA2kΟ]10 4 /aeGB06 x;?`gbd&>@-T0!*U(Nj_ KGMRs$M~s2C1.Eُ˘}(M=^zucKvGڛV>v|9M.O x׳_m7*˂H:-,dvkpBQ>Y&7M34"54.J0K׏O,\j1 uZ j֋4z{eJ{5ρ A-8W݋ mbo:X"53P1 % Is|9 (~~ŀ߽y$Lej/-|(?&vY%gp >+Nq<w$d2e}r& WYTaAT;xdz-6GFaT1~lv3ihaSePX͇$Њ-֨ CH̞RTzeA9@G4Pf۳e:"`b^CH:W*NB9jldcz\HIEdw=5@CǿܒpEPElUr-`%H>ne>v([@]& e-;G4k$_zkmH :, +r=%`oͨݳJO; z>Oۦ!u`02/w ]e:D0—ZGI;/`o* 7[xXEib2 s+t >y9LÏZ@Tț gv20Zѳ۫ؿ @vv|X5c@@CG/ı+\ !K5La 3ȁ$:tjQj}O9c~G6$PSa"~(SrNfG-89hPˀvяv/@ [\3S&HK؆*B@:ڗQ0E"^Sq tEZҐn@Z+DzA|h UY=뫴4o`,G3E [ zhGEY9b[4T8ȀmE$#j2C0>#@%`+Ax1@k$`%#8i+J{]r vG<_^;stQ#p-;0w1 9?vBeEgqMcjFt-ms!5-`1BVdvV@A„{$FX9 *@P8 0yąe C9!® 6L:={!\`Cag̓@̇,2'@oGfVaq;2,*X:5p" xd>G.G.8t ,GBpaeo u0Xh H1*a~6/F QpCeB IpH]?7юT};Ia״B,z-a%٥Ѝj~IV3A+j"Nć,e 2$)j [x'j˫E;nUrZ%7&o "ZXc3@0RF J"D -]`u4,@2AMbi4Ej/t4/s VbrˇP _0BfXm†l\#M4y#_,ht #J^aqD+f'- w4s ?1zآ ڮ "C`>m3=KP }HA>` aZ9Kۧ"Ō1{xǀ.h993B%jg͋H^+:a,nvA`a@q &3DhfD\ 1kVpwAkDpOYPz*,)&<9%DhCZC{#e4\ |\l?ZKdgN}+zsB@k0gr }BjT!bPF{5}"]ZY7O!N6`-Ki~ϬHFÊ'$3g=ps?lh O瑋~b)3B!ﰞa/)*-Qcహ(֤!f!Hσ Vb/!2B8lk,c abBKp %M?κSn#C @ 59aqĀ=b@ 1_⒃ #xJ;@AG S8ց_.5~Y{!lPZ־cF+hyX>ubdޑcdY 6F*a711X x*ek@eO>x'J;g@ LOܔa7E<,`eWǐPt$ڏ5+ ͺ@[9 r ́!oE=@{F@uo(>0*&Fx&X%݋SKZ)/G((j;Lȣds!M7b3Pm"r /eM@A#9-EN#PlKpfY'h7,S``a ,Z KN]h?Z@C]5ݲ3L6c3! 1^LB2Ƚc4,:r[4ᏂQ0 1(l;j ,AsC{݈qd]=HZc! H{B GSaQ? +Q@5,e%#|3bt)І5H;pQF<NoD*;#|ƙsM#h? F(&0OCo]tdVBG(Y|]SG)## 9|o)@| F Q0 FUb@: ?P;#$d1 G"vm(d2tXRaO` F'mG(TGy g@7WB7sl/@̪{W] ul?|2R}>t@`FfeȜpAl‡&`ECT#(k푶@%jzy-G(T3yO89 P c&`4b:v"GYHGQ@1n?i*;={<ƏybB)RC h? F(x q%[baxv  z ?`@EnciZK,#*Q0 FUj_tfHMa0@o_ފ>ƀ|C*W~:Š4ZQ0 3>e@) ro Ϣ^.AїOJBb2@6o1 Q?|=j)[t//Q@5lr|#DyX Zy8p3u1ݶw3v2"] ,rk= @(j`Bs`&,F l,rqpkrRc8ԀXw/@6U7W H8 끅Q4ZQ0  \!fa Asgawe@ gW*ТTi_5Zc=Q@uPvLX q^(Ȏ,hg@rw0s* 1l+Ew0Q@P~ :gBVVR5_rM7^ueʘ1gP/D\)o6X ]=8@`lYЋt |Ն/+_):,y`둚K ߡ-G(``h`h`h`h`h`h`h`pwm &uHb10 "7'~gx|9u~>}~$Rۥ]'ckxyYkZz62 6 CY#d?^fLe8\F`Ẇ[y_dA }7o/ػAa%xeli?ɱ#;\y4> Ye©Poly%(cE3'!Opnǁ6*VWKSr\8dbuܫ=9=~' v`NiHą*+çï6mv¨r^25ı38vu墜N94;,j4"d0i᷊֭C'})FQ?Cj[Gε铖r,S WBڡr U4e|iF}sn7MH_&##Us}V+_Wu5 ݼ0ֽ}GnkCਓ]UKgFkuyn$O\%#uD\'I%JpX'w97uoswkxݚ[%| {G։DUjuk8#9V;9@uG3rY:1П[sPdD NK(ڡNR֩0&EUC)U]]5nm OZWZީh)bHY [JLL([v!q3@6P!FDWO./4W8!ہM_@E9\q:e @Owjc;tSkJ=|&ՇaT\$Dy<_ܞ&2\cR[O>xԫoZ]ruq q0IYbbyEJo1 _Jӷu$fv$3I)a}7E u **s"D>7@G;hO|G$:qkG֝ӽ*HߵOؽQ`qAN&iK\dBD)W 3aHnIɘc%! ")= \]+36u׶b.AL% C+}vߋ؁jbK$ pXձY=3BծA++-tk ?8ӹN)+'=?$(a3?JnZ^Hئ$Sc!KhrQR\_'_|ӌ#-][r`_?:<ҡ݊ 37 Pshu0ܝV}(~ ӹuW2`=qjm4 6%: 1{҃@=01-4bZ2/)Wr3 L/:PKT@NwАB)&U^yj\`s WEGߐ LR[\~MzÜP^*60"[ rIO]A}F4"΋$K7)$t5(2;Mb;AU씲]C&>pyԷuqq]1Su6vNeN` 1 k'A%.: Vl/L2f6 IwjmobH t=QLc/]d 6U)Kj̣[UVӉlu4g_:_lBjKrZ# lRR tR]myא*srL?E"?}KlsE*&uqC4bLi CCNܓwo{Sdr_iH,_9atO8-=-j0A'|nٴoQ=+SoeJigg {kMx^=^ 4Sot6n3.Ԩ. 5X| K`u7C2F@1Ǘ $} cK#o褙LQQa;A܈ m'1Q4h2s$3[8cfvfM|[6#9ڵixlm?_:TLS 2d.\*bsXRt~i5/<-Mj i58$Ѷ|ؚGQj')!b=d(` ? F( F Q0 F(! F Q0 F(! F Q0 F(! F Q0 F(! F Q0 F(! F Q0 F(! F Q0 F(! V0,$iaX _..b>ik1rNEo穴F'oG~VgOx^Wduuu2]>>٪3-yib<6g-wg@AjB&60MPs|הRW>';S@A|ZSzN)@uECui .W͎ .Tt).#QQ[(.*j μ8BwBpŸb&~i[ -IsmL y }EВe#@7w[Sx|r}ۿ,-*C;sh|hnkM͗Vȧi9d| Q>qgqq Az[4 e5'Fn,F,Dj؀BS #3 S3NS[bɷ#,NwC}yGS?8nb;q 'qF>9cbgKVZKMAt1`wTSk.GzT{*/$|QIFi;Q_Wftwvunb"bۄBͼ՞^KJOret@tKb5ע׿t;t]> \E;Ap1<vsqd$8GuVҳDtU *_`k<%T :ڸ Y +. ~n@qͰ3|+FAu 50d`qf"B.xQNH$T$c[M2>pKη&3@^Rwՙ2nu qAz[{E A|yA)I)$O L{SqOeIv!vgD9tIN [91"4Ǧhd;[[.) [C̯6 *Tj@]<mZx6[ȩᦙ5@U΁ 0p(\ 8]ۻĒ,rkwk /a\aH\mЉie^5sDA4\ϜK4# MSKTOÉKi={b)4s-mv}r6ަԘ63mҏ |]ktw[jRl.u!'ˌh\0@.EGL/ gx81l> X?cS!QC pE WK R@@@BqS+8v3sk}>31^|֞9e{^tvy_Tz߇} r޽cksM?p<p+E>*ܾ֏ W]vfgvw>yv!'+_dLom]aD֭G7L#ͯ굿>tbލW-,.{>E,&+֬h /ڳ{^>߄z;?/ ;po}U.tKGŇf6)o]EZ91|}~ W--7C4 ;%씍ozۍ! g|aNgnMo,VKU!@0G|׶nڱk5pJog&;W5B8tpao;[6:׽O\ 2R wͮr|Ο=ZEB|[aղ,ꭿw~x VmB)Fhcݷ}\In}GSefc"vԪg/-3j"`wogpr>Aqow;7O ̄ & AXA۰ض8pS9pMNwnmჃja~n,v?s{v}7ُOP v4!tTUMOh[( 0 :4uȢ(;'yn9?Scbጙ!|'/͛7+Wu_իNH)D^'jFĪBĐ,h.%49 0ofH_ CSv&/ʁnzhɖtJ?ܽ^q6]^~ӣ&5)G1!@2 ĺۖzf r\饊6?7=_ޓwַ/;w'W7y'^jc wzCDqbO1H!޴a f曖cP26,dhRЀص0hO~ί{r/VOMVl^}5#ey_\3*1D7*Jx*4+Dƨ$",X^SxAJpǏvpǏ8Gݍ׮YmeM޲zr\ǎ1?L NɈq X4L Iz!u$Dw!P!X(3J⺫~w>*;o; fLթѦO{ A?MtzU:z47"qsHNDS@8ɜn5A75e<3atH` _ v:,~J;I`U"* af#Pp wD0US"Vka`9zT5^W/8Ognl?P2⠥Qs8;A T"J9%3^]vݫ )%@_jBxɘ9w'fT(0 -/lpaAAi#aEN0NKm7]E;tz`@~$Zɘ`xD^1CQƱoN@pu,@z%jS6:ઔEk)e  "`h "TFPv,GbrJ>5ZS TP'4QuGlb*-+18ʨ>L Uǣ",*V1,E, *U;Ή@&D "Ǐљ&}a5\ )"JF0U3 sh БB+isa+  J)-M:&Xɸ "y4âd-TrD@#Ȱ(q@S<ܶwŧULЛYE*J.D B#%Cdb`#j0#qZUT)h}7kCPK VfZaoI@PB iA ֩#  F Q>JdŽP1IP"XTlXE"ia"Z@<hsaЎgyhCL_) LMttzb|[{v0Fԕ*A"E" 'U(G3.ElPSZsD %;١B1rch&3Ԅ%$D9FbwƓ963{;ydb vXs<2N80qY?fza\!9zTr;O97c2~ie8]щblE˜ |\IENDB`lgi-0.9.2/samples/gtk-demo/background.jpg000066400000000000000000000533131316674307300203100ustar00rootroot00000000000000JFIFHHCreated with The GIMPC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"8!1AQ"a2q#BR3rbC1 ?T[C GzFh ʎ^h| lFT{(v4 TQ|Q ¡KHylmUw$"G4dvEL C1H2;ށY $=3Y4pVM'UDkYR#B@X㘧X"A8$rB Kиd֩1`Np˛MTDL~)oϊ%%FQ"-ó<P \(@E/zg[}ST-ɌpD* $EV$~7U ` {U-yZTꍻk :ԃD!7@`5{~(1`vֲ0ݤ}N[ͰPb"7"1 rfV $Oo~Pe5'HqTfڠXEgU27?)7fW]ۉ?mos9 hb$CYQG-3]:sRH;սXf$}YڶB[Pr$G N,T4hx>iFER*@=sb;k|*(O>(P$S aN<2UpXK&v^*=Fse{]t߭e-胯WeqM@~t{ u-ҳqmAjX&O5@W ڝyp*ʾ;hrI =C!9g(ǚ 3X:46P c h46P1:Hd{ EDnH:XE; Pn- OZxB2Ց1#>H1Hӏ(3b]0H:TɽԶ(L#ɥZùa{im-ZKPm?.ucʁRKT-[npX3 k-9\x CSnDSڣNrQ(~(`K UuQv)aseTI2)e+a{$W>৐;U ߎi+P꣰t= :{p8ULZ*k$My N_q&^MFy:N嶽Ř0PtUBuH=Ywz̈%)PzpX!\\'jt9 W\*i$ ' WOhl/K{@`d~h`@2y)K,-UB} ) c梳gdbDH)B@M>GYh -剃<^e{ZgMm-HU'Pfh P#9g; 'GH؊e]V._%DkTobvh:` cMs\`Ph99 ~ᕰ@=A[%Ǹ:*)>cE lF܍H2'4l<:+^%m9ƷLtU@QIJ) r?={. ?[p9-7Mmn:b].ۻY!wU ˃ <]zFڤ*2'B2cnRދ9b7ⅲ}H=sxs@m*-W[;1 ~p *ΉhX悳75,4$x5FPg114 1ދLS.:> Pˤvu9"t4Od|ċi,H"=1K2R{οXu2OĞ>k)!8wTPd yb`5^S[rqgg4[CkԎ2vڨ+dL FKb*=v*YXD I~@A}R΢V٠mȂb5qU@}5VcځllKx@U@-r7'g$h?5 ˘S\zxxs3UhQDe&vf1(HCl6Si@8("o[캤KxUn[Sy`椷K=5%pvhnUd hB TsZ+'.40LcwGqjȝ ,nط[>ªbuAඡ@&;P"1P@*"I<,CL~GuInkF -S)wü zfڵZv ?W0Xb}؏hIKVГ6>dOl@#Ku h?{HDH #E@*K 11As Y v,VmP fl(]*?o\[}: P;-KKA ?;eFIրImRAPǽrݫCI~5pYeK}g@ v ӕB$w!| e r1 S1v3(* #=b'KSIrNKyTnތfU"u:~( [H/RI2I317)ڏbOo`L{(i3tk J+;!Ҵq*!yـLB+cڟ#&c*& РGQ@B'Qa0=I6t&d 䊨,nط[>ªbuAඡ@&;TR*I$)}21dy~T>6:Tkx0`2|; ~nͫ]5`p>jLn &*)݈DTo 0+0#cDH6̴9P̶aT@D 9 Tx[;bN eJb`vlU2 ~Ojnbq۲31F5ŷӮ ð^ jAszFThĝh E$ { -ڴ?`q?Q 6^'{b 9T,NZqojXֹ`m< v!m+;zmw$UXȘR[{ i.9Z:nap+q#KGdx^=EИT\׮qz{Nxaڼuze6Ŷq֦ rO›*qle5\(UQu|Q޵K\r} A(83,g[;GޑWӑ8 ==:M$GP)v(@sW SG$=C1ԁ$fi_UMmVDӺgp-dDk5' A(=TqY$ַm3  U fv"qS*`Bݏ?OyZP=c&g3h LE Ŏ,s1kY N(6*< 7X sO>ɓ'A- EIv o+ DLbk7S 3LAQLWitτ! >aIڃ*=T5[ ?K([f{P)9?jֹ`m< v!m+;zmw$U)%[`5ȗ bz_{4xN] Omz hyW W]gSl[i'jj*^ w$)|Gk0Pc\P͜}QUh0']η=\@T G42; A2ɌuHKaTxqM9P܃ڭcJ )A m>P f {(^>PH>)L<9ղXAYE_VITT̘biQQ=z|,2P*P\dR[y$N⢂(Y$qvE-?޶6n\8}^womc Ѵ{y1ոu=O4ޡԨ{J81{'k*CNF 􂋬ξcܚA$F5UFpX7Hf׶\6vKq>)A m>P f {(^>PH>)L<9ղXAYE_VITT̘biQQ=z|,2P*P\dR[y$N⢂(Y$qvE-?޶6n\8}^womc Ѵ{y1\EtonsEZ*?UGlbR.w :W%D&1Q{z MHwwn8^Z l)$5 Z|S?4!-+V( \ĉ1Y%wi-ALU]axX-+r}6UOj RVia*JݲOrP% Yై5@팪b;H@2\" e(PHzWp"ډ1t8;,++\c5O4 t6B:4Urv(qU<- r>;QM'<_IϨ lg( $ncof6ӥ*e B^~hP:f5QqSݸ.3l QBTsFtœ f9WPyEk :'&OmtzyLXzS>N;iReUq/En~ہ@үQY-qL3q2k/\ A⹺wf׻q&893,H␲Rg,4UWP\P@B2b`KF_oimP/к#gAUSXˀ<8fŲI&gj Gڎ0YV  UP{2a汶 2y 5T[]?LL3$r; j95PPX2Z3q'PҰiR/{{@Ы@H9l* @TfX !e)ɢ2ϘXi@*d1YP0,>f_u$# F` yrN>ؤg%R~)U 2ۍNPlG n2,]V!)R.NG~ G:ɊtLkY_$*[v$h a=iF. @'02IcX$ӥ@ ?j5`ANyWǸ`a-)N⋹!f{M#x۞Oڛ ,dyB[9?4WrA#T#[M*n@)Ҥ:Ivs)P'FbΦ˾D.8ԽhDT)OHKAƝL|Q=*i0 vMf[4" EbqT,,xb!x[aP;%?!-ROv'1G0Y/i5nFg#G _ @'zk2V6;_@a,kĚ/̷Qw7&Z FFPAb-j4@b;^f1Vjjl^^ ~sEGӴ0R:q:WL¬€2 X7fAjı8vbnԅ!53ж"<RT Umo6cڵ7oV`|df"džMJ9 Ub9=-r֡)`)0&55<5@]D=kpl? -rZDRPpuUM#`lsk m7ʆkV@5HΪ 1<$P"LGv(Iv,< 2+9lڊ>p@WC)"KhwUIk,1se[P; KK;d 5U7L[NA^.L)Gi2$p/ AUE(ñ:2.kvRfL6&IX80$ʑW׹E۷Qvy[iQӲ-[>3?5Q4:v&&A|ǦO5y"B0[ĝE⵷gkPa1+{ ds ip]GTy>h[Li(~Y'T q588*4H>Kmq@I3?PIi8QdyDS"*+N"`ڀ$YĒq$qYR촋kwnx NLDOsA*\')BvQJ}(hB/;$@ؖhl@$a@@UBAk-솝"c(arFB{&rV:QHRn(2N5飻!Mh@qAD1}o` 4$R]Ndn>*_='@J;gm753ikǓiDYLfO}TSnB! Nк!bL;UwpKiPhj`chTA rA>&Icj}GC@#7sN s^bm#gt1O4Taʏ$UCV䝘nР(9:8Q.oLs!Ar#u%dVwOg7ٮ{6LY().ZN4&>*a 梪O4 +@Y?]#Hb&!'Fnx NLDOsA*\')BvQJ}(hB/;$@ؖhl@$a@@UBAk-솝"c(arFB{&rV:QHRn(2N5飻!Mh@qAD1}o` 4$R]Ndn>*_='@J;gm753ikǓiDYLfO}TSnB! Nк!bL;UwpKiPPKr@ :pf}ܓM)wA;m-#tm#(w{xp o\ޟfw'@*S6[ޙY\E(UTJ2Di46 {UF V ʼnf*:IHM1gGu5l"FB0Ŷ8&GbPJj&fh vbp5!*1\CR}G$tfi d4cftwQMsV)9d*Nc [cM2l4v 4ffH`'f)\Rn0=y4='rI'C+cjq'z'f6'>")[;i[eN "-y1bFTT3DA&h}CB(aR5kV`~$$p?4PỸfPT!Ʃ`" >hYsm4IOdIX ?zP$ (ղ@?+*ǙQ2i~4V.C\=D*# @1X;k"2mРF̬@S}SNŷ1ʠP"a@;a.޳΢M *b?ZկF%}dɬTO H*, !j'SL>#ǓAmEQZiK*O>!mL Dl(S:קҪɮvMr?yuO&uC7aAzt-xdž2-4 M%3ykV}DZIf 9KjGjqp8xJ)LAuv"~yn(#{e*P*O 4~Xk Z`+hjSB?P`8 ,fALd(AfcrcTH4, $觲O$I(fcj LIcj(b4H!݀ Ѭ[L5I6hP#fVw)SbۈYeP(|@LD\|zYQJ&PD±fڭjףhHvvqo'Qm D9a#q+*;z4Ŧp 9'B椑!w{رPI[lG"!tI<T$ߚY>h| lFT{(v4 TQ|Q ¡KHylmUw$"G4dvEL C1H2;ށY $=3Y4pVM'UDkYR#B@X㘧X"A8$rB Kиd֩1`Np˛MTDL~)oϊ%%FQ"-ó<P \(@E/zg[}ST-ɌpD* $EV$~7U ` {U-yZTꍻk :ԃD!7@`5{~(1`vֲ0ݤ}N[ͰPb"7"1 rfV $Oo~Pe5'HqTfڠXEgU27?)7fW]ۉ?mos9 hb$CYQG-3]:sRH;սXf$}YڶB[Pr$G N,T4hx>iFER*@=sb;k|*(O>(P$S aN<2UpXK&v^*=Fse{]t߭e-胯WeqM@~t{ u-ҳqmAjX&O5@W ڝyp*ʾ;hrI =C!9g(ǚ 3X:46P c h46P1:Hd{ EDnH:XE; Pn- OZxB2Ց1#>H1Hӏ(3b]0H:TɽԶ(L#ɥZùa{im-ZKPm?.ucʁRKT-[npX3 k-9\x CSnDSڣNrQ(~(`K UuQv)aseTI2)e+a{$W>৐;U ߎi+P꣰t= :{p8ULZ*k$My N_q&^MFy:N嶽Ř0PtUBuH=Ywz̈%)PzpX!\\'jt9 W\*i$ ' WOhl/K{@`d~h`@2y)K,-UB} ) c梳gdbDH)B@M>GYh -剃<^e{ZgMm-HU'Pfh P#9g; 'GH؊e]V._%DkTobvh:` cMs\`Ph99 ~ᕰ@=A[%Ǹ:*)>cE lF܍H2'4l<:+^%m9ƷLtU@QIJ) r?={. ?[p9-7Mmn:b].ۻY!wU ˃ <]zFڤ*2'B2cnRދ9b7ⅲ}H=sxs@m*-W[;1 ~p *ΉhX悳75,4$x5FPg114 1ދLS.:> Pˤvu9"t4Od|ċi,H"=1K2R{οXu2OĞ>k)!8wTPd yb`5^S[rqgg4[CkԎ2vڨ+dL FKb*=v*YXD I~@A}R΢V٠mȂb5qU@}5VcځllKx@U@-r7'g$h?5 ˘S\zxxs3UhQDe&vf1(HCl6Si@8("o[캤KxUn[Sy`椷K=5%pvhnUd hB TsZ+'.40LcwGqjȝ ,nط[>ªbuAඡ@&;P"1P@*"I<,CL~GuInkF -S)wü zfڵZv ?W0Xb}؏hIKVГ6>dOl@#Ku h?{HDH #E@*K 11As Y v,VmP fl(]*?o\[}: P;-KKA ?;eFIրImRAPǽrݫCI~5pYeK}g@ v ӕB$w!| e r1 S1v3(* #=b'KSIrNKyTnތfU"u:~( [H/RI2I317)ڏbOo`L{(i3tk J+;!Ҵq*!yـLB+cڟ#&c*& РGQ@B'Qa0=I6t&d 䊨,nط[>ªbuAඡ@&;TR*I$)}21dy~T>6:Tkx0`2|; ~nͫ]5`p>jLn &*)݈DTo 0+0#cDH6̴9P̶aT@D 9 Tx[;bN eJb`vlU2 ~Ojnbq۲31F5ŷӮ ð^ jAszFThĝh E$ { -ڴ?`q?Q 6^'{b 9T,NZqojXֹ`m< v!m+;zmw$UXȘR[{ i.9Z:nap+q#KGdx^=EИT\׮qz{Nxaڼuze6Ŷq֦ rO›*qle5\(UQu|Q޵K\r} A(83,g[;GޑWӑ8 ==:M$GP)v(@sW SG$=C1ԁ$fi_UMmVDӺgp-dDk5' A(=TqY$ַm3  U fv"qS*`Bݏ?OyZP=c&g3h LE Ŏ,s1kY N(6*< 7X sO>ɓ'A- EIv o+ DLbk7S 3LAQLWitτ! >aIڃ*=T5[ ?K([f{P)9?jֹ`m< v!m+;zmw$U)%[`5ȗ bz_{4xN] Omz hyW W]gSl[i'jj*^ w$)|Gk0Pc\P͜}QUh0']η=\@T G42; A2ɌuHKaTxqM9P܃ڭcJ )A m>P f {(^>PH>)L<9ղXAYE_VITT̘biQQ=z|,2P*P\dR[y$N⢂(Y$qvE-?޶6n\8}^womc Ѵ{y1ոu=O4ޡԨ{J81{'k*CNF 􂋬ξcܚA$F5UFpX7Hf׶\6vKq>)A m>P f {(^>PH>)L<9ղXAYE_VITT̘biQQ=z|,2P*P\dR[y$N⢂(Y$qvE-?޶6n\8}^womc Ѵ{y1\EtonsEZ*?UGlbR.w :W%D&1Q{z MHwwn8^Z l)$5 Z|S?4!-+V( \ĉ1Y%wi-ALU]axX-+r}6UOj RVia*JݲOrP% Yై5@팪b;H@2\" e(PHzWp"ډ1t8;,++\c5O4 t6B:4Urv(qU<- r>;QM'<_IϨ lg( $ncof6ӥ*e B^~hP:f5QqSݸ.3l QBTsFtœ f9WPyEk :'&OmtzyLXzS>N;iReUq/En~ہ@үQY-qL3q2k/\ A⹺wf׻q&893,H␲Rg,4UWP\P@B2b`KF_oimP/к#gAUSXˀ<8fŲI&gj Gڎ0YV  UP{2a汶 2y 5T[]?LL3$r; j95PPX2Z3q'PҰiR/{{@Ы@H9l* @TfX !e)ɢ2ϘXi@*d1YP0,>f_u$# F` yrN>ؤg%R~)U 2ۍNPlG n2,]V!)R.NG~ G:ɊtLkY_$*[v$h a=iF. @'02IcX$ӥ@ ?j5`ANyWǸ`a-)N⋹!f{M#x۞Oڛ ,dyB[9?4WrA#Tlgi-0.9.2/samples/gtk-demo/demo-application.lua000066400000000000000000000202071316674307300214130ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local GObject = lgi.GObject local Gtk = lgi.Gtk local GdkPixbuf = lgi.GdkPixbuf local assert = lgi.assert -- Register icon. Gtk.stock_add { Gtk.StockItem { stock_id = 'demo-gtk-logo', label = "_GTK!", } } local logo_pixbuf = GdkPixbuf.Pixbuf.new_from_file( dir:get_child('gtk-logo-rgb.gif'):get_path()) logo_pixbuf = logo_pixbuf and logo_pixbuf:add_alpha(true, 0xff, 0xff, 0xff) local icon_factory = Gtk.IconFactory() icon_factory:add_default() icon_factory:add('demo-gtk-logo', Gtk.IconSet.new_from_pixbuf(logo_pixbuf)) local window = Gtk.Window { title = "Application Window", icon_name = 'document-open', default_width = 200, default_height = 200, } local function activate_action(action) local dialog = Gtk.MessageDialog { transient_for = window, destroy_with_parent = true, message_type = 'INFO', buttons = 'CLOSE', text = ("You activated action: '%s' of type '%s'"):format( action.name, GObject.Type.name(action._type)), on_response = Gtk.Widget.destroy } dialog:show() end local message_label = Gtk.Label() message_label:show() local function change_radio_action(action) if action.active then message_label.label = (("You activated radio action: '%s' of type '%s'." .."\nCurrent value: %d"):format( action.name, GObject.Type.name(action._type), action.value)) window.child.infobar.message_type = action.value window.child.infobar:show() end end local function set_theme(action) local settings = Gtk.Settings.get_default() settings.gtk_application_prefer_dark_theme = action.active end local function about_cb() local about = Gtk.AboutDialog { program_name = "GTK+ Lgi Code Demos", version = ("Running against GTK+ %d.%d.%d, Lgi %s"):format( Gtk.get_major_version(), Gtk.get_minor_version(), Gtk.get_micro_version(), lgi._VERSION), copyright = "(C) 2012 Pavel Holejsovsky", license_type = 'MIT_X11', website = 'http://github.org/pavouk/lgi', comments = "Port of original GTK+ demo, (C) 1997-2009 The GTK+ Team", authors = { "Pavel Holejsovsky", "Peter Mattis", "Spencer Kimball", "Josh MacDonald", "and many more...", }, documenters = { "Owen Taylor", "Tony Gale", "Matthias Clasen ", "and many more...", }, logo = logo_pixbuf, title = "About GTK+ Code Demos", } about:run() about:hide() end local color = { RED = 1, GREEN = 2, BLUE = 3 } local shape = { SQUARE = 1, RECTANGLE = 2, OVAL = 3 } local action_group = Gtk.ActionGroup { name = 'AppWindowActions', Gtk.Action { name = 'FileMenu', label = "_File" }, Gtk.Action { name = 'OpenMenu', label = "_Open" }, Gtk.Action { name = 'PreferencesMenu', label = "_Preferences" }, Gtk.Action { name = 'ColorMenu', label = "_Color" }, Gtk.Action { name = 'ShapeMenu', label = "_Shape" }, Gtk.Action { name = 'HelpMenu', label = "_Help" }, { Gtk.Action { name = 'New', label = "_New", stock_id = Gtk.STOCK_NEW, tooltip = "Create a new file", on_activate = activate_action }, accelerator = 'N' }, Gtk.Action { name = 'File1', label = "File1", tooltip = "Open first file", on_activate = activate_action }, Gtk.Action { name = 'Open', label = "_Open", stock_id = Gtk.STOCK_OPEN, tooltip = "Open a file", on_activate = activate_action }, Gtk.Action { name = 'File1', label = "File1", tooltip = "Open first file", on_activate = activate_action }, { Gtk.Action { name = 'Save', label = "_Save", tooltip = "Save current file", stock_id = Gtk.STOCK_SAVE, on_activate = activate_action }, accelerator = 'S' }, Gtk.Action { name = 'SaveAs', label = "Save _As", tooltip = "Save to a file", stock_id = Gtk.STOCK_SAVE, on_activate = activate_action }, { Gtk.Action { name = 'Quit', label = "_Quit", tooltip = "Quit", stock_id = Gtk.STOCK_QUIT, on_activate = activate_action }, accelerator = 'Q' }, Gtk.Action { name = 'About', label = "_About", tooltip = "About", on_activate = about_cb }, Gtk.Action { name = 'Logo', stock_id = 'demo-gtk-logo', tooltip = "GTK+ on LGI", on_activate = activate_action }, { Gtk.ToggleAction { name = 'Bold', stock_id = Gtk.STOCK_BOLD, label = "_Bold", tooltip = "Bold", on_activate = activate_action, active = true }, accelerator = 'B' }, Gtk.ToggleAction { name = 'DarkTheme', label = "_Prefer dark theme", tooltip = "Prefer dark theme", active = false, on_activate = set_theme }, { { Gtk.RadioAction { name = 'Red', label = "_Red", tooltip = "Blood", value = color.RED, }, accelerator = 'R' }, { Gtk.RadioAction { name = 'Green', label = "_Green", tooltip = "Grass", value = color.GREEN, }, accelerator = 'G' }, { Gtk.RadioAction { name = 'Blue', label = "_Blue", tooltip = "Sky", value = color.BLUE, }, accelerator = 'B' }, on_change = change_radio_action, }, { { Gtk.RadioAction { name = 'Square', label = "_Square", tooltip = "Square", value = shape.SQUARE, }, accelerator = 'S' }, { Gtk.RadioAction { name = 'Rectangle', label = "_Rectangle", tooltip = "Rectangle", value = shape.RECTANGLE, }, accelerator = 'R' }, { Gtk.RadioAction { name = 'Oval', label = "_Oval", tooltip = "Oval", value = shape.OVAL, }, accelerator = 'O' }, on_change = change_radio_action, }, } local merge = Gtk.UIManager {} merge:insert_action_group (action_group, 0) window:add_accel_group(merge:get_accel_group()) assert(merge:add_ui_from_string([[

]], -1)) local menubar = merge:get_widget('/MenuBar') menubar.halign = 'FILL' local toolbar = merge:get_widget('/ToolBar') toolbar.halign = 'FILL' window.child = Gtk.Grid { orientation = 'VERTICAL', menubar, toolbar, Gtk.InfoBar { id = 'infobar', no_show_all = true, halign = 'FILL', on_response = Gtk.Widget.hide }, Gtk.ScrolledWindow { shadow_type = 'IN', halign = 'FILL', valign = 'FILL', expand = true, Gtk.TextView { id = 'view', }, }, Gtk.Statusbar { id = 'statusbar', halign = 'FILL', }, } local buffer = window.child.view.buffer local statusbar = window.child.statusbar -- Updates statusbar according to the buffer of the view local function update_statusbar() statusbar:pop(0) local iter = buffer:get_iter_at_mark(buffer:get_insert()) local msg = statusbar:push( 0, ("Cursor at row %d column %d - %d chars in document"):format( iter:get_line(), iter:get_line_offset(), buffer:get_char_count())) end function buffer:on_changed() update_statusbar() end function buffer:on_mark_set() update_statusbar() end -- Perform initial statusbar update. update_statusbar() -- Add infobar area. local info_area = window.child.infobar:get_content_area() info_area:add(message_label) window.child.infobar:add_button(Gtk.STOCK_OK, Gtk.ResponseType.OK) window:show_all() window.child.view:grab_focus() return window end, "Application main window", [[Demonstrates a typical application window with menubar, toolbar, statusbar.]] lgi-0.9.2/samples/gtk-demo/demo-assistant.lua000066400000000000000000000042471316674307300211270ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local GLib = lgi.GLib local Gtk = lgi.Gtk local assistant = Gtk.Assistant { default_height = 300, { title = "Page 1", type = 'INTRO', Gtk.Grid { id = 'page1', orientation = 'HORIZONTAL', Gtk.Label { label = "You must fill out this entry to continue:" }, Gtk.Entry { vexpand = true, id = 'entry', activates_default = true, }, }, }, { title = "Page 2", complete = true, Gtk.Grid { orientation = 'VERTICAL', Gtk.CheckButton { label = "This is optional data, you may continue " .. "even if you do not check this" } }, }, { title = "Confirmation", type = 'CONFIRM', complete = true, Gtk.Label { label = "This is a confirmation page, press 'Apply' " .. "to apply changes" }, }, { title = "Applying changes", type = 'PROGRESS', Gtk.ProgressBar { id = 'progressbar', halign = 'CENTER', valign = 'CENTER' }, }, } function assistant.child.entry:on_changed() assistant.property.page1.complete = (self.text ~= '') end function assistant:on_cancel() self:destroy() end function assistant:on_close() self:destroy() end function assistant:on_prepare() self.title = ("Sample assistant (%d of %d)"):format( self:get_current_page() + 1, self:get_n_pages()) if self:get_current_page() == 3 then -- The changes are permanent and cannot be revisited. assistant:commit() end end local progressbar = assistant.child.progressbar function assistant:on_apply() GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, function() -- Simulate work. local fraction = progressbar.fraction + 0.05 if fraction < 1 then progressbar.fraction = fraction return true else assistant:destroy() return false end end) end assistant.child.page1:show_all() --assistant:set_screen(parent:get_screen()) assistant:show_all() return assistant end, "Assistant", table.concat { "Demonstrates a sample multi-step assistant.\n", "Assistants are used to divide an operation into several simpler ", "sequential steps, and to guide the user through these steps." } lgi-0.9.2/samples/gtk-demo/demo-builder.lua000066400000000000000000000011461316674307300205370ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local Gtk = lgi.Gtk local assert = lgi.assert local builder = Gtk.Builder() assert(builder:add_from_file(dir:get_child('demo.ui'):get_path())) local ui = builder.objects -- Get top-level window from the builder. local window = ui.window1 -- Connect 'Quit' and 'About' actions. function ui.Quit:on_activate() window:destroy() end function ui.About:on_activate() ui.aboutdialog1:run() ui.aboutdialog1:hide() end window:show_all() return window end, "Builder", table.concat { [[Demonstrates an interface loaded from a XML description.]], } lgi-0.9.2/samples/gtk-demo/demo-buttonboxes.lua000066400000000000000000000026321316674307300214660ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local Gtk = lgi.Gtk local function create_bbox(orientation, title, spacing, layout) return Gtk.Frame { label = title, Gtk.ButtonBox { orientation = orientation, border_width = 5, layout_style = layout, spacing = spacing, Gtk.Button { use_stock = true, label = Gtk.STOCK_OK }, Gtk.Button { use_stock = true, label = Gtk.STOCK_CANCEL }, Gtk.Button { use_stock = true, label = Gtk.STOCK_HELP } }, } end local window = Gtk.Window { title = "Button Boxes", border_width = 10, Gtk.Box { orientation = 'VERTICAL', Gtk.Frame { label = "Horizontal Button Boxes", Gtk.Box { orientation = 'VERTICAL', border_width = 10, create_bbox('HORIZONTAL', "Spread", 40, 'SPREAD'), create_bbox('HORIZONTAL', "Edge", 40, 'EDGE'), create_bbox('HORIZONTAL', "Start", 40, 'START'), create_bbox('HORIZONTAL', "End", 40, 'END') }, }, Gtk.Frame { label = "Vertical Button Boxes", Gtk.Box { orientation = 'HORIZONTAL', border_width = 10, create_bbox('VERTICAL', "Spread", 30, 'SPREAD'), create_bbox('VERTICAL', "Edge", 30, 'EDGE'), create_bbox('VERTICAL', "Start", 30, 'START'), create_bbox('VERTICAL', "End", 30, 'END') }, }, } } window:show_all() return window end, "Button Boxes", "The Button Box widgets are used to arrange buttons with padding." lgi-0.9.2/samples/gtk-demo/demo-changedisplay.lua000066400000000000000000000201651316674307300217260ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local GLib = lgi.GLib local GObject = lgi.GObject local Gtk = lgi.Gtk local Gdk = lgi.Gdk -- Create main dialog window local window = Gtk.Dialog { title = "Change Screen or Display", transient_for = parent, default_width = 300, default_height = 400, buttons = { { Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE }, { "Change", Gtk.ResponseType.OK }, }, } -- Define column names for 'display' model. local DisplayColumn = { NAME = 1, DISPLAY = 2, } -- Define column numbers for 'screens' model. local ScreenColumn = { NUMBER = 1, SCREEN = 2, } -- Add content of the main dialog. window:get_content_area():add( Gtk.Box { orientation = 'VERTICAL', spacing = 5, border_width = 8, Gtk.Frame { label = "Display", Gtk.Box { orientation = 'HORIZONTAL', spacing = 8, border_width = 8, Gtk.ScrolledWindow { shadow_type = 'IN', hscrollbar_policy = 'NEVER', expand = true, Gtk.TreeView { id = 'displays', headers_visible = false, model = Gtk.ListStore.new { [DisplayColumn.NAME] = GObject.Type.STRING, [DisplayColumn.DISPLAY] = Gdk.Display, }, Gtk.TreeViewColumn { title = "Name", { Gtk.CellRendererText(), { text = DisplayColumn.NAME } }, }, }, }, Gtk.Box { id = 'displays_box', orientation = 'VERTICAL', spacing = 5, Gtk.Button { id = 'display_open', label = "_Open...", use_underline = true, }, Gtk.Button { id = 'display_close', label = "Close", use_underline = true, }, }, }, }, Gtk.Frame { label = "Screen", Gtk.Box { orientation = 'HORIZONTAL', spacing = 8, border_width = 8, Gtk.ScrolledWindow { shadow_type = 'IN', hscrollbar_policy = 'NEVER', expand = true, Gtk.TreeView { id = 'screens', headers_visible = false, model = Gtk.ListStore.new { [ScreenColumn.NUMBER] = GObject.Type.INT, [ScreenColumn.SCREEN] = Gdk.Screen, }, Gtk.TreeViewColumn { title = "Number", { Gtk.CellRendererText(), { text = ScreenColumn.NUMBER } }, }, }, }, Gtk.Box { id = 'screens_box', orientation = 'VERTICAL', spacing = 5, }, }, }, }) local current_display, current_screen local display_selection = window.child.displays:get_selection() local screen_selection = window.child.screens:get_selection() display_selection.mode = 'BROWSE' function display_selection:on_changed() local model, iter = self:get_selected() if model then current_display = model[iter][DisplayColumn.DISPLAY] local screens = window.child.screens.model screens:clear() for i = 0, current_display:get_n_screens() - 1 do local iter = screens:append { [ScreenColumn.NUMBER] = i, [ScreenColumn.SCREEN] = current_display:get_screen(i), } if i == 0 then screen_selection:select_iter(iter) end end else current_display = nil end end screen_selection.mode = 'BROWSE' function screen_selection:on_changed() local model, iter = self:get_selected() current_screen = model and model[iter][ScreenColumn.SCREEN] end window.child.display_open:get_child().halign = 'START' window.child.display_close:get_child().halign = 'START' local size_group = Gtk.SizeGroup() size_group:add_widget(window.child.displays_box) size_group:add_widget(window.child.screens_box) function window.child.display_open:on_clicked() local dialog = Gtk.Dialog { title = "Open Display", transient_for = window, modal = true, buttons = { { Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL }, { Gtk.STOCK_OK, Gtk.ResponseType.OK }, }, } dialog:set_default_response(Gtk.ResponseType.OK) local label = Gtk.Label { label = "Please enter the name of\nthe new display\n", } local entry = Gtk.Entry { activates_default = true } local content = dialog:get_content_area() content:add(label) content:add(entry) entry.has_focus = true dialog:show_all() while true do if dialog:run() ~= Gtk.ResponseType.OK then break end local name = entry.text if name ~= '' then local display = Gdk.Display.open(name) if display then break end label.label = ("Can't open display :\n\t%s\n" .. "please try another one\n"):format(name) end end dialog:destroy() end local function change_display() local screen = window:get_screen() local display = screen:get_display() local popup = Gtk.Window { type = 'POPUP', window_position = 'CENTER', modal = true, transient_for = window, Gtk.Frame { shadow_type = 'OUT', Gtk.Label { margin = 10, label = "Please select the toplevel\n" .. "to move to the new screen", }, }, } popup:set_screen(screen) popup:show_all() local cursor = Gdk.Cursor.new_for_display(display, 'CROSSHAIR') local toplevel local device = Gtk.get_current_event_device() local grab_status = device:grab( popup.window, 'APPLICATION', false, 'BUTTON_RELEASE_MASK', cursor, Gdk.CURRENT_TIME) if grab_status == 'SUCCESS' then -- Process events until user clicks. local clicked function popup:on_button_release_event() clicked = true return true end while not clicked do GLib.MainContext.default():iteration(true) end -- Find toplevel at current pointer position. local gdk_window = device:get_window_at_position() local widget = gdk_window and gdk_window:get_widget() toplevel = widget and widget:get_toplevel() if toplevel == popup then toplevel = nil end end popup:destroy() -- Make sure that grab is really broken. Gdk.flush() -- Switch target window to selected screen. if toplevel then toplevel:set_screen(current_screen) else display:beep() end end function window:on_response(response_id) if response_id == Gtk.ResponseType.OK then change_display() else self:destroy() end end -- Adds new display into the list and hooks it do that it is -- automatically removed when the display is closed. local function add_display(display) local store = window.child.displays.model local iter = store:append { [DisplayColumn.NAME] = display:get_name(), [DisplayColumn.DISPLAY] = display, } local path = store:get_path(iter) local handler_id = display.on_closed:connect( function(is_error) store:remove(store:get_iter(path)) end) function window:on_destroy() GObject.signal_handler_disconnect(display, handler_id) end end -- Populate initial list of displays. local display_manager = Gdk.DisplayManager.get() for _, display in ipairs(display_manager:list_displays()) do add_display(display) end local handler_id = display_manager.on_display_opened:connect( function(display) add_display(display) end) function window:on_destroy() GObject.signal_handler_disconnect(display_manager, handler_id) end window:show_all() return window end, "Change Display", table.concat { [[Demonstrates migrating a window between different displays ]], [[and screens. A display is a mouse and keyboard with some number ]], [[of associated monitors. A screen is a set of monitors grouped ]], [[into a single physical work area. The neat thing about having ]], [[multiple displays is that they can be on a completely separate ]], [[computers, as long as there is a network connection to ]], [[the computer where the application is running. ]], [[Only some of the windowing systems where GTK+ runs have the concept ]], [[of multiple displays and screens. (The X Window System is the main ]], [[example.) Other windowing systems can only handle one keyboard ]], [[and mouse, and combine all monitors into a single screen. ]], [[This is a moderately complex example, and demonstrates: - Tracking the currently open displays and screens - Changing the screen for a window - Letting the user choose a window by clicking on it - Using Gtk.ListStore and Gtk.TreeView - Using Gtk.Dialog]] } lgi-0.9.2/samples/gtk-demo/demo-clipboard.lua000066400000000000000000000106171316674307300210530ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local Gtk = lgi.Gtk local Gdk = lgi.Gdk local log = lgi.log.domain 'gtk-demo' local function get_image_pixbuf(image) if image.storage_type == 'PIXBUF' then return image.pixbuf elseif image.storage_type == 'STOCK' then return image:render_icon_pixbuf(image.stock, image.icon_size) else log.warning(('Image storage type "%s" not handled'):format( image.storage_type)) end end local function drag_begin(ebox, context) Gtk.drag_set_icon_pixbuf(context, get_image_pixbuf(ebox:get_child()), -2, -2) end local function drag_data_get(ebox, context, selection_data) selection_data:set_pixbuf(get_image_pixbuf(ebox:get_child())) end local function drag_data_received(ebox, context, x, y, selection_data) if selection_data:get_length() > 0 then ebox:get_child().pixbuf = selection_data:get_pixbuf() end end local function button_press(ebox, event_button) if event_button.button ~= 3 then return false end local clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) local image = ebox:get_child() local menu = Gtk.Menu { Gtk.ImageMenuItem { use_stock = true, label = Gtk.STOCK_COPY, on_activate = function() local pixbuf = get_image_pixbuf(image) clipboard:set_image(pixbuf) end }, Gtk.ImageMenuItem { use_stock = true, label = Gtk.STOCK_PASTE, on_activate = function() local pixbuf = clipboard:wait_for_image() if pixbuf then image.pixbuf = pixbuf end end }, } menu:show_all() menu:popup(nil, nil, nil, event_button.button, event_button.time) end local window = Gtk.Window { title = "Clipboard demo", Gtk.Box { orientation = 'VERTICAL', border_width = 8, Gtk.Label { label = "\"Copy\" will copy the text\n" .. "in the entry to the clipboard" }, Gtk.Box { orientation = 'HORIZONTAL', spacing = 4, border_width = 8, Gtk.Entry { id = 'copy_entry', hexpand = true }, Gtk.Button { id = 'copy_button', use_stock = true, label = Gtk.STOCK_COPY }, }, Gtk.Label { label = "\"Paste\" will paste the text from " .. "the clipboard to the entry" }, Gtk.Box { orientation = 'HORIZONTAL', spacing = 4, border_width = 8, Gtk.Entry { id = 'paste_entry', hexpand = true }, Gtk.Button { id = 'paste_button', use_stock = true, label = Gtk.STOCK_PASTE }, }, Gtk.Label { label = "Images can be transferred via the clipboard, too" }, Gtk.Box { orientation = 'HORIZONTAL', spacing = 4, border_width = 8, Gtk.EventBox { id = 'ebox1', on_drag_begin = drag_begin, on_drag_data_get = drag_data_get, on_drag_data_received = drag_data_received, on_button_press_event = button_press, Gtk.Image { stock = Gtk.STOCK_DIALOG_WARNING, icon_size = Gtk.IconSize.BUTTON } }, Gtk.EventBox { id = 'ebox2', on_drag_begin = drag_begin, on_drag_data_get = drag_data_get, on_drag_data_received = drag_data_received, on_button_press_event = button_press, Gtk.Image { stock = Gtk.STOCK_STOP, icon_size = Gtk.IconSize.BUTTON } }, }, } } function window.child.copy_button:on_clicked() local entry = window.child.copy_entry local clipboard = entry:get_clipboard(Gdk.SELECTION_CLIPBOARD) clipboard:set_text(entry.text, -1) end function window.child.paste_button:on_clicked() local entry = window.child.paste_entry local clipboard = entry:get_clipboard(Gdk.SELECTION_CLIPBOARD) clipboard:request_text(function(clipboard, text) if text then entry.text = text end end) end -- Make eboxes drag sources and destinations. for _, ebox in ipairs { window.child.ebox1, window.child.ebox2 } do ebox:drag_source_set('BUTTON1_MASK', nil, 'COPY') ebox:drag_source_add_image_targets() ebox:drag_dest_set('ALL', nil, 'COPY') ebox:drag_dest_add_image_targets(ebox) end -- Tell the clipboard manager to make the data persistent. Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD):set_can_store(nil) window:show_all() return window end, "Clipboard", table.concat { "Gtk.Clipboard is used for clipboard handling. This demo shows how to ", "copy and paste text to and from the clipboard.\n", "It also shows how to transfer images via the clipboard or via ", "drag-and-drop, and how to make clipboard contents persist after ", "the application exits. Clipboard persistence requires a clipboard ", "manager to run." } lgi-0.9.2/samples/gtk-demo/demo-colorselector.lua000066400000000000000000000036371316674307300217770ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local Gtk = lgi.Gtk local Gdk = lgi.Gdk local cairo = lgi.cairo local window = Gtk.Window { title = "Color Selection", border_width = 8, Gtk.Box { orientation = 'VERTICAL', spacing = 8, border_width = 8, Gtk.Frame { shadow_type = 'IN', Gtk.DrawingArea { id = 'area', expand = true, width = 200, height = 200, }, }, Gtk.Button { id = 'change', label = "_Change the above color", use_underline = true, halign = 'END', valign = 'CENTER', }, }, } local area = window.child.area area:override_background_color( 0, Gdk.RGBA { red = 0, green = 0, blue = 1, alpha = 1 }) function area:on_draw(cr) cr:set_source_rgba(self.style_context:get_background_color('NORMAL')) cr:paint() return true end function window.child.change:on_clicked() local dialog if Gtk.ColorChooserDialog then dialog = Gtk.ColorChooserDialog { title = "Changing color", transient_for = window, rgba = self.style_context:get_background_color('NORMAL') } function dialog:on_response(response_id) if response_id == Gtk.ResponseType.OK then area:override_background_color(0, self.rgba) end dialog:hide() end else dialog = Gtk.ColorSelectionDialog { title = "Changing color", transient_for = window, } dialog.color_selection.current_rgba = self.style_context:get_background_color('NORMAL') function dialog:on_response(response_id) if response_id == Gtk.ResponseType.OK then area:override_background_color( 0, self.color_selection.current_rgba) end dialog:hide() end end dialog:show_all() end window:show_all() return window end, "Color Selector", table.concat { [[Gtk.ColorSelection lets the user choose a color. ]], [[Gtk.ColorSelectionDialog is a prebuilt dialog containing ]], [[a Gtk.ColorSelection.]], } lgi-0.9.2/samples/gtk-demo/demo-comboboxes.lua000066400000000000000000000121011316674307300212420ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local GObject = lgi.GObject local Gtk = lgi.Gtk local Gdk = lgi.Gdk local GdkPixbuf = lgi.GdkPixbuf local ComboColumn = { PIXBUF = 1, TEXT = 2, } local function create_stock_icon_store() local cellview = Gtk.CellView {} local store = Gtk.ListStore.new { [ComboColumn.PIXBUF] = GdkPixbuf.Pixbuf, [ComboColumn.TEXT] = GObject.Type.STRING } for _, stock in ipairs { Gtk.STOCK_DIALOG_WARNING, Gtk.STOCK_STOP, Gtk.STOCK_NEW, Gtk.STOCK_CLEAR, '', Gtk.STOCK_OPEN, } do if stock ~= '' then store:append { [ComboColumn.PIXBUF] = cellview:render_icon_pixbuf( stock, Gtk.IconSize.BUTTON), [ComboColumn.TEXT] = Gtk.stock_lookup(stock).label:gsub('_', '') } else store:append { [ComboColumn.TEXT] = 'separator' } end end return store end local function set_sensitive(layout, cell, model, iter) local indices = model:get_path(iter):get_indices() cell.sensitive = (indices[1] ~= 1) end local function create_capital_store() local store = Gtk.TreeStore.new { GObject.Type.STRING } for _, group in ipairs { { name = "A - B", "Albany", "Annapolis", "Atlanta", "Augusta", "Austin", "Baton Rouge", "Bismarck", "Boise", "Boston" }, { name = "C - D", "Carson City", "Charleston", "Cheyenne", "Columbia", "Columbus", "Concord", "Denver", "Des Moines", "Dover" }, { name = "E - J", "Frankfort", "Harrisburg", "Hartford", "Helena", "Honolulu", "Indianapolis", "Jackson", "Jefferson City", "Juneau" }, { name = "K - O", "Lansing", "Lincoln", "Little Rock", "Madison", "Montgomery", "Montpelier", "Nashville", "Oklahoma City", "Olympia" }, { name = "P - S", "Phoenix", "Pierre", "Providence", "Raleigh", "Richmond", "Sacramento", "Salem", "Salt Lake City", "Santa Fe", "Springfield", "St. Paul" }, { name = "T - Z", "Tallahassee", "Topeka", "Trenton" }, } do local gi = store:append(nil, { [1] = group.name }) for _, city in ipairs(group) do store:append(gi, { [1] = city }) end end return store end local function is_capital_sensitive(layout, cell, model, iter) cell.sensitive = not model:iter_has_child(iter) end local window = Gtk.Window { title = "Combo boxes", border_width = 10, Gtk.Box { orientation = 'VERTICAL', spacing = 10, Gtk.Frame { label = "Some stock icons", Gtk.Box { orientation = 'VERTICAL', border_width = 5, Gtk.ComboBox { id = 'icons', model = create_stock_icon_store(), active = 0, cells = { { Gtk.CellRendererPixbuf(), { pixbuf = ComboColumn.PIXBUF }, align = 'start', data_func = set_sensitive, }, { Gtk.CellRendererText(), { text = ComboColumn.TEXT }, align = 'start', data_func = set_sensitive, }, }, }, }, }, Gtk.Frame { label = "Where are we?", Gtk.Box { orientation = 'VERTICAL', border_width = 5, Gtk.ComboBox { id = 'capitals', model = create_capital_store(), cells = { { Gtk.CellRendererText(), { text = 1 }, align = 'start', data_func = is_capital_sensitive, } } }, }, }, Gtk.Frame { label = "Editable", Gtk.Box { orientation = 'VERTICAL', border_width = 5, Gtk.ComboBoxText { id = 'entry', has_entry = true, entry_text_column = 0, }, }, }, Gtk.Frame { label = "String IDs", Gtk.Box { orientation = 'VERTICAL', border_width = 5, Gtk.ComboBoxText { id = 'stringids', entry_text_column = 0, id_column = 1, }, Gtk.Entry { id = 'ids_entry' }, }, }, }, } window.child.icons:set_row_separator_func( function(model, iter) return model:get_path(iter):get_indices()[1] == 4 end) local capitals = window.child.capitals capitals:set_active_iter(capitals.model:get_iter( Gtk.TreePath.new_from_string('0:8'))) for _, label in ipairs { "One", "Two", "2½", "Three" } do window.child.entry:append_text(label) end local entry = window.child.entry:get_child() local allowed = { ["One"] = true, ["Two"] = true, ["2½"] = true, ["Three"] = true } function entry:on_changed() local color if not self.text:match('^[0-9]*$') and not allowed[self.text] then color = Gdk.RGBA { red = 1, green = 0.9, blue = 0.9, alpha = 1 } end self:override_color(0, color) end for id, text in pairs { never = "Not visible", when_active = "Visible when active", always = "Always visible", } do window.child.stringids:append(id, text) end window.child.stringids:bind_property( 'active-id', window.child.ids_entry, 'text', 'BIDIRECTIONAL') window:show_all() return window end, "Combo boxes", table.concat { "The ComboBox widget allows to select one option out of a list. ", "The ComboBoxEntry additionally allows the user to enter a value ", "that is not in the list of options.\n", "How the options are displayed is controlled by cell renderers.", } lgi-0.9.2/samples/gtk-demo/demo-dialogs.lua000066400000000000000000000065731316674307300205440ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local GObject = lgi.GObject local Gtk = lgi.Gtk local Gdk = lgi.Gdk local GdkPixbuf = lgi.GdkPixbuf local window = Gtk.Window { title = "Dialogs", border_width = 8, Gtk.Frame { label = "Dialogs", Gtk.Box { orientation = 'VERTICAL', border_width = 8, spacing = 8, Gtk.Box { orientation = 'HORIZONTAL', spacing = 8, Gtk.Button { id = 'message_button', label = "_Message Dialog", use_underline = true, }, }, Gtk.Separator { orientation = 'HORIZONTAL' }, Gtk.Box { orientation = 'HORIZONTAL', spacing = 8, Gtk.Box { orientation = 'VERTICAL', Gtk.Button { id = 'interactive_button', label = "_Interactive Dialog", use_underline = true, }, }, Gtk.Grid { row_spacing = 4, column_spacing = 4, { left_attach = 0, top_attach = 0, Gtk.Label { label = "_Entry 1", use_underline = true, }, }, { left_attach = 1, top_attach = 0, Gtk.Entry { id = 'entry1', }, }, { left_attach = 0, top_attach = 1, Gtk.Label { label = "E_ntry 2", use_underline = true, }, }, { left_attach = 1, top_attach = 1, Gtk.Entry { id = 'entry2', }, }, }, }, }, }, } local popup_count = 1 function window.child.message_button:on_clicked() local dialog = Gtk.MessageDialog { transient_for = window, modal = true, destroy_with_parent = true, message_type = 'INFO', buttons = 'OK', text = "This message box has been popped up the following\n" .. "number of times:", secondary_text = ('%d'):format(popup_count), } dialog:run() dialog:destroy() popup_count = popup_count + 1 end function window.child.interactive_button:on_clicked() local dialog = Gtk.Dialog { title = "Interactive Dialog", transient_for = window, modal = true, destroy_with_parent = true, buttons = { { Gtk.STOCK_OK, Gtk.ResponseType.OK }, { "_Non-stock Button", Gtk.ResponseType.CANCEL }, }, } local hbox = Gtk.Box { orientation = 'HORIZONTAL', spacing = 8, border_width = 8, Gtk.Image { stock = Gtk.STOCK_DIALOG_QUESTION, icon_size = Gtk.IconSize.DIALOG, }, Gtk.Grid { row_spacing = 4, column_spacing = 4, { left_attach = 0, top_attach = 0, Gtk.Label { label = "_Entry 1", use_underline = true, }, }, { left_attach = 1, top_attach = 0, Gtk.Entry { id = 'entry1', text = window.child.entry1.text, }, }, { left_attach = 0, top_attach = 1, Gtk.Label { label = "E_ntry 2", use_underline = true, }, }, { left_attach = 1, top_attach = 1, Gtk.Entry { id = 'entry2', text = window.child.entry2.text, }, }, } } dialog:get_content_area():add(hbox) hbox:show_all() if dialog:run() == Gtk.ResponseType.OK then window.child.entry1.text = hbox.child.entry1.text window.child.entry2.text = hbox.child.entry2.text end dialog:destroy() end window:show_all() return window end, "Dialog and Message Boxes", table.concat { "Dialog widgets are used to pop up a transient window for user feedback.", } lgi-0.9.2/samples/gtk-demo/demo-drawingarea.lua000066400000000000000000000100411316674307300213670ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local Gtk = lgi.Gtk local Gdk = lgi.Gdk local cairo = lgi.cairo local window = Gtk.Window { title = "Drawing Area", border_width = 8, Gtk.Box { orientation = 'VERTICAL', spacing = 8, border_width = 8, Gtk.Label { use_markup = true, label = "Checkerboard pattern", }, Gtk.Frame { shadow_type = 'IN', expand = true, Gtk.DrawingArea { id = 'checkerboard', width = 100, height = 100, }, }, Gtk.Label { use_markup = true, label = "Scribble area", }, Gtk.Frame { shadow_type = 'IN', expand = true, Gtk.DrawingArea { id = 'scribble', width = 100, height = 100, }, }, }, } -- Handling of checkerboard area. local SPACING = 2 local CHECK_SIZE = 10 function window.child.checkerboard:on_draw(cr) local i = SPACING local xcount = 0 while i < self.width do local j = SPACING local ycount = xcount % 2 while j < self.height do if ycount % 2 ~= 0 then cr:set_source_rgb(0.45777, 0, 0.45777) else cr:set_source_rgb(1, 1, 1) end cr:rectangle(i, j, CHECK_SIZE, CHECK_SIZE) cr:fill() j = j + CHECK_SIZE + SPACING ycount = ycount + 1 end i = i + CHECK_SIZE + SPACING xcount = xcount + 1 end return true end -- Setup and handling of scribble area. local scribble = window.child.scribble local surface function scribble:on_configure_event(event) -- Create new surface of appropriate size to store the scribbles. local allocation = self.allocation surface = self.window:create_similar_surface( 'COLOR', allocation.width, allocation.height) -- Initialize the surface to white. local cr = cairo.Context.create(surface) cr:set_source_rgb(1, 1, 1) cr:paint() return true end function scribble:on_draw(cr) -- Redraw the screen from the buffer. cr:set_source_surface(surface, 0, 0) cr:paint() return true end -- Draw a rectangle on the scribble surface. local function draw_brush(widget, x, y) local update_rect = Gdk.Rectangle { x = x - 3, y = y - 3, width = 6, height = 6 } -- Paint to the scribble surface local cr = cairo.Context(surface) cr:rectangle(update_rect) cr:fill() -- Invalidate affected region of the paint area. widget.window:invalidate_rect(update_rect, false) end function scribble:on_motion_notify_event(event) -- This call is very important; it requests the next motion event. -- If you don't call Gdk.Window.get_pointer() you'll only get -- a single motion event. The reason is that we specified -- Gdk.EventMask.POINTER_MOTION_HINT_MASK to Gtk.Widget.add_events(). -- If we hadn't specified that, we could just use event.x, event.y -- as the pointer location. But we'd also get deluged in events. -- By requesting the next event as we handle the current one, -- we avoid getting a huge number of events faster than we -- can cope. local _, x, y, state = event.window:get_device_position(event.device) if state.BUTTON1_MASK then draw_brush(self, x, y) end return true end function scribble:on_button_press_event(event) if event.button == Gdk.BUTTON_PRIMARY then draw_brush(self, event.x, event.y) end return true end scribble:add_events(Gdk.EventMask { 'LEAVE_NOTIFY_MASK', 'BUTTON_PRESS_MASK', 'POINTER_MOTION_MASK', 'POINTER_MOTION_HINT_MASK' }) window:show_all() return window end, "Drawing Area", table.concat { [[Gtk.DrawingArea is a blank area where you can draw custom displays ]], [[of various kinds. ]], [[This demo has two drawing areas. The checkerboard area shows how ]], [[you can just draw something; all you have to do is write a signal ]], [[handler for expose_event, as shown here. ]], [[The "scribble" area is a bit more advanced, and shows how to handle ]], [[events such as button presses and mouse motion. Click the mouse ]], [[and drag in the scribble area to draw squiggles. Resize the window ]], [[to clear the area.]] } lgi-0.9.2/samples/gtk-demo/demo-entry-buffer.lua000066400000000000000000000014701316674307300215210ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local Gtk = lgi.Gtk local window = Gtk.Dialog { title = "Entry Buffer", resizable = false, on_response = Gtk.Widget.destroy, buttons = { { Gtk.STOCK_CLOSE, Gtk.ResponseType.NONE }, }, } local buffer = Gtk.EntryBuffer() local content = Gtk.Box { orientation = 'VERTICAL', spacing = 5, border_width = 5, Gtk.Label { label = "Entries share a buffer. " .. "Typing in one is reflected in the other.", use_markup = true, }, Gtk.Entry { buffer = buffer, }, Gtk.Entry { buffer = buffer, visibility = false, }, } window:get_content_area():add(content) window:show_all() return window end, "Entry/Entry Buffer", table.concat { "Gtk.EntryBuffer provides the text content in a Gtk.Entry.", } lgi-0.9.2/samples/gtk-demo/demo-entry-completion.lua000066400000000000000000000020051316674307300224140ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local GObject = lgi.GObject local Gtk = lgi.Gtk local window = Gtk.Dialog { title = "GtkEntryCompletion", resizable = false, on_response = Gtk.Widget.destroy, buttons = { { Gtk.STOCK_CLOSE, Gtk.ResponseType.NONE }, }, } local store = Gtk.ListStore.new { GObject.Type.STRING } for _, name in ipairs { "GNOME", "total", "totally" } do store:append { name } end local content = Gtk.Box { orientation = 'VERTICAL', spacing = 5, border_width = 5, Gtk.Label { label = "Completion demo, try writing total or gnome " .. "for example.", use_markup = true, }, Gtk.Entry { id = 'entry', completion = Gtk.EntryCompletion { model = store, text_column = 0, }, }, } window:get_content_area():add(content) window:show_all() return window end, "Entry/Entry Completion", table.concat { "Gtk.EntryCompletion provides a mechanism for adding support for ", "completion in Gtk.Entry.", } lgi-0.9.2/samples/gtk-demo/demo-entry-search.lua000066400000000000000000000105321316674307300215140ustar00rootroot00000000000000return function(parent, dir) local coroutine = require 'coroutine' local lgi = require 'lgi' local GLib = lgi.GLib local GObject = lgi.GObject local Gtk = lgi.Gtk local window = Gtk.Dialog { title = "Search Entry", resizable = false, on_response = Gtk.Widget.destroy, buttons = { { Gtk.STOCK_CLOSE, Gtk.ResponseType.NONE }, }, } local content = Gtk.Box { orientation = 'VERTICAL', spacing = 5, border_width = 5, Gtk.Label { label = "Search entry demo", use_markup = true, }, Gtk.Box { orientation = 'HORIZONTAL', spacing = 10, Gtk.Entry { id = 'entry', secondary_icon_stock = Gtk.STOCK_CLEAR, }, Gtk.Notebook { id = 'buttons', show_tabs = false, show_border = false, Gtk.Button { id = 'button_find', label = "Find", sensitive = false, }, Gtk.Button { id = 'button_cancel', label = "Cancel", }, }, }, } window:get_content_area():add(content) local entry = content.child.entry local search = { { menu = "Search by _name", stock = Gtk.STOCK_FIND, placeholder = "name", tooltip = "Search by name\n" .. "Click here to change the search type", }, { menu = "Search by _description", stock = Gtk.STOCK_EDIT, placeholder = "description", tooltip = "Search by description\n" .. "Click here to change the search type", }, { menu = "Search by _file name", stock = Gtk.STOCK_OPEN, placeholder = "filename", tooltip = "Search by file name\n" .. "Click here to change the search type", }, } local function search_by(method) entry.primary_icon_stock = method.stock entry.primary_icon_tooltip_text = method.tooltip entry.placeholder_text = method.placeholder end search_by(search[1]) local function create_search_menu() local menu = Gtk.Menu { visible = true } for i = 1, #search do menu:append( Gtk.ImageMenuItem { image = Gtk.Image { stock = search[i].stock, icon_size = Gtk.IconSize.MENU }, label = search[i].menu, use_underline = true, visible = true, always_show_image = true, on_activate = function() search_by(search[i]) end, }) end return menu end local menu = create_search_menu() menu:attach_to_widget(entry) function entry:on_populate_popup(menu) for _, item in ipairs { Gtk.SeparatorMenuItem {}, Gtk.MenuItem { label = "C_lear", use_underline = true, visible = true, on_activate = function() entry.text = '' end }, Gtk.MenuItem { label = "Search by", submenu = create_search_menu(), visible = true, }, } do item.visible = true menu:append(item) end end function entry:on_icon_press(position, event) if position == 'PRIMARY' then menu:popup(nil, nil, nil, nil, event.button, event.time) else entry.text = '' end end function entry.on_notify:text(pspec) local has_text = entry.text ~= '' entry.secondary_icon_sensitive = has_text content.child.button_find.sensitive = has_text end local search_progress_id, finish_search_id local function finish_search() content.child.buttons.page = 0 if search_progress_id then GLib.source_remove(search_progress_id) search_progress_id = nil end if finish_search_id then GLib.source_remove(finish_search_id) finish_search_id = nil end entry.progress_fraction = 0 end local function start_search() content.child.buttons.page = 1 search_progress_id = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, function() search_progress_id = GLib.timeout_add( GLib.PRIORITY_DEFAULT, 100, function() entry:progress_pulse() return true end) return false end) finish_search_id = GLib.timeout_add_seconds( GLib.PRIORITY_DEFAULT, 15, finish_search) end function entry:on_activate() if not search_progress_id then start_search() end end function content.child.button_find:on_clicked() start_search() end function content.child.button_cancel:on_clicked() finish_search() end function window:on_destroy() finish_search() end window:show_all() return window end, "Entry/Search Entry", table.concat { "Gtk.Entry allows to display icons and progress information. ", "This demo shows how to use these features in a search entry." } lgi-0.9.2/samples/gtk-demo/demo-expander.lua000066400000000000000000000015071316674307300207200ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local Gtk = lgi.Gtk local window = Gtk.Dialog { title = "GtkExpander", transient_for = parent, resizable = false, buttons = { { Gtk.STOCK_CLOSE, Gtk.ResponseType.NONE }, }, on_response = Gtk.Widget.destroy, } local content = Gtk.Box { orientation = 'VERTICAL', spacing = 5, border_width = 5, Gtk.Label { label = "Expander demo. Click on the triangle for details.", }, Gtk.Expander { label = "Details", Gtk.Label { label = "Details can be shown or hidden.", } }, } window:get_content_area():add(content) window:show_all() return window end, "Expander", table.concat { [[Gtk.Expander allows to provide additional content that is ]], [[initially hidden. This is also known as "disclosure triangle".]] } lgi-0.9.2/samples/gtk-demo/demo-iconview-basics.lua000066400000000000000000000104651316674307300222020ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local GLib = lgi.GLib local GObject = lgi.GObject local Gio = lgi.Gio local Gtk = lgi.Gtk local GdkPixbuf = lgi.GdkPixbuf local assert = lgi.assert local pixbuf = { REGULAR = assert(GdkPixbuf.Pixbuf.new_from_file( dir:get_child('gnome-fs-regular.png'):get_path())), DIRECTORY = assert(GdkPixbuf.Pixbuf.new_from_file( dir:get_child('gnome-fs-directory.png'):get_path())), } local ViewColumn = { PATH = 1, DISPLAY_NAME = 2, PIXBUF = 3, IS_DIRECTORY = 4, } local store = Gtk.ListStore.new { [ViewColumn.PATH] = Gio.File, [ViewColumn.DISPLAY_NAME] = GObject.Type.STRING, [ViewColumn.PIXBUF] = GdkPixbuf.Pixbuf, [ViewColumn.IS_DIRECTORY] = GObject.Type.BOOLEAN, } store:set_default_sort_func( function(model, a, b) -- Sort folders before files. a = model[a] b = model[b] local is_dir_a, is_dir_b = a[ViewColumn.IS_DIRECTORY], b[ViewColumn.IS_DIRECTORY] if not is_dir_a and is_dir_b then return 1 elseif is_dir_a and not is_dir_b then return -1 else return GLib.utf8_collate(a[ViewColumn.DISPLAY_NAME], b[ViewColumn.DISPLAY_NAME]) end end) store:set_sort_column_id(Gtk.TreeSortable.DEFAULT_SORT_COLUMN_ID, 'ASCENDING') local window = Gtk.Window { default_width = 650, default_height = 400, title = "Gtk.IconView demo", Gtk.Box { orientation = 'VERTICAL', Gtk.Toolbar { Gtk.ToolButton { id = 'up_button', stock_id = Gtk.STOCK_GO_UP, is_important = true, sensitive = false, }, Gtk.ToolButton { id = 'home_button', stock_id = Gtk.STOCK_HOME, is_important = true, }, }, Gtk.ScrolledWindow { shadow_type = 'ETCHED_IN', Gtk.IconView { id = 'icon_view', expand = true, selection_mode = 'MULTIPLE', model = store, text_column = ViewColumn.DISPLAY_NAME - 1, pixbuf_column = ViewColumn.PIXBUF - 1, has_focus = true, }, }, }, } local current_dir = Gio.File.new_for_path('/') local cancellable local function fill_store() -- If the opertion is already running, just cancel it. It will -- restart itself when cancel is detected. if cancellable then cancellable:cancel() return end -- Asynchronously started worker routine. local function fill() local function check(condition, err) return not cancellable:is_cancelled() and assert(condition, err) end local dir = current_dir cancellable = Gio.Cancellable() Gio.Async.cancellable = cancellable store:clear() window.child.up_button.sensitive = (current_dir:get_parent() ~= nil) local enum = check(dir:async_enumerate_children('standard::*', 'NONE')) while not cancellable:is_cancelled() do local infos = check(enum:async_next_files(16)) if not infos or #infos == 0 then break end for _, info in pairs(infos) do store:append { [ViewColumn.PATH] = current_dir:get_child(info:get_name()), [ViewColumn.DISPLAY_NAME] = info:get_display_name(), [ViewColumn.IS_DIRECTORY] = info:get_file_type() == 'DIRECTORY', [ViewColumn.PIXBUF] = pixbuf[info:get_file_type()], } end end -- Signalize that we are finished. cancellable = nil -- Check, whether someone else already requested different -- request. If yes, spawn another query. if dir ~= current_dir then Gio.Async.start(fill)() end end -- Perform actual fill asynchronously. Gio.Async.start(fill)() end -- Initial fill of the store. fill_store() function window.child.up_button:on_clicked() current_dir = current_dir:get_parent() fill_store() end function window.child.home_button:on_clicked() current_dir = Gio.File.new_for_path(GLib.get_home_dir()) fill_store() end function window.child.icon_view:on_item_activated(path) local row = store[path] if row[ViewColumn.IS_DIRECTORY] then current_dir = row[ViewColumn.PATH] fill_store() end end function window:on_destroy() if cancellable then cancellable:cancel() end end window:show_all() return window end, "Icon View/Icon View Basics", table.concat { [[The Gtk.IconView widget is used to display and manipulate icons. ]], [[It uses a Gtk.TreeModel for data storage, so the list store example ]], [[might be helpful.]] } lgi-0.9.2/samples/gtk-demo/demo-iconview-editing.lua000066400000000000000000000032611316674307300223550ustar00rootroot00000000000000return function(parent, dir) local math = require 'math' local lgi = require 'lgi' local GObject = lgi.GObject local Gtk = lgi.Gtk local Gdk = lgi.Gdk local GdkPixbuf = lgi.GdkPixbuf local store = Gtk.ListStore.new { GObject.Type.STRING } for _, name in ipairs { "Red", "Green", "Blue", "Yellow" } do store:append { name } end local function edited(cell, path_string, text) store[Gtk.TreePath.new_from_string(path_string)][1] = text end local function set_cell_color(layout, cell, model, iter) local rgba, pixel = Gdk.RGBA(), 0 local label = model[iter][1] if label and rgba:parse(label) then pixel = math.floor(rgba.red * 255) * (256 * 256 * 256) + math.floor(rgba.green * 255) * (256 * 256) + math.floor(rgba.blue * 255) * 256 end cell.pixbuf = GdkPixbuf.Pixbuf.new('RGB', false, 8, 24, 24) cell.pixbuf:fill(pixel) end local window = Gtk.Window { title = "Editing and Drag-and-Drop", Gtk.IconView { id = 'icon_view', expand = true, model = store, selection_mode = 'SINGLE', item_orientation = 'HORIZONTAL', columns = 2, reorderable = true, cells = { { align = 'start', expand = true, Gtk.CellRendererPixbuf(), set_cell_color, }, { align = 'start', expand = true, Gtk.CellRendererText { editable = true, on_edited = edited, }, { text = 1 } }, }, }, } window:show_all() return window end, "Icon View/Editing and Drag-and-Drop", table.concat { [[The Gtk.IconView widget supports Editing and Drag-and-Drop. ]], [[This example also demonstrates using the generic Gtk.CellLayout ]], [[interface to set up cell renderers in an icon view.]] } lgi-0.9.2/samples/gtk-demo/demo-images.lua000066400000000000000000000076751316674307300203730ustar00rootroot00000000000000return function(parent, dir) local coroutine = require 'coroutine' local lgi = require 'lgi' local bytes = require 'bytes' local GLib = lgi.GLib local Gio = lgi.Gio local Gtk = lgi.Gtk local GdkPixbuf = lgi.GdkPixbuf local window = Gtk.Window { title = 'Images', border_width = 8, Gtk.Box { id = 'vbox', orientation = 'VERTICAL', spacing = 8, border_width = 8, Gtk.Label { label = "Image loaded from a file:", use_markup = true, }, Gtk.Frame { shadow_type = 'IN', halign = 'CENTER', valign = 'CENTER', Gtk.Image { file = dir:get_child('gtk-logo-rgb.gif'):get_path(), }, }, Gtk.Label { label = "Animation loaded from a file:", use_markup = true, }, Gtk.Frame { shadow_type = 'IN', halign = 'CENTER', valign = 'CENTER', Gtk.Image { file = dir:get_child('floppybuddy.gif'):get_path(), }, }, Gtk.Label { label = "Symbolic themed icon", use_markup = true, }, Gtk.Frame { shadow_type = 'IN', halign = 'CENTER', valign = 'CENTER', Gtk.Image { gicon = Gio.ThemedIcon.new_with_default_fallbacks( 'battery-caution-charging-symbolic'), icon_size = Gtk.IconSize.DIALOG, }, }, Gtk.Label { label = "Progressive image loading", use_markup = true, }, Gtk.Frame { shadow_type = 'IN', halign = 'CENTER', valign = 'CENTER', Gtk.Image { id = 'progressive', }, }, Gtk.ToggleButton { id = 'sensitive', label = "_Insensitive", use_underline = true, }, } } function window.child.sensitive:on_toggled() for _, child in ipairs(window.child.vbox.child) do if child ~= self then child.sensitive = not self.active end end end local function do_error(err) local dialog = Gtk.MessageDialog { transient_for = window, destroy_with_parent = true, text = "Failure reading image 'alphatest.png'", secondary_text = err, message_type = 'ERROR', buttons = 'CLOSE', on_response = Gtk.Widget.destroy, } dialog:show_all() end local abort_load, timer_id local load_coro = coroutine.create(function() while not abort_load do local stream, err = dir:get_child('alphatest.png'):read() if not stream then do_error(err) abort_load = true break end -- Create pixbuf loader and register callbacks. local loader = GdkPixbuf.PixbufLoader() function loader:on_area_prepared() local pixbuf = self:get_pixbuf() pixbuf:fill(0xaaaaaaff) window.child.progressive.pixbuf = pixbuf end function loader:on_area_updated() -- Let the image know that the pixbuf changed. window.child.progressive:queue_draw() end while not abort_load do -- Wait for the next timer tick. coroutine.yield(true) -- Load a chunk from the stream. local buffer = bytes.new(256) local read, err = stream:read(buffer) if read < 0 then do_error(err) abort_load = true end if read <= 0 then break end -- Send it to the pixbuf loader. if not loader:write(tostring(buffer):sub(1, read)) then do_error(err) abort_load = true end end loader:close() end -- Make sure that timeout is unregistered when the coroutine does -- not run any more. timer_id = nil return false end) timer_id = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 150, load_coro) -- Stop loading when the window is destroyed. function window:on_destroy() abort_load = true if timer_id then GLib.source_remove(timer_id) coroutine.resume(load_coro) end end window:show_all() return window end, "Images", table.concat { [[Gtk.Image is used to display an image; the image can be in ]], [[a number of formats. Typically, you load an image into a Gdk.Pixbuf, ]], [[then display the pixbuf. This demo code shows some of the more obscure cases, in the simple ]], [[case a call to Gtk.Image.new_from_file() is all you need.]], } lgi-0.9.2/samples/gtk-demo/demo-infobar.lua000066400000000000000000000033351316674307300205330ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local GLib = lgi.GLib local Gtk = lgi.Gtk local window = Gtk.Window { title = 'Info Bars', border_width = 8, Gtk.Box { orientation = 'VERTICAL', Gtk.InfoBar { id = 'info', message_type = 'INFO', }, Gtk.InfoBar { id = 'warning', message_type = 'WARNING', }, Gtk.InfoBar { id = 'question', buttons = { { Gtk.STOCK_OK, Gtk.ResponseType.OK }, }, message_type = 'QUESTION', }, Gtk.InfoBar { id = 'error', message_type = 'ERROR', }, Gtk.InfoBar { id = 'other', message_type = 'OTHER', }, Gtk.Frame { label = "Info Bars", Gtk.Box { orientation = 'VERTICAL', spacing = 8, border_width = 8, { padding = 8, Gtk.Label { label = "An example of different info bars", }, } }, }, }, } -- Create contents for the infobars. for _, id in ipairs { 'info', 'warning', 'question', 'error', 'other' } do window.child[id]:get_content_area():add( Gtk.Label { label = ("This is an info bar with " .. "message type Gtk.MessageType.%s"):format( GLib.ascii_strup(id, -1)), }) end function window.child.question:on_response(response_id) local dialog = Gtk.MessageDialog { transient_for = window, modal = true, destroy_with_parent = true, message_type = 'INFO', buttons = 'OK', text = "You clicked a button on an info bar", secondary_text = ("Your response has id %d"):format(response_id) } dialog:run() dialog:destroy() end window:show_all() return window end, "Info bar", table.concat { [[Info bar widgets are used to report important messages to the user.]], } lgi-0.9.2/samples/gtk-demo/demo-links.lua000066400000000000000000000022651316674307300202340ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local Gtk = lgi.Gtk local window = Gtk.Window { title = 'Links', border_width = 12, Gtk.Label { id = 'label', label = [[Some text may be marked up as hyperlinks, which can be clicked or activated via keynav]], use_markup = true, }, } function window.child.label:on_activate_link(uri) if uri == 'keynav' then local dialog = Gtk.MessageDialog { transient_for = self:get_toplevel(), destroy_with_parent = true, message_type = 'INFO', buttons = 'OK', use_markup = true, text = [[The term keynav is a shorthand for ]] .. [[keyboard navigation and refers to the process of using ]] .. [[a program (exclusively) via keyboard input.]], on_response = Gtk.Widget.destroy, } dialog:present() return true else return false end end window:show_all() return window end, "Links", table.concat { [[Gtk.Label can show hyperlinks. The default action is to call ]], [[Gtk.show_uri() on their URI, but it is possible to override ]], [[this with a custom handler.]], } lgi-0.9.2/samples/gtk-demo/demo-menus.lua000066400000000000000000000060331316674307300202400ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local Gtk = lgi.Gtk local function create_menu(depth, tearoff) if depth < 1 then return nil end local menu = Gtk.Menu() if tearoff then menu:append(Gtk.TearoffMenuItem { visible = true }) end local group = nil for i = 1, 5 do local item = Gtk.RadioMenuItem { group = group, label = ("item %2d - %d"):format(depth, i), submenu = create_menu(depth - 1, true), sensitive = (i ~= 4), } if not group then group = item end menu:append(item) end return menu end local window = Gtk.Window { title = "Menus", Gtk.Box { orientation = 'VERTICAL', Gtk.MenuBar { id = 'menubar', Gtk.MenuItem { label = "test\nline2", visible = true, submenu = create_menu(2, true), }, Gtk.MenuItem { label = "foo", visible = true, submenu = create_menu(3), }, Gtk.MenuItem { label = "bar", visible = true, submenu = create_menu(4, true), }, }, Gtk.Box { orientation = 'VERTICAL', spacing = 10, border_width = 10, Gtk.Button { id = 'flip', label = "Flip", }, Gtk.Button { id = 'close', label = "Close", }, }, }, } function window.child.close:on_clicked() window:destroy() end function window.child.flip:on_clicked() local menubar = window.child.menubar local orientation = menubar.parent.orientation orientation = (orientation == 'HORIZONTAL' and 'VERTICAL' or 'HORIZONTAL') menubar.parent.orientation = orientation menubar.pack_direction = (orientation == 'VERTICAL' and 'LTR' or 'TTB') end window:show_all() return window end, "Menus", table.concat { [[There are several widgets involved in displaying menus. ]], [[The Gtk.MenuBar widget is a menu bar, which normally appears ]], [[horizontally at the top of an application, but can also be ]], [[layed out vertically. The Gtk.Menu widget is the actual menu ]], [[that pops up. Both Gtk.MenuBar and Gtk.Menu are subclasses ]], [[of Gtk.MenuShell; a Gtk.MenuShell contains menu items (Gtk.MenuItem). ]], [[Each menu item contains text and/or images and can be selected ]], [[by the user. ]], [[There are several kinds of menu item, including plain ]], [[Gtk.MenuItem, Gtk.CheckMenuItem which can be checked/unchecked, ]], [[Gtk.RadioMenuItem which is a check menu item that's in a mutually ]], [[exclusive group, Gtk.SeparatorMenuItem which is a separator bar, ]], [[Gtk.TearoffMenuItem which allows a Gtk.Menu to be torn off, ]], [[and Gtk.ImageMenuItem which can place a Gtk.Image or other widget ]], [[next to the menu text. ]], [[A Gtk.MenuItem can have a submenu, which is simply a Gtk.Menu ]], [[to pop up when the menu item is selected. Typically, all menu ]], [[items in a menu bar have submenus. ]], [[Gtk.UIManager provides a higher-level interface for creating ]], [[menu bars and menus; while you can construct menus manually, ]], [[most people don't do that. There's a separate demo for ]], [[Gtk.UIManager.]], } lgi-0.9.2/samples/gtk-demo/demo-ofw-mirror.lua000066400000000000000000000177511316674307300212250ustar00rootroot00000000000000return function(parent, dir) local math = require 'math' local lgi = require 'lgi' local GObject = lgi.GObject local Gtk = lgi.Gtk local Gdk = lgi.Gdk local cairo = lgi.cairo local GtkDemo = lgi.GtkDemo local log = lgi.log.domain 'gtk-demo' GtkDemo:class('MirrorBin', Gtk.Bin) function GtkDemo.MirrorBin:_init() self.has_window = true end local function to_child(bin, widget_x, widget_y) return widget_x, widget_y end local function to_parent(bin, offscreen_x, offscreen_y) return offscreen_x, offscreen_y end function GtkDemo.MirrorBin:do_realize() self.realized = true -- Create Gdk.Window and bind it with the widget. local events = self.events events.EXPOSURE_MASK = true events.POINTER_MOTION_MASK = true events.BUTTON_PRESS_MASK = true events.BUTTON_RELEASE_MASK = true events.SCROLL_MASK = true events.ENTER_NOTIFY_MASK = true events.LEAVE_NOTIFY_MASK = true local attributes = Gdk.WindowAttr { x = self.allocation.x + self.border_width, y = self.allocation.y + self.border_width, width = self.allocation.width - 2 * self.border_width, height = self.allocation.height - 2 * self.border_width, window_type = 'CHILD', event_mask = Gdk.EventMask(events), visual = self:get_visual(), wclass = 'INPUT_OUTPUT', } local window = Gdk.Window.new(self:get_parent_window(), attributes, { 'X', 'Y', 'VISUAL' }) self:set_window(window) window.widget = self local bin = self function window:on_pick_embedded_child(widget_x, widget_y) if bin.priv.child and bin.priv.child.visible then local x, y = to_child(bin, widget_x, widget_y) local child_area = bin.allocation if x >= 0 and x < child_area.width and y >= 0 and y < child_area.height then return bin.priv.offscreen_window end end end -- Create and hook up the offscreen window. attributes.window_type = 'OFFSCREEN' local child_requisition = Gtk.Requisition { width = 0, height = 0 } if self.priv.child and self.priv.child.visible then local child_allocation = self.priv.child.allocation attributes.width = child_allocation.width attributes.height = child_allocation.height end self.priv.offscreen_window = Gdk.Window.new(self.root_window, attributes, { 'X', 'Y', 'VISUAL' }) self.priv.offscreen_window.widget = self if self.priv.child then self.priv.child:set_parent_window(bin.priv.offscreen_window) end Gdk.offscreen_window_set_embedder(self.priv.offscreen_window, window) function self.priv.offscreen_window:on_to_embedder(offscreen_x, offscreen_y) return to_parent(bin, offscreen_x, offscreen_y) end function self.priv.offscreen_window:on_from_embedder(parent_x, parent_y) return to_child(bin, parent_x, parent_y) end -- Set background of the windows according to current context. self.style_context:set_background(window) self.style_context:set_background(self.priv.offscreen_window) self.priv.offscreen_window:show() end function GtkDemo.MirrorBin:do_unrealize() -- Destroy offscreen window. self.priv.offscreen_window.widget = nil self.priv.offscreen_window:destroy() self.priv.offscreen_window = nil -- Chain to parent. GtkDemo.MirrorBin._parent.do_unrealize(self) end function GtkDemo.MirrorBin:do_child_type() return self.priv.child and GObject.Type.NONE or Gtk.Widget end function GtkDemo.MirrorBin:do_add(widget) if not self.priv.child then if self.priv.offscreen_window then widget:set_parent_window(self.priv.offscreen_window) end widget:set_parent(self) self.priv.child = widget else log.warning("GtkDemo.MirrorBin cannot have more than one child") end end function GtkDemo.MirrorBin:do_remove(widget) local was_visible = widget.visible if self.priv.child == widget then widget:unparent() self.priv.child = nil if was_visible and self.visible then self:queue_resize() end end end function GtkDemo.MirrorBin:do_forall(include_internals, callback) if self.priv.child then callback(self.priv.child, callback.user_data) end end local function size_request(self) local child_requisition = Gtk.Requisition() if self.priv.child and self.priv.child.visible then child_requisition = self.priv.child:get_preferred_size() end local w = child_requisition.width + 10 + 2 * self.border_width local h = child_requisition.height + 10 + 2 * self.border_width return w, h end function GtkDemo.MirrorBin:do_get_preferred_width() local w, h = size_request(self) return w, w end function GtkDemo.MirrorBin:do_get_preferred_height() local w, h = size_request(self) return h, h end function GtkDemo.MirrorBin:do_size_allocate(allocation) self:set_allocation(allocation) local w = allocation.width - self.border_width * 2 local h = allocation.height - self.border_width * 2 if self.realized then self.window:move_resize(allocation.x + self.border_width, allocation.y + self.border_width, w, h) end if self.priv.child and self.priv.child.visible then local child_requisition = self.priv.child:get_preferred_size() local child_allocation = Gtk.Allocation { height = child_requisition.height, width = child_requisition.width } if self.realized then self.priv.offscreen_window:move_resize(child_allocation.x, child_allocation.y, child_allocation.width, child_allocation.height) end self.priv.child:size_allocate(child_allocation) end end function GtkDemo.MirrorBin:do_damage(event) self.window:invalidate_rect(nil, false) return true end function GtkDemo.MirrorBin:_class_init() -- Unfortunately, damage-event signal does not have virtual -- function associated, so we have to go through following funky -- dance to install default signal handler. GObject.signal_override_class_closure( GObject.signal_lookup('damage-event', Gtk.Widget), GtkDemo.MirrorBin, GObject.Closure(GtkDemo.MirrorBin.do_damage, Gtk.Widget.on_damage_event)) end function GtkDemo.MirrorBin:do_draw(cr) if cr:should_draw_window(self.window) then if self.priv.child and self.priv.child.visible then local surface = Gdk.offscreen_window_get_surface( self.priv.offscreen_window) local height = self.priv.offscreen_window:get_height() -- Paint the offscreen child cr:set_source_surface(surface, 0, 0) cr:paint() local matrix = cairo.Matrix { xx = 1, yx = 0, xy = 0.3, yy = 1, x0 = 0, y0 = 0 } matrix:scale(1, -1) matrix:translate(-10, -3 * height, - 10) cr:transform(matrix) cr:set_source_surface(surface, 0, height) -- Create linear gradient as mask-pattern to fade out the source local mask = cairo.LinearPattern(0, height, 0, 2 * height) mask:add_color_stop_rgba(0, 0, 0, 0, 0) mask:add_color_stop_rgba(0.25, 0, 0, 0, 0.01) mask:add_color_stop_rgba(0.5, 0, 0, 0, 0.25) mask:add_color_stop_rgba(0.75, 0, 0, 0, 0.5) mask:add_color_stop_rgba(1, 0, 0, 0, 1) -- Paint the reflection cr:mask(mask) end elseif cr:should_draw_window(self.priv.offscreen_window) then Gtk.render_background(self.style_context, cr, 0, 0, self.priv.offscreen_window:get_width(), self.priv.offscreen_window:get_height()) if self.priv.child then self:propagate_draw(self.priv.child, cr) end end return false end local window = Gtk.Window { title = "Effects", border_width = 10, Gtk.Box { orientation = 'VERTICAL', expand = true, GtkDemo.MirrorBin { Gtk.Box { orientation = 'HORIZONTAL', spacing = 6, Gtk.Button { Gtk.Image { stock = Gtk.STOCK_GO_BACK, icon_size = 4, }, }, Gtk.Entry { expand = true, }, Gtk.Button { Gtk.Image { stock = Gtk.STOCK_APPLY, icon_size = 4, }, }, }, }, }, } window:show_all() return window end, "Offscreen windows/Effects", table.concat { [[Offscreen windows can be used to render elements multiple times ]], [[to achieve various effects.]] } lgi-0.9.2/samples/gtk-demo/demo-ofw-rotbutton.lua000066400000000000000000000227221316674307300217450ustar00rootroot00000000000000return function(parent, dir) local math = require 'math' local lgi = require 'lgi' local GObject = lgi.GObject local Gtk = lgi.Gtk local Gdk = lgi.Gdk local GtkDemo = lgi.GtkDemo local log = lgi.log.domain 'gtk-demo' GtkDemo:class('RotatedBin', Gtk.Bin) function GtkDemo.RotatedBin:_init() self.has_window = true self.priv.angle = 0 end local function to_child(bin, widget_x, widget_y) local s, c = math.sin(bin.priv.angle), math.cos(bin.priv.angle) local child_area = bin.priv.child.allocation local w = c * child_area.width + s * child_area.height local h = s * child_area.width + c * child_area.height local x = widget_x - w / 2 local y = widget_y - h / 2 local xr = x * c + y * s local yr = y * c - x * s return xr + child_area.width / 2, yr + child_area.height / 2 end local function to_parent(bin, offscreen_x, offscreen_y) local s, c = math.sin(bin.priv.angle), math.cos(bin.priv.angle) local child_area = bin.priv.child.allocation local w = c * child_area.width + s * child_area.height local h = s * child_area.width + c * child_area.height local x = offscreen_x - child_area.width / 2 local y = offscreen_y - child_area.height / 2 local xr = x * c - y * s local yr = x * s + y * c return xr + child_area.width - w / 2, yr + child_area.height - h / 2 end function GtkDemo.RotatedBin:do_realize() self.realized = true -- Create Gdk.Window and bind it with the widget. local events = self.events events.EXPOSURE_MASK = true events.POINTER_MOTION_MASK = true events.BUTTON_PRESS_MASK = true events.BUTTON_RELEASE_MASK = true events.SCROLL_MASK = true events.ENTER_NOTIFY_MASK = true events.LEAVE_NOTIFY_MASK = true local attributes = Gdk.WindowAttr { x = self.allocation.x + self.border_width, y = self.allocation.y + self.border_width, width = self.allocation.width - 2 * self.border_width, height = self.allocation.height - 2 * self.border_width, window_type = 'CHILD', event_mask = Gdk.EventMask(events), visual = self:get_visual(), wclass = 'INPUT_OUTPUT', } local window = Gdk.Window.new(self:get_parent_window(), attributes, { 'X', 'Y', 'VISUAL' }) self:set_window(window) window.widget = self local bin = self function window:on_pick_embedded_child(widget_x, widget_y) if bin.priv.child and bin.priv.child.visible then local x, y = to_child(bin, widget_x, widget_y) local child_area = bin.allocation if x >= 0 and x < child_area.width and y >= 0 and y < child_area.height then return bin.priv.offscreen_window end end end -- Create and hook up the offscreen window. attributes.window_type = 'OFFSCREEN' local child_requisition = Gtk.Requisition { width = 0, height = 0 } if self.priv.child and self.priv.child.visible then local child_allocation = self.priv.child.allocation attributes.width = child_allocation.width attributes.height = child_allocation.height end self.priv.offscreen_window = Gdk.Window.new(self.root_window, attributes, { 'X', 'Y', 'VISUAL' }) self.priv.offscreen_window.widget = self if self.priv.child then self.priv.child:set_parent_window(bin.priv.offscreen_window) end Gdk.offscreen_window_set_embedder(self.priv.offscreen_window, window) function self.priv.offscreen_window:on_to_embedder(offscreen_x, offscreen_y) return to_parent(bin, offscreen_x, offscreen_y) end function self.priv.offscreen_window:on_from_embedder(parent_x, parent_y) return to_child(bin, parent_x, parent_y) end -- Set background of the windows according to current context. self.style_context:set_background(window) self.style_context:set_background(self.priv.offscreen_window) self.priv.offscreen_window:show() end function GtkDemo.RotatedBin:do_unrealize() -- Destroy offscreen window. self.priv.offscreen_window.widget = nil self.priv.offscreen_window:destroy() self.priv.offscreen_window = nil -- Chain to parent. GtkDemo.RotatedBin._parent.do_unrealize(self) end function GtkDemo.RotatedBin:do_child_type() return self.priv.child and GObject.Type.NONE or Gtk.Widget end function GtkDemo.RotatedBin:do_add(widget) if not self.priv.child then if self.priv.offscreen_window then widget:set_parent_window(self.priv.offscreen_window) end widget:set_parent(self) self.priv.child = widget else log.warning("GtkDemo.RotatedBin cannot have more than one child") end end function GtkDemo.RotatedBin:do_remove(widget) local was_visible = widget.visible if self.priv.child == widget then widget:unparent() self.priv.child = nil if was_visible and self.visible then self:queue_resize() end end end function GtkDemo.RotatedBin:do_forall(include_internals, callback) if self.priv.child then callback(self.priv.child, callback.user_data) end end function GtkDemo.RotatedBin:set_angle(angle) self.priv.angle = angle self:queue_resize() self.priv.offscreen_window:geometry_changed() end local function size_request(self) local child_requisition = Gtk.Requisition() if self.priv.child and self.priv.child.visible then child_requisition = self.priv.child:get_preferred_size() end local s, c = math.sin(self.priv.angle), math.cos(self.priv.angle) local w = c * child_requisition.width + s * child_requisition.height local h = s * child_requisition.width + c * child_requisition.height return w + 2 * self.border_width, h + 2 * self.border_width end function GtkDemo.RotatedBin:do_get_preferred_width() local w, h = size_request(self) return w, w end function GtkDemo.RotatedBin:do_get_preferred_height() local w, h = size_request(self) return h, h end function GtkDemo.RotatedBin:do_size_allocate(allocation) self:set_allocation(allocation) local w = allocation.width - self.border_width * 2 local h = allocation.height - self.border_width * 2 if self.realized then self.window:move_resize(allocation.x + self.border_width, allocation.y + self.border_width, w, h) end if self.priv.child and self.priv.child.visible then local s, c = math.sin(self.priv.angle), math.cos(self.priv.angle) local child_requisition = self.priv.child:get_preferred_size() local child_allocation = Gtk.Allocation { height = child_requisition.height } if c == 0 then child_allocation.width = h / s elseif s == 0 then child_allocation.width = w / c else child_allocation.width = math.min( (w - s * child_allocation.height) / c, (h - c * child_allocation.width) / s) end if self.realized then self.priv.offscreen_window:move_resize(child_allocation.x, child_allocation.y, child_allocation.width, child_allocation.height) end child_allocation.x = 0 child_allocation.y = 0 self.priv.child:size_allocate(child_allocation) end end function GtkDemo.RotatedBin:do_damage(event) self.window:invalidate_rect(nil, false) return true end function GtkDemo.RotatedBin:_class_init() -- Unfortunately, damage-event signal does not have virtual -- function associated, so we have to go through following funky -- dance to install default signal handler. GObject.signal_override_class_closure( GObject.signal_lookup('damage-event', Gtk.Widget), GtkDemo.RotatedBin, GObject.Closure(GtkDemo.RotatedBin.do_damage, Gtk.Widget.on_damage_event)) end function GtkDemo.RotatedBin:do_draw(cr) if cr:should_draw_window(self.window) then if self.priv.child and self.priv.child.visible then local surface = Gdk.offscreen_window_get_surface( self.priv.offscreen_window) local child_area = self.priv.child.allocation -- transform local s, c = math.sin(self.priv.angle), math.cos(self.priv.angle) local w = c * child_area.width + s * child_area.height local h = s * child_area.width + c * child_area.height cr:translate((w - child_area.width) / 2, (h - child_area.height) / 2) cr:translate(child_area.width / 2, child_area.height / 2) cr:rotate(self.priv.angle) cr:translate(-child_area.width / 2, -child_area.height / 2) -- clip cr:rectangle(0, 0, self.priv.offscreen_window:get_width(), self.priv.offscreen_window:get_height()) cr:clip() -- paint cr:set_source_surface(surface, 0, 0) cr:paint() end end if cr:should_draw_window(self.priv.offscreen_window) then Gtk.render_background(self.style_context, cr, 0, 0, self.priv.offscreen_window:get_width(), self.priv.offscreen_window:get_height()) if self.priv.child then self:propagate_draw(self.priv.child, cr) end end return false end local window = Gtk.Window { title = "Rotated widget", border_width = 10, Gtk.Box { orientation = 'VERTICAL', Gtk.Scale { id = 'scale', orientation = 'HORIZONTAL', adjustment = Gtk.Adjustment { lower = 0, upper = math.pi / 2, step_increment = 0.01, }, draw_value = false, }, GtkDemo.RotatedBin { id = 'bin', Gtk.Button { label = "A Button", expand = true, }, }, } } function window.child.scale:on_value_changed() window.child.bin:set_angle(self.adjustment.value) end window:override_background_color(0, Gdk.RGBA.parse('black')) window:show_all() return window end, "Offscreen windows/Rotated button", table.concat { [[Offscreen windows can be used to transform parts or a ]], [[widget hierarchy. Note that the rotated button is fully functional.]], } lgi-0.9.2/samples/gtk-demo/demo-paned.lua000066400000000000000000000071711316674307300202040ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local Gtk = lgi.Gtk local window = Gtk.Window { title = "Panes", Gtk.Box { orientation = 'VERTICAL', Gtk.Paned { orientation = 'VERTICAL', Gtk.Paned { id = 'paned_top', orientation = 'HORIZONTAL', Gtk.Frame { id = 'paned_left', shadow_type = 'IN', height_request = 60, width_request = 60, Gtk.Button { label = "_Hi there", use_underline = true, }, }, Gtk.Frame { id = 'paned_right', shadow_type = 'IN', height_request = 80, width_request = 60, }, }, Gtk.Frame { id = 'paned_bottom', shadow_type = 'IN', height_request = 60, width_request = 80, }, }, Gtk.Frame { label = "Horizontal", border_width = 4, Gtk.Grid { { left_attach = 0, top_attach = 0, Gtk.Label { label = "Left" }, }, { left_attach = 0, top_attach = 1, Gtk.CheckButton { id = 'resize_left', label = "_Resize", use_underline = true, }, }, { left_attach = 0, top_attach = 2, Gtk.CheckButton { id = 'shrink_left', label = "_Shrink", use_underline = true, }, }, { left_attach = 1, top_attach = 0, Gtk.Label { label = "Right" }, }, { left_attach = 1, top_attach = 1, Gtk.CheckButton { id = 'resize_right', label = "_Resize", use_underline = true, }, }, { left_attach = 1, top_attach = 2, Gtk.CheckButton { id = 'shrink_right', label = "_Shrink", use_underline = true, }, }, }, }, Gtk.Frame { label = "Vertical", border_width = 4, Gtk.Grid { { left_attach = 0, top_attach = 0, Gtk.Label { label = "Top" }, }, { left_attach = 0, top_attach = 1, Gtk.CheckButton { id = 'resize_top', label = "_Resize", use_underline = true, }, }, { left_attach = 0, top_attach = 2, Gtk.CheckButton { id = 'shrink_top', label = "_Shrink", use_underline = true, }, }, { left_attach = 1, top_attach = 0, Gtk.Label { label = "Bottom" }, }, { left_attach = 1, top_attach = 1, Gtk.CheckButton { id = 'resize_bottom', label = "_Resize", use_underline = true, }, }, { left_attach = 1, top_attach = 2, Gtk.CheckButton { id = 'shrink_bottom', label = "_Shrink", use_underline = true, }, }, }, }, }, } -- Connect servicing routines for all toggles. for _, pos in ipairs { 'left', 'right', 'top', 'bottom' } do local child = window.child['paned_' .. pos] local paned = child.parent local resize = window.child['resize_' .. pos] resize.active = paned.property[child].resize function resize:on_clicked() paned.property[child].resize = self.active end local shrink = window.child['shrink_' .. pos] shrink.active = paned.property[child].shrink function shrink:on_clicked() paned.property[child].shrink = self.active end end window:show_all() return window end, "Paned Widgets", table.concat { [[The Gtk.Paned widget divide its content area into two panes ]], [[with a divider in between that the user can adjust. A separate ]], [[child is placed into each pane. ]], [[There are a number of options that can be set for each pane. ]], [[This test contains both a horizontal and a vertical widget, ]], [[and allows you to adjust the options for each side of each widget.]], } lgi-0.9.2/samples/gtk-demo/demo-pickers.lua000066400000000000000000000035131316674307300205510ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local Gtk = lgi.Gtk local window = Gtk.Window { title = "Pickers", border_width = 10, Gtk.Grid { border_width = 10, row_spacing = 3, column_spacing = 10, { left_attach = 0, top_attach = 0, Gtk.Label { label = "Color:", halign = 'START', valign = 'CENTER', hexpand = true, }, }, { left_attach = 1, top_attach = 0, Gtk.ColorButton {}, }, { left_attach = 0, top_attach = 1, Gtk.Label { label = "Font:", halign = 'START', valign = 'CENTER', hexpand = true, }, }, { left_attach = 1, top_attach = 1, Gtk.FontButton {}, }, { left_attach = 0, top_attach = 2, Gtk.Label { label = "File:", halign = 'START', valign = 'CENTER', hexpand = true, }, }, { left_attach = 1, top_attach = 2, Gtk.FileChooserButton { title = "Pick a File", action = 'OPEN', }, }, { left_attach = 0, top_attach = 3, Gtk.Label { label = "Folder:", halign = 'START', valign = 'CENTER', hexpand = true, }, }, { left_attach = 1, top_attach = 3, Gtk.FileChooserButton { title = "Pick a Folder", action = 'SELECT_FOLDER', }, }, { left_attach = 0, top_attach = 4, Gtk.Label { label = "Mail:", halign = 'START', valign = 'CENTER', hexpand = true, }, }, { left_attach = 1, top_attach = 4, Gtk.AppChooserButton { content_type = 'x-scheme-handler/mailto', show_dialog_item = true, }, }, } } window:show_all() return window end, "Pickers", table.concat { [[These widgets are mainly intended for use in preference ]], [[dialogs. They allow to select colors, fonts, files, directories ]], [[and applications.]] } lgi-0.9.2/samples/gtk-demo/demo-pixbufs.lua000066400000000000000000000061061316674307300205720ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local GLib = lgi.GLib local Gtk = lgi.Gtk local Gdk = lgi.Gdk local GdkPixbuf = lgi.GdkPixbuf local cairo = lgi.cairo local assert = lgi.assert -- Load pixbuf images. local background = assert(GdkPixbuf.Pixbuf.new_from_file( dir:get_child('background.jpg'):get_path())) local back_width, back_height = background.width, background.height local images = {} for _, name in ipairs { 'apple-red.png', 'gnome-applets.png', 'gnome-calendar.png', 'gnome-foot.png', 'gnome-gmush.png', 'gnome-gimp.png', 'gnome-gsame.png', 'gnu-keys.png' } do images[#images + 1] = assert(GdkPixbuf.Pixbuf.new_from_file( dir:get_child(name):get_path())) end local window = Gtk.Window { title = "Pixbufs", resizable = false, Gtk.DrawingArea { id = 'area', width = back_width, height = back_height, }, } local frame = GdkPixbuf.Pixbuf.new('RGB', false, 8, back_width, back_height) local area = window.child.area function area:on_draw(cr) cr:set_source_pixbuf(frame, 0, 0) cr:paint() return true end local FRAME_DELAY = 50 local CYCLE_LEN = 60 local frame_num = 0 local sin, cos, pi, floor, abs, min, max = math.sin, math.cos, math.pi, math.floor, math.abs, math.min, math.max local timeout_id = GLib.timeout_add( GLib.PRIORITY_DEFAULT, FRAME_DELAY, function() background:copy_area(0, 0, back_width, back_height, frame, 0, 0) local f = (frame_num % CYCLE_LEN) / CYCLE_LEN local xmid, ymid = back_width / 2, back_height / 2 local radius = min(xmid, ymid) / 2 local r1 = Gdk.Rectangle() local r2 = Gdk.Rectangle { x = 0, y = 0, width = back_width, height = back_height } for i = 1, #images do local ang = 2 * pi * (i / #images - f) local iw, ih = images[i].width, images[i].height local r = radius + (radius / 3) * sin(2 * pi * f) local xpos = floor(xmid + r * cos(ang) - iw / 2 + 0.5) local ypos = floor(ymid + r * sin(ang) - ih / 2 + 0.5) local k = (i % 2 == 0) and sin(f * 2 * pi) or cos(f * 2 * pi) k = max(2 * k * k, 0.25) r1.x = xpos r1.y = ypos r1.width = iw * k r1.height = ih * k local dest = Gdk.Rectangle.intersect(r1, r2) if dest then local alpha = (i % 1 == 0) and sin(f * 2 * pi) or cos(f * 2 * pi) images[i]:composite(frame, dest.x, dest.y, dest.width, dest.height, xpos, ypos, k, k, 'NEAREST', max(127, abs(alpha))) end end area:queue_draw() frame_num = frame_num + 1 return GLib.SOURCE_CONTINUE end) function window:on_destroy() GLib.source_remove(timeout_id) end window:show_all() return window end, "Pixbufs", table.concat { [[A Gdk.Pixbuf represents an image, normally in RGB or RGBA format. ]], [[Pixbufs are normally used to load files from disk and perform image ]], [[scaling. ]], [[This demo is not all that educational, but looks cool. It was ]], [[written by Extreme Pixbuf Hacker Federico Mena Quintero. It also ]], [[shows off how to use Gtk.DrawingArea to do a simple animation. ]], [[Look at the Image demo for additional pixbuf usage examples.]], } lgi-0.9.2/samples/gtk-demo/demo-printing.lua000066400000000000000000000066771316674307300207610ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local GLib = lgi.GLib local Gio = lgi.Gio local Gtk = lgi.Gtk local Gdk = lgi.Gdk local cairo = lgi.cairo local Pango = lgi.Pango local PangoCairo = lgi.PangoCairo local assert = lgi.assert -- Prepare settings. local settings = Gtk.PrintSettings {} local outdir = GLib.get_user_special_dir('DIRECTORY_DOCUMENTS') or GLib.get_home_dir() settings:set(Gtk.PRINT_OUTPUT_URI, 'file://' .. outdir .. '/gtk-demo.' .. (settings:get(Gtk.PRINT_OUTPUT_FILE_FORMAT) or 'pdf')) -- Create the print operation. local operation = Gtk.PrintOperation { use_full_page = true, unit = 'POINTS', embed_page_setup = true, print_settings = settings } local HEADER_HEIGHT = 10 * 72 / 25.4 local HEADER_GAP = 3 * 72 / 25.4 local font_size = 12 local contents function operation:on_begin_print(context) contents = {} local height = context:get_height() - HEADER_HEIGHT - HEADER_GAP contents.lines_per_page = math.floor(height / font_size) -- Parse input stream into lines. local file = dir:get_child('demo-printing.lua') contents.filename = file:query_info( Gio.FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, 'NONE'):get_display_name() local input = Gio.DataInputStream { newline_type = 'ANY', base_stream = assert(file:read()), } while true do local line, len = input:read_line_utf8() if not line and len == 0 then break end assert(line, len) contents[#contents + 1] = line end contents.num_pages = math.floor((#contents - 1) / contents.lines_per_page + 1) self:set_n_pages(contents.num_pages) end function operation:on_draw_page(context, page_nr) local cr = context:get_cairo_context() local width = context:get_width() cr:rectangle(0, 0, width, HEADER_HEIGHT) cr:set_source_rgb(0.8, 0.8, 0.8) cr:fill_preserve() cr:set_source_rgb(0, 0, 0) cr.line_width = 1 cr:stroke() local layout = context:create_pango_layout() layout.font_description = Pango.FontDescription.from_string('sans 14') layout.text = contents.filename local text_width, text_height = layout:get_pixel_size() if text_width > width then layout.width = width layout.ellipsize = 'START' text_width, text_height = layout:get_pixel_size() end cr:move_to((width - text_width) / 2, (HEADER_HEIGHT - text_height) / 2) cr:show_layout(layout) layout.text = ("%d/%d"):format(page_nr + 1, contents.num_pages) layout.width = -1 text_width, text_height = layout:get_pixel_size() cr:move_to(width - text_width - 4, (HEADER_HEIGHT - text_height) / 2) cr:show_layout(layout) layout = context:create_pango_layout() layout.font_description = Pango.FontDescription.from_string('monospace') cr:move_to(0, HEADER_HEIGHT + HEADER_GAP) local line = page_nr * contents.lines_per_page for i = 1, math.min(#contents - line, contents.lines_per_page) do layout.text = contents[line + i] cr:show_layout(layout) cr:rel_move_to(0, font_size) end end -- Run the operation local ok, err = operation:run('PRINT_DIALOG', parent) if not ok then local dialog = Gtk.MessageDialog { transient_for = parent, destroy_with_parent = true, message_type = 'ERROR', buttons = 'CLOSE', message = err, on_response = Gtk.Widget.destroy, } dialog:show_all() end end, "Printing", table.concat { [[Gtk.PrintOperation offers a simple API to support printing ]], [[in a cross-platform way.]], } lgi-0.9.2/samples/gtk-demo/demo-rotatedtext.lua000066400000000000000000000102101316674307300214500ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local Gtk = lgi.Gtk local Gdk = lgi.Gdk local cairo = lgi.cairo local Pango = lgi.Pango local PangoCairo = lgi.PangoCairo local RADIUS = 150 local N_WORDS = 5 local FONT = 'Serif 18' local HEART = '♥' local TEXT = 'I ♥ GTK+' local window = Gtk.Window { title = "Rotated Text", default_width = 4 * RADIUS, default_height = 2 * RADIUS, Gtk.Box { orientation = 'HORIZONTAL', homogeneous = true, Gtk.DrawingArea { id = 'circle', }, Gtk.Label { id = 'label', angle = 45, label = TEXT, }, } } -- Override background color of circle drawing area. window.child.circle:override_background_color( 'NORMAL', Gdk.RGBA { red = 1, green = 1, blue = 1, alpha = 1 }) local function fancy_shape_renderer(cr, attr, do_path) cr:translate(cr:get_current_point()) cr:scale(attr.ink_rect.width / Pango.SCALE, attr.ink_rect.height / Pango.SCALE) -- Draw the manually. cr:move_to(0.5, 0) cr:line_to(0.9, -0.4) cr:curve_to(1.1, -0.8, 0.5, -0.9, 0.5, -0.5) cr:curve_to(0.5, -0.9, -0.1, -0.8, 0.1, -0.4) cr:close_path() if not do_path then cr:set_source_rgb(1, 0, 0) cr:fill() end end local function create_fancy_attr_list_for_layout(layout) -- Get font metrics and prepare fancy shape size. local ascent = layout.context:get_metrics(layout.font_description).ascent local rect = Pango.Rectangle { x = 0, width = ascent, y = -ascent, height = ascent } -- Create attribute list, add specific shape renderer for every -- occurence of heart symbol. local attrs = Pango.AttrList() local start_index, end_index = 1, 1 while true do start_index, end_index = TEXT:find(HEART, end_index + 1, true) if not start_index then break end local attr = Pango.Attribute.shape_new(rect, rect) attr.start_index = start_index - 1 attr.end_index = end_index attrs:insert(attr) end return attrs end function window.child.circle:on_draw(cr) -- Create a cairo context and set up a transformation matrix so that -- the user space coordinates for the centered square where we draw -- are [-RADIUS, RADIUS], [-RADIUS, RADIUS]. We first center, then -- change the scale. local device_radius = math.min(self.width, self.height) / 2 cr:translate(device_radius + (self.width - 2 * device_radius) / 2, device_radius + (self.height - 2 * device_radius) / 2) cr:scale(device_radius / RADIUS, device_radius / RADIUS) -- Create a subtle gradient source and use it. local pattern = cairo.Pattern.create_linear(-RADIUS, -RADIUS, RADIUS, RADIUS) pattern:add_color_stop_rgb(0, 0.5, 0, 0) pattern:add_color_stop_rgb(1, 0, 0, 0.5) cr:set_source(pattern) -- Create a Pango.Context and set up our shape renderer. local context = self:create_pango_context() context.shape_renderer = fancy_shape_renderer -- Create a Pango.Layout, set the text, font and attributes. local layout = Pango.Layout.new(context) layout.text = TEXT layout.font_description = Pango.FontDescription.from_string(FONT) layout.attributes = create_fancy_attr_list_for_layout(layout) -- Draw the layout N_WORDS times in a circle. for i = 1, N_WORDS do -- Inform Pango to re-layout the text with the new transformation -- matrix. cr:update_layout(layout) local width, height = layout:get_pixel_size() cr:move_to(-width / 2, -RADIUS * 0.9) cr:show_layout(layout) -- Rotate for the next turn. cr:rotate(2 * math.pi / N_WORDS) end end -- Set up fancy stuff on the label. local label = window.child.label local layout = label:get_layout() layout.context.shape_renderer = fancy_shape_renderer label:set_attributes(create_fancy_attr_list_for_layout(layout)) window:show_all() return window end, "Rotated Text", table.concat { [[This demo shows how to use PangoCairo to draw rotated and transformed ]], [[text. The right pane shows a rotated Gtk.Label widget. ]], [[In both cases, a custom PangoCairo shape renderer is installed ]], [[to draw a red heard using cairo drawing operations instead of ]], [[the Unicode heart character.]] } lgi-0.9.2/samples/gtk-demo/demo-sizegroup.lua000066400000000000000000000050441316674307300211410ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local Gtk = lgi.Gtk local window = Gtk.Dialog { title = "Gtk.SizeGroup", transient_for = parent, buttons = { { Gtk.STOCK_CLOSE, Gtk.ResponseType.NONE }, }, resizable = false, on_response = Gtk.Widget.destroy, } window:get_content_area():add( Gtk.Box { orientation = 'VERTICAL', border_width = 5, spacing = 5, Gtk.Frame { label = "Color Options", Gtk.Grid { id = 'colors', row_spacing = 5, column_spacing = 10, } }, Gtk.Frame { label = "Line Options", Gtk.Grid { id = 'lines', row_spacing = 5, column_spacing = 10, } }, Gtk.CheckButton { id = 'enable_grouping', label = "_Enable grouping", use_underline = true, active = true, }, }) local size_group = Gtk.SizeGroup { mode = 'HORIZONTAL' } local function add_row(grid, row, label, strings) local combo = Gtk.ComboBoxText {} for _, text in ipairs(strings) do combo:append_text(text) end combo.active = 0 size_group:add_widget(combo) grid:add { left_attach = 0, top_attach = row, Gtk.Label { label = label, use_underline = true, halign = 'START', valign = 'END', hexpand = true, mnemonic_widget = combo, } } grid:add { left_attach = 1, top_attach = row, combo } end add_row(window.child.colors, 0, "_Foreground", { "Red", "Green", "Blue", }) add_row(window.child.colors, 1, "_Background", { "Red", "Green", "Blue", }) add_row(window.child.lines, 0, "_Dashing", { "Solid", "Dashed", "Dotted", }) add_row(window.child.lines, 1, "_Line ends", { "Square", "Round", "Arrow", }) function window.child.enable_grouping:on_toggled() size_group.mode = self.active and 'HORIZONTAL' or 'NONE' end window:show_all() return window end, "Size Groups", table.concat { [[Gtk.SizeGroup provides a mechanism for grouping a number of widgets ]], [[together so they all request the same amount of space. This is ]], [[typically useful when you want a column of widgets to have the same ]], [[size, but you can't use a Gtk.Grid widget. ]], [[Note that size groups only affect the amount of space requested, ]], [[not the size that the widgets finally receive. If you want ]], [[the widgets in a Gtk.SizeGroup to actually be the same size, ]], [[you need to pack them in such a way that they get the size they ]], [[request and not more. For example, if you are packing your widgets ]], [[into a grid, you would not include the 'FILL' flag.]], } lgi-0.9.2/samples/gtk-demo/demo-spinner.lua000066400000000000000000000024601316674307300205670ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local Gtk = lgi.Gtk local window = Gtk.Dialog { title = "Gtk.Spinner", transient_for = parent, buttons = { { Gtk.STOCK_CLOSE, Gtk.ResponseType.NONE }, }, resizable = false, on_response = Gtk.Widget.destroy, } window:get_content_area():add( Gtk.Box { orientation = 'VERTICAL', border_width = 5, spacing = 5, Gtk.Box { orientation = 'HORIZONTAL', spacing = 5, Gtk.Spinner { id = 'sensitive', active = true, }, Gtk.Entry {}, }, Gtk.Box { orientation = 'HORIZONTAL', spacing = 5, Gtk.Spinner { id = 'insensitive', sensitive = false, active = true, }, Gtk.Entry {}, }, Gtk.Button { id = 'play', label = Gtk.STOCK_MEDIA_PLAY, use_stock = true, }, Gtk.Button { id = 'stop', label = Gtk.STOCK_MEDIA_STOP, use_stock = true, }, }) function window.child.play:on_clicked() window.child.sensitive.active = true window.child.insensitive.active = true end function window.child.stop:on_clicked() window.child.sensitive.active = false window.child.insensitive.active = false end window:show_all() return window end, "Spinner", table.concat { [[Gtk.Spinner allows to show that background activity is on-going.]] } lgi-0.9.2/samples/gtk-demo/demo-stockbrowser.lua000066400000000000000000000127121316674307300216410ustar00rootroot00000000000000return function(parent, dir) local table = require 'table' local lgi = require 'lgi' local GLib = lgi.GLib local GObject = lgi.GObject local Gtk = lgi.Gtk local GdkPixbuf = lgi.GdkPixbuf local BrowserColumn = { ID = 1, LABEL = 2, SMALL_ICON = 3, ACCEL_STR = 4, MACRO = 5, } local function create_model() local store = Gtk.ListStore.new { [BrowserColumn.ID] = GObject.Type.STRING, [BrowserColumn.LABEL] = GObject.Type.STRING, [BrowserColumn.SMALL_ICON] = GdkPixbuf.Pixbuf, [BrowserColumn.ACCEL_STR] = GObject.Type.STRING, [BrowserColumn.MACRO] = GObject.Type.STRING, } local ids = Gtk.stock_list_ids() table.sort(ids) local icon_width, icon_height = Gtk.IconSize.lookup(Gtk.IconSize.MENU) for _, id in ipairs(ids) do local item = Gtk.stock_lookup(id) local macro = GLib.ascii_strup(id, -1):gsub( '^GTK%-', 'Gtk.STOCK_'):gsub('%-', '_') local small_icon local icon_set = Gtk.IconFactory.lookup_default(id) if icon_set then -- Prefer menu size if it exists, otherwise take the first -- available size. local sizes = icon_set:get_sizes() local size = sizes[0] for i = 1, #sizes do if sizes[i] == Gtk.IconSize.MENU then size = Gtk.IconSize.MENU break end end small_icon = parent:render_icon_pixbuf(id, size) if size ~= Gtk.IconSize.MENU then -- Make the result proper size for thumbnail. small_icon = small_icon:scale_simple(icon_width, icon_height, 'BILINEAR') end end local accel_str if item and item.keyval ~= 0 then accel_str = Gtk.accelerator_name(item.keyval, item.modifier) end store:append { [BrowserColumn.ID] = id, [BrowserColumn.LABEL] = item and item.label, [BrowserColumn.SMALL_ICON] = small_icon, [BrowserColumn.ACCEL_STR] = accel_str, [BrowserColumn.MACRO] = macro, } end return store end local window = Gtk.Window { title = "Stock Icons and Items", default_height = 500, border_width = 8, Gtk.Box { orientation = 'HORIZONTAL', spacing = 8, Gtk.ScrolledWindow { hscrollbar_policy = 'NEVER', expand = true, Gtk.TreeView { id = 'treeview', model = create_model(), Gtk.TreeViewColumn { title = "Identifier", { Gtk.CellRendererPixbuf {}, align = 'start', { stock_id = BrowserColumn.ID }, }, { Gtk.CellRendererText {}, expand = true, align = 'start', { text = BrowserColumn.MACRO }, }, }, Gtk.TreeViewColumn { title = "Label", { Gtk.CellRendererText {}, { text = BrowserColumn.LABEL }, }, }, Gtk.TreeViewColumn { title = "Accel", { Gtk.CellRendererText {}, { text = BrowserColumn.ACCEL_STR }, }, }, Gtk.TreeViewColumn { title = "ID", { Gtk.CellRendererText {}, { text = BrowserColumn.ID }, }, }, }, }, Gtk.Box { orientation = 'VERTICAL', spacing = 8, border_width = 4, Gtk.Label { id = 'type_label', }, Gtk.Image { id = 'icon_image', }, Gtk.Label { id = 'accel_label', use_underline = true, }, Gtk.Label { id = 'macro_label', }, Gtk.Label { id = 'id_label', }, }, } } local display = { type_label = window.child.type_label, icon_image = window.child.icon_image, accel_label = window.child.accel_label, macro_label = window.child.macro_label, id_label = window.child.id_label, } local selection = window.child.treeview:get_selection() selection.mode = 'BROWSE' function selection:on_changed() local model, iter = self:get_selected() local view = self:get_tree_view() if model and iter then local row = model[iter] if row[BrowserColumn.SMALL_ICON] and row[BrowserColumn.LABEL] then display.type_label.label = "Icon and Item" elseif row[BrowserColumn.SMALL_ICON] then display.type_label.label = "Icon only" elseif row[BrowserColumn.LABEL] then display.type_label.label = "Item Only" else display.type_label.label = '' end display.id_label.label = row[BrowserColumn.ID] display.macro_label.label = row[BrowserColumn.MACRO] if row[BrowserColumn.LABEL] then display.accel_label.label = ("%s %s"):format( row[BrowserColumn.LABEL], row[BrowserColumn.ACCEL_STR] or '') else display.accel_label.label = '' end if row[BrowserColumn.SMALL_ICON] then -- Find the larget available icon size. local best_size, best_pixels = Gtk.IconSize.INVALID, 0 for _, size in ipairs(Gtk.IconFactory.lookup_default( row[BrowserColumn.ID]):get_sizes()) do local width, height = Gtk.IconSize.lookup(size) if width * height > best_pixels then best_pixels = width * height best_size = size end end display.icon_image.stock = row[BrowserColumn.ID] display.icon_image.icon_size = best_size else display.icon_image.pixbuf = nil end else display.type_label.label = "No selected item" display.macro_label.label = '' display.id_label.label = '' display.accel_label.label = '' display.icon_image.pixbuf = nil end end window:show_all() return window end, "Stock Item and Icon Browser", table.concat { [[This source code for this demo doesn't demonstrate anything ]], [[particularly useful in applications. The purpose of the "demo" ]], [[is just to provide a handy place to browse the available stock ]], [[icons and stock items.]] } lgi-0.9.2/samples/gtk-demo/demo-text-hypertext.lua000066400000000000000000000107531316674307300221330ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local Gtk = lgi.Gtk local Gdk = lgi.Gdk local Pango = lgi.Pango local window = Gtk.Window { title = "Hypertext", default_width = 450, default_height = 450, Gtk.ScrolledWindow { Gtk.TextView { id = 'textview', wrap_mode = 'WORD', }, }, } -- Sample hypertext content. local content = { intro = [[ Some text to show that simple [hypertext hypertext] can easily be realized with [tags tags]. ]], hypertext = [[ *hypertext:* machine-readable text that is not sequential but is organized so that related items of information are connected. [intro Go back] ]], tags = [[ A tag is an attribute that can be applied to some range of text. For example, a tag might be called "bold" and make the text inside the tag bold. However, the tag concept is more general than that; tags don't have to affect appearance. They can instead affect the behavior of mouse and key presses, "lock" a range of text so the user can't edit it, or countless other things. [intro Go back] ]], } local active_links local handlers = { ['^([^%[%*]+)'] = -- Plaintext. function(text) return text end, ['^%[(%w+) ([^%]]+)%]'] = -- Link. function(link, text) local tag = Gtk.TextTag { foreground = 'blue', underline = Pango.Underline.SINGLE, } active_links[tag] = link return text, tag end, ['^%*([^%*]+)%*'] = -- Bold text. function(text) return text, Gtk.TextTag { weight = Pango.Weight.BOLD, } end, } local function fill_page(page) local buffer = window.child.textview.buffer buffer.text = '' active_links = {} if not page then return end local iter = buffer:get_iter_at_offset(0) local pos = 1 repeat for pattern, handler in pairs(handlers) do local start, stop, m1, m2 = page:find(pattern, pos) if start then -- Extract next part of the text. local text, tag = handler(m1, m2) -- Add text into the buffer. start = iter:get_offset() buffer:insert(iter, text, -1) -- Apply tag, if available. if tag then buffer.tag_table:add(tag) buffer:apply_tag(tag, buffer:get_iter_at_offset(start), iter) end -- Prepare for the next iteration. pos = stop + 1 break end end until pos >= #page end local cursors = { [true] = Gdk.Cursor.new('HAND2'), [false] = Gdk.Cursor.new('XTERM'), } local hovering = false local function set_cursor_if_appropriate(view, x, y) local tags = view:get_iter_at_location(x, y):get_tags() local should_hover = false for i = 1, #tags do if active_links[tags[i]] then should_hover = true break end end if hovering ~= should_hover then hovering = should_hover view:get_window('TEXT'):set_cursor(cursors[hovering]) end end local textview = window.child.textview function textview:on_motion_notify_event(event) set_cursor_if_appropriate( self, self:window_to_buffer_coords('WIDGET', event.x, event.y)) return false end function textview:on_visibility_notify_event(event) local x, y = self.window:get_pointer() if x and y then set_cursor_if_appropriate( self, self:window_to_buffer_coords('WIDGET', x, y)) end return false end local function follow_if_link(view, iter) for _, tag in ipairs(iter:get_tags()) do if active_links[tag] then fill_page(content[active_links[tag]]) break end end end function textview:on_event_after(event) if event.type == 'BUTTON_RELEASE' and event.button.button == 1 then -- Don't follow link if anything is selected. local start, stop = self.buffer:get_selection_bounds() if not start or not stop or start:get_offset() == stop:get_offset() then follow_if_link(self, self:get_iter_at_location( self:window_to_buffer_coords( 'WIDGET', event.button.x, event.button.y))) end end end function textview:on_key_press_event(event) if event.keyval == Gdk.KEY_Return or event.keyval == Gdk.KEY_KP_Enter then follow_if_link( self, self.buffer:get_iter_at_mark(self.buffer:get_insert())) end return false end -- Initially fill the intro page. fill_page(content.intro) window:show_all() return window end, "Text Widget/Hypertext", table.concat { [[Usually, tags modify the appearance of text in the view, ]], [[e.g. making it bold or colored or underlined. But tags are not ]], [[restricted to appearance. They can also affect the behavior of ]], [[mouse and key presses, as this demo shows.]], } lgi-0.9.2/samples/gtk-demo/demo-text-multiview.lua000066400000000000000000000231351316674307300221220ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local Gtk = lgi.Gtk local Gdk = lgi.Gdk local Pango = lgi.Pango local GdkPixbuf = lgi.GdkPixbuf -- Create shared text buffer. local buffer = Gtk.TextBuffer { tag_table = Gtk.TextTagTable { -- Create a bunch of tags. Gtk.TextTag { name = 'heading', weight = Pango.Weight.BOLD, size = 15 * Pango.SCALE, }, Gtk.TextTag { name = 'italic', style = Pango.Style.ITALIC, }, Gtk.TextTag { name = 'bold', weight = Pango.Weight.BOLD, }, Gtk.TextTag { name = 'big', -- points times the Pango.SCALE factor size = 20 * Pango.SCALE, }, Gtk.TextTag { name = 'xx-small', scale = Pango.SCALE_XX_SMALL, }, Gtk.TextTag { name = 'x-large', scale = Pango.SCALE_X_LARGE, }, Gtk.TextTag { name = 'monospace', family = 'monospace', }, Gtk.TextTag { name = 'blue_foreground', foreground = 'blue', }, Gtk.TextTag { name = 'red_background', background = 'red', }, Gtk.TextTag { name = 'big_gap_before_line', pixels_above_lines = 30, }, Gtk.TextTag { name = 'big_gap_after_line', pixels_below_lines = 30, }, Gtk.TextTag { name = 'double_spaced_line', pixels_inside_wrap = 10, }, Gtk.TextTag { name = 'not_editable', editable = false, }, Gtk.TextTag { name = 'word_wrap', wrap_mode = 'WORD', }, Gtk.TextTag { name = 'char_wrap', wrap_mode = 'CHAR', }, Gtk.TextTag { name = 'no_wrap', wrap_mode = 'NONE', }, Gtk.TextTag { name = 'center', justification = 'CENTER', }, Gtk.TextTag { name = 'right_justify', justification = 'RIGHT', }, Gtk.TextTag { name = 'wide_margins', left_margin = 50, right_margin = 50, }, Gtk.TextTag { name = 'strikethrough', strikethrough = true, }, Gtk.TextTag { name = 'underline', underline = 'SINGLE', }, Gtk.TextTag { name = 'double_underline', underline = 'DOUBLE', }, Gtk.TextTag { name = 'superscript', rise = 10 * Pango.SCALE, -- 10 pixels size = 8 * Pango.SCALE, -- 8 points }, Gtk.TextTag { name = 'subscript', rise = -10 * Pango.SCALE, -- 10 pixels size = 8 * Pango.SCALE, -- 8 points }, Gtk.TextTag { name = 'rtl_quote', wrap_mode = 'WORD', direction = 'RTL', indent = 30, left_margin = 20, right_margin = 20, }, }, } local pixbuf = GdkPixbuf.Pixbuf.new_from_file( dir:get_child('gtk-logo-rgb.gif'):get_path()) pixbuf = pixbuf:scale_simple(32, 32, 'BILINEAR') local anchors = {} local iter = buffer:get_iter_at_offset(0) for _, item in ipairs { { [[ The text widget can display text with all kinds of nifty attributes. It also supports multiple views of the same buffer; this demo is showing the same buffer in two places. ]] }, { [[Font styles. ]], 'heading' }, { [[For example, you can have ]] }, { [[italic]], 'italic' }, { [[, ]] }, { [[bold]], 'bold' }, { [[, or ]] }, { [[monospace (typewriter)]], 'monospace' }, { [[, or ]] }, { [[big]], 'big' }, { [[ text. ]] }, { [[ It's best not to hardcode specific text sizes; you can use relative sizes as with CSS, such as ]] }, { [[xx-small]], 'xx-small' }, { [[ or ]] }, { [[x-large]], 'x-large' }, { [[ to ensure that your program properly adapts if the user changes the default font size. ]] }, { [[Colors. ]], 'heading' }, { [[Colors such as ]] }, { [[a blue foreground]], 'blue_foreground' }, { [[ or ]] }, { [[a red background]], 'red_background' }, { [[ or even ]] }, { [[a blue foreground on red background]], 'blue_foreground', 'red_background' }, { [[ (select that to read it) can be used. ]] }, { [[Underline, strikethrough and rise. ]], 'heading' }, { [[Strikethrough]], 'strikethrough' }, { [[, ]] }, { [[underline]], 'underline' }, { [[, ]] }, { [[double underline]], 'double_underline' }, { [[, ]] }, { [[superscript]], 'superscript' }, { [[, and ]] }, { [[subscript]], 'subscript' }, { [[ are all supported. ]] }, { [[Images. ]], 'heading' }, { [[The buffer can have images in it: ]] }, { pixbuf }, { pixbuf }, { pixbuf }, { [[ for example. ]] }, { [[Spacing. ]], 'heading' }, { [[You can adjust the amount of space before each line. ]] }, { [[This line has a whole lot of space before it. ]], 'big_gap_before_line', 'wide_margins' }, { [[You can also adjust the amount of space after each line; this line has a while lot of space after it ]], 'big_gap_after_line', 'wide_margins' }, { [[You can also adjust the amount of space between wrapped lines; this line has extra space between each wrapped line in the same paragraph. To show off wrapping some filler text: the quick brown fox jumped over the lazy dog. Blah blah blah blah blah blah blah blah blah. ]], 'double_spaced_line', 'wide_margins' }, { [[Editability. ]], 'heading' }, { [[This line is 'locked down' and can't be edited by the user - just try it! You can't delete this line. ]], 'not_editable' }, { [[Wrapping. ]], 'heading' }, { [[This line (and most of the others in this buffer) is word-wrapped, using the proper Unicode algorithm. Word wrap should work in all scripts and languages that GTK+ supports. Let's make this a long paragraph to demonstrate: blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah ]] }, { [[This line has character-based wrapping, and can wrap between any two character glyphs. Let's make this a long paragraph to demonstrate: blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah ]], 'char_wrap' }, { [[This line has all wrapping turned off, so it makes the horizontal scrollbar appear. ]], 'no_wrap' }, { [[Justification. ]], 'heading' }, { [[ This line has center justification ]], 'center' }, { [[This line has right jusitification]], 'right_justify' }, { [[ This line has big wide margins. Text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text ]], 'wide_margins' }, { [[Internationalization. ]], 'heading' }, { [[ You can put all sorts of Unicode text in the buffer. German (Deutsch Süd) Grüß Gott Greek (Ελληνικά) Γειά σας Hebrew שלום Japanese (日本語) The widget properly handles bidirectional text, word wrapping, DOS/UNIX/Unicode paragraph separators, grapheme boundaries, and so on using the Pango internationalization framework. Here's a word-wrapped quote in a right-to-left language: ]] }, { [[وقد بدأ ثلاث من أكثر المؤسسات تقدما في شبكة اكسيون برامجها كمنظمات لا تسعى للربح، ثم تحولت في السنوات الخمس الماضية إلى مؤسسات مالية منظمة، وباتت جزءا من النظام المالي في بلدانها، ولكنها تتخصص في خدمة قطاع المشروعات الصغيرة. وأحد أكثر هذه المؤسسات نجاحا هو »بانكوسول« في بوليفيا. ]], 'rtl_quote' }, { [[You can put widgets in the buffer: Here s a button: ]] }, function() return Gtk.Button { label = "Click Me", } end, { [[ and a menu: ]] }, function() local combo = Gtk.ComboBoxText {} combo:append_text("Option 1") combo:append_text("Option 2") combo:append_text("Option 3") return combo end, { [[ and a scale: ]] }, function() local scale = Gtk.Scale { adjustment = Gtk.Adjustment { lower = 0, upper = 100, } } scale:set_size_request(70, -1) return scale end, { [[ and an animation: ]] }, function() return Gtk.Image { file = dir:get_child('floppybuddy.gif'):get_path() } end, { [[ finally a text entry: ]] }, function() return Gtk.Entry() end, { [[. This demo does not demonstrate all the Gtk.TextBuffer features; it leaves out, for example: invisible/hidden text, tab stops, application-drawn areas on the sides of the widget for displaying breakpoints and such...]] } } do if type(item) == 'function' then anchors[buffer:create_child_anchor(iter)] = item elseif type(item[1]) == 'string' then local offset = iter:get_offset() buffer:insert(iter, item[1], -1) for i = 2, #item do buffer:apply_tag_by_name( item[i], buffer:get_iter_at_offset(offset), iter) end elseif GdkPixbuf.Pixbuf:is_type_of(item[1]) then buffer:insert_pixbuf(iter, item[1]) end end -- Apply word_wrap tag to the whole buffer. buffer:apply_tag_by_name('word_wrap', buffer:get_bounds()) local window = Gtk.Window { title = "TextView", default_width = 450, default_height = 450, Gtk.Paned { orientation = 'VERTICAL', border_width = 5, Gtk.ScrolledWindow { Gtk.TextView { id = 'view1', buffer = buffer, }, }, Gtk.ScrolledWindow { Gtk.TextView { id = 'view2', buffer = buffer, } }, }, } -- Create and attach widgets to anchors. for _, view in pairs { window.child.view1, window.child.view2 } do for anchor, creator in pairs(anchors) do view:add_child_at_anchor(creator(), anchor) end end window:show_all() return window end, "Text Widget/Multiple Views", table.concat { [[The Gtk.TextView widget displays a Gtk.TextBuffer. One ]], [[Gtk.TextBuffer can be displayed by multiple Gtk.TextViews. ]], [[This demo has two views displaying a single buffer, and shows off ]], [[the widget's text formatting features.]], } lgi-0.9.2/samples/gtk-demo/demo-text-scrolltoend.lua000066400000000000000000000056371316674307300224340ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local GLib = lgi.GLib local Gtk = lgi.Gtk local window = Gtk.Window { title = "Automatic scrolling", default_width = 600, default_height = 400, Gtk.Box { orientation = 'HORIZONTAL', spacing = 6, homogeneous = true, Gtk.ScrolledWindow { Gtk.TextView { id = 'view1', expand = true, } }, Gtk.ScrolledWindow { Gtk.TextView { id = 'view2', expand = true, } }, }, } for i = 1, 2 do local view = window.child['view' .. i] local buffer = view.buffer local timer if i == 1 then -- If we want to scroll to the end, including horizontal -- scrolling, then we just create a mark with right gravity at -- the end of the buffer. It will stay at the end unless -- explicitely moved with Gtk.TextBuffer.move_mark(). local mark = buffer:create_mark(nil, buffer:get_end_iter(), false) local count = 0 timer = GLib.timeout_add( GLib.PRIORITY_DEFAULT, 50, function() -- Insert to the 'end' mark. buffer:insert(buffer:get_iter_at_mark(mark), '\n' .. (' '):rep(count) .. "Scroll to end scroll to end scroll to end " .."scroll to end ", -1) -- Scroll so that the mark is visible onscreen. view:scroll_mark_onscreen(mark) -- Move to the next column, or if we got too far, scroll -- back to left again. count = (count <= 150) and (count + 1) or 0 return true end) else -- If we want to scroll to the bottom, but not scroll -- horizontally, then an end mark won't do the job. Just use -- Gtk.TextView.scroll_mark_onscreen() explicitely when -- needed. local count = 0 local mark = buffer:create_mark(nil, buffer:get_end_iter(), true) timer = GLib.timeout_add( GLib.PRIORITY_DEFAULT, 100, function() -- Insert some text into the buffer. local iter = buffer:get_end_iter() buffer:insert(iter, '\n' .. (' '):rep(count) .. "Scroll to bottom scroll to bottom scroll to bottom " .."scroll to bottom ", -1) -- Move the iterator to the beginning of line, so we don't -- scroll in horizontal direction. iter:set_line_offset(0) -- Place mark at iter. buffer:move_mark(mark, iter) -- Scroll the mark onscreen. view:scroll_mark_onscreen(mark) -- Move to the next column, or if we got too far, scroll -- back to left again. count = (count <= 40) and (count + 1) or 0 return true end) end -- Make sure that the timer is destroyed when the view is destroyed too. function view:on_destroy() GLib.source_remove(timer) end end window:show_all() return window end, "Text Widget/Automatic scrolling", table.concat { [[This example demonstrates how to use the gravity of Gtk.TextMarks ]], [[to keep a text view scrolled to the bottom when appending text.]] } lgi-0.9.2/samples/gtk-demo/demo-treeview-editable.lua000066400000000000000000000102101316674307300225020ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local GObject = lgi.GObject local Gtk = lgi.Gtk local ItemColumn = { NUMBER = 1, PRODUCT = 2, YUMMY = 3, } local NumberColumn = { TEXT = 1, NUMBER = 2, } -- Fill store with initial items. local item_store = Gtk.ListStore.new { [ItemColumn.NUMBER] = GObject.Type.INT, [ItemColumn.PRODUCT] = GObject.Type.STRING, [ItemColumn.YUMMY] = GObject.Type.INT, } for _, item in ipairs { { [ItemColumn.NUMBER] = 3, [ItemColumn.PRODUCT] = "bottles of coke", [ItemColumn.YUMMY] = 20, }, { [ItemColumn.NUMBER] = 5, [ItemColumn.PRODUCT] = "packages of noodles", [ItemColumn.YUMMY] = 50, }, { [ItemColumn.NUMBER] = 2, [ItemColumn.PRODUCT] = "packages of chocolate chip cookies", [ItemColumn.YUMMY] = 90, }, { [ItemColumn.NUMBER] = 1, [ItemColumn.PRODUCT] = "can vanilla ice cream", [ItemColumn.YUMMY] = 60, }, { [ItemColumn.NUMBER] = 6, [ItemColumn.PRODUCT] = "eggs", [ItemColumn.YUMMY] = 10, }, } do item_store:append(item) end -- Fill store with numbers. local number_store = Gtk.ListStore.new { [NumberColumn.TEXT] = GObject.Type.INT, [NumberColumn.NUMBER] = GObject.Type.STRING, } for i = 1, 10 do number_store:append { [NumberColumn.TEXT] = i, [NumberColumn.NUMBER] = i, } end local window = Gtk.Window { title = "Shopping list", default_width = 320, default_height = 200, border_width = 5, Gtk.Box { orientation = 'VERTICAL', spacing = 5, Gtk.Label { label = "Shopping list (you can edit the cells!)", }, Gtk.ScrolledWindow { shadow_type = 'ETCHED_IN', expand = true, Gtk.TreeView { id = 'view', model = item_store, Gtk.TreeViewColumn { title = "Number", { Gtk.CellRendererCombo { id = 'number_renderer', model = number_store, text_column = NumberColumn.TEXT, has_entry = false, editable = true }, { text = ItemColumn.NUMBER, } }, }, Gtk.TreeViewColumn { title = "Product", { Gtk.CellRendererText { id = 'product_renderer', editable = true, }, { text = ItemColumn.PRODUCT, } } }, Gtk.TreeViewColumn { title = "Yummy", { Gtk.CellRendererProgress {}, { value = ItemColumn.YUMMY, }, } }, }, }, Gtk.Box { orientation = 'HORIZONTAL', spacing = 4, homogeneous = true, Gtk.Button { id = 'add', label = "Add item", }, Gtk.Button { id = 'remove', label = "Remove item", }, }, } } function window.child.number_renderer:on_edited(path_string, new_text) local path = Gtk.TreePath.new_from_string(path_string) item_store[path][ItemColumn.NUMBER] = new_text end function window.child.number_renderer:on_editing_started(editable, path) editable:set_row_separator_func( function(model, iter) return model:get_path(iter):get_indices()[1] == 5 end) end function window.child.product_renderer:on_edited(path_string, new_text) local path = Gtk.TreePath.new_from_string(path_string) item_store[path][ItemColumn.PRODUCT] = new_text end local selection = window.child.view:get_selection() selection.mode = 'SINGLE' function window.child.add:on_clicked() item_store:append { [ItemColumn.NUMBER] = 0, [ItemColumn.PRODUCT] = "Description here", [ItemColumn.YUMMY] = 50, } end function window.child.remove:on_clicked() local model, iter = selection:get_selected() if model and iter then model:remove(iter) end end window:show_all() return window end, "Tree View/Editable Cells", table.concat { [[This demo demonstrates the use of editable cells in a Gtk.TreeView. ]], [[If you're new to the Gtk.TreeView widgets and associates, look into ]], [[the Gtk.ListStore example first. It also shows how to use ]], [[the Gtk.CellRenderer::editing-started signal to do custom setup of ]], [[the editable widget. The cell renderers used in this demo are Gtk.CellRendererText, ]], [[Gtk.CellRendererCombo and GtkCell.RendererProgress.]] } lgi-0.9.2/samples/gtk-demo/demo-treeview-liststore.lua000066400000000000000000000112351316674307300227710ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local GLib = lgi.GLib local GObject = lgi.GObject local Gtk = lgi.Gtk local Column = { FIXED = 1, NUMBER = 2, SEVERITY = 3, DESCRIPTION = 4, PULSE = 5, ICON = 6, ACTIVE = 7, SENSITIVE = 8, } -- Create the list store. local store = Gtk.ListStore.new { [Column.FIXED] = GObject.Type.BOOLEAN, [Column.NUMBER] = GObject.Type.UINT, [Column.SEVERITY] = GObject.Type.STRING, [Column.DESCRIPTION] = GObject.Type.STRING, [Column.PULSE] = GObject.Type.UINT, [Column.ICON] = GObject.Type.STRING, [Column.ACTIVE] = GObject.Type.BOOLEAN, [Column.SENSITIVE] = GObject.Type.BOOLEAN, } -- Populate it with sample data. for i, item in ipairs { { false, 60482, "Normal", "scrollable notebooks and hidden tabs" }, { false, 60620, "Critical", "gdk_window_clear_area (gdkwindow-win32.c) is not thread-safe" }, { false, 50214, "Major", "Xft support does not clean up correctly" }, { true, 52877, "Major", "GtkFileSelection needs a refresh method. " }, { false, 56070, "Normal", "Can't click button after setting in sensitive" }, { true, 56355, "Normal", "GtkLabel - Not all changes propagate correctly" }, { false, 50055, "Normal", "Rework width/height computations for TreeView" }, { false, 58278, "Normal", "gtk_dialog_set_response_sensitive () doesn't work" }, { false, 55767, "Normal", "Getters for all setters" }, { false, 56925, "Normal", "Gtkcalender size" }, { false, 56221, "Normal", "Selectable label needs right-click copy menu" }, { true, 50939, "Normal", "Add shift clicking to GtkTextView" }, { false, 6112, "Enhancement","netscape-like collapsable toolbars" }, { false, 1, "Normal", "First bug :=)" }, } do if i == 2 or i == 4 then item[Column.ICON] = 'battery-caution-charging-symbolic' end item[Column.SENSITIVE] = (i ~= 4) store:append(item) end local window = Gtk.Window { title = "Gtk.ListStore demo", default_width = 280, default_height = 250, border_width = 8, Gtk.Box { orientation = 'VERTICAL', spacing = 8, Gtk.Label { label = "This is the bug list (note: not based on real data, " .. "it would be nice to have a nice ODBC interface to bugzilla " .. "or so, though" }, Gtk.ScrolledWindow { shadow_type = 'ETCHED_IN', hscrollbar_policy = 'NEVER', expand = true, Gtk.TreeView { id = 'view', model = store, Gtk.TreeViewColumn { title = "Fixed?", sizing = 'FIXED', fixed_width = 50, { Gtk.CellRendererToggle { id = 'fixed_renderer' }, { active = Column.FIXED }, }, }, Gtk.TreeViewColumn { title = "Bug number", sort_column_id = Column.NUMBER - 1, { Gtk.CellRendererText {}, { text = Column.NUMBER }, }, }, Gtk.TreeViewColumn { title = "Severity", sort_column_id = Column.SEVERITY - 1, { Gtk.CellRendererText {}, { text = Column.SEVERITY }, }, }, Gtk.TreeViewColumn { title = "Description", sort_column_id = Column.DESCRIPTION - 1, { Gtk.CellRendererText {}, { text = Column.DESCRIPTION }, }, }, Gtk.TreeViewColumn { title = "Spinning", sort_column_id = Column.PULSE - 1, { Gtk.CellRendererSpinner {}, { pulse = Column.PULSE, active = Column.ACTIVE }, }, }, Gtk.TreeViewColumn { title = "Symbolic icon", sort_column_id = Column.ICON - 1, { Gtk.CellRendererPixbuf { follow_state = true }, { icon_name = Column.ICON, sensitive = Column.SENSITIVE, }, }, }, }, }, }, } function window.child.fixed_renderer:on_toggled(path_str) local path = Gtk.TreePath.new_from_string(path_str) -- Change current value. store[path][Column.FIXED] = not store[path][Column.FIXED] end -- Add 'animation' for the spinner. local timer = GLib.timeout_add( GLib.PRIORITY_DEFAULT, 80, function() local row = store[store:get_iter_first()] local pulse = row[Column.PULSE] pulse = (pulse > 32768) and 0 or pulse + 1 row[Column.PULSE] = pulse row[Column.ACTIVE] = true return true end) function window:on_destroy() GLib.source_remove(timer) end window:show_all() return window end, "Tree View/List Store", table.concat { [[The Gtk.ListStore is used to store data in list form, to be used later ]], [[on by a Gtk.TreeView to display it. This demo builds a simple ]], [[Gtk.ListStore and displays it. See the Stock Browser demo for a more ]], [[advanced example.]], } lgi-0.9.2/samples/gtk-demo/demo-treeview-treestore.lua000066400000000000000000000137631316674307300227650ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local GObject = lgi.GObject local Gtk = lgi.Gtk local Column = { HOLIDAY_NAME = 1, ALEX = 2, HAVOC = 3, TIM = 4, OWEN = 5, DAVE = 6, VISIBLE = 7, WORLD = 8, } local store = Gtk.TreeStore.new { [Column.HOLIDAY_NAME] = GObject.Type.STRING, [Column.ALEX] = GObject.Type.BOOLEAN, [Column.HAVOC] = GObject.Type.BOOLEAN, [Column.TIM] = GObject.Type.BOOLEAN, [Column.OWEN] = GObject.Type.BOOLEAN, [Column.DAVE] = GObject.Type.BOOLEAN, [Column.VISIBLE] = GObject.Type.BOOLEAN, [Column.WORLD] = GObject.Type.BOOLEAN, } for _, month in ipairs { { "January", { { "New Years Day", true, true, true, true, false, true, }, { "Presidential Inauguration", false, true, false, true, false, false }, {"Martin Luther King Jr. day", false, true, false, true, false, false }, } }, { "February", { { "Presidents' Day", false, true, false, true, false, false }, { "Groundhog Day", false, false, false, false, false, false }, { "Valentine's Day", false, false, false, false, true, true }, } }, { "March", { { "National Tree Planting Day", false, false, false, false, false, false }, { "St Patrick's Day", false, false, false, false, false, true }, } }, { "April", { { "April Fools' Day", false, false, false, false, false, true }, { "Army Day", false, false, false, false, false, false }, { "Earth Day", false, false, false, false, false, true }, { "Administrative Professionals' Day", false, false, false, false, false, false }, } }, { "May", { { "Nurses' Day", false, false, false, false, false, false }, { "National Day of Prayer", false, false, false, false, false, false }, { "Mothers' Day", false, false, false, false, false, true }, { "Armed Forces Day", false, false, false, false, false, false }, { "Memorial Day", true, true, true, true, false, true }, } }, { "June", { { "June Fathers' Day", false, false, false, false, false, true }, { "Juneteenth (Liberation of Slaves)", false, false, false, false, false, false }, { "Flag Day", false, true, false, true, false, false }, } }, { "July", { { "Parents' Day", false, false, false, false, false, true }, { "Independence Day", false, true, false, true, false, false }, } }, { "August", { { "Air Force Day", false, false, false, false, false, false }, { "Coast Guard Day", false, false, false, false, false, false }, { "Friendship Day", false, false, false, false, false, false }, } }, { "September", { { "Grandparents' Day", false, false, false, false, false, true }, { "Citizenship Day or Constitution Day", false, false, false, false, false, false }, { "Labor Day", true, true, true, true, false, true }, } }, { "October", { { "National Children's Day", false, false, false, false, false, false }, { "Bosses' Day", false, false, false, false, false, false }, { "Sweetest Day", false, false, false, false, false, false }, { "Mother-in-Law's Day", false, false, false, false, false, false }, { "Navy Day", false, false, false, false, false, false }, { "Columbus Day", false, true, false, true, false, false }, { "Halloween", false, false, false, false, false, true }, } }, { "November", { { "Marine Corps Day", false, false, false, false, false, false }, { "Veterans' Day", true, true, true, true, false, true }, { "Thanksgiving", false, true, false, true, false, false }, } }, { "December", { { "Pearl Harbor Remembrance Day", false, false, false, false, false, false }, { "Christmas", true, true, true, true, false, true }, { "Kwanzaa", false, false, false, false, false, false }, } }, } do local iter = store:append(nil, { [Column.HOLIDAY_NAME] = month[1] }) for _, holiday in ipairs(month[2]) do store:append(iter, { [Column.HOLIDAY_NAME] = holiday[1], [Column.ALEX] = holiday[2], [Column.HAVOC] = holiday[3], [Column.TIM] = holiday[4], [Column.OWEN] = holiday[5], [Column.DAVE] = holiday[6], [Column.VISIBLE] = true, [Column.WORLD] = holiday[7], }) end end local window = Gtk.Window { title = "Card planning sheet", default_width = 650, default_height = 400, Gtk.Box { orientation = 'VERTICAL', spacing = 8, border_width = 8, Gtk.Label { label = "Jonathan's Holiday Card Planning Sheet", }, Gtk.ScrolledWindow { shadow_type = 'ETCHED_IN', expand = true, Gtk.TreeView { id = 'view', model = store, rules_hint = true, Gtk.TreeViewColumn { title = "Holiday", { Gtk.CellRendererText {}, { text = Column.HOLIDAY_NAME } }, }, }, }, } } local view = window.child.view local selection = view:get_selection() selection.mode = 'MULTIPLE' -- Add columns programmatically. for _, info in ipairs { { Column.ALEX, "Alex", true }, { Column.HAVOC, "Havoc" }, { Column.TIM, "Tim", true }, { Column.OWEN, "Owen" }, { Column.DAVE, "Dave" }, } do -- Prepare renderer and connect its on_toggled signal. local col = info[1] local renderer = Gtk.CellRendererToggle { xalign = 0, } function renderer:on_toggled(path_str) local row = store[Gtk.TreePath.new_from_string(path_str)] row[col] = not row[col] end -- Add new column to the view. view:append_column( Gtk.TreeViewColumn { title = info[2], sizing = 'FIXED', fixed_width = 50, clickable = true, { renderer, { active = col, visible = Column.VISIBLE, activatable = info[3] and Column.WORLD or nil, } } }) end -- Expand all rows after treeview has been realized. view.on_realize = view.expand_all window:show_all() return window end, "Tree View/Tree Store", table.concat { [[The Gtk.TreeStore is used to store data in tree form, to be used later ]], [[on by a Gtk.TreeView to display it. This demo builds a simple ]], [[Gtk.TreeStore and displays it. If you're new to the Gtk.TreeView ]], [[widgets and associates, look into the Gtk.ListStore example first.]], } lgi-0.9.2/samples/gtk-demo/demo-uimanager.lua000066400000000000000000000117201316674307300210600ustar00rootroot00000000000000return function(parent, dir) local lgi = require 'lgi' local Gtk = lgi.Gtk local log = lgi.log.domain('uimanager-demo') local function activate_action(action) log.message('Action "%s" activated', action.name) end local function activate_radio_action(action) log.message('Radio action "%s" selected', action.name) end local COLOR = { RED = 1, GREEN = 2, BLUE = 3 } local SHAPE = { SQUARE = 1, RECTANGLE = 2, OVAL = 3 } local actions = Gtk.ActionGroup { name = 'Actions', Gtk.Action { name = 'FileMenu', label = "_File" }, Gtk.Action { name = 'PreferencesMenu', label = "_Preferences" }, Gtk.Action { name = 'ColorMenu', label = "_Color" }, Gtk.Action { name = 'ShapeMenu', label = "_Shape" }, Gtk.Action { name = 'HelpMenu', label = "_Help" }, { Gtk.Action { name = 'New', stock_id = Gtk.STOCK_NEW, label = "_New", tooltip = "Create a new file", on_activate = activate_action, }, accelerator = 'N', }, { Gtk.Action { name = 'Open', stock_id = Gtk.STOCK_OPEN, label = "_Open", tooltip = "Open a file", on_activate = activate_action, }, accelerator = 'O', }, { Gtk.Action { name = 'Save', stock_id = Gtk.STOCK_SAVE, label = "_Save", tooltip = "Save current file", on_activate = activate_action, }, accelerator = 'S', }, Gtk.Action { name = 'SaveAs', stock_id = Gtk.STOCK_SAVE, label = "Save _As...", tooltip = "Save to a file", on_activate = activate_action, }, { Gtk.Action { name = 'Quit', stock_id = Gtk.STOCK_QUIT, label = "_Quit", tooltip = "Quit", on_activate = activate_action, }, accelerator = 'Q', }, { Gtk.Action { name = 'About', stock_id = Gtk.STOCK_ABOUT, label = "_About", tooltip = "About", on_activate = activate_action, }, accelerator = 'A', }, Gtk.Action { name = 'Logo', stock_id = 'demo-gtk-logo', tooltip = "GTK+", on_activate = activate_action, }, { Gtk.ToggleAction { name = 'Bold', stock_id = Gtk.STOCK_BOLD, label = "_Bold", tooltip = "Bold", active = true, on_activate = activate_action }, accelerator = "B", }, { { Gtk.RadioAction { name = 'Red', label = "_Red", tooltip = "Blood", value = COLOR.RED, active = true, }, accelerator = 'R', }, { Gtk.RadioAction { name = 'Green', label = "_Green", tooltip = "Grass", value = COLOR.GREEN }, accelerator = 'G', }, { Gtk.RadioAction { name = 'Blue', label = "_Blue", tooltip = "Sky", value = COLOR.BLUE }, accelerator = 'B', }, on_change = activate_radio_action, }, { { Gtk.RadioAction { name = 'Square', label = "_Square", tooltip = "Square", value = SHAPE.SQUARE, }, accelerator = 'S', }, { Gtk.RadioAction { name = 'Rectangle', label = "_Rectangle", tooltip = "Rectangle", value = SHAPE.RECTANGLE }, accelerator = 'R', }, { Gtk.RadioAction { name = 'Oval', label = "_Oval", tooltip = "Oval", value = SHAPE.OVAL, active = true }, accelerator = 'O', }, on_change = activate_radio_action, }, } local ui = Gtk.UIManager() ui:insert_action_group(actions, 0) local ok, err = ui:add_ui_from_string( [[ ]], -1) if not ok then log.message('building menus failed: %s', err) end local window = Gtk.Window { title = "UI Manager", Gtk.Box { orientation = 'VERTICAL', ui:get_widget('/MenuBar'), Gtk.Label { id = 'label', label = "Type\n\n to start", halign = 'CENTER', valign = 'CENTER', expand = true, }, Gtk.Separator { orientation = 'HORIZONTAL', }, Gtk.Box { orientation = 'VERTICAL', spacing = 10, border_width = 10, Gtk.Button { id = 'close', label = "close", can_default = true, }, }, }, } window:add_accel_group(ui:get_accel_group()) function window.child.close:on_clicked() window:destroy() end window.child.close:grab_default() window.child.label:set_size_request(200, 200) window:show_all() return window end, "UI Manager", table.concat { [[The Gtk.UIManager object allows the easy creation of menus from ]], [[an array of actions and a description of the menu hierarchy.]], } lgi-0.9.2/samples/gtk-demo/demo.ui000066400000000000000000000276111316674307300167540ustar00rootroot00000000000000 John Doe 25 This is the John Doe row Mary Unknown 50 This is the Mary Unknown row Copy Copy selected object into the clipboard gtk-copy Cut Cut selected object into the clipboard gtk-cut EditMenu _Edit FileMenu _File New Create a new file gtk-new Open Open a file gtk-open Paste Paste object from the Clipboard gtk-paste Quit Quit the program gtk-quit Save True Save a file gtk-save SaveAs Save with a different name gtk-save-as HelpMenu _Help About gtk-about GtkBuilder demo 250 440 GtkBuilder demo True True The menubar False True The toolbar False 1 automatic in True automatic True liststore1 3 Name list A list of person with name, surname and age columns Name 0 Surname 1 Age 2 2 True False 3 lgi-0.9.2/samples/gtk-demo/floppybuddy.gif000066400000000000000000000121401316674307300205100ustar00rootroot00000000000000GIF89aPF  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~ぁ=v{! NETSCAPE2.0! ,L> H*\ȰÇ#JHŋ3jȱǏ CIdA(S\rɅNjI̗ cJϞ@H 2hJ2rD)@*0.O[vDٕ@Xsjj׶l6$٫w5mW)9-͢{,ҁEgu8 5|bSCJ%x3|Cf ۸Icqhq}vmBN-8(U/gys_9qٵ^?9yï>d|p_M6~ϕv(ބ`.} W!*I؅І9gNAȚkhq8S)5Hgf>$JBHL:mh:QIe!I6 $0JY$dHX'umAh긑*9|jB昉aрx&be1aJYziviJ:*A*=ҫꚔ,Bk$KO9Ūl}<WL%*QMi.W~HS•;Rvն;m_2xn~).kn2zk0 .9,o[.fw#! ,L> H*\ȰÇ#JHŋ3jȱǏ CIdA(S\rɅNjI̗ cJϞ@&΃EE4eSF@TL}Z 4TIb͚P vX(Dːյj7)֩VU{n*4YR{5*ʹ ,hq |PJ|]x'ФsWZ_Sޙ'[}u[[E%č_O|9sϥ.PassSWo8yguh` w%V"h rY]ɗփvN(ǁd`]&L%Db'\~ rуxvڌԈҍ&x!AjFd("6DP8%JUqE[V``)eH_qy&[kx'QG)"}y~`1Uh!;)ZބNNf꒤v*iI+¤ҨhꥄVkj*t뎵*+Od!U_Dlv5`u]6k؎4-MfҷyfbnWJ5Ӣc$K~n 7G,T!This GIF file was assembled by CDavis with GIF Construction Set from: Alchemy Mindworks Inc. P.O. Box 500 Beeton, Ontario L0G 1A0 CANADA. ! ,L> H*\ȰÇ#JHŋ3jȱǏ CIdA(S\rɅNjI̗ cJϞ@H 2hJ2rD)@*0.O[vDٕ@Xsjj׶l6$٫w5mW)9-͢{,ҁEgu8 5|bSCJ%x3|Cf ۸Icqhq}vmBN-8(U/gys_9qٵ^?9yï>d|p_M6~ϕv(ބ`.} W!*I؅І9gNAȚkhq8S)5Hgf>$JBHL:mh:QIe!I6 $0JY$dHX'umAh긑*9|jB昉aрx&be1aJYziviJ:*A*=ҫꚔ,Bk$KO9Ūl}<WL%*QMi.W~HS•;Rvն;m_2xn~).kn2zk0 .9,o[.fw#! ,L> H*\ȰÇ#JHŋ3jȱǏ CIdA(S\rɅNjI̗ cJϞ@&΃EE4eSF@TL}Z 4TIb͚P vX(Dːյj7)֩VU{n*4YR{5*ʹ ,hq |PJ|]x'ФsWZ_Sޙ'[}u[[E%č_O|9sϥ.PassSWo8yguh` w%V"h rY]ɗփvN(ǁd`]&L%Db'\~ rуxvڌԈҍ&x!AjFd("6DP8%JUqE[V``)eH_qy&[kx'QG)"}y~`1Uh!;)ZބNNf꒤v*iI+¤ҨhꥄVkj*t뎵*+Od!U_Dlv5`u]6k؎4-MfҷyfbnWJ5Ӣc$K~n 7G,T!This space for rent...! ,L> H*\ȰÇ#JHŋ3jȱǏ CIdA(S\rɅNjI̗ cJϞ@H 2hJ2rD)@*0.O[vDٕ@Xsjj׶l6$٫w5mW)9-͢{,ҁEgu8 5|bSCJ%x3|Cf ۸Icqhq}vmBN-8(U/gys_9qٵ^?9yï>d|p_M6~ϕv(ބ`.} W!*I؅І9gNAȚkhq8S)5Hgf>$JBHL:mh:QIe!I6 $0JY$dHX'umAh긑*9|jB昉aрx&be1aJYziviJ:*A*=ҫꚔ,Bk$KO9Ūl}<WL%*QMi.W~HS•;Rvն;m_2xn~).kn2zk0 .9,o[.fw#! ,L> H*\ȰÇ#JHŋ3jȱǏ CIdA(S\rɅNjI̗ cJϞ@&΃EE4eSF@TL}Z 4TIb͚P vX(Dːյj7)֩VU{n*4YR{5*ʹ ,hq |PJ|]x'ФsWZ_Sޙ'[}u[[E%č_O|9sϥ.PassSWo8yguh` w%V"h rY]ɗփvN(ǁd`]&L%Db'\~ rуxvڌԈҍ&x!AjFd("6DP8%JUqE[V``)eH_qy&[kx'QG)"}y~`1Uh!;)ZބNNf꒤v*iI+¤ҨhꥄVkj*t뎵*+Od!U_Dlv5`u]6k؎4-MfҷyfbnWJ5Ӣc$K~n 7G,T!This GIF file was assembled with GIF Construction Set from: Alchemy Mindworks Inc. P.O. Box 500 Beeton, Ontario L0G 1A0 CANADA. This comment block will not appear in files created with a registered version of GIF Construction Set;lgi-0.9.2/samples/gtk-demo/gnome-applets.png000066400000000000000000000060221316674307300207430ustar00rootroot00000000000000PNG  IHDR00WgAMA abKGD pHYs``zxEtIME ( IDATxYuګg㐢9ҖdkMіI@ӒF 'P q^b=F I6<Yk I @L٢hQ#҈Ԉξw5}(e<) ={#qs$L.KgK v\&H a-'r#pUVW cs1,'og])6˽~ @c[ : (&POsp3_:z|dxp4Ȓ4':NZeMo}ߘ]XY3B|y@.ρ|sy޽_$,H( <ϱ]vX[>G/nN(9&P*/=ra0**,!!IRB_쯹 +baGRR/dS*?{ķ+}5,)nj]R7 4%Mb$%NRUS/.Cȥw}TT*S)K覉a訪,ɿ$4!c8!c8ŕ;%l3j>?w\R*)U*eR4Q5EQd.$邕}(8ˆLKzk}}^o| Ǟ{_꺎ii J ib)2 ,S)Ұt+N9voinʲhF ,Q*WɃYӻ໱/ ,QT$uMes;B6x@Ldp edY)2cPUwH2xId Y\'N*$2YzRra/ۥ'}x"K3,#R,,f4%C|&+!4#R$EcyI(Wpz`Rӿ*2 {AަXS#it2EDa@xv| €(KӘ$Iӄ4MIqyxq7/ӱ]TMR.?݌mj8ꮰQݕw\s|}K" C(&j54z^z;Flj☡a\Ͽ/xAډHdOA@{xظݱpm ױ|;>( g}}3gޣ^gV\,rIݮ= +]iE!Bs=_feNt=/~SJ uD*] U(Cǧ/ֱ]xg"e]iJr|qXZ\4?ȓ?JLN& >]CܵGf׮a'2,bychl!{>A!I8V==X6M�{8*f9 xWJ%Iqˬ?ÿ4X= I,ϒ)aF1AA1iS2M8V#lľW_}N`nvCCyȲ Ο'==? ʪ!q#2%äJozw}yusl^[[[a RNZ1l{ &'%BR 45$&Ŭq% $ } ZZ\4c>{`˲fFFF5A (lvnzSo}͖mwszr;BAk*"$ i"+2*aP_K˫/ߚ8juj'F!%{T;%IUUZa*W):r0體 RVjhɩ19nZoO>.^n(HpZFFmfQ{l CM쯡j*#JwKt72y?\v♶ ' m멅# V,ˆg C3TEsuM!YQU)#m+_܈?%! @C^>kR(uc3e՞]lA_x)n/o+uhZQ`Q,<liOU cg7yA|527]B9p @xq bo?ڧE~p{ ցtc Ř5.a[A}/ ef b\GYD@6$-<E/E,E=<VD8q//.!<+dM`PV<}Cvwb}HB&*za+9B{z4zQ$Pr8C7ze^)|oթ {y շX~o$QajmӢ "EBbxvPZs|EkS؎%_X{.^52* @NwBhJiO{]XdZ8'Z O YG2Ad;@zooAkIENDB`lgi-0.9.2/samples/gtk-demo/gnome-calendar.png000066400000000000000000000053031316674307300210450ustar00rootroot00000000000000PNG  IHDR00WgAMA1_ zIDATx]lWwvvwNvc7֤}B"TDAVT臢ҨQ Ayh)BB R>ѠTb'󹉿];wwfa7N*Du5;wN;Nnn0;x}?&Ex @twRJ"T!@QW @ a H_(yJ6'C""a$ BuZ BiM* *=ܪD;ϏweBB E*es8N2H)A6/O^ZJ)[?J%#/hl34*aH(ω|>*c76 $2'\O6.8y}B1aᅊmsu'X aHB|rןP(J(^)RԑRDJNDΗ^ac_kG-VVW6l |#bhm!ne~ye9FhfA^G#7)rp$^<|] QJ"py2 KV^o $ʈ C;Qؐ{Iȥnc2}8ڭ;rk LdGui 8ƃ@ "Z!&7ֽ@^xO,k*)-l ]o[fMCz8@oܸ K;7ę v(d6LJxh-`Oˌ]5MdU`PHw@1c; 9D5#M@.}@7vg):?yyF+C ? lSKl =˓R)/fYZ)JgDeYZXbbGF@o84ށX5e!J:OS)N)w3ccdYv 180H+Mm֢@QNtR|'C^_Cgzt%eTkÐZ25;5эIXخh&BrĻt[jEZjc'ak*ͮa1yKTu]T%onl(r  ~ضrC4d'Ƅ:o!:zGcXd&zI} !ҩh+-G7 I4[=`ŵ $Mce_ܿ'F{1>7wccۀ^k1.Z\10ŁNVdf9v)c|t=< 3ccL̔1E94y0nzz=Du:3?2T ,T\0s _e/ׇ o>Н:qj^ڷo߉mXpd WMjo1W[?kll,cСT*5:6M$eW2ob x3}3}xgPUjI3&c+wM& rտ%v{v$+Q):ښNƘUOdΑ/R7ODzHKR"xDb!zKAHiY0 RedLU(fyQݵr]] Cj6ѪU!J-a>奂Jz|q 琈E0ym{A=J|7xx#luS6}Wz6#gޘ%r[%-Mi|Zov,e |YQp"z}_wԪ_hUvyc ѦY|)B&_; qիtMb\^cϏ& G}F+8nu;hdhp6Zbew7%5 3 3j`*-nxw-8ph$Rwmcg.#\ٚmd몂g Kdj,J(*}k_z~'H^sk#-dz7vq5_VONKF8%5*'Qc]Xioiٟĥûo岹#vDP0Ƕ`YoozVYT?/l$,D!M%c-V0̟lV+T%drL\0y?mlВ'FGL%4D2IEM MF>Oy3׳ݽ}X|wo_o,l biuS/#[(=948Ǡ`zz{a[aYGPIg,# Hu^-Cm]X_;+{>VWU mX0, ϣ4C<ЪW`Y"Fni|e̤Pk $>\|r=N"6  CJYǒb!ǶZ@MC#\V˫V)c=vjn̻ [5 VIa[xAqD1׶ XYZd2gr[AWfD/Wk3GiZeyx'eBD1D8%kkeL%OO<^-Fޜ$ 1-;єdhF$EmS'eQ]Ӊm0M,g2x8v,~7|/jf0ֽ%ю^0l8$-I (J=j;.XaZbMo;MC| -Q8<< .Zv9^[O|7~GQTm[TCHW"I*x5 9`aaq6ti_L>y8~a\{-'Z +D*A~6W^S?чwoajB§?ukA0 ~,Sԇ5k<ǷE[ {`3w??H3g[_UUfg^rBNv!ڝ"jꂣZBqQGg?{sal,B vU2+FwQ3ǞDq,cTD&L`jpoEM+bA /Vxs5d D:NTgE]);1 GɊ4O#]0\=AZ+ƝW$T;m t}eg bU mJ(Q7+cb,)gJ]tfRDOxIa%m2A1Wxigt!2kfi: .4$< ]90Lh6A;@P\.ٕ6L#0q\Gs|fS Xˤ2;V )(VĂ-] -0A\!CfDp MgCV+ }fK2X{(iUUD﹬,LӪ+}f!Xvb$O X@N v%%V_t[ˮ;aJl-j`AԐ{~G|uU9pULI.^x  Vh5_|H֍em=5Eg9ǙӿF@nO~OL>p8sM M|1TҹMBYC"O=6jڟoMzy} Qs;[o&$J X*U7 tEIz|=uFM֖cWrޢ>kαs_MGCIENDB`lgi-0.9.2/samples/gtk-demo/gnome-fs-regular.png000066400000000000000000000034031316674307300213420ustar00rootroot00000000000000PNG  IHDR04̓bKGDCIDATx՚KoWC4³*HtEʢ,RB+O]Y#P%XtQHx4< MTv`ĵӅ3;wSP+d߹w|?5w,N`u wP(|b(cccrQ_{@*VܺuKDDfffފ@Jr,bQdttW8%R!"H* ="ɰyf._ X Mlݺu^G\-[044DkkSЗ۷oϋ~abbLM6q :::J~y58yGww77~M85Bs&0 J%sccc\zRĆ ݜ:u {`O&hDhڪy_#GP,QY={| Ju.D>/_N.X\8q߸dF<zlP=QpF_]+!JQTi<8v.`B.rTz*j"U* |t/7,6`*y5SumۦYE8x2ո(fhnnv"(Tbd2&=S_+W.t67ѬEĚ@{}I9'ۿe$,1q>veO|jTytXD)U|DWDXL2,\kI5 $;w͛׆2.Nf`%X!+"ݻϟ%AH~:--UH*(HI|hDbg.\8ϲe^@$fۘML&@H)RT,ʈ5F6%B LNd׮ŋ5HB8i\LNZsZzp\B^'ĶCzXHk. $p?ԜkqUUx$!U}fr1IRcbDgYjM2 [j\Lb&-[IaWJ !vOgwe+l$k KYbbqR"S\4Bgck(Y(Da:khhrָ%Mk!~^|QFљ8؊ð)/ǡCd'B##tiFK(n&6MBJ&S(Ξ=Ú5%$Z%sypn)MCg9n[rp0'C/[ܫ*"ўXOnQ|b"̙_Xv]rzPv۞!Bx߾rO-r}ŵopkJnϟ?GW (Lpzh˸[Xӧfݺ=$0;--u34rps=٩WÇIC\ڀL.~}]rIeޅ)!Т.!k)0CHP*MZ0/dm(C@eIENDB`lgi-0.9.2/samples/gtk-demo/gnome-gimp.png000066400000000000000000000065221316674307300202340ustar00rootroot00000000000000PNG  IHDR00WgAMA1_ IDATxYk]u>{9w޶gllc#ǯ_Qܹu7up,+ k, !]?w @u ܴry=>ّ/$~IDhxm~Gȱ=fR.w'{ˡC"_"#acv*KDv ܂VK6Wztl0[; . 9<ckGn΍lR;y介~ &/xԙ ~w:$\":An(EFW~#AcR)ĭ4\OKs{&R@*=tӪW|B$BDp-xgp7zK~RH ѢrW\OBy񟟞Ǔ4++ݟ~xWQ,Z`olRZ2-v hJC g@ 0`O\!8 3v3X 0zE_s]^fvy\o<ЦMz^0o-[~ c8ȤDIdR!,j9ic_[6#)fݼaÆm޼!!Dqbb?=ȶKF|/b``{ޏ5kǐJǖ-[ƪP*VR1Z[Jz3WpdTMdldS*wx===m۶GǽfIo&q<?ժxWh~~vi^{m?8رcꫯBJVR R*( h֒Ʊ='20I.gN;HCyQyBvr''=Z vڅF3'`Fm<3={e&$A;IIJ({f0$GV (mb 0V:@rhrrB,_ѣGP,šs@>q߃a֭\SOhnRf}uՙ0l#٩T*-uM08O*ܝ=== fFehZ(J(9ϝ93'0n~i(RJT 8{}Π^  &VfX &fgseג3|߇Ð(Bp hPڇo}+ex~ԅSHJkZm IښU@BfÜ_Lkqaya!mU'mhـ=`6`f,? Vl$j ApY)rԇs1ro}c}ɞi4˲waE{{zz0 Q.GbCZITPy>=erJkLL]TIq3|fnqTux+ֺZT (ðJa I\CYV AAlIHc뚶p2z y5@4<5}nב˦pM?qx^R{dyZIΜG&SHrk@+ĄnNF sqnb}3VZZkRv;[P!`if[`f9;;{xHISiFJ* CiTJ0;W<.^Z@d-(B/Stz@n/5̘jg<9덎]` ۼqL |,H6ՆBT,T*7BO="Sgr0}SK0H3zjSU^uWu~NLW̧0iORIENDB`lgi-0.9.2/samples/gtk-demo/gnome-gmush.png000066400000000000000000000062541316674307300204250ustar00rootroot00000000000000PNG  IHDR00WgAMA1_ cIDATx[$YNU{{qlbK"1R2QL B ͑xKH<8h`@/+lǻk{޹LwOwuu9UU }|O/}O ĭh(.]X}[W; <_^qƜȄX02#Lcŕ+kzk\Gw}?Ǔp2&N1iFuvIuvr;\{ |鯭4ӉQXd֐KRg5֊VCFϾ'CLXpzWGɬa0걺&=Y*_K+ǎ羓c̘I(c9\{k-)vGC$h 88K__xWWgW't-" .XLvb+Ng0Ƣ2Eeh8kqIz]T2Gd6%"@nY>| `oFX^=vxƚek-ΝEj @f22kU|as <5d$Z-~@RcF޴,:ۼ|GzrTfIZ!|: 8p 91a@Jߗx+6][=Ӧj>pOn_" bc3?ϛukO!DQkuffZlnl> RiΟybҌԦh2 Gl.hxW^wMDŽ " C(Bk &;;=]u4 ֮T*:œqfKssdWw>?v۝}tZ$nR*?x {!koƯ-?ܳ{rHNSGvIRa8  ~#&qL$R0ers7h$NXťuV$J&WrX._M9<|ߗ~K)58ILb,Zn#ʼni$ J}y`1j s*qȷomR)v{sp/oH?[~qX!RJj*RqOiA)d2A)E$ycZs 9GZ%X\gqao.κ<=stؗŷD>Rz{D)>t|cDQDE$IB) jlr!.-h68r mh6:O|=|?k8VۯGkY$ Ks$Ii2 DQD(A0;3#8~|޲Gfnрtww|LN\B<[?~+? q;h6xO|| \),T*cPJ!GUG @NI!:>rC t]qB̀f3'G'~~OwָcJBeH)B0L}BCkspFYufm_]^!tuG/4GK9RT*8Bnׅ!ֲHE0Dx<RzD⯟},BxX~+~u2c}U/dxkR1뽑V@D¿)i>ed!0 ihQJM H)WIFfIoZGn&js\*x-'FkHd$v>iRΎ1fZE'2zkܠ9Ph-'PX,0Okb–IL&&;|7W 9rUˣ_ρmw#$I SZk8- $sV2[/H)d$J81Sŗ <.diZOZ_rљ#Ϝ9R 9HӔ4M}cJGQV[l4K`MI&|^B»x7x,g~Swoߟk03֫6Y,bMWr?[?;j1񪴜S|zoY|쳷S;s%>sj\3Җ\+A {?+%y2 P/=S .>٘cƘ2j;_ 'VF;k wN;njYo9-dG-ӿw?ӟ=B()9 4&0L.~$%KL(K~| _ѹ|h#B4xۍKx`Fi猆F?Ikm!qcQ%7 BY`x)uJRc|'g8w:{S@6s:.&R0 =y;zRJß[޲t_}&[i؋.YoKU!Ejg;*sf[)y@E ^G٠L^B6v@$9J@YJ@cp@hs;}jP `8V\Ib+~fժR9֘0r#?chEb5USб2wdЙBDEF(ՠ-4ƶu ra"nR͜>Y1U @2q{78asC^,yr\ģ'\kW!WbJCvFdIJjDS2B #l\S8IMJV@]e߳83lu2mu6ZYÿvW녙th6jLLX{wNgy=;wd#lm'Y,}T[ 6NsD:bj^tG)ǟ{Mo󙝇{.[Fxt+(M AElZET'12nF/"QlҜ02J+%Am D)C~l"oKm! dn[yǹOyԣ%!)RjV)5jK)kRJ8L]" i昭nil M3"݄"OZVc6F.[#7UPxSDq[kSkmbst!D@<ߪv:;f4ZG(9Α) /Í#$'eZbkAx>.v^ @^޶ֶMkmZ+1Ba1BITn+$:vgjT3p$(c'h3Mr&&4 HPiz6%t N;jZkT!Xp55afvVhCE4Mb"Ռ7N:!hk3\ Jr$PF;* 6|k1L5!D0?9uY>@1nRIiĒ:6/ʉ Ɉ2ֶhQ8r\g*h^1b!D`E`3U @=v޴V0M "I $ 2.f[ `uu{W[Bkg^ ,hkB$Z;֎$W|?Ԭ7pGjz}أO<,mFWu̥O]{l#3-R=И^ZI+~`6t kur>dZ֒8Ɋ3T̬\3*_;w.6?:p`f9zDgnĮKk)ԆfBXJ Zztx{n޿w5g1IT+dy&,#I0 |$[7 n[H^ѷΏN8%>Zq^sw~) !zvb(pd:H(4IyFRE IZQ=.TZpZ%^+x:򖛿!If)I>3\Gh)cTZ)cGQz@զ2rGKkkHuNIApn˅^R7a@ݡ.QuԽ];vuZ!T}jQNRT+UfXC 0&' B?LKRnsu^\yJ yv~1qFCbðpq1 !B@K)m!)W.|;}?QS 4,JV~ okwN33^zb8)q]Yx2a%b !TVTRqr)egi9#o$4Ea,Q<ׄQ\|+eƽ®z@$GIL/c&J~TN\^VmR1h!e/4OQ ]$q]_x^Dh1'{&o f@&`%4j.)!DF 4n)4ιJڻ0n^@eB'qa_|9?R5-oL Jkgѣ␰4`aaA}l6fxӾVn_LLy^8 +)," }MR]gU/mCCҀ/ \KylA[_/M-uǽv%Jy#҄Dv @tpPKQq?66*y |tKRRUVen~bՊ5.emmqAI}y`=Nm+_ϾNiJg3ޤW e)q+|%Ķ;^s1aIENDB`lgi-0.9.2/samples/gtk-demo/gnu-keys.png000066400000000000000000000074141316674307300177400ustar00rootroot00000000000000PNG  IHDR00WgAMA aIDATxkt]e゙[rr-M&i4IoŦC;DgPQTTguleP^Ҧ-msk~9Im}y|)̗:~yy}.qK\2C^Fd"弋Kt᪵UrL+ͷRu-YVV^zU+W (=ݽo `L-(`Fy9&NaYV왳vmۏ:/L/zefB01o2jy_8Pϱup H)B0qm_[ ȿV;@V(\{^fr(6 IG{x3cKս( *H$|)T4h?]ȂeneY9kjx4Jbd+W^y%լhB L  [|aIl2F~~+&OG @Uө|A< _ 8nSOn$I1UfV^~<`wn~ t4'8͞-ZXYyށT{߽ۿ_qǎR\RȬJ Il[8y%e^UQ5Mc_EVvϕxK"yn /﮻iK,554k>+cY=\mOw0 xmO~/P Be``}r!ʊ)- 3blя[i%hF*$LcuTcP⒊;>u{)j0%yyB` p㉸퇿]WWWwBǁQ`rJn'7†jB4E4Lo߫4kp$/w]MP5b$q|tb嗱kҲr=elt_="GeG7tM0M'߽;~rt]G"IA4¡0hhlzFF 3:$ccc$IFc8z9zG BB&Mرc<.馛)/+W||f9pEQP <.ӿxb&8ҊoRinm&77Jj-_Ƽy9s4gΝ"JY8R"}qʦa&j`A4Mlذk\k  ԾZolDUUt]_}}RL7^L@G>2EUPu.+ZJBz{zof$6JEE% ;.' ˪g΂HC "Yz@0H__/CA=ơCعs'K,acض͑#u}ctL%Oxɼ@QQQov(G&]]]]Pe1HSPX+0LښhhlॗvHũcH.iiiq,*QUΞսGbYAx|;w$;7p(*斪yϝxd0 x _bf/H_y_|'<9Bcǎ8y3˙;gPs &pl}G3RK v5V>3tZ͆O~7^/f(=>.\wI àa&M/}TM#;+#?EQQ*̟;˗zΎNod*Iii2~n}.**gdh+HOjo.a 7\rY"&6>55<.<ץ-?žp7QZVi o=B:==Xu,KpUܱAR{`?wE 'OP6ן"#QtM! 12CʛKco}wc) R0u #%'4 U?>D"Yy_?? ,kf:ōm[+k&pmTTT,]=[(x3SE_8R88I*"@+_FW\=|v4 ,\W M -郵ugsrm7d mݽ횪BP@(hB;u…5?=x'uyjS_kOYpݡ:+>6M>۾d;O5;{}#6//oY''UTTT3L'lK}ACRJ%A kBg[ԌT4hzo>JhBkVjUSÙL DUU;?vTaYvȡc=gN7=='yiT]m-mV 3KhqCowL&0 NCDz.L\Τ/y۽e˗rsM5.b$ Dcx}k+V.Ng{cC#4y/x'QV]ʫYZZꍌ Y{Q~WRnaswkwD"&w?K\{;$誂IENDB`lgi-0.9.2/samples/gtk-demo/gtk-logo-rgb.gif000066400000000000000000000144331316674307300204510ustar00rootroot00000000000000GIF89ak󪫹ԏƹ/L\lux 4# "7x:Z5d Y$]'+4 ."dܻҼ-E76Z7nNdlG7]9$7L 5(4=`-LE*48  ; &M=r2fpz$L-M>v!C %e)<9 H FE,)7 IKj)%6#&89;4P 4E&@4ޕpA YhPJ,A~(t,0C&Η"Cwa9 ۔ @5qGZpX@QH"\6eSdX:bR\T 2AdvP0CM p% pRBYDMwlBL h6l5zP10abK%qE9JY@+,6儳G[j72YЀtVa-:#,4!*}51a "mBNh[V0vC0A TIZp&: *L(",h:1B[?8[;B: L֭;n|pΗ$#F*`EuY >CF9BCJ7 C#(0BBG@fCd6tuCsyNDWA[zv'%Ԡ7\]凇sSQTa[`hR".Ye-d, BӡJAqAv`0@i,UA<@\b'^ʐcDƝQPp6" zk|U)L\A #(y B !.PB>0\&n`Cbv`->`k0A a@ @H#!~"Y> RWۂ&Q>TzGJWX(c%-%JP xjb[R? E|@$@:\c @AVpOXĀn~I{i"NЁ x"P#` Xo6(nK IVG7@2ˆAREp I! .X `+c,AC?qQrO~% otDAyiBڇ ^N @$$ి",C.tR$$` d4(t&v  Եk]fv^Ć>xS#U <1`"7{N#ȠS5쌷\pA {ݐH$Ћ$KGZPP֠I$Ӈqy8hwum WЇ~C PK VZo!A @HWl{!q` [s r|Eݵ F ыK@o2}ѵrpq #<Ƹ t@~EAM)^o`un "ems H` p y|pqЀ~0[$x_H~47U` 6r `6Pr_#Wt < 3@27UtPcA}x !0U6``` gW `dO@2@}YRPct&j 16`|px `O ) l9j2$ mIZa0o @|~ ؚG~ p P @A $0o밓{ UplJ/ n  X.Lm~ߐQr`Y rĠ UQq.҃)8iJ@RtًAG `6P%; ]ʀR b lRܠ j0mov~飊@j`H` %FAȐ _hbUwФ叛 {Xm kjtrPtʦ ڐAY`Đ lb)Z!7pqeq" Y|4Un``p`pj6 f` UC`r '/yH[W5 O wh*ʰ<$oqQ"}PUTJ(FS% `ZdZ%0.Apg2 OG`. ZlmخK6e!7 °`GW֤N:Щp.7^Xv{ZRnpl&fAp Y ͺ(d",+ 嚮ٕf_! c+ɛؓ`ЭI [m3$+~@sz]WBP!K yڀ&XVJ ^PkmW[Zi ZbG+4`>2\l!GP (+`Ep[hXWR7+["yfo 2 !g] Q D`|G^f7} 1˛ٖu> !0h}j`km˚Z|tp%o&ЅUj au[m~ / , (V$P{`w% Q 7`` kqC6pgxv7ً[f0ʶ f2%`)7b`Wt A$0f⢐c YY& `H7p !`&PܲwɎP #\)`Ƥ  v WTL3[kUP lbzˋ؈T7 Mo! ΜMǰ DMrI 7נZ^h% ;\X;L3٬ -됡^m k6)!F@ GE4t= 3 then window.has_resize_grip = true end -- Create some more widgets for the window. local status_bar = Gtk.Statusbar() local toolbar = Gtk.Toolbar() local ctx = status_bar:get_context_id('default') status_bar:push(ctx, 'This is statusbar message.') -- When clicking at the toolbar 'quit' button, destroy the main window. toolbar:insert(Gtk.ToolButton { stock_id = 'gtk-quit', on_clicked = function() window:destroy() end, }, -1) -- About button in toolbar and its handling. local about_button = Gtk.ToolButton { stock_id = 'gtk-about' } function about_button:on_clicked() local dlg = Gtk.AboutDialog { program_name = 'LGI Demo', title = 'About...', name = 'LGI Hello', copyright = '(C) Copyright 2010, 2011 Pavel Holejšovský', authors = { 'Adrian Perez de Castro', 'Pavel Holejšovský', }, } if tonumber(Gtk._version) >= 3 then dlg.license_type = Gtk.License.MIT_X11 end dlg:run() dlg:hide() end toolbar:insert(about_button, -1) -- Pack everything into the window. local vbox = Gtk.VBox() vbox:pack_start(toolbar, false, false, 0) vbox:pack_start(Gtk.Label { label = 'Contents' }, true, true, 0) vbox:pack_end(status_bar, false, false, 0) window:add(vbox) -- Show window and start the loop. window:show_all() Gtk.main() lgi-0.9.2/samples/gtkpad.lua000077500000000000000000000022741316674307300157400ustar00rootroot00000000000000#! /usr/bin/env lua --[[-------------------------------------------------------------------------- Sample GTK Application program, simple notepad implementation. Copyright (c) 2010 Pavel Holejsovsky Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php --]]-------------------------------------------------------------------------- local lgi = require 'lgi' local Gtk = lgi.require('Gtk', '3.0') local Gio = lgi.Gio local app = Gtk.Application.new('org.lgi.GtkPad', Gio.ApplicationFlags.HANDLES_OPEN) local function new_editor(file) local contents = file and file:load_contents() local window = Gtk.Window { type = Gtk.WindowType.TOPLEVEL, default_width = 400, default_height = 300, application = app, title = file and file:get_parse_name() or '', child = Gtk.ScrolledWindow { child = Gtk.TextView { buffer = Gtk.TextBuffer { text = contents and tostring(contents) or '' } } } } window:show_all() return window end function app:on_activate() new_editor() end function app:on_open(files) for i = 1, #files do new_editor(files[i]) end end return app:run {arg[0], ...} lgi-0.9.2/samples/gtkterminal.lua000077500000000000000000000167221316674307300170120ustar00rootroot00000000000000#! /usr/bin/env lua -- -- Lua console using Vte widget. It uses homegrown poor-man's -- Lua-only readline implementation (most of the code of this sample, -- not really related to GLib/Gtk in any way). -- local lgi = require 'lgi' local Gtk = lgi.require('Gtk', '3.0') local Vte = lgi.require('Vte', '2.90') -- Simple readline implementation with asynchronous interface. local ReadLine = {} ReadLine.__index = ReadLine function ReadLine.new() return setmetatable( { insert_mode = true, columns = 80, history = {}, }, ReadLine) end function ReadLine:start_line(prompt) self.input = '' self.pos = 1 self.prompt = prompt or '' self.history_pos = #self.history + 1 self.display(self.prompt) end -- Translates input string position into line/column pair. local function getpos(rl, pos) local full, part = math.modf((pos + #rl.prompt - 1) / rl.columns) return full, math.floor(part * rl.columns + 0.5) end -- Redisplays currently edited line, moves cursor to newpos, assumes -- that rl.input is updated with new contents but rl.pos still holds -- old cursor position. local function redisplay(rl, newpos, modified) if newpos < rl.pos then -- Go back with the cursor local oldl, oldc = getpos(rl, rl.pos) local newl, newc = getpos(rl, newpos) if oldl ~= newl then rl.display(('\27[%dA'):format(oldl - newl)) end if oldc ~= newc then rl.display(('\27[%d%s'):format(math.abs(newc - oldc), oldc < newc and 'C' or 'D')) end elseif newpos > rl.pos then -- Redraw portion between old and new cursor. rl.display(rl.input:sub(rl.pos, newpos - 1)) end rl.pos = newpos if modified then -- Save cursor, redraw the rest of the string, clear the rest of -- the line and screen and restore cursor position back. rl.display('\27[s' .. rl.input:sub(newpos, -1) .. '\27[K\27[J\27[u') end end local bindings = {} function bindings.default(rl, key) if not key:match('%c') then rl.input = rl.input:sub(1, rl.pos - 1) .. key .. rl.input:sub(rl.pos + (rl.insert_mode and 0 or 1), -1) redisplay(rl, rl.pos + 1, rl.insert_mode) end end function bindings.enter(rl) redisplay(rl, #rl.input + 1) rl.display('\n') rl.commit(rl.input) end function bindings.back(rl) if rl.pos > 1 then redisplay(rl, rl.pos - 1) end end function bindings.forward(rl) if rl.pos <= #rl.input then redisplay(rl, rl.pos + 1) end end function bindings.home(rl) if rl.pos ~= 1 then redisplay(rl, 1) end end function bindings.goto_end(rl) if rl.pos ~= #rl.input then redisplay(rl, #rl.input + 1) end end function bindings.backspace(rl) if rl.pos > 1 then rl.input = rl.input:sub(1, rl.pos - 2) .. rl.input:sub(rl.pos, -1) redisplay(rl, rl.pos - 1, true) end end function bindings.delete(rl) if rl.pos <= #rl.input then rl.input = rl.input:sub(1, rl.pos - 1) .. rl.input:sub(rl.pos + 1, -1) redisplay(rl, rl.pos, true) end end function bindings.kill(rl) rl.input = rl.input:sub(1, rl.pos - 1) redisplay(rl, rl.pos, true) end function bindings.clear(rl) rl.input = '' rl.history_pos = #rl.history + 1 redisplay(rl, 1, true) end local function set_history(rl) rl.input = rl.history[rl.history_pos] or '' redisplay(rl, 1, true) redisplay(rl, #rl.input + 1) end function bindings.up(rl) if rl.history_pos > 1 then rl.history_pos = rl.history_pos - 1 set_history(rl) end end function bindings.down(rl) if rl.history_pos <= #rl.history then rl.history_pos = rl.history_pos + 1 set_history(rl) end end -- Real keys are here bound to symbolic names. local function ctrl(char) return string.char(char:byte() - ('a'):byte() + 1) end bindings[ctrl'b'] = bindings.back bindings['\27[D'] = bindings.back bindings[ctrl'f'] = bindings.forward bindings['\27[C'] = bindings.forward bindings[ctrl'a'] = bindings.home bindings['\27OH'] = bindings.home bindings[ctrl'e'] = bindings.goto_end bindings['\27OF'] = bindings.goto_end bindings[ctrl'h'] = bindings.backspace bindings[ctrl'd'] = bindings.delete bindings['\127'] = bindings.delete bindings[ctrl'k'] = bindings.kill bindings[ctrl'c'] = bindings.clear bindings[ctrl'p'] = bindings.up bindings['\27[A'] = bindings.up bindings[ctrl'n'] = bindings.down bindings['\27[B'] = bindings.down bindings['\r'] = bindings.enter function ReadLine:receive(key) (bindings[key] or bindings.default)(self, key) end function ReadLine:add_line(line) -- Avoid duplicating lines in history. if self.history[#self.history] ~= line then self.history[#self.history + 1] = line end end -- Instantiate terminal widget and couple it with our custom readline. local terminal = Vte.Terminal { delete_binding = Vte.TerminalEraseBinding.ASCII_DELETE, } local readline = ReadLine.new() if Vte.Terminal.on_size_allocate then -- 'size_allocate' signal is not present in some older Gtk-3.0.gir files -- due to bug in older GI versions. Make sure that this does not trip us -- completely, it only means that readline will not react on the terminal -- resize events. function terminal:on_size_allocate(rect) readline.columns = self:get_column_count() end end function readline.display(str) -- Make sure that \n is always replaced with \r\n. Also make sure -- that after \n, kill-rest-of-line is always issued, so that -- random garbage does not stay on the screen. str = str:gsub('([^\r]?)\n', '%1\r\n'):gsub('\r\n', '\27[K\r\n') terminal:feed(str, #str) end function terminal:on_commit(str, length) readline.columns = self:get_column_count() readline:receive(str) end function readline.commit(line) -- Try to execute input line. line = line:gsub('^%s?(=)%s*', 'return ') local chunk, answer = (loadstring or load)(line, '=stdin') if chunk then (function(ok, ...) if not ok then answer = tostring(...) else answer = {} for i = 1, select('#', ...) do answer[#answer + 1] = tostring(select(i, ...)) end answer = #answer > 0 and table.concat(answer, '\t') end end)(pcall(chunk)) end if answer then readline.display(answer .. '\n') end -- Store the line into rl history and start reading new line. readline:add_line(line) readline:start_line(_PROMPT or '> ') end -- Create the application. local app = Gtk.Application { application_id = 'org.lgi.samples.gtkconsole' } -- Pack terminal into the window with scrollbar. function app:on_activate() local grid = Gtk.Grid { child = terminal } grid:add(Gtk.Scrollbar { orientation = Gtk.Orientation.VERTICAL, adjustment = terminal.adjustment, }) terminal.expand = true readline.display [[ This is terminal emulation of standard Lua console. Enter Lua commands as in interactive Lua console. The advantage over standard console is that in this context, GMainLoop is running, so this console is ideal for interactive toying with Gtk (and other mainloop-based) components. Try following: Gtk = lgi.Gtk window = Gtk.Window { title = 'Test' } window:show_all() window.title = 'Different' ]] local window = Gtk.Window { application = self, title = 'Lua Terminal', default_width = 640, default_height = 480, has_resize_grip = true, child = grid, } window:show_all() readline.columns = terminal:get_column_count() readline:start_line(_PROMPT or '> ') -- For convenience, propagate 'lgi' into the global namespace. _G.lgi = lgi end -- Start the application. app:run { arg[0], ... } lgi-0.9.2/samples/markupthrough.lua000066400000000000000000000036301316674307300173600ustar00rootroot00000000000000#! /usr/bin/env lua -- -- Sample GMarkupParser which parses input into the table. Expects -- name of the markup file on the commandline. -- local io = require 'io' local lgi = require 'lgi' local GLib = lgi.GLib local assert = lgi.assert local function make_parser(doc) local stack = { doc } local parser = GLib.MarkupParser { start_element = function(context, tag, attr) local element = { tag = tag, attr = attr } stack[#stack + 1] = element end, text = function(context, text) -- Avoid just whitespace. if text:match('%S') then local element = stack[#stack] element[#element + 1] = text end end, end_element = function(context) local parent = stack[#stack - 1] parent[#parent + 1] = stack[#stack] stack[#stack] = nil end, } return parser end local function dump_element(result, element, indent) result[#result + 1] = ('%s<%s'):format(indent, element.tag) for name, value in pairs(element.attr) do if type(name) == 'string' then result[#result + 1] = (' %s=\'%s\''):format( name, GLib.markup_escape_text(value)) end end if #element == 0 then result[#result + 1] = '/>\n' else result[#result + 1] = '>\n' for i = 1, #element do if type(element[i]) == 'table' then dump_element(result, element[i], indent .. ' ') else result[#result + 1] = GLib.markup_escape_text(element[i]) result[#result + 1] = '\n' end end result[#result + 1] = ('%s\n'):format(indent, element.tag) end end local function dump_doc(doc) local buffer = {} dump_element(buffer, doc[1], '') return table.concat(buffer) end -- Parse standard input. local document = {} local parser = make_parser(document) local context = GLib.MarkupParseContext(parser, 'TREAT_CDATA_AS_TEXT') for line in io.lines((...), 512) do assert(context:parse(line)) end context:end_parse() print(dump_doc(document)) lgi-0.9.2/samples/mxwidgets.lua000077500000000000000000000061441316674307300165010ustar00rootroot00000000000000#! /usr/bin/env lua -- -- Basic MX sample, adapted from Vala code from -- http://live.gnome.org/Vala/MxSample -- local lgi = require('lgi') local GObject = lgi.GObject local Mx = lgi.require('Mx', '1.0') local Clutter = lgi.require('Clutter', '1.0') local app = Mx.Application { application_name = "MX Widget Factory" } local window = app:create_window() window.clutter_stage:set_size(500, 300) local hbox = Mx.BoxLayout() window.toolbar:add_actor(hbox) local button = Mx.Button { label = "Click me", tooltip_text = "Please click this button!", on_clicked = function(self) self.label = "Thank you!" end } local combo = Mx.ComboBox() for _, name in ipairs { "Africa", "Antarctica", "Asia", "Australia", "Europe", "North America", "South America" } do combo:append_text(name) end combo.index = 0 function combo.on_notify:index() print(("Selected continent: %s"):format(self.active_text)) end hbox:add(button, combo) local table = Mx.Table { column_spacing = 24, row_spacing = 24 } local button = Mx.Button { label = "Button" } table:add_actor(button, 0, 0) table.meta[button].y_fill = false local entry = Mx.Entry { text = "Entry" } table:add_actor(entry, 0, 1) table.meta[entry].y_fill = false local combo = Mx.ComboBox { active_text = "Combo Box" } combo:append_text("Hello") combo:append_text("Dave") table:add_actor(combo, 0, 2) table.meta[entry].y_fill = false local scrollbar = Mx.ScrollBar { adjustment = Mx.Adjustment { lower = 0, upper = 10, page_increment = 1, page_size = 1 }, height = 22 } table:add_actor(scrollbar, 1, 0) table.meta[entry].y_fill = false local progressbar = Mx.ProgressBar { progress = 0.7 } table:add_actor(progressbar, 1, 1) table.meta[progressbar].y_fill = false local slider = Mx.Slider() table:add_actor(slider, 1, 2) table.meta[slider].y_fill = false function slider.on_notify:value() progressbar.progress = slider.value end local pathbar = Mx.PathBar() for _, path in ipairs { "", "Path", "Bar" } do pathbar:push(path) end table:add_actor(pathbar, 2, 0) local expander = Mx.Expander { label = "Expander" } table:add_actor(expander, 2, 1) table.meta[expander].y_fill = false expander:add_actor(Mx.Label { text = "Hello" }) local toggle = Mx.Toggle() table:add_actor(toggle, 2, 2) table.meta[toggle].y_fill = false local togglebutton = Mx.Button { label = "Toggle", is_toggle = true } table:add_actor(togglebutton, 3, 0) table.meta[togglebutton].y_fill = false local checkbutton = Mx.Button { is_toggle = true } checkbutton:set_style_class('check-box') table:add_actor(checkbutton, 3, 1) table.meta[checkbutton].y_fill = false table.meta[checkbutton].x_fill = false -- Just for fun, create binding between both kinds of toggles. togglebutton:bind_property('toggled', checkbutton, 'toggled', GObject.BindingFlags.BIDIRECTIONAL) scrollbar = Mx.ScrollBar { adjustment = Mx.Adjustment { lower = 0, upper = 10, page_increment = 1, page_size = 1, }, orientation = Mx.Orientation.VERTICAL, width = 22 } table:add_actor(scrollbar, 0, 3) table.meta[scrollbar].row_span = 3 window.child = Mx.Frame { child = table } window.clutter_stage:show() app:run() lgi-0.9.2/samples/repobrowser.lua000077500000000000000000000071151316674307300170360ustar00rootroot00000000000000#! /usr/bin/env lua -- -- Implementation of simple browser of lgi repository. This is mainly -- treeview usage demonstration. -- local lgi = require 'lgi' local GObject = lgi.GObject local Gtk = lgi.require('Gtk', '3.0') local function RepoBrowser() local self = {} -- Create browser model. local column = { NAME = 1, VALUE = 2, } local model = Gtk.TreeStore.new { [column.NAME] = GObject.Type.STRING, [column.VALUE] = GObject.Type.STRING, } -- Merges repository table into the treemodel as child of given -- iterator. local blacklisted = { _parent = true, _implements = true, __index = true, } local function merge(parent, table) for name, value in pairs(table) do local string_value = '' if type(value) == 'string' then string_value = value elseif type(value) == 'number' then string_value = tonumber(value) elseif name == '_parent' then string_value = value._name end local iter = model:append(parent, { [column.NAME] = name, [column.VALUE] = string_value, }) if type(value) == 'table' and not blacklisted[name] then merge(iter, value) end end end -- Loads specified repo namespace into the model. function self.add(namespace) local iter = model:append(nil, { [column.NAME] = namespace._name, [column.VALUE] = namespace._version, }) merge(iter, namespace:_resolve(true)) end -- Create treeview widget with columns. local sorted = Gtk.TreeModelSort { model = model } sorted:set_sort_func(column.NAME, function(model, a, b) a = model[a][column.NAME] b = model[b][column.NAME] if a == b then return 0 elseif a < b then return -1 else return 1 end end) sorted:set_sort_column_id(column.NAME, Gtk.SortType.ASCENDING) self.view = Gtk.TreeView { model = sorted, Gtk.TreeViewColumn { title = "Name", resizable = true, sort_column_id = column.NAME, { Gtk.CellRendererText(), expand = true, { text = column.NAME } }, }, Gtk.TreeViewColumn { { Gtk.CellRendererText(), { text = column.VALUE } }, }, } return self end -- Create application and its window. local app = Gtk.Application { application_id = 'org.lgi.repobrowser' } function app:on_activate() local browser = RepoBrowser() browser.add(lgi.GObject) -- Global window. local window = Gtk.Window { application = app, title = "LGI Repository Browser", default_width = 800, default_height = 640, Gtk.Grid { row_spacing = 5, column_spacing = 5, margin = 5, { Gtk.Label { label = "Namespace" } }, { Gtk.Entry { id = 'name', hexpand = true }, left_attach = 1 }, { Gtk.Label { label = "Version" }, left_attach = 2 }, { Gtk.Entry { id = 'version', hexpand = true }, left_attach = 3 }, { Gtk.Button { id = 'add', label = Gtk.STOCK_ADD, use_stock = true }, left_attach = 4 }, { Gtk.ScrolledWindow { expand = true, browser.view }, left_attach = 0, top_attach = 1, width = 5 }, } } function window.child.add:on_clicked() local version = window.child.version.text if version == '' then version = nil end local ok, data = pcall(lgi.require, window.child.name.text, version) if ok then browser.add(data) else local message = Gtk.MessageDialog { text = "Failed to load requested namespace", secondary_text = data, message_type = Gtk.MessageType.ERROR, buttons = Gtk.ButtonsType.CLOSE, transient_for = window, } message:run() message:destroy() end end window:show_all() end app:run { arg[0], ... } lgi-0.9.2/samples/soupsvr.lua000066400000000000000000000047641316674307300162120ustar00rootroot00000000000000#! /usr/bin/env lua -- -- Sample server using libsoup library. Listens on 1080 port and serves -- local files from current directory. Allows to be terminated by query -- for /quit file (i.e. curl http://localhost:1080/quit) -- local coroutine = require 'coroutine' local lgi = require 'lgi' local bytes = require 'bytes' local GLib = lgi.GLib local Gio = lgi.Gio local Soup = lgi.Soup local app = Gio.Application { application_id = 'org.lgi.soupsvr' } function app:on_activate() app:hold() local server = Soup.Server { port = 1080 } -- Set up quit handler. server:add_handler('/quit', function(server, msg, path, query, ctx) msg.status_code = 200 msg.response_body:complete() server:quit() app:release() end) -- Set up file retriever handler. server:add_handler('/', function(server, msg, path, query, ctx) local stream = Gio.File.new_for_path(path:sub(2)):read() if stream then -- The whole is send by function running in coroutine. -- Coroutine yields when it waits either for data from the -- disk or signal that chunk was successfully sent. local next_chunk = coroutine.wrap(function() local buffer = bytes.new(4096) while true do -- Read another chunk of data from the source to the -- buffer. stream:read_async(buffer, GLib.PRIORITY_DEFAULT, nil, coroutine.running()) local size = stream.read_finish(coroutine.yield()) if size < 0 then -- IO error reading disk, this simply shuts our toy -- server down. server:quit() app:release() return end -- Send the chunk to the message body. msg.response_body:append(tostring(buffer):sub(1, size)) server:unpause_message(msg) -- Wait until soup signalizes that chunk was written. coroutine.yield() if size < #buffer then break end end msg.response_body:complete() end) -- Prepare sending using chunked method. msg.status_code = 200 msg.response_headers:set_encoding('CHUNKED') -- When headers are written, start writing body by initial -- resuming of sending coroutine. msg.on_wrote_headers = next_chunk -- When the chunk is sent, resume coroutine so that it starts -- reading and sending another chunk. msg.on_wrote_chunk = next_chunk else -- File was not found, send proper code. msg.status_code = 404 msg.response_body:complete() end end) -- Start the server running asynchronously. server:run_async() end app:run { arg[0], ... } lgi-0.9.2/tests/000077500000000000000000000000001316674307300134515ustar00rootroot00000000000000lgi-0.9.2/tests/.gitignore000066400000000000000000000000201316674307300154310ustar00rootroot00000000000000*.gir *.typelib lgi-0.9.2/tests/Makefile000066400000000000000000000041511316674307300151120ustar00rootroot00000000000000# # Makefile for compiling lgi testsuite support # # Author: Pavel Holejsovsky # License: MIT # HOST_OS = $(shell uname -s | tr A-Z a-z) ifneq ($(filter cygwin% msys% mingw%, $(HOST_OS)),) EXT = .dll PFX = cyg LIBFLAG = -shared else ifeq ($(HOST_OS),darwin) EXT = .so PFX = lib LIBFLAG = -bundle -undefined dynamic_lookup CCSHARED = -fno-common else EXT = .so PFX = lib LIBFLAG = -shared CCSHARED = -fPIC endif endif PKGS = gio-2.0 cairo cairo-gobject gobject-introspection-1.0 gmodule-2.0 libffi LUA = lua PKG_CONFIG = pkg-config ifndef CFLAGS ifndef COPTFLAGS CFLAGS = -Wall -g endif endif ALL_CFLAGS = $(CCSHARED) $(COPTFLAGS) $(LUA_CFLAGS) $(shell $(PKG_CONFIG) --cflags $(PKGS)) $(CFLAGS) -I . LIBS += $(shell $(PKG_CONFIG) --libs $(PKGS)) ALL_LDFLAGS = $(LIBFLAG) $(LDFLAGS) DEPCHECK = .depcheck # Precondition check $(DEPCHECK) : Makefile $(PKG_CONFIG) --exists '$(PKGS) >= 0.10.8' --print-errors touch $@ REGRESS = $(PFX)regress$(EXT) REGRESS_OBJS = regress.o .PHONY : all clean check all : Regress-1.0.typelib clean : rm -f $(REGRESS) $(REGRESS_OBJS) Regress-1.0.gir Regress-1.0.typelib check : all cd .. && LD_LIBRARY_PATH=tests:$$LD_LIBRARY_PATH \ GI_TYPELIB_PATH=tests:$$GI_TYPELIB_PATH \ LUA_PATH="./?.lua;${LUA_PATH};" \ LUA_CPATH="./?.so;${LUA_CPATH};" \ $(shell command -v dbus-run-session || echo /usr/bin/dbus-launch) $(LUA) tests/test.lua $(REGRESS) : regress.o $(CC) $(ALL_LDFLAGS) -o $@ regress.o $(LIBS) GIDATADIR = $(shell $(PKG_CONFIG) --variable=gidatadir gobject-introspection-1.0)/tests regress.o : $(GIDATADIR)/regress.c $(GIDATADIR)/regress.h $(DEPCHECK) $(CC) $(ALL_CFLAGS) -c -o $@ $< # Build .gir and .typelib Regress-1.0.gir : $(REGRESS) LDFLAGS="" CFLAGS="" \ g-ir-scanner --warn-all --no-libtool --quiet --output=$@ \ --namespace=Regress --nsversion=1.0 \ --include=cairo-1.0 --include=Gio-2.0 \ --library-path=/usr/lib --library-path=/usr/X11R6/lib \ --library-path=/usr/local/lib \ $(GIDATADIR)/regress.c $(GIDATADIR)/regress.h \ -lregress Regress-1.0.typelib : Regress-1.0.gir g-ir-compiler --output=$@ $< lgi-0.9.2/tests/cairo.lua000066400000000000000000000303071316674307300152540ustar00rootroot00000000000000--[[-------------------------------------------------------------------------- LGI testsuite, cairo overrides test group. Copyright (c) 2012 Pavel Holejsovsky Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php --]]-------------------------------------------------------------------------- local io = require 'io' local os = require 'os' local lgi = require 'lgi' local check = testsuite.check local checkv = testsuite.checkv local cairo = testsuite.group.new('cairo') function cairo.status() local cairo = lgi.cairo for name, value in pairs(cairo.Status) do if type(name) == 'string' and type(value) == 'number' then checkv(cairo.Status.to_string(name), cairo.Status.to_string(value), 'string') end end end -- Helper, checks that given value has number type and expected value, -- with some tolerance. local function checkvf(val, exp, tolerance) check(type(val) == 'number', string.format( "got type `%s', expected `number'", type(val)), 2) check(math.abs(val - exp) <= tolerance, string.format("got value `%s', expected `%s'", tostring(val), tostring(exp)), 2) end local function check_matrix(matrix, xx, yx, xy, yy, x0, y0) local t = 0.0000001 checkvf(matrix.xx, xx, t) checkvf(matrix.yx, yx, t) checkvf(matrix.xy, xy, t) checkvf(matrix.yy, yy, t) checkvf(matrix.x0, x0, t) checkvf(matrix.y0, y0, t) end function cairo.matrix() local cairo = lgi.cairo local matrix = cairo.Matrix() check_matrix(matrix, 0, 0, 0, 0, 0, 0) matrix = cairo.Matrix { xx = 1, yx =1.5, xy = 2, yy = 2.5, x0 = 3, y0 = 3.5 } check_matrix(matrix, 1, 1.5, 2, 2.5, 3, 3.5) end function cairo.matrix_getset() local cairo = lgi.cairo local surface = cairo.ImageSurface('ARGB32', 100, 100) local cr = cairo.Context(surface) local m = cairo.Matrix { xx = 1, yx =1.5, xy = 2, yy = 2.5, x0 = 3, y0 = 3.5 } cr.matrix = m local m2 = cr.matrix check(m.xx == m2.xx) check(m.yx == m2.yx) check(m.xy == m2.xy) check(m.yy == m2.yy) check(m.x0 == m2.x0) check(m.y0 == m2.y0) end function cairo.matrix_init() local cairo = lgi.cairo local m = cairo.Matrix.create_identity() check_matrix(m, 1, 0, 0, 1, 0, 0) local m = cairo.Matrix.create_translate(2, 3) check_matrix(m, 1, 0, 0, 1, 2, 3) local m = cairo.Matrix.create_scale(2, 3) check_matrix(m, 2, 0, 0, 3, 0, 0) local angle = math.pi / 2 local m = cairo.Matrix.create_rotate(angle) local c, s = math.cos(angle), math.sin(angle) check_matrix(m, c, s, -s, c, 0, 0) end function cairo.matrix_operations() local cairo = lgi.cairo local m = cairo.Matrix.create_identity() m:translate(2, 3) check_matrix(m, 1, 0, 0, 1, 2, 3) m:scale(-2, -3) check_matrix(m, -2, 0, 0, -3, 2, 3) m:rotate(0) check_matrix(m, -2, 0, 0, -3, 2, 3) local m2 = cairo.Matrix.create_translate(2, 3) local status = m2:invert() checkv(status, 'SUCCESS', 'string') check_matrix(m2, 1, 0, 0, 1, -2, -3) -- XXX: This API could be improved local result = cairo.Matrix.create_identity() result:multiply(m, m2) check_matrix(result, -2, 0, 0, -3, 0, 0) local x, y = m:transform_point(1, 1) checkv(x, 0, 'number') checkv(y, 0, 'number') local x, y = m:transform_distance(1, 1) checkv(x, -2, 'number') checkv(y, -3, 'number') end function cairo.dash() local cairo = lgi.cairo local surface = cairo.ImageSurface('ARGB32', 100, 100) local cr = cairo.Context(surface) local dash, offset = cr:get_dash() check(type(dash) == 'table') check(next(dash) == nil) cr:set_dash({ 1, 2, math.pi }, 2.22) dash, offset = cr:get_dash() check(#dash == 3) check(dash[1] == 1) check(dash[2] == 2) check(dash[3] == math.pi) check(offset == 2.22) cr:set_dash(nil, 0) dash, offset = cr:get_dash() check(type(dash) == 'table') check(next(dash) == nil) end function cairo.path() local cairo = lgi.cairo local surface = cairo.ImageSurface('ARGB32', 100, 100) local cr = cairo.Context(surface) cr:move_to(10, 11) cr:curve_to(1, 2, 3, 4, 5, 6) cr:close_path() cr:line_to(21, 22) local i = 1 for t, pts in cr:copy_path():pairs() do if i == 1 then checkv(t, 'MOVE_TO', 'string') check(type(pts) == 'table' and #pts == 1) checkv(pts[1].x, 10, 'number') checkv(pts[1].y, 11, 'number') elseif i == 2 then checkv(t, 'CURVE_TO', 'string') check(type(pts) == 'table' and #pts == 3) checkv(pts[1].x, 1, 'number') checkv(pts[1].y, 2, 'number') checkv(pts[2].x, 3, 'number') checkv(pts[2].y, 4, 'number') checkv(pts[3].x, 5, 'number') checkv(pts[3].y, 6, 'number') elseif i == 3 then checkv(t, 'CLOSE_PATH', 'string') check(type(pts) == 'table' and #pts == 0) elseif i == 4 then checkv(t, 'MOVE_TO', 'string') check(type(pts) == 'table' and #pts == 1) checkv(pts[1].x, 10, 'number') checkv(pts[1].y, 11, 'number') elseif i == 5 then checkv(t, 'LINE_TO', 'string') check(type(pts) == 'table' and #pts == 1) checkv(pts[1].x, 21, 'number') checkv(pts[1].y, 22, 'number') else check(false) end i = i + 1 end check(i == 6) end function cairo.surface_type() local cairo = lgi.cairo local surface = cairo.ImageSurface('ARGB32', 100, 100) local cr = cairo.Context(surface) check(cairo.ImageSurface:is_type_of(surface)) check(cairo.Surface:is_type_of(surface)) check(not cairo.RecordingSurface:is_type_of(surface)) local s2 = cr.target check(cairo.ImageSurface:is_type_of(s2)) check(cairo.Surface:is_type_of(s2)) check(not cairo.RecordingSurface:is_type_of(s2)) local s3 = cr.group_target check(cairo.ImageSurface:is_type_of(s3)) check(cairo.Surface:is_type_of(s3)) check(not cairo.RecordingSurface:is_type_of(s3)) end function cairo.pattern_type() local cairo = lgi.cairo local pattern pattern = cairo.Pattern.create_rgb(1, 1, 1) check(cairo.SolidPattern:is_type_of(pattern)) check(cairo.Pattern:is_type_of(pattern)) pattern = cairo.SolidPattern(1, 1, 1) check(cairo.SolidPattern:is_type_of(pattern)) pattern = cairo.SolidPattern(1, 1, 1, 1) check(cairo.SolidPattern:is_type_of(pattern)) local surface = cairo.ImageSurface('ARGB32', 100, 100) pattern = cairo.Pattern.create_for_surface(surface) check(select(2, pattern:get_surface()) == surface) check(cairo.SurfacePattern:is_type_of(pattern)) check(cairo.Pattern:is_type_of(pattern)) pattern = cairo.SurfacePattern(surface) check(cairo.SurfacePattern:is_type_of(pattern)) pattern = cairo.Pattern.create_linear(0, 0, 10, 10) check(cairo.LinearPattern:is_type_of(pattern)) check(cairo.GradientPattern:is_type_of(pattern)) check(cairo.Pattern:is_type_of(pattern)) pattern = cairo.LinearPattern(0, 0, 10, 10) check(cairo.LinearPattern:is_type_of(pattern)) pattern = cairo.Pattern.create_radial(0, 0, 5, 10, 10, 5) check(cairo.RadialPattern:is_type_of(pattern)) check(cairo.GradientPattern:is_type_of(pattern)) check(cairo.Pattern:is_type_of(pattern)) pattern = cairo.RadialPattern(0, 0, 5, 10, 10, 5) check(cairo.RadialPattern:is_type_of(pattern)) if cairo.version >= cairo.version_encode(1, 12, 0) then pattern = cairo.Pattern.create_mesh() check(cairo.MeshPattern:is_type_of(pattern)) check(not cairo.GradientPattern:is_type_of(pattern)) check(cairo.Pattern:is_type_of(pattern)) pattern = cairo.MeshPattern() check(cairo.MeshPattern:is_type_of(pattern)) end end function cairo.pattern_mesh() local cairo = lgi.cairo -- Mesh patterns are introduced in cairo 1.12 if cairo.version < cairo.version_encode(1, 12, 0) then return end local mesh = cairo.Pattern.create_mesh() local pattern = cairo.Pattern.create_radial(1, 2, 3, 4, 5, 6) check(cairo.Pattern:is_type_of(mesh)) check(cairo.MeshPattern:is_type_of(mesh)) check(cairo.Pattern:is_type_of(pattern)) check(not cairo.MeshPattern:is_type_of(pattern)) local function check_status(status) checkv(status, 'SUCCESS', 'string') end -- Taken from cairo's pattern-getters test and slightly adapted to use all -- functions of the mesh pattern API local status, count = mesh:get_patch_count() check_status(status) checkv(count, 0, 'number') mesh:begin_patch() mesh:move_to(0, 0) mesh:line_to(0, 3) mesh:line_to(3, 3) mesh:line_to(3, 0) mesh:set_corner_color_rgba(0, 1, 1, 1, 1) mesh:end_patch() local status, count = mesh:get_patch_count() check_status(status) checkv(count, 1, 'number') for k, v in pairs({ { 1, 1 }, { 1, 2 }, { 2, 2 }, { 2, 1 } }) do local status, x, y = mesh:get_control_point(0, k - 1) check_status(status) checkv(x, v[1], 'number') checkv(y, v[2], 'number') end mesh:begin_patch() mesh:move_to(0, 0) mesh:line_to(1, 0) mesh:curve_to(1, 1, 1, 2, 0, 1) mesh:set_corner_color_rgb(0, 1, 1, 1) mesh:set_control_point(2, 0.5, 0.5) mesh:end_patch() local status, count = mesh:get_patch_count() check_status(status) checkv(count, 2, 'number') for k, v in pairs({ 1, 0, 0, 1 }) do local status, r, g, b, a = mesh:get_corner_color_rgba(1, k - 1) check_status(status) checkv(r, v, 'number') checkv(g, v, 'number') checkv(b, v, 'number') checkv(a, v, 'number') end local i = 0 local expected = { { { 0, 1 }, { 0, 2 }, { 0, 3 } }, { { 1, 3 }, { 2, 3 }, { 3, 3 } }, { { 3, 2 }, { 3, 1 }, { 3, 0 } }, { { 2, 0 }, { 1, 0 }, { 0, 0 } }, } for t, pts in mesh:get_path(0):pairs() do if i == 0 then checkv(t, 'MOVE_TO', 'string') check(type(pts) == 'table' and #pts == 1) checkv(pts[1].x, 0, 'number') checkv(pts[1].y, 0, 'number') else -- Mesh patterns turn everything into curves. :-( checkv(t, 'CURVE_TO', 'string') check(type(pts) == 'table' and #pts == 3) for k, v in pairs(expected[i]) do checkv(pts[k].x, v[1], 'number') checkv(pts[k].y, v[2], 'number') end end i = i + 1 end check(i == #expected + 1) end function cairo.context_getset() local cairo = lgi.cairo local surface = cairo.ImageSurface('ARGB32', 100, 100) local cr = cairo.Context(surface) local s2 = cr.target check(s2 == surface) local s3 = cr.group_target check(s3 == surface) cr.source = cairo.Pattern.create_linear(0, 0, 10, 10) check(cairo.LinearPattern:is_type_of(cr.source)) cr.antialias = "BEST" check(cr.antialias == "BEST") cr.fill_rule = "EVEN_ODD" check(cr.fill_rule == "EVEN_ODD") cr.line_cap = "SQUARE" check(cr.line_cap == "SQUARE") cr.line_join = "BEVEL" check(cr.line_join == "BEVEL") cr.line_width = 42 check(cr.line_width == 42) cr.miter_limit = 5 check(cr.miter_limit == 5) cr.operator = "ATOP" check(cr.operator == "ATOP") cr.tolerance = 21 check(cr.tolerance == 21) local m = cairo.Matrix.create_translate(-1, 4) cr.matrix = m check_matrix(cr.matrix, 1, 0, 0, 1, -1, 4) local m = cairo.Matrix.create_scale(2, 3) cr.font_matrix = m check_matrix(cr.font_matrix, 2, 0, 0, 3, 0, 0) -- font size is read-only, but messes with the font matrix cr.font_size = 100 check_matrix(cr.font_matrix, 100, 0, 0, 100, 0, 0) local opt = cairo.FontOptions.create() cr.font_options = opt check(cr.font_options:equal(opt)) local font_face = cairo.ToyFontFace.create("Arial", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) cr.font_face = font_face check(cairo.ToyFontFace:is_type_of(cr.font_face)) local scaled_font = cairo.ScaledFont.create(font_face, m, m, opt) cr.scaled_font = scaled_font check(cairo.ScaledFont:is_type_of(cr.scaled_font)) end function cairo.context_transform() local cairo = lgi.cairo local surface = cairo.ImageSurface('ARGB32', 100, 100) local cr = cairo.Context(surface) function compare(a, b) check(math.abs(a-b) < 0.1) end cr:rotate(-math.pi / 2) cr:translate(100, 200) local x, y = cr:user_to_device(10, 20) compare(x, 220) compare(y, -110) local x, y = cr:device_to_user(220, -110) compare(x, 10) compare(y, 20) local x, y = cr:user_to_device_distance(10, 20) compare(x, 20) compare(y, -10) local x, y = cr:device_to_user_distance(20, -10) compare(x, 10) compare(y, 20) end lgi-0.9.2/tests/config.h000066400000000000000000000002021316674307300150610ustar00rootroot00000000000000/* Phony config file used only to shut up certain versions og GoI's regress.c which does unconditional #include "config.h". */ lgi-0.9.2/tests/corocbk.lua000066400000000000000000000027311316674307300156010ustar00rootroot00000000000000--[[-------------------------------------------------------------------------- LGI testsuite, coroutine-targetted callbacks Copyright (c) 2010, 2011 Pavel Holejsovsky Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php --]]-------------------------------------------------------------------------- local coroutine = require 'coroutine' local lgi = require 'lgi' local check, checkv = testsuite.check, testsuite.checkv -- Basic GObject testing local corocbk = testsuite.group.new('corocbk') function corocbk.resume_suspd() local GLib = lgi.GLib local main_loop = GLib.MainLoop() local coro = coroutine.create( function() coroutine.yield() coroutine.yield(true) main_loop:quit() end) coroutine.resume(coro) GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, coro) main_loop:run() end function corocbk.resume_init() local GLib = lgi.GLib local main_loop = GLib.MainLoop() local coro = coroutine.create( function() coroutine.yield(true) main_loop:quit() end) GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, coro) main_loop:run() end function corocbk.rethrow() local GLib = lgi.GLib local main_loop = GLib.MainLoop() local coro = coroutine.create( function() (function() error('err', 0) end)() end) GLib.idle_add(GLib.PRIORITY_DEFAULT, coro) local ok, err = pcall(main_loop.run, main_loop) checkv(ok, false, 'boolean') checkv(err, 'err', 'string') end lgi-0.9.2/tests/dbus.lua000066400000000000000000000100051316674307300151050ustar00rootroot00000000000000--[[-------------------------------------------------------------------------- lgi testsuite, Gio DBus test suite. Copyright (c) 2013 Pavel Holejsovsky Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php --]]-------------------------------------------------------------------------- local lgi = require 'lgi' local check = testsuite.check -- Basic GLib testing local dbus = testsuite.group.new('dbus') function dbus.info_basic() local Gio = lgi.Gio local node = Gio.DBusNodeInfo { path = '/some/path', interfaces = { Gio.DBusInterfaceInfo { name = 'SomeInterface', methods = { Gio.DBusMethodInfo { name = 'SomeMethod', in_args = { Gio.DBusArgInfo { name = 'args', signature = 's', }, Gio.DBusArgInfo { name = 'argi', signature = 'i', }, }, }, }, properties = { Gio.DBusPropertyInfo { name = 'someProperty', signature = 's', flags = { 'READABLE', 'WRITABLE' }, }, }, }, }, } check(node.path == '/some/path') check(node.ref_count == 1) check(node.interfaces[1].name == 'SomeInterface') check(node.interfaces[1].methods[1].name == 'SomeMethod') check(#node.interfaces[1].methods[1].in_args == 2) check(node.interfaces[1].methods[1].in_args[1].name == 'args') check(node.interfaces[1].methods[1].in_args[1].signature == 's') check(node.interfaces[1].methods[1].in_args[2].name == 'argi') check(node.interfaces[1].methods[1].in_args[2].signature == 'i') check(#node.interfaces[1].methods[1].out_args == 0) check(#node.interfaces[1].properties == 1) check(node.interfaces[1].properties[1].name == 'someProperty') check(node.interfaces[1].properties[1].signature == 's') check(node.interfaces[1].properties[1].flags.READABLE) check(node.interfaces[1].properties[1].flags.WRITABLE) end function dbus.info_xml() local GLib = lgi.GLib local Gio = lgi.Gio local node = Gio.DBusNodeInfo { path = '/some/path', interfaces = { Gio.DBusInterfaceInfo { name = 'SomeInterface', methods = { Gio.DBusMethodInfo { name = 'SomeMethod', in_args = { Gio.DBusArgInfo { name = 'args', signature = 's', }, Gio.DBusArgInfo { name = 'argi', signature = 'i', }, }, }, }, properties = { Gio.DBusPropertyInfo { name = 'someProperty', signature = 's', flags = { 'READABLE', 'WRITABLE' }, }, }, }, }, } local xml_builder = GLib.String('') node:generate_xml(0, xml_builder) local xml = node.xml check(xml_builder.str == xml) local node = Gio.DBusNodeInfo.new_for_xml(xml) check(node) check(node.path == '/some/path') check(node.ref_count == 1) check(node.interfaces[1].name == 'SomeInterface') check(node.interfaces[1].methods[1].name == 'SomeMethod') check(#node.interfaces[1].methods[1].in_args == 2) check(node.interfaces[1].methods[1].in_args[1].name == 'args') check(node.interfaces[1].methods[1].in_args[1].signature == 's') check(node.interfaces[1].methods[1].in_args[2].name == 'argi') check(node.interfaces[1].methods[1].in_args[2].signature == 'i') check(#node.interfaces[1].methods[1].out_args == 0) check(#node.interfaces[1].properties == 1) check(node.interfaces[1].properties[1].name == 'someProperty') check(node.interfaces[1].properties[1].signature == 's') check(node.interfaces[1].properties[1].flags.READABLE) check(node.interfaces[1].properties[1].flags.WRITABLE) end function dbus.proxy_get_interface_info() local Gio = lgi.Gio local interface = Gio.DBusInterfaceInfo { name = 'SomeInterface' } local bus = Gio.bus_get_sync(Gio.BusType.SESSION) local proxy, err = Gio.DBusProxy.new_sync(bus, Gio.DBusProxyFlags.NONE, interface, "org.foo", "/", "org.foo.SomeInterface", nil) assert(proxy, err) local interface2 = proxy:get_interface_info() -- Just so that we do test something assert(interface == interface2) end lgi-0.9.2/tests/gio.lua000066400000000000000000000041461316674307300147370ustar00rootroot00000000000000--[[-------------------------------------------------------------------------- LGI testsuite, GIo test suite. Copyright (c) 2016 Uli Schlachter Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php --]]-------------------------------------------------------------------------- local type = type local lgi = require 'lgi' local core = require 'lgi.core' local check = testsuite.check local checkv = testsuite.checkv local gio = testsuite.group.new('gio') function gio.read() local GLib, Gio = lgi.GLib, lgi.Gio -- Prepare the input to read local input input = "line" input = Gio.MemoryInputStream.new_from_data(input) input = Gio.DataInputStream.new(input) local line, length -- Read line line, length = input:read_line() checkv(line, "line", "string") checkv(length, 4, "number") -- Read EOF line, length = input:read_line() checkv(line, nil, "nil") checkv(length, 0, "number") end function gio.async_access() -- Sometimes this hangs with LuaJIT when the JIT is on, no idea why. -- FIXME: Figure out what is going on and fix this. -- See also https://github.com/LuaJIT/LuaJIT/issues/340. if jit then jit.off() end local Gio = lgi.Gio local res res = Gio.DBusProxy.async_new check(res ~= nil) check(type(res) == 'function') res = Gio.DBusProxy.async_call check(res ~= nil) check(type(res) == 'function') res = Gio.async_bus_get check(res ~= nil) check(type(res) == 'function') local file = Gio.File.new_for_path('.') res = Gio.Async.call(function(target) return target:async_query_info('standard::size', 'NONE') end)(file) check(res ~= nil) local b = Gio.Async.call(function() return Gio.async_bus_get('SESSION') end)() check(Gio.DBusConnection:is_type_of(b)) local proxy = Gio.Async.call(function(bus) return Gio.DBusProxy.async_new( bus, 'NONE', nil, 'org.freedesktop.DBus', '/', 'org.freedesktop.DBus') end)(b) check(Gio.DBusProxy:is_type_of(proxy)) if jit then jit.on() end end lgi-0.9.2/tests/gireg.lua000066400000000000000000001245171316674307300152630ustar00rootroot00000000000000--[[-------------------------------------------------------------------------- LGI testsuite, GI Regress-based testsuite. Copyright (c) 2010, 2011 Pavel Holejsovsky Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php --]]-------------------------------------------------------------------------- local lgi = require 'lgi' local bytes = require 'bytes' local fail = testsuite.fail local check = testsuite.check local checkv = testsuite.checkv local gireg = testsuite.group.new('gireg') function gireg.type_boolean() local R = lgi.Regress checkv(R.test_boolean(true), true, 'boolean') checkv(R.test_boolean(false), false, 'boolean') check(select('#', R.test_boolean(true)) == 1) check(select('#', R.test_boolean(false)) == 1) checkv(R.test_boolean(), false, 'boolean') checkv(R.test_boolean(nil), false, 'boolean') checkv(R.test_boolean(0), true, 'boolean') checkv(R.test_boolean(1), true, 'boolean') checkv(R.test_boolean('string'), true, 'boolean') checkv(R.test_boolean({}), true, 'boolean') checkv(R.test_boolean(function() end), true, 'boolean') end function gireg.type_int8() local R = lgi.Regress checkv(R.test_int8(0), 0, 'number') checkv(R.test_int8(1), 1, 'number') checkv(R.test_int8(-1), -1, 'number') checkv(R.test_int8(1.1), 1, 'number') checkv(R.test_int8(-1.1), -1, 'number') checkv(R.test_int8(0x7f), 0x7f, 'number') checkv(R.test_int8(-0x80), -0x80, 'number') check(not pcall(R.test_int8, 0x80)) check(not pcall(R.test_int8, -0x81)) check(not pcall(R.test_int8)) check(not pcall(R.test_int8, nil)) check(not pcall(R.test_int8, 'string')) check(not pcall(R.test_int8, true)) check(not pcall(R.test_int8, {})) check(not pcall(R.test_int8, function() end)) end function gireg.type_uint8() local R = lgi.Regress checkv(R.test_uint8(0), 0, 'number') checkv(R.test_uint8(1), 1, 'number') checkv(R.test_uint8(1.1), 1, 'number') checkv(R.test_uint8(0xff), 0xff, 'number') check(not pcall(R.test_uint8, 0x100)) check(not pcall(R.test_uint8, -1)) check(not pcall(R.test_uint8)) check(not pcall(R.test_uint8, nil)) check(not pcall(R.test_uint8, 'string')) check(not pcall(R.test_uint8, true)) check(not pcall(R.test_uint8, {})) check(not pcall(R.test_uint8, function() end)) end function gireg.type_int16() local R = lgi.Regress checkv(R.test_int16(0), 0, 'number') checkv(R.test_int16(1), 1, 'number') checkv(R.test_int16(-1), -1, 'number') checkv(R.test_int16(1.1), 1, 'number') checkv(R.test_int16(-1.1), -1, 'number') checkv(R.test_int16(0x7fff), 0x7fff, 'number') checkv(R.test_int16(-0x8000), -0x8000, 'number') check(not pcall(R.test_int16, 0x8000)) check(not pcall(R.test_int16, -0x8001)) check(not pcall(R.test_int16)) check(not pcall(R.test_int16, nil)) check(not pcall(R.test_int16, 'string')) check(not pcall(R.test_int16, true)) check(not pcall(R.test_int16, {})) check(not pcall(R.test_int16, function() end)) end function gireg.type_uint16() local R = lgi.Regress checkv(R.test_uint16(0), 0, 'number') checkv(R.test_uint16(1), 1, 'number') checkv(R.test_uint16(1.1), 1, 'number') checkv(R.test_uint16(0xffff), 0xffff, 'number') check(not pcall(R.test_uint16, 0x10000)) check(not pcall(R.test_uint16, -1)) check(not pcall(R.test_uint16)) check(not pcall(R.test_uint16, nil)) check(not pcall(R.test_uint16, 'string')) check(not pcall(R.test_uint16, true)) check(not pcall(R.test_uint16, {})) check(not pcall(R.test_uint16, function() end)) end function gireg.type_int32() local R = lgi.Regress checkv(R.test_int32(0), 0, 'number') checkv(R.test_int32(1), 1, 'number') checkv(R.test_int32(-1), -1, 'number') checkv(R.test_int32(1.1), 1, 'number') checkv(R.test_int32(-1.1), -1, 'number') checkv(R.test_int32(0x7fffffff), 0x7fffffff, 'number') checkv(R.test_int32(-0x80000000), -0x80000000, 'number') check(not pcall(R.test_int32, 0x80000000)) check(not pcall(R.test_int32, -0x80000001)) check(not pcall(R.test_int32)) check(not pcall(R.test_int32, nil)) check(not pcall(R.test_int32, 'string')) check(not pcall(R.test_int32, true)) check(not pcall(R.test_int32, {})) check(not pcall(R.test_int32, function() end)) end function gireg.type_uint32() local R = lgi.Regress checkv(R.test_uint32(0), 0, 'number') checkv(R.test_uint32(1), 1, 'number') checkv(R.test_uint32(1.1), 1, 'number') checkv(R.test_uint32(0xffffffff), 0xffffffff, 'number') check(not pcall(R.test_uint32, 0x100000000)) check(not pcall(R.test_uint32, -1)) check(not pcall(R.test_uint32)) check(not pcall(R.test_uint32, nil)) check(not pcall(R.test_uint32, 'string')) check(not pcall(R.test_uint32, true)) check(not pcall(R.test_uint32, {})) check(not pcall(R.test_uint32, function() end)) end function gireg.type_int64() local R = lgi.Regress checkv(R.test_int64(0), 0, 'number') checkv(R.test_int64(1), 1, 'number') checkv(R.test_int64(-1), -1, 'number') checkv(R.test_int64(1.1), 1, 'number') checkv(R.test_int64(-1.1), -1, 'number') check(not pcall(R.test_int64)) check(not pcall(R.test_int64, nil)) check(not pcall(R.test_int64, 'string')) check(not pcall(R.test_int64, true)) check(not pcall(R.test_int64, {})) check(not pcall(R.test_int64, function() end)) -- Following tests fail because Lua's internal number representation -- is always 'double', and conversion between double and int64 big -- constants is always lossy. Not sure if it can be solved somehow. -- checkv(R.test_int64(0x7fffffffffffffff), 0x7fffffffffffffff, 'number') -- checkv(R.test_int64(-0x8000000000000000), -0x8000000000000000, 'number') -- check(not pcall(R.test_int64, 0x8000000000000000)) -- check(not pcall(R.test_int64, -0x8000000000000001)) end function gireg.type_uint64() local R = lgi.Regress checkv(R.test_uint64(0), 0, 'number') checkv(R.test_uint64(1), 1, 'number') checkv(R.test_uint64(1.1), 1, 'number') check(not pcall(R.test_uint64, -1)) check(not pcall(R.test_uint64)) check(not pcall(R.test_uint64, nil)) check(not pcall(R.test_uint64, 'string')) check(not pcall(R.test_uint64, true)) check(not pcall(R.test_uint64, {})) check(not pcall(R.test_uint64, function() end)) -- See comment above about lossy conversions. -- checkv(R.test_uint64(0xffffffffffffffff), 0xffffffffffffffff, 'number') -- check(not pcall(R.test_uint64, 0x10000000000000000)) end function gireg.type_short() local R = lgi.Regress checkv(R.test_short(0), 0, 'number') checkv(R.test_short(1), 1, 'number') checkv(R.test_short(-1), -1, 'number') checkv(R.test_short(1.1), 1, 'number') checkv(R.test_short(-1.1), -1, 'number') end function gireg.type_ushort() local R = lgi.Regress checkv(R.test_ushort(0), 0, 'number') checkv(R.test_ushort(1), 1, 'number') checkv(R.test_ushort(1.1), 1, 'number') check(not pcall(R.test_ushort, -1)) end function gireg.type_int() local R = lgi.Regress checkv(R.test_int(0), 0, 'number') checkv(R.test_int(1), 1, 'number') checkv(R.test_int(-1), -1, 'number') checkv(R.test_int(1.1), 1, 'number') checkv(R.test_int(-1.1), -1, 'number') end function gireg.type_uint() local R = lgi.Regress checkv(R.test_uint(0), 0, 'number') checkv(R.test_uint(1), 1, 'number') checkv(R.test_uint(1.1), 1, 'number') check(not pcall(R.test_uint, -1)) end function gireg.type_ssize() local R = lgi.Regress checkv(R.test_ssize(0), 0, 'number') checkv(R.test_ssize(1), 1, 'number') checkv(R.test_ssize(-1), -1, 'number') checkv(R.test_ssize(1.1), 1, 'number') checkv(R.test_ssize(-1.1), -1, 'number') end function gireg.type_size() local R = lgi.Regress checkv(R.test_size(0), 0, 'number') checkv(R.test_size(1), 1, 'number') checkv(R.test_size(1.1), 1, 'number') check(not pcall(R.test_size, -1)) end -- Helper, checks that given value has requested type and value, with some -- tolerance because of low precision of gfloat type. local function checkvf(val, exp, tolerance) check(type(val) == 'number', string.format( "got type `%s', expected `number'", type(val)), 2) check(math.abs(val - exp) <= tolerance, string.format("got value `%s', expected `%s'", tostring(val), tostring(exp)), 2) end function gireg.type_float() local R = lgi.Regress local t = 0.0000001 checkvf(R.test_float(0), 0, t) checkvf(R.test_float(1), 1, t) checkvf(R.test_float(1.1), 1.1, t) checkvf(R.test_float(-1), -1, t) checkvf(R.test_float(-1.1), -1.1, t) checkvf(R.test_float(0x8000), 0x8000, t) checkvf(R.test_float(0xffff), 0xffff, t) checkvf(R.test_float(-0x8000), -0x8000, t) checkvf(R.test_float(-0xffff), -0xffff, t) check(not pcall(R.test_float)) check(not pcall(R.test_float, nil)) check(not pcall(R.test_float, 'string')) check(not pcall(R.test_float, true)) check(not pcall(R.test_float, {})) check(not pcall(R.test_float, function() end)) end function gireg.type_double() local R = lgi.Regress checkv(R.test_double(0), 0, 'number') checkv(R.test_double(1), 1, 'number') checkv(R.test_double(1.1), 1.1, 'number') checkv(R.test_double(-1), -1, 'number') checkv(R.test_double(-1.1), -1.1, 'number') checkv(R.test_double(0x80000000), 0x80000000, 'number') checkv(R.test_double(0xffffffff), 0xffffffff, 'number') checkv(R.test_double(-0x80000000), -0x80000000, 'number') checkv(R.test_double(-0xffffffff), -0xffffffff, 'number') check(not pcall(R.test_double)) check(not pcall(R.test_double, nil)) check(not pcall(R.test_double, 'string')) check(not pcall(R.test_double, true)) check(not pcall(R.test_double, {})) check(not pcall(R.test_double, function() end)) end function gireg.type_timet() local R = lgi.Regress checkv(R.test_timet(0), 0, 'number') checkv(R.test_timet(1), 1, 'number') checkv(R.test_timet(10000), 10000, 'number') check(not pcall(R.test_timet)) check(not pcall(R.test_timet, nil)) check(not pcall(R.test_timet, 'string')) check(not pcall(R.test_timet, true)) check(not pcall(R.test_timet, {})) check(not pcall(R.test_timet, function() end)) end function gireg.type_gtype() local R = lgi.Regress checkv(R.test_gtype(), nil, 'nil') checkv(R.test_gtype(nil), nil, 'nil') checkv(R.test_gtype(0), nil, 'nil') checkv(R.test_gtype('void'), 'void', 'string') checkv(R.test_gtype(4), 'void', 'string') checkv(R.test_gtype('GObject'), 'GObject', 'string') checkv(R.test_gtype(80), 'GObject', 'string') checkv(R.test_gtype(R.TestObj), 'RegressTestObj', 'string') check(not pcall(R.test_gtype, true)) check(not pcall(R.test_gtype, function() end)) end function gireg.utf8_const_return() local R = lgi.Regress local utf8_const = 'const \226\153\165 utf8' check(R.test_utf8_const_return() == utf8_const) end function gireg.utf8_nonconst_return() local R = lgi.Regress local utf8_nonconst = 'nonconst \226\153\165 utf8' check(R.test_utf8_nonconst_return() == utf8_nonconst) end function gireg.utf8_const_in() local R = lgi.Regress local utf8_const = 'const \226\153\165 utf8' R.test_utf8_const_in(utf8_const) R.test_utf8_const_in(bytes.new(utf8_const .. '\0')) end function gireg.utf8_out() local R = lgi.Regress local utf8_nonconst = 'nonconst \226\153\165 utf8' check(R.test_utf8_out() == utf8_nonconst) end function gireg.utf8_inout() local R = lgi.Regress local utf8_const = 'const \226\153\165 utf8' local utf8_nonconst = 'nonconst \226\153\165 utf8' check(R.test_utf8_inout(utf8_const) == utf8_nonconst) end function gireg.filename_return() local R = lgi.Regress local fns = R.test_filename_return() check(type(fns) == 'table') check(#fns == 2) check(fns[1] == 'åäö') check(fns[2] == '/etc/fstab') end function gireg.utf8_int_out_utf8() local R = lgi.Regress check(R.test_int_out_utf8('') == 0) check(R.test_int_out_utf8('abc') == 3) local utf8_const = 'const \226\153\165 utf8' check(R.test_int_out_utf8(utf8_const) == 12) end function gireg.multi_double_args() local R = lgi.Regress local o1, o2 = R.test_multi_double_args(1) check(o1 == 2 and o2 == 3) check(#{R.test_multi_double_args(1)} == 2) end function gireg.utf8_out_out() local R = lgi.Regress local o1, o2 = R.test_utf8_out_out() check(o1 == 'first' and o2 == 'second') check(#{R.test_utf8_out_out()} == 2) end function gireg.utf8_out_nonconst_return() local R = lgi.Regress local o1, o2 = R.test_utf8_out_nonconst_return() check(o1 == 'first' and o2 == 'second') check(#{R.test_utf8_out_nonconst_return()} == 2) end function gireg.utf8_null_in() local R = lgi.Regress R.test_utf8_null_in(nil) R.test_utf8_null_in() end function gireg.utf8_null_out() local R = lgi.Regress check(R.test_utf8_null_out() == nil) end function gireg.array_int_in() local R = lgi.Regress check(R.test_array_int_in{1,2,3} == 6) check(R.test_array_int_in{1.1,2,3} == 6) check(R.test_array_int_in{} == 0) check(not pcall(R.test_array_int_in, nil)) check(not pcall(R.test_array_int_in, 'help')) check(not pcall(R.test_array_int_in, {'help'})) end function gireg.array_int_out() local R = lgi.Regress local a = R.test_array_int_out() check(#a == 5) check(a[1] == 0 and a[2] == 1 and a[3] == 2 and a[4] == 3 and a[5] == 4) check(#{R.test_array_int_out()} == 1) end function gireg.array_int_inout() local R = lgi.Regress local a = R.test_array_int_inout({1, 2, 3, 4, 5}) check(#a == 4) check(a[1] == 3 and a[2] == 4 and a[3] == 5 and a[4] == 6) check(#{R.test_array_int_inout({1, 2, 3, 4, 5})} == 1) check(not pcall(R.test_array_int_inout, nil)) check(not pcall(R.test_array_int_inout, 'help')) check(not pcall(R.test_array_int_inout, {'help'})) end function gireg.array_gint8_in() local R = lgi.Regress check(R.test_array_gint8_in{1,2,3} == 6) check(R.test_array_gint8_in{1.1,2,3} == 6) check(R.test_array_gint8_in('0123') == 48 + 49 + 50 + 51) check(R.test_array_gint8_in(bytes.new('0123')) == 48 + 49 + 50 + 51) check(R.test_array_gint8_in{} == 0) check(not pcall(R.test_array_gint8_in, nil)) check(not pcall(R.test_array_gint8_in, {'help'})) end function gireg.array_gint16_in() local R = lgi.Regress check(R.test_array_gint16_in{1,2,3} == 6) check(R.test_array_gint16_in{1.1,2,3} == 6) check(R.test_array_gint16_in{} == 0) check(not pcall(R.test_array_gint16_in, nil)) check(not pcall(R.test_array_gint16_in, 'help')) check(not pcall(R.test_array_gint16_in, {'help'})) end function gireg.array_gint32_in() local R = lgi.Regress check(R.test_array_gint32_in{1,2,3} == 6) check(R.test_array_gint32_in{1.1,2,3} == 6) check(R.test_array_gint32_in{} == 0) check(not pcall(R.test_array_gint32_in, nil)) check(not pcall(R.test_array_gint32_in, 'help')) check(not pcall(R.test_array_gint32_in, {'help'})) end function gireg.array_gint64_in() local R = lgi.Regress check(R.test_array_gint64_in{1,2,3} == 6) check(R.test_array_gint64_in{1.1,2,3} == 6) check(R.test_array_gint64_in{} == 0) check(not pcall(R.test_array_gint64_in, nil)) check(not pcall(R.test_array_gint64_in, 'help')) check(not pcall(R.test_array_gint64_in, {'help'})) end function gireg.array_strv_in() local R = lgi.Regress check(R.test_strv_in{'1', '2', '3'}) check(not pcall(R.test_strv_in)) check(not pcall(R.test_strv_in, '1')) check(not pcall(R.test_strv_in, 1)) check(not R.test_strv_in{'3', '2', '1'}) check(not R.test_strv_in{'1', '2', '3', '4'}) end function gireg.array_gtype_in() local R = lgi.Regress local GObject = lgi.GObject local str = R.test_array_gtype_in { lgi.GObject.Value._gtype, lgi.GObject.type_from_name('gchar') } check(str == '[GValue,gchar,]') check(R.test_array_gtype_in({}) == '[]') check(not pcall(R.test_array_gtype_in)) check(not pcall(R.test_array_gtype_in, '')) check(not pcall(R.test_array_gtype_in, 1)) check(not pcall(R.test_array_gtype_in, function() end)) end function gireg.array_strv_out() local R = lgi.Regress local a = R.test_strv_out() check(type(a) == 'table' and #a == 5) check(table.concat(a, ' ') == 'thanks for all the fish') check(#{R.test_strv_out()} == 1) end function gireg.array_strv_out_container() local R = lgi.Regress local a = R.test_strv_out_container() check(type(a) == 'table' and #a == 3) check(table.concat(a, ' ') == '1 2 3') end function gireg.array_strv_outarg() local R = lgi.Regress local a = R.test_strv_outarg() check(type(a) == 'table' and #a == 3) check(table.concat(a, ' ') == '1 2 3') check(#{R.test_strv_outarg()} == 1) end function gireg.array_fixed_size_int_out() local R = lgi.Regress local a = R.test_array_fixed_size_int_out() check(type(a) == 'table' and #a == 5) check(a[1] == 0 and a[2] == 1 and a[3] == 2 and a[4] == 3 and a[5] == 4) check(#{R.test_array_fixed_size_int_out()} == 1) end function gireg.array_fixed_size_int_return() local R = lgi.Regress local a = R.test_array_fixed_size_int_return() check(type(a) == 'table' and #a == 5) check(a[1] == 0 and a[2] == 1 and a[3] == 2 and a[4] == 3 and a[5] == 4) check(#{R.test_array_fixed_size_int_return()} == 1) end function gireg.array_strv_out_c() local R = lgi.Regress local a = R.test_strv_out_c() check(type(a) == 'table' and #a == 5) check(table.concat(a, ' ') == 'thanks for all the fish') end function gireg.array_int_full_out() local R = lgi.Regress local a = R.test_array_int_full_out() check(type(a) == 'table' and #a == 5) check(a[1] == 0 and a[2] == 1 and a[3] == 2 and a[4] == 3 and a[5] == 4) check(#{R.test_array_int_full_out()} == 1) end function gireg.array_int_full_out() local R = lgi.Regress local a = R.test_array_int_full_out() check(type(a) == 'table' and #a == 5) check(a[1] == 0 and a[2] == 1 and a[3] == 2 and a[4] == 3 and a[5] == 4) check(#{R.test_array_int_full_out()} == 1) end function gireg.array_int_null_in() local R = lgi.Regress R.test_array_int_null_in() R.test_array_int_null_in(nil) end function gireg.array_int_null_out() local R = lgi.Regress local a = R.test_array_int_null_out() check(type(a) == 'table' and not next(a)) end function gireg.glist_nothing_return() local R = lgi.Regress check(select('#', R.test_glist_nothing_return()) == 1) a = R.test_glist_nothing_return() check(type(a) == 'table' and #a == 3) check(a[1] == '1' and a[2] == '2' and a[3] == '3') end function gireg.glist_nothing_return2() local R = lgi.Regress check(select('#', R.test_glist_nothing_return2()) == 1) a = R.test_glist_nothing_return2() check(type(a) == 'table' and #a == 3) check(a[1] == '1' and a[2] == '2' and a[3] == '3') end function gireg.glist_container_return() local R = lgi.Regress check(select('#', R.test_glist_container_return()) == 1) a = R.test_glist_container_return() check(type(a) == 'table' and #a == 3) check(a[1] == '1' and a[2] == '2' and a[3] == '3') end function gireg.glist_everything_return() local R = lgi.Regress check(select('#', R.test_glist_everything_return()) == 1) a = R.test_glist_everything_return() check(type(a) == 'table' and #a == 3) check(a[1] == '1' and a[2] == '2' and a[3] == '3') end function gireg.glist_nothing_in() local R = lgi.Regress R.test_glist_nothing_in {'1', '2', '3'} end function gireg.glist_nothing_in2() local R = lgi.Regress R.test_glist_nothing_in2 {'1', '2', '3'} end function gireg.glist_null_in() local R = lgi.Regress R.test_glist_null_in {} R.test_glist_null_in(nil) R.test_glist_null_in() end function gireg.glist_null_out() local R = lgi.Regress check(select('#', R.test_glist_null_out()) == 1) local a = R.test_glist_null_out() check(type(a) == 'table' and #a == 0) end function gireg.gslist_nothing_return() local R = lgi.Regress check(select('#', R.test_gslist_nothing_return()) == 1) a = R.test_gslist_nothing_return() check(type(a) == 'table' and #a == 3) check(a[1] == '1' and a[2] == '2' and a[3] == '3') end function gireg.gslist_nothing_return2() local R = lgi.Regress check(select('#', R.test_gslist_nothing_return2()) == 1) a = R.test_gslist_nothing_return2() check(type(a) == 'table' and #a == 3) check(a[1] == '1' and a[2] == '2' and a[3] == '3') end function gireg.gslist_container_return() local R = lgi.Regress check(select('#', R.test_gslist_container_return()) == 1) a = R.test_gslist_container_return() check(type(a) == 'table' and #a == 3) check(a[1] == '1' and a[2] == '2' and a[3] == '3') end function gireg.gslist_everything_return() local R = lgi.Regress check(select('#', R.test_gslist_everything_return()) == 1) a = R.test_gslist_everything_return() check(type(a) == 'table' and #a == 3) check(a[1] == '1' and a[2] == '2' and a[3] == '3') end function gireg.gslist_nothing_in() local R = lgi.Regress R.test_gslist_nothing_in {'1', '2', '3'} end function gireg.gslist_nothing_in2() local R = lgi.Regress R.test_gslist_nothing_in2 {'1', '2', '3'} end function gireg.gslist_null_in() local R = lgi.Regress R.test_gslist_null_in {} R.test_gslist_null_in(nil) R.test_gslist_null_in() end function gireg.gslist_null_out() local R = lgi.Regress check(select('#', R.test_gslist_null_out()) == 1) local a = R.test_gslist_null_out() check(type(a) == 'table' and #a == 0) end function gireg.ghash_null_return() local R = lgi.Regress check(select('#', R.test_ghash_null_return()) == 1) check(R.test_ghash_null_return() == nil) end local function size_htab(h) local size = 0 for _ in pairs(h) do size = size + 1 end return size end function gireg.ghash_nothing_return() local R = lgi.Regress local count = 0 check(select('#', R.test_ghash_nothing_return()) == 1) local h = R.test_ghash_nothing_return() check(type(h) == 'table') check(size_htab(h) == 3) check(h.foo == 'bar' and h.baz == 'bat' and h.qux == 'quux') end function gireg.ghash_container_return() local R = lgi.Regress local count = 0 check(select('#', R.test_ghash_container_return()) == 1) local h = R.test_ghash_container_return() check(type(h) == 'table') check(size_htab(h) == 3) check(h.foo == 'bar' and h.baz == 'bat' and h.qux == 'quux') end function gireg.ghash_everything_return() local R = lgi.Regress local count = 0 check(select('#', R.test_ghash_everything_return()) == 1) local h = R.test_ghash_everything_return() check(type(h) == 'table') check(size_htab(h) == 3) check(h.foo == 'bar' and h.baz == 'bat' and h.qux == 'quux') end function gireg.ghash_null_in() local R = lgi.Regress R.test_ghash_null_in(nil) R.test_ghash_null_in() check(not pcall(R.test_ghash_null_in,1)) check(not pcall(R.test_ghash_null_in,'string')) check(not pcall(R.test_ghash_null_in,function() end)) end function gireg.ghash_null_out() local R = lgi.Regress check(R.test_ghash_null_out() == nil) end function gireg.ghash_nothing_in() local R = lgi.Regress R.test_ghash_nothing_in({ foo = 'bar', baz = 'bat', qux = 'quux' }) check(not pcall(R.test_ghash_nothing_in)) check(not pcall(R.test_ghash_nothing_in, 1)) check(not pcall(R.test_ghash_nothing_in, 'test')) check(not pcall(R.test_ghash_nothing_in, function() end)) end function gireg.ghash_nested_everything_return() local R = lgi.Regress check(select('#', R.test_ghash_nested_everything_return) == 1); local a = R.test_ghash_nested_everything_return() check(type(a) == 'table') check(size_htab(a) == 1) check(type(a.wibble) == 'table') check(size_htab(a.wibble) == 3) check(a.wibble.foo == 'bar' and a.wibble.baz == 'bat' and a.wibble.qux == 'quux') end function gireg.enum() local R = lgi.Regress check(R.TestEnum.VALUE1 == 0) check(R.TestEnum.VALUE2 == 1) check(R.TestEnum.VALUE3 == -1) check(R.TestEnum[0] == 'VALUE1') check(R.TestEnum[1] == 'VALUE2') check(R.TestEnum[-1] == 'VALUE3') check(R.TestEnum[43] == 43) check(R.test_enum_param(0) == 'value1') check(R.test_enum_param(1) == 'value2') check(R.test_enum_param(-1) == 'value3') check(R.TestEnumUnsigned.VALUE1 == 1) check(R.TestEnumUnsigned.VALUE2 == 0x80000000) check(R.TestEnumUnsigned[1] == 'VALUE1') check(R.TestEnumUnsigned[0x80000000] == 'VALUE2') check(R.TestEnumUnsigned[-1] == -1) end function gireg.flags() local R = lgi.Regress check(R.TestFlags.FLAG1 == 1) check(R.TestFlags.FLAG2 == 2) check(R.TestFlags.FLAG3 == 4) check(R.TestFlags[7].FLAG1 == true) check(R.TestFlags[7].FLAG2 == true) check(R.TestFlags[7].FLAG3 == true) check(R.TestFlags[3].FLAG1 == true) check(R.TestFlags[3].FLAG2 == true) check(R.TestFlags[3].FLAG3 == nil) check(R.TestFlags[10].FLAG2 == true) check(R.TestFlags[10][1] == 8) checkv(R.TestFlags { 'FLAG1', 'FLAG2' }, 3, 'number') checkv(R.TestFlags { 1, 2, 'FLAG1', R.TestFlags.FLAG2 }, 3, 'number') checkv(R.TestFlags { 10, FLAG2 = 2 }, 10, 'number') checkv(R.TestFlags { 2, 'FLAG2' }, 2, 'number') end function gireg.flags_out() local R = lgi.Regress local out = R.global_get_flags_out() check(type(out) == 'table') check(out.FLAG1 == true) check(out.FLAG2 == nil) check(out.FLAG3 == true) check(#out == 0) end function gireg.const() local R = lgi.Regress checkv(R.INT_CONSTANT, 4422, 'number') checkv(R.DOUBLE_CONSTANT, 44.22, 'number') checkv(R.STRING_CONSTANT, 'Some String', 'string') checkv(R.Mixed_Case_Constant, 4423, 'number') end function gireg.struct_a() local R = lgi.Regress check(select('#', R.TestStructA()) == 1) local a = R.TestStructA() check(type(a) == 'userdata') a.some_int = 42 check(a.some_int == 42) a.some_int8 = 12 check(a.some_int8 == 12) a.some_double = 3.14 check(a.some_double == 3.14) a.some_enum = R.TestEnum.VALUE2 check(a.some_enum == 'VALUE2') a = R.TestStructA { some_int = 42, some_int8 = 12, some_double = 3.14, some_enum = 'VALUE2' } a.some_int = 43 a.some_int8 = 13 check(a.some_int == 43) check(a.some_int8 == 13) check(a.some_double == 3.14) check(a.some_enum == 'VALUE2') a.some_double = 3.15 check(a.some_int == 43) check(a.some_int8 == 13) check(a.some_double == 3.15) check(a.some_enum == 'VALUE2') a.some_enum = R.TestEnum.VALUE3 check(a.some_int == 43) check(a.some_int8 == 13) check(a.some_double == 3.15) check(a.some_enum == 'VALUE3') check(not pcall(function() return a.foo end)) check(not pcall(function() a.foo = 1 end)) check(select('#', (function() a.some_int = 0 end)()) == 0) check(select('#', (function() return a.some_int end)()) == 1) check(select('#', (function() local b = a.some_int end)()) == 0) end function gireg.struct_a_clone() local R = lgi.Regress local a = R.TestStructA { some_int = 42, some_int8 = 12, some_double = 3.14, some_enum = R.TestEnum.VALUE2 } check(a == a) check(select('#', a:clone()) == 1) local b = a:clone() check(type(b) == 'userdata') check(b ~= a) check(b == b) check(b.some_int == 42) check(b.some_int8 == 12) check(b.some_double == 3.14) check(b.some_enum == 'VALUE2') check(a.some_int == 42) check(a.some_int8 == 12) check(a.some_double == 3.14) check(a.some_enum == 'VALUE2') end function gireg.struct_b() local R = lgi.Regress local b = R.TestStructB() -- Basic fields assignments. b.some_int8 = 13 check(b.some_int8 == 13) b.nested_a.some_int = -1 check(b.some_int8 == 13) check(b.nested_a.some_int == -1) b.nested_a.some_int8 = -2 check(b.some_int8 == 13) check(b.nested_a.some_int == -1) check(b.nested_a.some_int8 == -2) -- Whole nested structure assignment. b.nested_a = { some_int = 42, some_int8 = 12, some_double = 3.14, some_enum = R.TestEnum.VALUE2 } check(b.nested_a.some_int == 42) check(b.nested_a.some_int8 == 12) check(b.nested_a.some_double == 3.14) check(b.nested_a.some_enum == 'VALUE2') -- Nested structure construction. b = R.TestStructB { some_int8 = 21, nested_a = { some_int = 42, some_int8 = 12, some_double = 3.14, some_enum = R.TestEnum.VALUE2 } } check(b.some_int8 == 21) check(b.nested_a.some_int == 42) check(b.nested_a.some_int8 == 12) check(b.nested_a.some_double == 3.14) check(b.nested_a.some_enum == 'VALUE2') end function gireg.struct_b_clone() local R = lgi.Regress local b = R.TestStructB { some_int8 = 21, nested_a = { some_int = 42, some_int8 = 12, some_double = 3.14, some_enum = R.TestEnum.VALUE2 } } check(b == b) check(select('#', b:clone()) == 1) local bc = b:clone() check(type(bc) == 'userdata') check(bc ~= b) check(bc == bc) check(bc.some_int8 == 21) check(bc.nested_a.some_int == 42) check(bc.nested_a.some_int8 == 12) check(bc.nested_a.some_double == 3.14) check(bc.nested_a.some_enum == 'VALUE2') check(bc.nested_a.some_int == 42) check(bc.nested_a.some_int8 == 12) check(bc.nested_a.some_double == 3.14) check(bc.nested_a.some_enum == 'VALUE2') check(b.some_int8 == 21) check(b.nested_a.some_int == 42) check(b.nested_a.some_int8 == 12) check(b.nested_a.some_double == 3.14) check(b.nested_a.some_enum == 'VALUE2') check(b.nested_a.some_int == 42) check(b.nested_a.some_int8 == 12) check(b.nested_a.some_double == 3.14) check(b.nested_a.some_enum == 'VALUE2') end function gireg.boxed_a_equals() local R = lgi.Regress check(R.TestSimpleBoxedA({ some_int = 1, some_int8 = 2, some_double = 3.14 }):equals( R.TestSimpleBoxedA({ some_int = 1, some_int8 = 2, some_double = 3.14 }))) check(not R.TestSimpleBoxedA({ some_int = 2, some_int8 = 2, some_double = 3.14 }):equals( R.TestSimpleBoxedA({ some_int = 1, some_int8 = 2, some_double = 3.14 }))) check(R.TestSimpleBoxedA():equals(R.TestSimpleBoxedA())) check(not pcall(R.TestSimpleBoxedA().equals)) check(not pcall(R.TestSimpleBoxedA().equals, nil)) check(not pcall(R.TestSimpleBoxedA().equals, {})) check(not pcall(R.TestSimpleBoxedA().equals, 1)) check(not pcall(R.TestSimpleBoxedA().equals, 'string')) check(not pcall(R.TestSimpleBoxedA().equals, function() end)) end function gireg.boxed_a_const_return() local R = lgi.Regress check(select('#', R.test_simple_boxed_a_const_return()) == 1) local a = R.test_simple_boxed_a_const_return() check(a.some_int == 5) check(a.some_int8 == 6) check(a.some_double == 7) end function gireg.boxed_new() local R = lgi.Regress check(select('#', R.TestBoxed.new()) == 1) local bn = R.TestBoxed.new() local bac1 = R.TestBoxed.new_alternative_constructor1(1) check(bac1.some_int8 == 1) local bac2 = R.TestBoxed.new_alternative_constructor2(1, 2) check(bac2.some_int8 == 3) local bac3 = R.TestBoxed.new_alternative_constructor3('25') check(bac3.some_int8 == 25) end function gireg.boxed_copy() local R = lgi.Regress local b = R.TestBoxed.new() b.some_int8 = 1 b.nested_a = { some_int = 1, some_int8 = 2, some_double = 3.14 } check(select('#', b:copy()) == 1) local bc = b:copy() check(bc ~= b) check(bc.some_int8 == 1) check(bc.nested_a.some_int == 1) check(bc.nested_a.some_int8 == 2) check(bc.nested_a.some_double == 3.14) end function gireg.boxed_equals() local R = lgi.Regress local b1 = R.TestBoxed.new() b1.some_int8 = 1 b1.nested_a = { some_int = 1, some_int8 = 2, some_double = 3.14 } local b2 = R.TestBoxed.new() b2.some_int8 = 1 b2.nested_a = { some_int = 1, some_int8 = 2, some_double = 3.14 } check(b1:equals(b2)) b1.some_int8 = 2 check(not b1:equals(b2)) b1.some_int8 = 1 b1.nested_a.some_int = 2 check(not b1:equals(b2)) b1.nested_a.some_int = 1 check(b1:equals(b2)) end function gireg.closure_simple() local R = lgi.Regress local GObject = lgi.GObject local closure = GObject.Closure(function(...) check(select('#', ...) == 0) return 42 end) checkv(R.test_closure(closure, 42), 42, 'number') local res = GObject.Value('gint') closure:invoke(res, {}, nil) check(res.gtype == 'gint' and res.value == 42) end function gireg.closure_arg() local GObject = lgi.GObject local R = lgi.Regress local closure = GObject.Closure(function(int, ...) check(select('#', ...) == 0) return int end) checkv(R.test_closure_one_arg(closure, 43), 43, 'number') local res = GObject.Value('gint') closure:invoke(res, { GObject.Value('gint', 43) }, nil) check(res.gtype == 'gint' and res.value == 43) end function gireg.gvalue_assign() local GObject = lgi.GObject local V = GObject.Value local v = V() check(v.gtype == nil) check(v.value == nil) v.gtype = 'gchararray' check(v.gtype == 'gchararray') check(v.value == nil) v.value = 'label' check(v.value == 'label') v.value = nil check(v.value == nil) check(v.gtype == 'gchararray') v.value = 'label' v.gtype = nil check(v.gtype == nil) check(v.value == nil) v.gtype = 'gint' v.value = 1 check(v.gtype == 'gint') check(v.value == 1) v.gtype = 'gdouble' check(v.gtype == 'gdouble') check(v.value == 1) v.value = 3.14 v.gtype = 'gint' check(v.gtype == 'gint') check(v.value == 3) end function gireg.gvalue_arg() local GObject = lgi.GObject local R = lgi.Regress checkv(R.test_int_value_arg(GObject.Value('gint', 42)), 42, 'number') end function gireg.gvalue_return() local R = lgi.Regress local v = R.test_value_return(43) checkv(v.value, 43, 'number') check(v.gtype == 'gint', 'incorrect value type') end function gireg.gvalue_date() local GObject = lgi.GObject local GLib = lgi.GLib local R = lgi.Regress local v v = R.test_date_in_gvalue() check(v.gtype == 'GDate') check(v.value:get_day() == 5) check(v.value:get_month() == 'DECEMBER') check(v.value:get_year() == 1984) local d = GLib.Date() d:set_dmy(25, 1, 1975) v = GObject.Value(GLib.Date, d) check(v.gtype == 'GDate') check(v.value:get_day() == 25) check(v.value:get_month() == 'JANUARY') check(v.value:get_year() == 1975) end function gireg.gvalue_strv() local GObject = lgi.GObject local R = lgi.Regress local v = R.test_strv_in_gvalue() check(v.gtype == 'GStrv') check(#v.value == 3) check(v.value[1] == 'one') check(v.value[2] == 'two') check(v.value[3] == 'three') v = GObject.Value('GStrv', { '1', '2', '3' }) check(#v.value == 3) check(v.value[1] == '1') check(v.value[2] == '2') check(v.value[3] == '3') end function gireg.obj_create() local R = lgi.Regress local o = R.TestObj() check(o) check(type(o) == 'userdata') check(select('#', R.TestObj()) == 1) o = R.TestObj.new_from_file('unused') check(type(o) == 'userdata') check(select('#', R.TestObj.new_from_file('unused')) == 1) end function gireg.obj_methods() local R = lgi.Regress local GLib = lgi.GLib local Gio = lgi.Gio if R.TestObj._method.do_matrix then R.TestObj._method.invoke_matrix = R.TestObj._method.do_matrix R.TestObj._method.do_matrix = nil end local o = R.TestObj() check(o:instance_method() == -1) check(o.static_method(42) == 42) local y, z, q = o:torture_signature_0(1, 'foo', 2) check(y == 1) check(z == 2) check(q == 5) local y, z, q = o:torture_signature_1(1, 'foo', 2) check(y == 1) check(z == 2) check(q == 5) local res, err = o:torture_signature_1(1, 'foo', 3) check(not res) check(GLib.Error:is_type_of(err)) check(err:matches(Gio.IOErrorEnum, 'FAILED')) check(o:invoke_matrix('unused') == 42) end function gireg.obj_null_args() local R = lgi.Regress R.func_obj_null_in(nil) R.func_obj_null_in() check(R.TestObj.null_out() == nil) check(select('#', R.TestObj.null_out()) == 1) end function gireg.obj_virtual_methods() local R = lgi.Regress local o = R.TestObj() check(o:do_matrix('unused') == 42) end function gireg.obj_prop_int() local R = lgi.Regress local o = R.TestObj() check(o.int == 0) o.int = 42 check(o.int == 42) check(not pcall(function() o.int = {} end)) check(not pcall(function() o.int = 'lgi' end)) check(not pcall(function() o.int = nil end)) check(not pcall(function() o.int = function() end end)) end function gireg.obj_prop_float() local R = lgi.Regress local o = R.TestObj() check(o.float == 0) o.float = 42.1 checkvf(o.float, 42.1, 0.00001) check(not pcall(function() o.float = {} end)) check(not pcall(function() o.float = 'lgi' end)) check(not pcall(function() o.float = nil end)) check(not pcall(function() o.float = function() end end)) end function gireg.obj_prop_double() local R = lgi.Regress local o = R.TestObj() check(o.double == 0) o.double = 42.1 checkvf(o.double, 42.1, 0.0000000001) check(not pcall(function() o.double = {} end)) check(not pcall(function() o.double = 'lgi' end)) check(not pcall(function() o.double = nil end)) check(not pcall(function() o.double = function() end end)) end function gireg.obj_prop_string() local R = lgi.Regress local o = R.TestObj() check(o.string == nil) o.string = 'lgi' check(o.string == 'lgi') o.string = nil check(o.string == nil) check(not pcall(function() o.string = {} end)) check(not pcall(function() o.string = function() end end)) end function gireg.obj_prop_bare() local R = lgi.Regress local o = R.TestObj() check(o.bare == nil) local pv = R.TestObj() o.bare = pv check(o.bare == pv) o.bare = nil check(o.bare == nil) o:set_bare(pv) check(o.bare == pv) o.bare = nil check(o.bare == nil) check(not pcall(function() o.bare = {} end)) check(not pcall(function() o.bare = 42 end)) check(not pcall(function() o.bare = 'lgi' end)) check(not pcall(function() o.bare = function() end end)) check(not pcall(function() o.bare = R.TestBoxed() end)) end function gireg.obj_prop_boxed() local R = lgi.Regress local o = R.TestObj() check(o.boxed == nil) local pv = R.TestBoxed() o.boxed = pv check(o.boxed:equals(pv)) o.boxed = nil check(o.boxed == nil) check(not pcall(function() o.boxed = {} end)) check(not pcall(function() o.boxed = 42 end)) check(not pcall(function() o.boxed = 'lgi' end)) check(not pcall(function() o.boxed = function() end end)) check(not pcall(function() o.boxed = R.TestObj() end)) end function gireg.obj_prop_hash() local R = lgi.Regress local o = R.TestObj() check(o.hash_table == nil) o.hash_table = { a = 1, b = 2 } local ov = o.hash_table check(ov.a == 1 and ov.b == 2) check(not pcall(function() o.hash_table = 42 end)) check(not pcall(function() o.hash_table = 'lgi' end)) check(not pcall(function() o.hash_table = function() end end)) check(not pcall(function() o.hash_table = R.TestObj() end)) check(not pcall(function() o.hash_table = R.TestBoxed() end)) end function gireg.obj_prop_list() local R = lgi.Regress local o = R.TestObj() check(o.hash_table == nil) o.list = { 'one', 'two', 'three', } local ov = o.list check(#ov == 3 and ov[1] == 'one' and ov[2] == 'two' and ov[3] == 'three') check(not pcall(function() o.list = 42 end)) check(not pcall(function() o.list = 'lgi' end)) check(not pcall(function() o.list = function() end end)) check(not pcall(function() o.list = R.TestObj() end)) check(not pcall(function() o.list = R.TestBoxed() end)) end function gireg.obj_prop_dynamic() local R = lgi.Regress local o = R.TestObj() -- Remove static property information, force lgi to use dynamic -- GLib property system. local old_prop = R.TestObj.int R.TestObj._property.int = nil R.TestObj._cached = nil check(R.TestObj.int == nil) check(o.int == 0) o.int = 3 check(o.int == 3) check(not pcall(function() o.int = 'string' end)) check(not pcall(function() o.int = {} end)) check(not pcall(function() o.int = true end)) check(not pcall(function() o.int = function() end end)) -- Restore TestObj to work normally. R.TestObj._property.int = old_prop end function gireg.obj_subobj() local R = lgi.Regress local o = R.TestSubObj() local pv = R.TestObj() check(o:instance_method() == 0) o.bare = pv check(o.bare == pv) o:unset_bare() check(o.bare == nil) o = R.TestSubObj.new() o:set_bare(pv) check(o.bare == pv) o:unset_bare() check(o.bare == nil) end function gireg.obj_naming() local R = lgi.Regress local o = R.TestWi8021x() o:set_testbool(true) check(o.testbool == true) o.testbool = false check(o:get_testbool() == false) end function gireg.obj_floating() local R = lgi.Regress local o = R.TestFloating() check(o) o = nil collectgarbage() collectgarbage() end function gireg.obj_fundamental() local R = lgi.Regress local f = R.TestFundamentalSubObject.new('foo-nda-mental') check(f) check(f.data == 'foo-nda-mental') local v = lgi.GObject.Value(R.TestFundamentalSubObject, f) check(v.value == f) f = nil collectgarbage() end function gireg.callback_simple() local R = lgi.Regress check(R.test_callback(function() return 42 end) == 42) check(R.test_callback() == 0) check(R.test_callback(nil) == 0) check(not pcall(R.test_callback, 1)) check(not pcall(R.test_callback, 'foo')) check(not pcall(R.test_callback, {})) check(not pcall(R.test_callback, R)) check(R.test_multi_callback(function() return 22 end) == 44) check(R.test_multi_callback() == 0) end function gireg.callback_data() local R = lgi.Regress local called R.test_simple_callback(function() called = true end) check(called) check(R.test_callback_user_data(function() return 42 end) == 42) called = nil R.TestObj.static_method_callback(function() called = true return 42 end) check(called) local o = R.TestObj() called = nil o.static_method_callback(function() called = true return 42 end) check(called) called = nil o:instance_method_callback(function() called = true return 42 end) check(called) end function gireg.callback_notified() local R = lgi.Regress check(R.test_callback_destroy_notify(function() return 1 end) == 1) check(R.test_callback_destroy_notify(function() return 2 end) == 2) check(R.test_callback_destroy_notify(function() return 3 end) == 3) collectgarbage() collectgarbage() check(R.test_callback_thaw_notifications() == 6) R.TestObj.new_callback(function() return 1 end) collectgarbage() collectgarbage() check(R.test_callback_thaw_notifications() == 1) end function gireg.callback_async() local R = lgi.Regress R.test_callback_async(function() return 1 end) collectgarbage() collectgarbage() check(R.test_callback_thaw_async() == 1) end lgi-0.9.2/tests/glib.lua000066400000000000000000000074261316674307300151020ustar00rootroot00000000000000--[[-------------------------------------------------------------------------- LGI testsuite, GLib test suite. Copyright (c) 2013 Pavel Holejsovsky Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php --]]-------------------------------------------------------------------------- local lgi = require 'lgi' local check = testsuite.check -- Basic GLib testing local glib = testsuite.group.new('glib') function glib.timer() local Timer = lgi.GLib.Timer check(Timer.new) check(Timer.start) check(Timer.stop) check(Timer.continue) check(Timer.elapsed) check(Timer.reset) check(not Timer.destroy) local timer = Timer() check(Timer:is_type_of(timer)) timer = Timer.new() check(Timer:is_type_of(timer)) local el1, ms1 = timer:elapsed() check(type(el1) == 'number') check(type(ms1) == 'number') for i = 1, 1000000 do end local el2, ms2 = timer:elapsed() check(el1 < el2) timer:stop() el2 = timer:elapsed() for i = 1, 1000000 do end check(timer:elapsed() == el2) end function glib.markup_base() local MarkupParser = lgi.GLib.MarkupParser local MarkupParseContext = lgi.GLib.MarkupParseContext local p = MarkupParser() local el, at = {}, {} function p.start_element(context, element_name, attrs) el[#el + 1] = element_name at[#at + 1] = attrs end function p.end_element(context) end function p.text(context, text, len) end function p.passthrough(context, text, len) end local pc = MarkupParseContext(p, {}) local ok, err = pc:parse([[ ]]) check(ok) check(#el == 2) check(el[1] == 'map') check(el[2] == 'entry') check(#at == 2) check(not next(at[1])) check(at[2].key == 'method') check(at[2].value == 'printf') end function glib.markup_error1() local MarkupParser = lgi.GLib.MarkupParser local MarkupParseContext = lgi.GLib.MarkupParseContext local saved_err local parser = MarkupParser { error = function(context, error) saved_err = error end, } local context = MarkupParseContext(parser, 0) local ok, err = context:parse('invalid>uh') check(not ok) check(err:matches(saved_err)) end function glib.markup_error2() local GLib = lgi.GLib local MarkupParser = GLib.MarkupParser local MarkupParseContext = GLib.MarkupParseContext local saved_err local parser = MarkupParser { error = function(context, error) saved_err = error end, start_element = function(context, element) error(GLib.MarkupError('UNKNOWN_ELEMENT', 'snafu %d', 1)) end, } local context = MarkupParseContext(parser, {}) local ok, err = context:parse('') check(not ok) check(err:matches(GLib.MarkupError, 'UNKNOWN_ELEMENT')) check(err.message == 'snafu 1') check(saved_err:matches(err)) end function glib.gsourcefuncs() local GLib = lgi.GLib local called local source_funcs = GLib.SourceFuncs { prepare = function(source, timeout) called = source return true, 42 end } local source = GLib.Source(source_funcs) local res, timeout = source_funcs.prepare(source) check(res == true) check(timeout == 42) check(called == source) end function glib.coroutine_related_crash() -- This test does not have a specific assert() or check() call. Instead it -- is a regression test: Once upon a time this caused a segmentation fault -- due to a use-after-free. local glib = require("lgi").GLib local mainloop = glib.MainLoop.new() coroutine.wrap(function() local co = coroutine.running() for i=1,100 do glib.timeout_add(glib.PRIORITY_DEFAULT, 0, function() coroutine.resume(co) return false end) coroutine.yield() end mainloop:quit() end)() mainloop:run() end lgi-0.9.2/tests/gobject.lua000066400000000000000000000232531316674307300155760ustar00rootroot00000000000000--[[-------------------------------------------------------------------------- LGI testsuite, GObject.Object test suite. Copyright (c) 2010, 2011 Pavel Holejsovsky Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php --]]-------------------------------------------------------------------------- local lgi = require 'lgi' local core = require 'lgi.core' local check = testsuite.check local checkv = testsuite.checkv -- Basic GObject testing local gobject = testsuite.group.new('gobject') function gobject.env_base() local GObject = lgi.GObject local obj = GObject.Object() check(type(core.object.env(obj)) == 'table') check(core.object.env(obj) == core.object.env(obj)) check(next(core.object.env(obj)) == nil) end function gobject.env_persist() local Gtk = lgi.Gtk local window = Gtk.Window() local label = Gtk.Label() local env = core.object.env(label) window:_method_add(label) label = nil collectgarbage() label = window:get_child() check(env == core.object.env(label)) end function gobject.object_new() local GObject = lgi.GObject local o = GObject.Object() o = nil collectgarbage() end function gobject.initunk_new() local GObject = lgi.GObject local o = GObject.InitiallyUnowned() -- Simulate sink by external container o:ref_sink() o:unref() o = nil collectgarbage() end function gobject.native() local GObject = lgi.GObject local o = GObject.Object() local p = o._native check(type(p) == 'userdata') check(GObject.Object(p) == o) end function gobject.gtype_create() local GObject = lgi.GObject local Gio = lgi.Gio local m = GObject.Object.new(Gio.ThemedIcon, { name = 'icon' }) check(Gio.ThemedIcon:is_type_of(m)) end function gobject.subclass_derive1() local GObject = lgi.GObject local Derived = GObject.Object:derive('LgiTestDerived1') local der = Derived() check(Derived:is_type_of(der)) check(not Derived:is_type_of(GObject.Object())) end function gobject.subclass_derive2() local GObject = lgi.GObject local Derived = GObject.Object:derive('LgiTestDerived2') local Subderived = Derived:derive('LgiTestSubDerived2') local der = Derived() check(Derived:is_type_of(der)) check(not Subderived:is_type_of(der)) local sub = Subderived() check(Subderived:is_type_of(sub)) check(Derived:is_type_of(sub)) check(GObject.Object:is_type_of(sub)) end function gobject.subclass_override1() local GObject = lgi.GObject local Derived = GObject.Object:derive('LgiTestOverride1') local state = 0 local obj function Derived:do_constructed() obj = self state = state + 1 end function Derived:do_dispose() state = state + 2 end check(state == 0) local der = Derived() check(der == obj) check(state == 1) der = nil obj = nil collectgarbage() check(state == 3) end function gobject.subclass_override2() local GObject = lgi.GObject local state = 0 local Derived = GObject.Object:derive('LgiTestOverride2') function Derived:do_constructed() self.priv.id = 1 state = state + 1 end function Derived:do_dispose() state = state + 2 GObject.Object.do_dispose(self) end function Derived:custom_method() state = state + 8 self.priv.id = self.priv.id + 4 end local Subderived = Derived:derive('LgiTestOverrideSub2') function Subderived:do_constructed() Derived.do_constructed(self) self.priv.id = self.priv.id + 2 state = state + 4 end check(state == 0) local sub = Subderived() check(state == 5) check(sub.priv.id == 3) sub:custom_method() check(state == 13) check(sub.priv.id == 7) sub = nil collectgarbage() check(state == 15) end function gobject.subclass_derive3() local GObject = lgi.GObject local history = {} local Derived = GObject.InitiallyUnowned:derive('LgiTestDerived3') function Derived:_class_init() history[#history + 1] = 'class_init' check(self == Derived) collectgarbage() end function Derived:_init() history[#history + 1] = 'init' collectgarbage() end function Derived:do_constructed() history[#history + 1] = 'constructed' Derived._parent.do_constructed(self) collectgarbage() end -- function Derived:do_dispose() -- history[#history + 1] = 'dispose' -- Derived._parent.do_dispose(self) -- end local obj = Derived() check(history[1] == 'class_init') check(history[2] == 'init') check(history[3] == 'constructed') obj = nil collectgarbage() -- check(history[4] == 'dispose') end function gobject.iface_virtual() local Gio = lgi.Gio local file = Gio.File.new_for_path('hey') check(file:is_native() == file:do_is_native()) check(file:get_basename() == file:do_get_basename()) end function gobject.iface_impl() local GObject = lgi.GObject local Gio = lgi.Gio local FakeFile = GObject.Object:derive('LgiTestFakeFile1', { Gio.File }) function FakeFile:do_get_basename() return self.priv.basename end function FakeFile:set_basename(basename) self.priv.basename = basename end local fakefile = FakeFile() fakefile:set_basename('fakename') check(fakefile:get_basename() == 'fakename') end function gobject.treemodel_impl() local GObject = lgi.GObject local Gtk = lgi.Gtk local Model = GObject.Object:derive('LgiTestModel1', { Gtk.TreeModel }) function Model:do_get_n_columns() return 2 end function Model:do_get_column_type(index) return index == 0 and GObject.Type.STRING or GObject.Type.INT end function Model:do_get_iter(path) local iter = Gtk.TreeIter() iter.user_data = path._native return iter end function Model:do_get_value(iter, column) return GObject.Value(self:get_column_type(column), 1) end local model = Model() check(model:get_n_columns() == 2) check(model:get_column_type(0) == GObject.Type.STRING) check(model:get_column_type(1) == GObject.Type.INT) local p = Gtk.TreePath.new_from_string('0') local i = model:get_iter(p) check(i.user_data == p._native) check(model[Gtk.TreeIter()][1] == '1') check(model[Gtk.TreeIter()][2] == 1) end -- Lua 5.3 and 5.1 seems to have some problem with aggressive GC settings used -- here and completing this test takes literally ages, so skip it. Enable only if explicitely asked for. if rawget(_G, '_LGI_ENABLE_ALL_TESTS') then function gobject.ctor_gc() local Gtk = lgi.Gtk local oldpause = collectgarbage('setpause', 10) local oldstepmul = collectgarbage('setstepmul', 10000) for i = 1, 1000 do local window = Gtk.Window { width = 800, height = 600, title = "Test", } end collectgarbage('setpause', oldpause) collectgarbage('setstepmul', oldstepmul) end end function gobject.subclass_prop1() local GObject = lgi.GObject local Derived = GObject.Object:derive('LgiTestDerivedProp1') Derived._property.string = GObject.ParamSpecString( 'string', 'Nick string', 'Blurb string', 'string-default', { 'READABLE', 'WRITABLE', 'CONSTRUCT' } ) local der = Derived() checkv(der.string, 'string-default', 'string') der.string = 'assign' checkv(der.string, 'assign', 'string') checkv(der.priv.string, 'assign', 'string') end function gobject.subclass_prop_inherit() local GObject = lgi.GObject local Gio = lgi.Gio local FakeMonitor = GObject.Object:derive( 'LgiTestFakeMonitor1', { Gio.Initable, Gio.NetworkMonitor }) FakeMonitor._property.network_available = GObject.ParamSpecBoolean('network-available', 'LgiTestFakeMonitor1NetworkAvailable', 'Whether the network is available.', false, { GObject.ParamFlags.READABLE }) FakeMonitor._property.connectivity = GObject.ParamSpecEnum('connectivity', 'LgiTestFakeMonitor1Connectivity', 'Type of connectivity.', Gio.NetworkConnectivity, Gio.NetworkConnectivity.LOCAL, { GObject.ParamFlags.READABLE }) function FakeMonitor:do_init(cancellable) self.priv.inited = true return true end local fakemonitor = FakeMonitor() -- Gio.Initable is invoked automatically by lgi after object -- construction. check(fakemonitor.priv.inited) -- If the object is not constructed yet, it defaults to false check(fakemonitor:get_network_available() == false) check(fakemonitor.network_available == false) fakemonitor.priv.network_available = true check(fakemonitor.network_available == true) check(fakemonitor:get_network_available() == true) fakemonitor.priv.network_available = false check(fakemonitor.network_available == false) check(fakemonitor:get_network_available() == false) end function gobject.subclass_prop_getset() local GObject = lgi.GObject local Derived = GObject.Object:derive('LgiTestDerivedPropGetSet') Derived._property.str = GObject.ParamSpecString( 'str', 'Nick string', 'Blurb string', 'string-default', { 'READABLE', 'WRITABLE', 'CONSTRUCT' } ) local propval function Derived._property_set:str(new_value) propval = new_value end function Derived._property_get:str() return propval end local der = Derived() checkv(der.str, 'string-default', 'string') der.str = 'assign' checkv(der.str, 'assign', 'string') checkv(propval, 'assign', 'string') end function gobject.signal_query() local GObject = lgi.GObject local id = GObject.signal_lookup('notify', GObject.Object) check(id ~= 0) local query = GObject.signal_query(id) check(query.signal_id == id) check(query.signal_name == 'notify') check(query.n_params == 1) check(#query.param_types == 1) check(query.param_types[1] == GObject.Type.name(GObject.Type.PARAM)) end lgi-0.9.2/tests/gtk.lua000066400000000000000000000301151316674307300147410ustar00rootroot00000000000000--[[-------------------------------------------------------------------------- LGI testsuite, Gtk overrides test group. Copyright (c) 2010, 2011 Pavel Holejsovsky Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php --]]-------------------------------------------------------------------------- local io = require 'io' local os = require 'os' local lgi = require 'lgi' local check = testsuite.check local checkv = testsuite.checkv local gtk = testsuite.group.new('gtk') function gtk.widget_style() local Gtk = lgi.Gtk local GObject = lgi.GObject local w = Gtk.ProgressBar() local v = GObject.Value(GObject.Type.INT) w:style_get_property('xspacing', v) checkv(w.style.xspacing, v.value, 'number') check(not pcall(function() return w.style.nonexistent end)) end function gtk.buildable_id() local Gtk = lgi.Gtk local w = Gtk.Label() checkv(w.id, nil, nil) w.id = 'label_id' checkv(w.id, 'label_id', 'string') end function gtk.container_property() local Gtk = lgi.Gtk local GObject = lgi.GObject local c, w, v = Gtk.Grid(), Gtk.Label() c:add(w) c.property[w].left_attach = 1 v = GObject.Value(GObject.Type.INT) c:child_get_property(w, 'left-attach', v) checkv(v.value, 1, 'number') v.value = 2 c:child_set_property(w, 'left-attach', v) checkv(c.property[w].left_attach, 2) check(not pcall(function() c.property[w].notexistent = 1 end)) end function gtk.container_add_method() local Gtk = lgi.Gtk local c, w c, w = Gtk.Grid(), Gtk.Label() c:add(w) check(w.parent == c) c, w = Gtk.Grid(), Gtk.Label() c:add { w, left_attach = 0, width = 2 } check(w.parent == c) checkv(c.property[w].left_attach, 0, 'number') checkv(c.property[w].width, 2, 'number') c, w = Gtk.Grid(), Gtk.Label() c:add(w, { left_attach = 0, width = 2 }) check(w.parent == c) checkv(c.property[w].left_attach, 0, 'number') checkv(c.property[w].width, 2, 'number') end function gtk.container_add_child() local Gtk = lgi.Gtk local c, w c, w = Gtk.Grid(), Gtk.Label() c.child = w check(w.parent == c) c, w = Gtk.Grid(), Gtk.Label() c.child = { w, left_attach = 0, width = 2 } check(w.parent == c) checkv(c.property[w].left_attach, 0, 'number') checkv(c.property[w].width, 2, 'number') end function gtk.container_add_ctor() local Gtk = lgi.Gtk local l1, l2 = Gtk.Label(), Gtk.Label() local c = Gtk.Grid { { l1, width = 2 }, { l2, height = 3 } } check(l1.parent == c) check(l2.parent == c) checkv(c.property[l1].width, 2, 'number') checkv(c.property[l2].height, 3, 'number') end function gtk.container_child_find() local Gtk = lgi.Gtk local l1, l2 = Gtk.Label { id = 'id_l1' }, Gtk.Label { id = 'id_l2' } local c = Gtk.Grid { { l1, width = 2 }, Gtk.Grid { id = 'in_g', { l2, height = 3 } } } check(c.child.id_l1 == l1) check(c.child.id_l2 == l2) check(c.child.id_l2.parent == c.child.in_g) check(c.child.notexistent == nil) end local uidef = [[ False True False True False 0 0 1 1 True False True True label 0 1 1 1 True False vertical 2 0 2 1 1 ]] function gtk.builder_add_from_string() local Gtk = lgi.Gtk local b = Gtk.Builder() local res, err = b:add_from_string('syntax error') check(not res and lgi.GLib.Error:is_type_of(err)) res, err = b:add_from_string(uidef) check(res and not err) check(b:get_object('window1')) end function gtk.builder_add_objects_from_string() local Gtk = lgi.Gtk local b = Gtk.Builder() check(b:add_objects_from_string(uidef, -1, { 'statusbar1', 'label1' })) check(b:get_object('statusbar1') and b:get_object('label1')) check(not b:get_object('window1') and not b:get_object('toolbar1')) end function gtk.builder_add_from_file() local Gtk = lgi.Gtk local tempname = os.tmpname() local tempfile = io.open(tempname, 'w+') tempfile:write(uidef) tempfile:close() local b = Gtk.Builder() local res, err = b:add_from_string('syntax error') check(not res and lgi.GLib.Error:is_type_of(err)) res, err = b:add_from_file(tempname) check(res and not err) check(b:get_object('window1')) os.remove(tempname) end function gtk.builder_add_objects_from_file() local Gtk = lgi.Gtk local tempname = os.tmpname() local tempfile = io.open(tempname, 'w+') tempfile:write(uidef) tempfile:close() local b = Gtk.Builder() check(b:add_objects_from_file(tempname, { 'statusbar1', 'label1' })) check(b:get_object('statusbar1') and b:get_object('label1')) check(not b:get_object('window1') and not b:get_object('toolbar1')) os.remove(tempname) end function gtk.builder_objects() local Gtk = lgi.Gtk local builder = Gtk.Builder() check(builder:add_from_string(uidef)) check(builder.objects.window1 == builder:get_object('window1')) check(builder.objects.statusbar1 == builder:get_object('statusbar1')) check(not builder.objects.notexistent) end function gtk.text_tag_table_ctor() local Gtk = lgi.Gtk local t1, t2 = Gtk.TextTag { name = 'tag1' }, Gtk.TextTag { name = 'tag2' } local t = Gtk.TextTagTable { t1, t2 } check(t:lookup('tag1') == t1) check(t:lookup('tag2') == t2) check(t:lookup('notexist') == nil) end function gtk.text_tag_table_tag() local Gtk = lgi.Gtk local t1, t2 = Gtk.TextTag { name = 'tag1' }, Gtk.TextTag { name = 'tag2' } local t = Gtk.TextTagTable { t1, t2 } check(t.tag.tag1 == t1) check(t.tag.tag2 == t2) check(t.tag.notexist == nil) end function gtk.liststore() local Gtk = lgi.Gtk local GObject = lgi.GObject local cols = { int = 1, string = 2 } local store = Gtk.ListStore.new { GObject.Type.INT, GObject.Type.STRING } local first = store:insert(0, { [cols.int] = 42, [cols.string] = 'hello' }) checkv(store:get_value(first, cols.int - 1).value, 42, 'number') checkv(store[first][cols.int], 42, 'number') checkv(store:get_value(first, cols.string - 1).value, 'hello', 'string') checkv(store[first][cols.string], 'hello', 'string') store[first] = { [cols.string] = 'changed' } checkv(store[first][cols.string], 'changed', 'string') checkv(store[first][cols.int], 42, 'number') store[first][cols.int] = 16 checkv(store[first][cols.string], 'changed', 'string') checkv(store[first][cols.int], 16, 'number') end function gtk.treestore() local Gtk = lgi.Gtk local GObject = lgi.GObject local cols = { int = 1, string = 2 } local store = Gtk.TreeStore.new { GObject.Type.INT, GObject.Type.STRING } local first = store:insert( nil, 0, { [cols.int] = 42, [cols.string] = 'hello' }) checkv(store:get_value(first, cols.int - 1).value, 42, 'number') checkv(store[first][cols.int], 42, 'number') checkv(store:get_value(first, cols.string - 1).value, 'hello', 'string') checkv(store[first][cols.string], 'hello', 'string') store[first] = { [cols.string] = 'changed' } checkv(store[first][cols.string], 'changed', 'string') checkv(store[first][cols.int], 42, 'number') store[first][cols.int] = 16 checkv(store[first][cols.string], 'changed', 'string') checkv(store[first][cols.int], 16, 'number') end function gtk.treeiter() local Gtk = lgi.Gtk local GObject = lgi.GObject local giter = Gtk.TreeIter() giter.user_data = giter._native local Model = GObject.Object:derive('LgiTestModel2', { Gtk.TreeModel }) function Model:do_get_iter(path) return giter end local model = Model() local niter = model:get_iter(Gtk.TreePath.new_from_string('0')) check(giter.user_data == niter.user_data) check(giter ~= niter) end function gtk.treemodel_pairs() local Gtk = lgi.Gtk local GObject = lgi.GObject local cols = { int = 1, string = 2 } local store = Gtk.TreeStore.new { GObject.Type.INT, GObject.Type.STRING } local first = store:append( nil, { [cols.int] = 42, [cols.string] = 'hello' }) local sub1 = store:append( first, { [cols.int] = 100, [cols.string] = 'sub1' }) local sub2 = store:append( first, { [cols.int] = 101, [cols.string] = 'sub2' }) local count = 0 for i, item in store:pairs() do count = count + 1 check(Gtk.TreeIter:is_type_of(i)) check(item[cols.string] == 'hello') end check(count == 1) count = 0 for i, item in store:pairs(first) do count = count + 1 check(Gtk.TreeIter:is_type_of(i)) if (count == 1) then check(item[cols.string] == 'sub1') else check(item[cols.string] == 'sub2') end end check(count == 2) count = 0 for i, item in store:pairs(sub1) do count = count + 1 end check(count == 0) end function gtk.treeview() local Gtk = lgi.Gtk local GObject = lgi.GObject local cols = { int = 1, string = 2 } local store = Gtk.TreeStore.new { GObject.Type.INT, GObject.Type.STRING } local renderer = Gtk.CellRendererText { id = 'renderer' } local column = Gtk.TreeViewColumn { id = 'column', { renderer, { text = cols.int } }, { Gtk.CellRendererText {}, expand = true, pack = 'end', function(column, cell, model, iter) return model[iter][cols.string]:toupper() end }, } local view = Gtk.TreeView { id = 'view', model = store, column } -- Check that column is accessible by its 'id' attribute. check(view.child.view == view) check(view.child.column == column) -- Check that renderer is accessible by its 'id' attribute. check(view.child.renderer == renderer) end function gtk.actiongroup_add() local Gtk = lgi.Gtk -- Adding normal action and action with an accelerator. local ag = Gtk.ActionGroup() local a1, a2 = Gtk.Action { name = 'a1' }, Gtk.Action { name = 'a2' } ag:add(a1) check(#ag:list_actions() == 1) check(ag:get_action('a1') == a1) ag:add { a2, accelerator = 'A' } check(#ag:list_actions() == 2) check(ag:get_action('a2') == a2) -- Adding a group of radio actions, this time inside the group ctor. local chosen a1 = Gtk.RadioAction { name = 'a1', value = 1 } a2 = Gtk.RadioAction { name = 'a2', value = 2 } ag = Gtk.ActionGroup { { a1, { a2, accelerator = 'a' }, on_change = function(action) chosen = action end } } check(#ag:list_actions() == 2) check(ag:get_action('a1') == a1) check(ag:get_action('a2') == a2) check(chosen == nil) a1:activate() check(chosen == a1) a2:activate() check(chosen == a2) end function gtk.actiongroup_index() local Gtk = lgi.Gtk local a1, a2 = Gtk.Action { name = 'a1' }, Gtk.Action { name = 'a2' } local ag = Gtk.ActionGroup { a1, a2 } check(ag.action.a1 == a1) check(ag.action.a2 == a2) end lgi-0.9.2/tests/marshal.lua000066400000000000000000000013731316674307300156070ustar00rootroot00000000000000--[[-------------------------------------------------------------------------- LGI testsuite, specific marshalling tests Copyright (c) 2010, 2011 Pavel Holejsovsky Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php --]]-------------------------------------------------------------------------- local lgi = require 'lgi' local check = testsuite.check -- Basic GObject testing local marshal = testsuite.group.new('marshal') function marshal.callback_hidedata() local GLib = lgi.GLib local main_loop = GLib.MainLoop() local argc GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, function(...) argc = select('#', ...) main_loop:quit() end) main_loop:run() check(argc == 0) end lgi-0.9.2/tests/pango.lua000066400000000000000000000030271316674307300152620ustar00rootroot00000000000000--[[-------------------------------------------------------------------------- lgi testsuite, Pango test suite. Copyright (c) 2013 Pavel Holejsovsky Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php --]]-------------------------------------------------------------------------- local lgi = require 'lgi' local core = require 'lgi.core' local check = testsuite.check -- Pango overrides testing local pango = testsuite.group.new('pango') -- Test originating from https://github.com/pavouk/lgi/issues/68 function pango.glyphstring() local Pango = lgi.Pango local pal = Pango.AttrList.new(); pal:insert(Pango.Attribute.language_new(Pango.Language.from_string("he"))) pal:insert(Pango.Attribute.family_new("Adobe Hebrew")) pal:insert(Pango.Attribute.size_new(12)) local fm = lgi.PangoCairo.FontMap.get_default() local pango_context = Pango.FontMap.create_context(fm) pango_context:set_language(Pango.Language.from_string("he")) local s = "ltr שָׁוְא ltr" items = Pango.itemize(pango_context, s, 0, string.len(s), pal, nil) for i in pairs(items) do local offset = items[i].offset local length = items[i].length local analysis = items[i].analysis local pgs = Pango.GlyphString() Pango.shape(string.sub(s,1+offset), length, analysis, pgs) -- Pull out individual glyphs with pgs.glyphs local glyphs = pgs.glyphs check(type(glyphs) == 'table') check(#glyphs > 0) check(Pango.GlyphInfo:is_type_of(glyphs[1])) end end lgi-0.9.2/tests/performance.lua000066400000000000000000000034511316674307300164600ustar00rootroot00000000000000------------------------------------------------------------------------------ -- -- LGI Performance test module -- -- Copyright (c) 2013 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local lgi = require("lgi") local cairo = lgi.cairo local Gtk = lgi.Gtk local GLib = lgi.GLib local width, height = 200, 200 local surf = cairo.ImageSurface('ARGB32', width, height) local cr = cairo.Context(surf) local w = Gtk.Window() local cairo_move_to = cairo.Context.move_to for _, test in ipairs { { 100000, function() cr:move_to(100, 100) end }, { 100000, function() cairo.Context.move_to(cr, 100, 100) end }, { 100000, function() cairo_move_to(cr, 100, 100) end }, { 100000, function() cr.line_width = 1 end }, { 100000, function() cr:set_line_width(1) end }, { 10000, function() w:set_title('title') end }, { 10000, function() Gtk.Window.set_title(w, 'title') end }, { 10000, function() w.title = 'title' end }, } do local results = {} local timer = GLib.Timer() for i = 1, test[1] do test[2]() end timer:stop() io.write(string.format('%0.2f', timer:elapsed())) io.write('\t') io.flush() end print('\n') --[[ *** 0.7.2: 1.43 0.82 0.09 1.46 1.40 4.27 1.00 4.85 *** Remove and inline _access_element 1.19 0.83 0.09 1.19 1.15 3.35 1.04 3.84 *** Add caching of methods into main type table or _cached subtable 0.65 0.10 0.10 0.93 0.62 1.62 0.03 2.78 *** Add support for automatic async_ invocation 0.65 0.11 0.10 0.97 0.64 1.72 0.03 2.88 *** 0.8.0: (On Lua5.2) 0.65 0.10 0.10 0.93 0.62 1.62 0.03 2.78 *** 0.9.0: (On Lua5.2) 0.65 0.09 0.09 0.96 0.63 1.76 0.02 2.94 *** 0.9.1: (On Lua5.3.2) 0.62 0.10 0.10 0.92 0.60 1.55 0.02 2.65 --]] lgi-0.9.2/tests/record.lua000066400000000000000000000012201316674307300154250ustar00rootroot00000000000000--[[-------------------------------------------------------------------------- LGI testsuite, record test suite. Copyright (c) 2012 Pavel Holejsovsky Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php --]]-------------------------------------------------------------------------- local lgi = require 'lgi' local GLib = lgi.GLib local GObject = lgi.GObject local check = testsuite.check -- Basic GObject testing local record = testsuite.group.new('record') function record.native() local c = GObject.EnumValue() local p = c._native check(type(p) == 'userdata') check(GObject.EnumValue(p) == c) end lgi-0.9.2/tests/test.lua000066400000000000000000000103421316674307300151330ustar00rootroot00000000000000--[[-------------------------------------------------------------------------- LGI core testsuite runner. Copyright (c) 2010, 2011 Pavel Holejsovsky Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php --]]-------------------------------------------------------------------------- -- Available groups. local groups = {} -- Testing infrastructure, tests are grouped in testgroup instances. testsuite = {} testsuite.group = {} testsuite.group.__index = testsuite.group -- Creates new named testgroup. function testsuite.group.new(name) local group = setmetatable({ name = name, results = { total = 0, failed = 0 } }, testsuite.group) groups[#groups + 1] = name groups[name] = group return group end -- Adds new test. function testsuite.group:__newindex(name, func) assert(not self[name], "test already exists in the group") rawset(self, name, func) rawset(self, #self + 1, name) end -- Runs specified test(s), either by numeric id or by regexp mask. function testsuite.group:run(id) local function runfunc(num) self.results.total = self.results.total + 1 if testsuite.verbose then io.write(('%-8s:%3d:%-35s'):format(self.name, num, self[num])) io.flush() end local ok, msg local func = self[self[num]] if self.debug then func() ok = true else ok, msg = xpcall(func, debug.traceback) end collectgarbage() if not ok then self.results.failed = self.results.failed + 1 if not testsuite.verbose then io.write(('%-8s:%3d:%-35s'):format(self.name, num, self[num])) end io.write('FAIL\n ' .. tostring(msg) .. '\n') return end if testsuite.verbose then io.write('PASS\n') end end id = id or '' self.results.total = 0 self.results.failed = 0 if type(id) == 'number' then runfunc(id) else for i = 1, #self do if self[i] ~= 'debug' and self[i]:match(id) then runfunc(i) end end if (self.results.failed == 0) then io.write(('%-8s: all %d tests passed.\n'):format( self.name, self.results.total)) else io.write(('%-8s: FAILED %d of %d tests\n'):format( self.name, self.results.failed, self.results.total)) end end end -- Fails given test with error, number indicates how many functions on -- the stack should be skipped when reporting error location. function testsuite.fail(msg, skip) error(msg or 'failure', (skip or 1) + 1) end function testsuite.check(cond, msg, skip) if not cond then testsuite.fail(msg, (skip or 1) + 1) end end -- Helper, checks that given value has requested type and value. function testsuite.checkv(val, exp, exptype) if exptype then testsuite.check(type(val) == exptype, string.format("got type `%s', expected `%s'", type(val), exptype), 2) end testsuite.check(val == exp, string.format("got value `%s', expected `%s'", tostring(val), tostring(exp)), 2) end -- Load all known test source files. local testpath = arg[0]:sub(1, arg[0]:find('[^%/\\]+$') - 1):gsub('[/\\]$', '') for _, sourcefile in ipairs { 'gireg.lua', 'marshal.lua', 'corocbk.lua', 'record.lua', 'gobject.lua', 'glib.lua', 'variant.lua', 'dbus.lua', 'gtk.lua', 'cairo.lua', 'pango.lua', 'gio.lua', } do dofile(testpath .. '/' .. sourcefile) end -- Check for debug mode. if tests_debug or package.loaded.debugger then -- Make logs verbose (do not mute DEBUG level). testsuite.verbose = true require('lgi').log.DEBUG = 'verbose' for _, name in ipairs(groups) do groups[name].debug = true _G[name] = groups[name] end end -- Cmdline runner. local failed = false if select('#', ...) == 0 then -- Run everything. for _, group in ipairs(groups) do groups[group]:run() failed = failed or groups[group].results.failed > 0 end else -- Run just those which pass the mask. for _, mask in ipairs { ... } do local group, groupmask = mask:match('^(.-):(.+)$') if not group or not groups[group] then io.write(("No test group for mask `%s' found.\n"):format(mask)) return 2 end groups[group]:run(groupmask) failed = failed or groups[group].results.failed > 0 end end if failed then os.exit(1) end lgi-0.9.2/tests/variant.lua000066400000000000000000000162301316674307300156220ustar00rootroot00000000000000--[[-------------------------------------------------------------------------- LGI testsuite, GLib.Variant test suite. Copyright (c) 2010, 2011 Pavel Holejsovsky Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php --]]-------------------------------------------------------------------------- local lgi = require 'lgi' local GLib = lgi.GLib local GObject = lgi.GObject local check = testsuite.check -- Variant testing local variant = testsuite.group.new('variant') function variant.gvalue() local var1, var2 = GLib.Variant.new_string('foo'), GLib.Variant.new_string('bar') local val = GObject.Value(GObject.Type.VARIANT, var1) check(val.gtype == GObject.Type.VARIANT) check(val.value == var1) val.value = var2 check(val.value == var2) val.value = nil check(val.value == nil) check(val.gtype == GObject.Type.VARIANT) end function variant.newv_basic() local V, v = GLib.Variant v = V.new('b', true) check(v.type == 'b' and v:get_boolean() == true) v = V.new('y', 32) check(v.type == 'y' and v:get_byte() == 32) v = V.new('n', 13) check(v.type == 'n' and v:get_int16() == 13) v = V.new('q', 38) check(v.type == 'q' and v:get_uint16() == 38) v = V.new('i', 32) check(v.type == 'i' and v:get_int32() == 32) v = V.new('u', 35) check(v.type == 'u' and v:get_uint32() == 35) v = V.new('x', 39) check(v.type == 'x' and v:get_int64() == 39) v = V.new('t', 987) check(v.type == 't' and v:get_uint64() == 987) v = V.new('d', 3.1415927) check(v.type == 'd' and v:get_double() == 3.1415927) v = V.new('s', 'Hello') check(v.type == 's' and v:get_string() == 'Hello') v = V.new('o', '/object/path') check(v.type == 'o' and v:get_string() == '/object/path') v = V.new('g', "asi") check(v.type == 'g' and v:get_string() == 'asi') local vv = V.new('s', 'inner') v = V.new('v', vv) check(v.type == 'v' and v:get_variant() == vv) v = V.new('ay', 'bytestring') check(v.type == 'ay' and tostring(v:get_bytestring()) == 'bytestring') end function variant.newv_variant() local V, v, vv = GLib.Variant vv = V('i', 14) v = V('v', vv) check(v.type == 'v' and v:n_children() == 1 and v:get_child_value(0) == vv) end function variant.newv_maybe() local V, v = GLib.Variant v = V('mi', 42) check(v.type == 'mi' and v:n_children() == 1 and v:get_child_value(0).type == 'i' and v:get_child_value(0):get_int32() == 42) v = V('mi') check(v.type == 'mi' and v:n_children() == 0) end function variant.newv_tuple() local V, v = GLib.Variant v = V.new('()') check(v.type == '()' and v:n_children() == 0) v = V.new('(i)', {42}) check(v.type == '(i)' and v:n_children() == 1 and v:get_child_value(0).type == 'i' and v:get_child_value(0):get_int32() == 42) v = V.new('(mii)', { nil, 1 }) check(v.type == '(mii)' and v:n_children() == 2 and v:get_child_value(0):n_children() == 0) end function variant.newv_dictentry() local V, v = GLib.Variant v = V('{is}', {42, 'Hello'}) check(v.type == '{is}' and v:n_children() == 2 and v:get_child_value(0).type == 'i' and v:get_child_value(0):get_int32() == 42 and v:get_child_value(1).type == 's' and v:get_child_value(1):get_string() == 'Hello') end function variant.newv_array() local V, v = GLib.Variant v = V('as', { 'Hello', 'world' }) check(v.type == 'as' and v:n_children() == 2 and v:get_child_value(0):get_string() == 'Hello' and v:get_child_value(1):get_string() == 'world') v = V('as', {}) check(v:n_children() == 0) v = V('ams', { 'Hello', nil, 'world', n = 3 }) check(v:n_children() == 3) check(v:get_child_value(0):n_children() == 1 and v:get_child_value(0):get_child_value(0):get_string() == 'Hello') check(v:get_child_value(1):n_children() == 0) check(v:get_child_value(2):n_children() == 1 and v:get_child_value(2):get_child_value(0):get_string() == 'world') end function variant.newv_dictionary() local V, v, vv = GLib.Variant v = V('a{sd}', { PI = 3.14, one = 1 }) check(v:n_children() == 2) vv = v:lookup_value('PI', GLib.VariantType.DOUBLE) check(vv.type == 'd' and vv:get_double() == 3.14) vv = v:lookup_value('one', GLib.VariantType.DOUBLE) check(vv.type == 'd' and vv:get_double() == 1) end function variant.newv_badtype() local V, v = GLib.Variant check(not pcall(V.new, '{vs}')) check(not pcall(V.new, '{s}')) check(not pcall(V.new, '{}')) check(not pcall(V.new, '())')) check(not pcall(V.new, 'a')) check(not pcall(V.new, 'm')) check(not pcall(V.new, '{asi}')) check(not pcall(V.new, '{mdd}')) check(not pcall(V.new, '{is')) check(not pcall(V.new, '{is)')) check(not pcall(V.new, 'r')) check(not pcall(V.new, '*')) check(not pcall(V.new, '?')) check(not pcall(V.new, 'ii')) end function variant.value_simple() local V, v = GLib.Variant check(V('b', true).value == true) check(V('y', 10).value == 10) check(V('n', 11).value == 11) check(V('q', 12).value == 12) check(V('i', 13).value == 13) check(V('u', 14).value == 14) check(V('q', 15).value == 15) check(V('t', 16).value == 16) check(V('s', 'I').value == 'I') check(V('o', '/o/p').value == '/o/p') check(V('g', '(ii)').value == '(ii)') v = V('i', 1) check(V('v', v).value == v) check(V('ay', 'bytestring').value == 'bytestring') end function variant.value_container() local V, v = GLib.Variant check(V('mi', 1).value == 1) check(V('mi', nil).value == nil) local r r = V('{sd}', {'one', 1}).value check(type(r) == 'table' and #r == 2 and r[1] == 'one' and r[2] == 1) r = V('(imii)', {2, nil, 1}).value check(type(r) == 'table' and r.n == 3 and r[1] == 2 and r[2] == nil and r[3] == 1) v = V('as', {}) check(v.value == v) end function variant.value_dictionary() local V, v = GLib.Variant v = V('a{sd}', { one = 1, two = 2 }) check(v.value.one == 1) check(v.value.two == 2) check(v.value.three == nil) check(v.value[1] == nil) v = V('a{is}', { [1] = 'one', [2] = 'two' }) check(v.value[1] == 'one') check(v.value[2] == 'two') check(v.value[3] == nil) check(v.value.three == nil) end function variant.length() local V, v = GLib.Variant check(#V('s', 'Hello') == 0) check(#V('i', 1) == 0) check(#V('v', V('i', 1)) == 1) check(#V('mi', nil) == 0) check(#V('mi', 1) == 1) check(#V('(ii)', {1, 2}) == 2) check(#V('{sd}', { 'one', 1 }) == 2) check(#V('a{sd}', { one = 1 }) == 1) check(#V('ai', {}) == 0) check(#V('ami', { 1, nil, 2, n = 3 }) == 3) end function variant.indexing() local V, v = GLib.Variant v = V('mi', 1) check(v[1] == 1 and v[2] == nil) v = V('{sd}', { 'one', 1 }) check(v[1] == 'one' and v[2] == 1 and v[3] == nil) v = V('a{sd}', { one = 1 }) check(v[1][1] == 'one' and v[1][2] == 1 and v[2] == nil) v = V('(si)', { 'hi', 3 }) check(v[1] == 'hi' and v[2] == 3 and v[3] == nil) check(V('s', 'hello')[1] == nil) end function variant.serialize() local V, v1, v2 = GLib.Variant v1 = V('s', 'Hello') v2 = V.new_from_data(v1.type, v1.data) check(v1:equal(v2)) -- Make sure that new_from_data properly keeps underlying data alive. v1 = nil collectgarbage() local _ = v2:print(true) end lgi-0.9.2/tools/000077500000000000000000000000001316674307300134475ustar00rootroot00000000000000lgi-0.9.2/tools/dump-typelib.lua000077500000000000000000000127431316674307300165770ustar00rootroot00000000000000#! /usr/bin/env lua ------------------------------------------------------------------------------ -- -- LGI tools for dumping typelib fragments into readable text format. -- -- Copyright (c) 2010, 2011 Pavel Holejsovsky -- Licensed under the MIT license: -- http://www.opensource.org/licenses/mit-license.php -- ------------------------------------------------------------------------------ local lgi_core = require 'lgi.core' local gi = lgi_core.gi require 'debugger' -- Implements BaseInfo object, capable of dump itself. local infos = {} infos.base = { attrs = { 'name', 'namespace', 'type', 'deprecated' }, cats = {}, } infos.base.__index = infos.base -- Creates new info wrapper according to info type. function infos.new(info) if info then return setmetatable({ info = info }, infos[info.type] or infos.base) end end -- Derives new baseinfo subtype. function infos.base:derive(attrs, cats) local new_attrs = {} for _, val in ipairs(self.attrs) do new_attrs[#new_attrs + 1] = val end for _, val in ipairs(attrs or {}) do new_attrs[#new_attrs + 1] = val end local new_cats = {} for _, val in ipairs(self.cats) do new_cats[#new_cats + 1] = val end for _, val in ipairs(cats or {}) do new_cats[#new_cats + 1] = val end local new = setmetatable({ attrs = new_attrs, cats = new_cats }, self) new.__index = new return new end -- Gets given attribute or category. function infos.base:get(name, depth) local item = self.info[name] if gi.isinfo(item) then item = infos.new(item) if depth then item = item:dump(depth) end else for _, cat in pairs(self.cats) do if cat == name then item = infos.category.new(item) end end end return item end -- Dumps all attributes into the target table. function infos.base:dump_attrs(target, depth) for _, attr in ipairs(self.attrs) do target[attr] = self:get(attr, depth - 1) end return attrs end -- Dumps all categories into the target table. function infos.base:dump_cats(target, depth) local cats = {} for _, cat in ipairs(self.cats) do target[cat] = self:get(cat):dump(depth - 1) end return cats end function infos.base:dump(depth) if depth <= 0 then return '...' end local t = {} self:dump_attrs(t, depth) local cats = {} self:dump_cats(cats, depth) if next(cats) then t.cats = cats end return t end -- Implementation of 'subcategory' pseudoinfo. infos.category = infos.base:derive() function infos.category.new(category) return setmetatable({ info = category }, infos.category) end function infos.category:dump(depth) local t = {} for i = 1, #self.info do t[i] = infos.new(self.info[i]):dump(depth) end return t end infos.type = infos.base:derive( { 'tag', 'is_basic', 'interface', 'array_type', 'is_zero_terminated', 'array_length', 'fixed_size', 'is_pointer' }, { 'params' }) function infos.type:dump_cats(target, depth) local params = {} for i, param in ipairs(self.info.params or {}) do params[i] = infos.new(param):dump(depth - 1) end if next(params) then target.params = params end end infos.registered = infos.base:derive({ 'gtype' }, {}) infos.object = infos.registered:derive( { 'parent', 'type_struct', }, { 'interfaces', 'fields', 'vfuncs', 'methods', 'constants', 'properties', 'signals' }) infos.interface = infos.registered:derive( { 'type_struct', }, { 'prerequisites', 'vfuncs', 'methods', 'constants', 'properties', 'signals' }) infos.property = infos.base:derive({ 'typeinfo', 'flags', 'transfer' }) infos.callable = infos.base:derive( { 'return_type', 'return_transfer' }, { 'args' }) infos['function'] = infos.callable:derive({ 'flags' }) infos.signal = infos.callable:derive({ 'flags' }) infos.callback = infos.callable:derive() infos.vfunc = infos.callable:derive() infos.arg = infos.base:derive( { 'typeinfo', 'direction', 'transfer', 'optional', 'typeinfo' } ) infos.struct = infos.registered:derive({ 'is_gtype_struct', 'size' }, { 'fields', 'methods' }) infos.union = infos.registered:derive({ 'size' }, { 'fields', 'methods' }) infos.field = infos.base:derive({ 'typeinfo', 'flags', 'size', 'offset' }) infos.enum = infos.registered:derive({ 'storage' }, { 'values' }) infos.value = infos.base:derive({ 'value' }) infos.constant = infos.base:derive({ 'typeinfo', 'value' }) -- Implementation of info wrapper for namespace pseudoinfo. infos.namespace = infos.base:derive({ 'name', 'version', 'dependencies' }) function infos.namespace:get(name) local item = self.info[name] return item and infos.new(item) end function infos.namespace:dump_cats(target, depth) if depth <= 0 then return '...' end for i = 1, #self.info do local info = self.info[i] target[info.name] = infos.new(info):dump(depth - 1) end end function infos.namespace.new(info) return setmetatable({ info = info }, infos.namespace) end -- Implementation of root element pseudoinfo. infos.root = infos.base:derive() function infos.root:get(name) return infos.namespace.new(gi.require(name)) end -- Commandline processing arg = arg or {} paths = {} depth = 3 for i = 1, #arg do if tonumber(arg[i]) then depth = tonumber(arg[i]) else paths[#paths + 1] = arg[i] end end -- Go through all paths and dump them. for _, path in ipairs(paths) do local info = infos.root for name in path:gmatch('([^%.]+)%.?') do info = info:get(name) if not info then break end end if not info then error(('%s not found'):format(path)) end dump(info:dump(depth), depth * 2) end