aboutsummaryrefslogtreecommitdiff
path: root/liblogdiag/ld-lua.c
diff options
context:
space:
mode:
authorPřemysl Janouch <p.janouch@gmail.com>2011-01-10 16:49:13 +0100
committerPřemysl Janouch <p.janouch@gmail.com>2011-01-10 17:07:02 +0100
commit616c49a5053830a5e0a31c71fd6114926e43235f (patch)
tree8a21f60862a86d5fb2faf5ed7fd70aa7a2ce69d5 /liblogdiag/ld-lua.c
parent63b36a2b5b8e04f5d96fa9aa8d212a01c73aad49 (diff)
downloadlogdiag-616c49a5053830a5e0a31c71fd6114926e43235f.tar.gz
logdiag-616c49a5053830a5e0a31c71fd6114926e43235f.tar.xz
logdiag-616c49a5053830a5e0a31c71fd6114926e43235f.zip
Make a separate library.
This is required for gtkdoc-scangobj. So far it's much like it's been before, the main differences are that source files are in two directories from now on and the build process has two stages.
Diffstat (limited to 'liblogdiag/ld-lua.c')
-rw-r--r--liblogdiag/ld-lua.c808
1 files changed, 808 insertions, 0 deletions
diff --git a/liblogdiag/ld-lua.c b/liblogdiag/ld-lua.c
new file mode 100644
index 0000000..47a41b5
--- /dev/null
+++ b/liblogdiag/ld-lua.c
@@ -0,0 +1,808 @@
+/*
+ * ld-lua.c
+ *
+ * This file is a part of logdiag.
+ * Copyright Přemysl Janouch 2010 - 2011. All rights reserved.
+ *
+ * See the file LICENSE for licensing information.
+ *
+ */
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include "liblogdiag.h"
+#include "config.h"
+
+#include "ld-lua-private.h"
+#include "ld-lua-symbol-private.h"
+
+
+/**
+ * SECTION:ld-lua
+ * @short_description: Lua symbol engine.
+ * @see_also: #LdLuaSymbol
+ *
+ * #LdLua is a symbol engine that uses Lua scripts to manage symbols.
+ */
+
+/*
+ * LdLuaPrivate:
+ * @L: Lua state.
+ *
+ * The library contains the real function for rendering.
+ */
+struct _LdLuaPrivate
+{
+ lua_State *L;
+};
+
+/* registry.logdiag_symbols
+ * -> A table indexed by pointers to LdLuaSymbol objects
+ * registry.logdiag_symbols.object.render(cr)
+ * -> The rendering function
+ */
+
+#define LD_LUA_LIBRARY_NAME "logdiag"
+#define LD_LUA_DATA_INDEX LD_LUA_LIBRARY_NAME "_data"
+#define LD_LUA_SYMBOLS_INDEX LD_LUA_LIBRARY_NAME "_symbols"
+
+/*
+ * LdLuaData:
+ * @self: A reference to self.
+ * @load_callback: A callback for newly registered symbols.
+ * @load_user_data: User data to be passed to the callback.
+ *
+ * Full user data to be stored in Lua registry.
+ */
+typedef struct _LdLuaData LdLuaData;
+
+struct _LdLuaData
+{
+ LdLua *self;
+ LdLuaLoadCallback load_callback;
+ gpointer load_user_data;
+};
+
+typedef struct _LdLuaDrawData LdLuaDrawData;
+
+struct _LdLuaDrawData
+{
+ LdLuaSymbol *symbol;
+ cairo_t *cr;
+ unsigned save_count;
+};
+
+static void ld_lua_finalize (GObject *gobject);
+
+static void *ld_lua_alloc (void *ud, void *ptr, size_t osize, size_t nsize);
+
+static int ld_lua_private_draw_cb (lua_State *L);
+static int ld_lua_private_unregister_cb (lua_State *L);
+
+static int ld_lua_logdiag_register (lua_State *L);
+static int process_registration (lua_State *L);
+static gchar *get_translation (lua_State *L, int index);
+static gboolean read_symbol_area (lua_State *L, int index, LdRectangle *area);
+static gboolean read_terminals (lua_State *L, int index,
+ LdPointArray **terminals);
+
+static void push_cairo_object (lua_State *L, LdLuaDrawData *draw_data);
+static gdouble get_cairo_scale (cairo_t *cr);
+static int ld_lua_cairo_save (lua_State *L);
+static int ld_lua_cairo_restore (lua_State *L);
+static int ld_lua_cairo_get_line_width (lua_State *L);
+static int ld_lua_cairo_set_line_width (lua_State *L);
+static int ld_lua_cairo_move_to (lua_State *L);
+static int ld_lua_cairo_line_to (lua_State *L);
+static int ld_lua_cairo_curve_to (lua_State *L);
+static int ld_lua_cairo_arc (lua_State *L);
+static int ld_lua_cairo_arc_negative (lua_State *L);
+static int ld_lua_cairo_new_path (lua_State *L);
+static int ld_lua_cairo_new_sub_path (lua_State *L);
+static int ld_lua_cairo_close_path (lua_State *L);
+static int ld_lua_cairo_stroke (lua_State *L);
+static int ld_lua_cairo_stroke_preserve (lua_State *L);
+static int ld_lua_cairo_fill (lua_State *L);
+static int ld_lua_cairo_fill_preserve (lua_State *L);
+static int ld_lua_cairo_clip (lua_State *L);
+static int ld_lua_cairo_clip_preserve (lua_State *L);
+
+
+static luaL_Reg ld_lua_logdiag_lib[] =
+{
+ {"register", ld_lua_logdiag_register},
+ {NULL, NULL}
+};
+
+static luaL_Reg ld_lua_cairo_table[] =
+{
+ {"save", ld_lua_cairo_save},
+ {"restore", ld_lua_cairo_restore},
+ {"get_line_width", ld_lua_cairo_get_line_width},
+ {"set_line_width", ld_lua_cairo_set_line_width},
+ {"move_to", ld_lua_cairo_move_to},
+ {"line_to", ld_lua_cairo_line_to},
+ {"curve_to", ld_lua_cairo_curve_to},
+ {"arc", ld_lua_cairo_arc},
+ {"arc_negative", ld_lua_cairo_arc_negative},
+ {"new_path", ld_lua_cairo_new_path},
+ {"new_sub_path", ld_lua_cairo_new_sub_path},
+ {"close_path", ld_lua_cairo_close_path},
+ {"stroke", ld_lua_cairo_stroke},
+ {"stroke_preserve", ld_lua_cairo_stroke_preserve},
+ {"fill", ld_lua_cairo_fill},
+ {"fill_preserve", ld_lua_cairo_fill_preserve},
+ {"clip", ld_lua_cairo_clip},
+ {"clip_preserve", ld_lua_cairo_clip_preserve},
+ {NULL, NULL}
+};
+
+
+/* ===== Generic =========================================================== */
+
+G_DEFINE_TYPE (LdLua, ld_lua, G_TYPE_OBJECT);
+
+static void
+ld_lua_class_init (LdLuaClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = ld_lua_finalize;
+
+ g_type_class_add_private (klass, sizeof (LdLuaPrivate));
+}
+
+static void
+ld_lua_init (LdLua *self)
+{
+ lua_State *L;
+ LdLuaData *ud;
+
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE
+ (self, LD_TYPE_LUA, LdLuaPrivate);
+
+ L = self->priv->L = lua_newstate (ld_lua_alloc, NULL);
+ g_return_if_fail (L != NULL);
+
+ /* TODO: lua_atpanic () */
+
+ /* Load some safe libraries. */
+ lua_pushcfunction (L, luaopen_string);
+ lua_call (L, 0, 0);
+
+ lua_pushcfunction (L, luaopen_table);
+ lua_call (L, 0, 0);
+
+ lua_pushcfunction (L, luaopen_math);
+ lua_call (L, 0, 0);
+
+ /* Load the application library. */
+ luaL_register (L, LD_LUA_LIBRARY_NAME, ld_lua_logdiag_lib);
+
+ /* Store user data to the registry. */
+ ud = lua_newuserdata (L, sizeof (LdLuaData));
+ ud->self = self;
+ ud->load_callback = NULL;
+ ud->load_user_data = NULL;
+
+ lua_setfield (L, LUA_REGISTRYINDEX, LD_LUA_DATA_INDEX);
+
+ /* Create an empty symbol table. */
+ lua_newtable (L);
+ lua_setfield (L, LUA_REGISTRYINDEX, LD_LUA_SYMBOLS_INDEX);
+}
+
+static void
+ld_lua_finalize (GObject *gobject)
+{
+ LdLua *self;
+
+ self = LD_LUA (gobject);
+ lua_close (self->priv->L);
+
+ /* Chain up to the parent class. */
+ G_OBJECT_CLASS (ld_lua_parent_class)->finalize (gobject);
+}
+
+/**
+ * ld_lua_new:
+ *
+ * Create an instance of #LdLua.
+ */
+LdLua *
+ld_lua_new (void)
+{
+ return g_object_new (LD_TYPE_LUA, NULL);
+}
+
+static void *
+ld_lua_alloc (void *ud, void *ptr, size_t osize, size_t nsize)
+{
+ if (!nsize)
+ {
+ g_free (ptr);
+ return NULL;
+ }
+ else
+ return g_try_realloc (ptr, nsize);
+}
+
+/**
+ * ld_lua_check_file:
+ * @self: An #LdLua object.
+ * @filename: The file to be checked.
+ *
+ * Check if the given filename can be loaded by #LdLua.
+ */
+gboolean
+ld_lua_check_file (LdLua *self, const gchar *filename)
+{
+ g_return_val_if_fail (LD_IS_LUA (self), FALSE);
+ g_return_val_if_fail (filename != NULL, FALSE);
+
+ return g_str_has_suffix (filename, ".lua")
+ && g_file_test (filename, G_FILE_TEST_IS_REGULAR);
+}
+
+/**
+ * ld_lua_load_file:
+ * @self: An #LdLua object.
+ * @filename: The file to be loaded.
+ * @callback: A callback for newly registered symbols.
+ * The callee is responsible for referencing the symbol.
+ * @user_data: User data to be passed to the callback.
+ *
+ * Loads a file and creates #LdLuaSymbol objects for contained symbols.
+ *
+ * Returns: TRUE if no error has occured, FALSE otherwise.
+ */
+gboolean
+ld_lua_load_file (LdLua *self, const gchar *filename,
+ LdLuaLoadCallback callback, gpointer user_data)
+{
+ gint retval;
+ LdLuaData *ud;
+
+ g_return_val_if_fail (LD_IS_LUA (self), FALSE);
+ g_return_val_if_fail (filename != NULL, FALSE);
+ g_return_val_if_fail (callback != NULL, FALSE);
+
+ /* XXX: If something from the following fails, Lua will call exit(). */
+ lua_getfield (self->priv->L, LUA_REGISTRYINDEX, LD_LUA_DATA_INDEX);
+ ud = lua_touserdata (self->priv->L, -1);
+ lua_pop (self->priv->L, 1);
+ g_return_val_if_fail (ud != NULL, FALSE);
+
+ ud->load_callback = callback;
+ ud->load_user_data = user_data;
+
+ retval = luaL_loadfile (self->priv->L, filename);
+ if (retval)
+ goto ld_lua_lftc_fail;
+
+ retval = lua_pcall (self->priv->L, 0, 0, 0);
+ if (retval)
+ goto ld_lua_lftc_fail;
+
+ ud->load_callback = NULL;
+ ud->load_user_data = NULL;
+ return TRUE;
+
+ld_lua_lftc_fail:
+ g_warning ("Lua error: %s", lua_tostring (self->priv->L, -1));
+ lua_remove (self->priv->L, -1);
+
+ ud->load_callback = NULL;
+ ud->load_user_data = NULL;
+ return FALSE;
+}
+
+/* ===== LdLuaSymbol callbacks ============================================= */
+
+/**
+ * ld_lua_private_draw:
+ * @self: An #LdLua object.
+ * @symbol: A symbol to be drawn.
+ * @cr: A Cairo context to be drawn onto.
+ *
+ * Draw a symbol onto a Cairo context.
+ */
+void
+ld_lua_private_draw (LdLua *self, LdLuaSymbol *symbol, cairo_t *cr)
+{
+ LdLuaDrawData data;
+
+ g_return_if_fail (LD_IS_LUA (self));
+ g_return_if_fail (LD_IS_LUA_SYMBOL (symbol));
+ g_return_if_fail (cr != NULL);
+
+ data.symbol = symbol;
+ data.cr = cr;
+ data.save_count = 0;
+
+ if (lua_cpcall (self->priv->L, ld_lua_private_draw_cb, &data))
+ {
+ g_warning ("Lua error: %s", lua_tostring (self->priv->L, -1));
+ lua_pop (self->priv->L, 1);
+ }
+
+ while (data.save_count--)
+ cairo_restore (cr);
+}
+
+static int
+ld_lua_private_draw_cb (lua_State *L)
+{
+ LdLuaDrawData *data;
+
+ data = lua_touserdata (L, -1);
+
+ /* Retrieve the function for rendering from the registry. */
+ lua_getfield (L, LUA_REGISTRYINDEX, LD_LUA_SYMBOLS_INDEX);
+ lua_pushlightuserdata (L, data->symbol);
+ lua_gettable (L, -2);
+
+ luaL_checktype (L, -1, LUA_TTABLE);
+ lua_getfield (L, -1, "render");
+ luaL_checktype (L, -1, LUA_TFUNCTION);
+
+ /* Call the function do draw the symbol. */
+ push_cairo_object (L, data);
+ lua_pcall (L, 1, 0, 0);
+ return 0;
+}
+
+/**
+ * ld_lua_private_unregister:
+ * @self: An #LdLua object.
+ * @symbol: A symbol to be unregistered.
+ *
+ * Unregister a symbol from the internal Lua state.
+ */
+void
+ld_lua_private_unregister (LdLua *self, LdLuaSymbol *symbol)
+{
+ g_return_if_fail (LD_IS_LUA (self));
+ g_return_if_fail (LD_IS_LUA_SYMBOL (symbol));
+
+ if (lua_cpcall (self->priv->L, ld_lua_private_unregister_cb, symbol))
+ {
+ g_warning ("Lua error: %s", lua_tostring (self->priv->L, -1));
+ lua_pop (self->priv->L, 1);
+ }
+}
+
+static int
+ld_lua_private_unregister_cb (lua_State *L)
+{
+ /* Set the entry in the symbol table to nil. */
+ lua_getfield (L, LUA_REGISTRYINDEX, LD_LUA_SYMBOLS_INDEX);
+ lua_insert (L, -2);
+ lua_pushnil (L);
+ lua_settable (L, -3);
+ return 0;
+}
+
+/* ===== Application library =============================================== */
+
+static int
+ld_lua_logdiag_register (lua_State *L)
+{
+ LdLuaData *ud;
+ LdLuaSymbol *symbol;
+
+ lua_getfield (L, LUA_REGISTRYINDEX, LD_LUA_DATA_INDEX);
+ ud = lua_touserdata (L, -1);
+ lua_pop (L, 1);
+ g_return_val_if_fail (ud != NULL, 0);
+
+ /* Use a protected environment, so script errors won't cause leaking
+ * of the symbol object. Only a failure of the last three function calls
+ * before lua_pcall() may cause the symbol to leak.
+ */
+ lua_checkstack (L, 3);
+ symbol = g_object_new (LD_TYPE_LUA_SYMBOL, NULL);
+
+ lua_pushlightuserdata (L, symbol);
+ lua_pushcclosure (L, process_registration, 1);
+ lua_insert (L, 1);
+
+ /* On the stack, there are function arguments plus the function itself. */
+ if (lua_pcall (L, lua_gettop (L) - 1, 0, 0))
+ {
+ luaL_where (L, 1);
+ lua_insert (L, -2);
+ lua_concat (L, 2);
+
+ g_warning ("Lua symbol registration failed: %s",
+ lua_tostring (L, -1));
+ lua_pushboolean (L, FALSE);
+ }
+ else
+ {
+ /* We don't want an extra LdLua reference either. */
+ symbol->priv->lua = ud->self;
+ g_object_ref (ud->self);
+
+ ud->load_callback (LD_SYMBOL (symbol), ud->load_user_data);
+ lua_pushboolean (L, TRUE);
+ }
+ g_object_unref (symbol);
+
+ return 1;
+}
+
+/*
+ * process_registration:
+ * @L: A Lua state.
+ *
+ * Parse arguments, write them to a symbol object and register the object.
+ */
+static int
+process_registration (lua_State *L)
+{
+ LdLuaSymbol *symbol;
+ gchar *human_name;
+
+ int i, type, types[] =
+ {LUA_TSTRING, LUA_TTABLE, LUA_TTABLE, LUA_TTABLE, LUA_TFUNCTION};
+ int n_args_needed = sizeof (types) / sizeof (int);
+
+ if (lua_gettop (L) < n_args_needed)
+ return luaL_error (L, "Too few arguments.");
+
+ for (i = 0; i < n_args_needed; i++)
+ if ((type = lua_type (L, i + 1)) != types[i])
+ return luaL_error (L, "Bad type of argument #%d."
+ " Expected %s, got %s.", i + 1,
+ lua_typename (L, types[i]), lua_typename (L, type));
+
+ symbol = LD_LUA_SYMBOL (lua_touserdata (L, lua_upvalueindex (1)));
+ symbol->priv->name = g_strdup (lua_tostring (L, 1));
+
+ human_name = get_translation (L, 2);
+ if (!human_name)
+ human_name = g_strdup (symbol->priv->name);
+ symbol->priv->human_name = human_name;
+
+ if (!read_symbol_area (L, 3, &symbol->priv->area))
+ return luaL_error (L, "Malformed symbol area array.");
+ if (!read_terminals (L, 4, &symbol->priv->terminals))
+ return luaL_error (L, "Malformed terminals array.");
+
+ lua_getfield (L, LUA_REGISTRYINDEX, LD_LUA_SYMBOLS_INDEX);
+ lua_pushlightuserdata (L, symbol);
+
+ lua_newtable (L);
+ lua_pushvalue (L, 5);
+ lua_setfield (L, -2, "render");
+
+ lua_settable (L, -3);
+ return 0;
+}
+
+/*
+ * get_translation:
+ * @L: A Lua state.
+ * @index: Stack index of the table.
+ *
+ * Select an applicable translation from a table.
+ * The return value has to be freed with g_free().
+ *
+ * Return value: The translation, if found. If none was found, returns NULL.
+ */
+static gchar *
+get_translation (lua_State *L, int index)
+{
+ const gchar *const *lang;
+ gchar *result;
+
+ for (lang = g_get_language_names (); *lang; lang++)
+ {
+ lua_getfield (L, 2, *lang);
+ if (lua_isstring (L, -1))
+ {
+ result = g_strdup (lua_tostring (L, -1));
+ lua_pop (L, 1);
+ return result;
+ }
+ lua_pop (L, 1);
+ }
+ return NULL;
+}
+
+/*
+ * read_symbol_area:
+ * @L: A Lua state.
+ * @index: Stack index of the table.
+ * @area: Where the area will be returned.
+ *
+ * Read a symbol area from a Lua table.
+ *
+ * Return value: TRUE on success, FALSE on failure.
+ */
+static gboolean
+read_symbol_area (lua_State *L, int index, LdRectangle *area)
+{
+ lua_Number x1, x2, y1, y2;
+
+ if (lua_objlen (L, index) != 4)
+ return FALSE;
+
+ lua_rawgeti (L, index, 1);
+ if (!lua_isnumber (L, -1))
+ return FALSE;
+ x1 = lua_tonumber (L, -1);
+
+ lua_rawgeti (L, index, 2);
+ if (!lua_isnumber (L, -1))
+ return FALSE;
+ y1 = lua_tonumber (L, -1);
+
+ lua_rawgeti (L, index, 3);
+ if (!lua_isnumber (L, -1))
+ return FALSE;
+ x2 = lua_tonumber (L, -1);
+
+ lua_rawgeti (L, index, 4);
+ if (!lua_isnumber (L, -1))
+ return FALSE;
+ y2 = lua_tonumber (L, -1);
+
+ area->x = MIN (x1, x2);
+ area->y = MIN (y1, y2);
+ area->width = ABS (x2 - x1);
+ area->height = ABS (y2 - y1);
+
+ lua_pop (L, 4);
+ return TRUE;
+}
+
+/*
+ * read_terminals:
+ * @L: A Lua state.
+ * @index: Stack index of the table.
+ * @area: Where the point array will be returned.
+ *
+ * Read symbol terminals from a Lua table.
+ *
+ * Return value: TRUE on success, FALSE on failure.
+ */
+static gboolean
+read_terminals (lua_State *L, int index, LdPointArray **terminals)
+{
+ LdPointArray *points;
+ size_t num_points;
+ unsigned i = 0;
+
+ num_points = lua_objlen (L, index);
+ points = ld_point_array_new (num_points);
+
+ lua_pushnil (L);
+ while (lua_next (L, index) != 0)
+ {
+ g_assert (i < num_points);
+
+ if (!lua_istable (L, -1) || lua_objlen (L, -1) != 2)
+ goto read_terminals_fail;
+
+ lua_rawgeti (L, -1, 1);
+ if (!lua_isnumber (L, -1))
+ goto read_terminals_fail;
+ points->points[i].x = lua_tonumber (L, -1);
+ lua_pop (L, 1);
+
+ lua_rawgeti (L, -1, 2);
+ if (!lua_isnumber (L, -1))
+ goto read_terminals_fail;
+ points->points[i].y = lua_tonumber (L, -1);
+
+ lua_pop (L, 2);
+ i++;
+ }
+ *terminals = points;
+ return TRUE;
+
+read_terminals_fail:
+ ld_point_array_free (points);
+ *terminals = NULL;
+ return FALSE;
+}
+
+
+/* ===== Cairo ============================================================= */
+
+static void
+push_cairo_object (lua_State *L, LdLuaDrawData *draw_data)
+{
+ luaL_Reg *fn;
+
+ /* Create a table. */
+ lua_newtable (L);
+
+ /* Add methods. */
+ /* XXX: The light user data pointer gets invalid after the end of
+ * "render" function invocation. If the script stores the "cr" object
+ * in some global variable and then tries to reuse it the next time,
+ * the application may go SIGSEGV.
+ *
+ * The solution is creating a full user data instead, referencing
+ * the cairo object and dereferencing it upon garbage collection
+ * of the user data object.
+ */
+ for (fn = ld_lua_cairo_table; fn->name; fn++)
+ {
+ lua_pushlightuserdata (L, draw_data);
+ lua_pushcclosure (L, fn->func, 1);
+ lua_setfield (L, -2, fn->name);
+ }
+}
+
+static gdouble
+get_cairo_scale (cairo_t *cr)
+{
+ double dx = 1, dy = 0;
+
+ cairo_user_to_device_distance (cr, &dx, &dy);
+ return dx;
+}
+
+#define LD_LUA_CAIRO_TRIVIAL(name) \
+static int \
+ld_lua_cairo_ ## name (lua_State *L) \
+{ \
+ LdLuaDrawData *data; \
+ data = lua_touserdata (L, lua_upvalueindex (1)); \
+ cairo_ ## name (data->cr); \
+ return 0; \
+}
+
+LD_LUA_CAIRO_TRIVIAL (new_path)
+LD_LUA_CAIRO_TRIVIAL (new_sub_path)
+LD_LUA_CAIRO_TRIVIAL (close_path)
+
+LD_LUA_CAIRO_TRIVIAL (stroke)
+LD_LUA_CAIRO_TRIVIAL (stroke_preserve)
+LD_LUA_CAIRO_TRIVIAL (fill)
+LD_LUA_CAIRO_TRIVIAL (fill_preserve)
+LD_LUA_CAIRO_TRIVIAL (clip)
+LD_LUA_CAIRO_TRIVIAL (clip_preserve)
+
+static int
+ld_lua_cairo_save (lua_State *L)
+{
+ LdLuaDrawData *data;
+
+ data = lua_touserdata (L, lua_upvalueindex (1));
+ if (data->save_count + 1)
+ {
+ data->save_count++;
+ cairo_save (data->cr);
+ }
+ return 0;
+}
+
+static int
+ld_lua_cairo_restore (lua_State *L)
+{
+ LdLuaDrawData *data;
+
+ data = lua_touserdata (L, lua_upvalueindex (1));
+ if (data->save_count)
+ {
+ data->save_count--;
+ cairo_restore (data->cr);
+ }
+ return 0;
+}
+
+static int
+ld_lua_cairo_get_line_width (lua_State *L)
+{
+ LdLuaDrawData *data;
+
+ data = lua_touserdata (L, lua_upvalueindex (1));
+ lua_pushnumber (L, cairo_get_line_width (data->cr)
+ * get_cairo_scale (data->cr));
+ return 1;
+}
+
+static int
+ld_lua_cairo_set_line_width (lua_State *L)
+{
+ LdLuaDrawData *data;
+
+ data = lua_touserdata (L, lua_upvalueindex (1));
+ cairo_set_line_width (data->cr, luaL_checknumber (L, 1)
+ / get_cairo_scale (data->cr));
+ return 0;
+}
+
+static int
+ld_lua_cairo_move_to (lua_State *L)
+{
+ LdLuaDrawData *data;
+ lua_Number x, y;
+
+ data = lua_touserdata (L, lua_upvalueindex (1));
+
+ x = luaL_checknumber (L, 1);
+ y = luaL_checknumber (L, 2);
+
+ cairo_move_to (data->cr, x, y);
+ return 0;
+}
+
+static int
+ld_lua_cairo_line_to (lua_State *L)
+{
+ LdLuaDrawData *data;
+ lua_Number x, y;
+
+ data = lua_touserdata (L, lua_upvalueindex (1));
+
+ x = luaL_checknumber (L, 1);
+ y = luaL_checknumber (L, 2);
+
+ cairo_line_to (data->cr, x, y);
+ return 0;
+}
+
+static int
+ld_lua_cairo_curve_to (lua_State *L)
+{
+ LdLuaDrawData *data;
+ lua_Number x1, y1, x2, y2, x3, y3;
+
+ data = lua_touserdata (L, lua_upvalueindex (1));
+
+ x1 = luaL_checknumber (L, 1);
+ y1 = luaL_checknumber (L, 2);
+ x2 = luaL_checknumber (L, 3);
+ y2 = luaL_checknumber (L, 4);
+ x3 = luaL_checknumber (L, 5);
+ y3 = luaL_checknumber (L, 6);
+
+ cairo_curve_to (data->cr, x1, y1, x2, y2, x3, y3);
+ return 0;
+}
+
+static int
+ld_lua_cairo_arc (lua_State *L)
+{
+ LdLuaDrawData *data;
+ lua_Number xc, yc, radius, angle1, angle2;
+
+ data = lua_touserdata (L, lua_upvalueindex (1));
+
+ xc = luaL_checknumber (L, 1);
+ yc = luaL_checknumber (L, 2);
+ radius = luaL_checknumber (L, 3);
+ angle1 = luaL_checknumber (L, 4);
+ angle2 = luaL_checknumber (L, 5);
+
+ cairo_arc (data->cr, xc, yc, radius, angle1, angle2);
+ return 0;
+}
+
+static int
+ld_lua_cairo_arc_negative (lua_State *L)
+{
+ LdLuaDrawData *data;
+ lua_Number xc, yc, radius, angle1, angle2;
+
+ data = lua_touserdata (L, lua_upvalueindex (1));
+
+ xc = luaL_checknumber (L, 1);
+ yc = luaL_checknumber (L, 2);
+ radius = luaL_checknumber (L, 3);
+ angle1 = luaL_checknumber (L, 4);
+ angle2 = luaL_checknumber (L, 5);
+
+ cairo_arc_negative (data->cr, xc, yc, radius, angle1, angle2);
+ return 0;
+}
+