lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Dimiter 'malkia' Stanev wrote:
> Mike just put support for callbacks for x86/x64

Yep. Attached are two examples using FFI callbacks. You'll need
LuaJIT git HEAD on x86 or x64 to run these.

The first example is for Windows that lists all window titles, the
other is a simple GTK+ demo (tested on Linux). Excuse the length
of the GTK+ demo, but programming in GTK+ gets rather verbose.

Docs are here: http://luajit.org/ext_ffi_semantics.html#callback

tl;dr: Just pass a Lua function to a C function taking a callback
argument and the FFI will do all the magic in the background.

The FFI callback feature for x86/x64 has been sponsored by a
corporate sponsor, who wishes to remain anonymous at this time.
Ports to other architectures will follow, depending on sponsorship.
Or if someone wants to grind their teeth at a really challenging
assembler project for the CPU of your choice. :-)

--Mike
local ffi = require("ffi")

ffi.cdef[[
typedef int (__stdcall *WNDENUMPROC)(void *hwnd, intptr_t l);
int EnumWindows(WNDENUMPROC func, intptr_t l);
int SendMessageA(void *hwnd, uint32_t msg, int w, intptr_t l);
enum { WM_GETTEXT = 13 };
]]

local C = ffi.C
local buf = ffi.new("char[?]", 256)
local lbuf = ffi.cast("intptr_t", buf)
C.EnumWindows(function(hwnd, l)
  if C.SendMessageA(hwnd, C.WM_GETTEXT, 255, lbuf) ~= 0 then
    io.write(ffi.string(buf), "\n")
  end
  return true
end, 0)
-- LuaJIT FFI example: Simple GTK+ 2.x demo, showing FFI callbacks.
-- Written by Mike Pall. Public domain.

local ffi = require("ffi")

-- You may need to change this, depending on your OS.
local g = ffi.load("libgtk-x11-2.0.so")

ffi.cdef[[
// These are just the bare minimum C declarations needed for this demo.
// Some stuff has been simplified compared to the original Glib/GTK+ headers.

typedef struct _GClosure GClosure;
typedef void (*GCallback)(void);
typedef void (*GClosureNotify)(void *data, GClosure *closure);
typedef int (*GSourceFunc)(void *data);

typedef enum { G_CONNECT_AFTER = 1, G_CONNECT_SWAPPED = 2 } GConnectFlags;

unsigned long g_signal_connect_data(void *obj, const char *sig,
  GCallback handler, void *data, GClosureNotify destroy_data,
  GConnectFlags connect_flags);
unsigned int g_timeout_add(unsigned int interval, GSourceFunc f, void *data);

typedef union _GdkEvent GdkEvent;

// Declare these as the same type to avoid tons of casts.
typedef struct _GtkInternal GtkWidget;
typedef struct _GtkInternal GtkContainer;
typedef struct _GtkInternal GtkWindow;
typedef struct _GtkInternal GtkButton;

typedef enum {
  GTK_WINDOW_TOPLEVEL, GTK_WINDOW_POPUP
} GtkWindowType;

typedef enum {
  GTK_WIN_POS_NONE, GTK_WIN_POS_CENTER, GTK_WIN_POS_MOUSE,
  GTK_WIN_POS_CENTER_ALWAYS, GTK_WIN_POS_CENTER_ON_PARENT
} GtkWindowPosition;

void gtk_init(int *argc, char ***argv);
void gtk_main(void);
void gtk_main_quit(void);

GtkWidget *gtk_window_new(GtkWindowType type);
void gtk_window_set_position(GtkWindow *window, GtkWindowPosition position);
void gtk_window_set_default_size (GtkWindow *window, int width, int height);

void gtk_widget_show_all(GtkWidget *widget);

void gtk_container_add(GtkContainer *container, GtkWidget *widget);
void gtk_container_set_border_width(GtkContainer *container,
  unsigned int border_width);

GtkWidget *gtk_button_new_with_label(const char *label);
void gtk_button_set_label(GtkButton *button, const char *label);

// Our own callback types.
typedef void (*GtkCB_w)(GtkWidget *, void *);
typedef int (*GtkCB_ev)(GtkWidget *, GdkEvent *, void *);
]]

-- Convenience function for glib signals.
local function g_signal_connect(win, sig, ftype, f, data, flags)
  return g.g_signal_connect_data(win, sig,
    ffi.cast("GCallback", ffi.cast(ftype, f)), data, nil, flags or 0)
end

-- Pass in the program name.
do
  local name = arg[0]
  local arg0 = ffi.new("char[?]", #name+1, name)
  local argv = ffi.new("char *[1]", arg0)
  local argcp = ffi.new("int[1]", 1)
  local argvp = ffi.new("char **[1]", argv)
  g.gtk_init(argcp, argvp)
end

-- Create the toplevel window.
local win = g.gtk_window_new("GTK_WINDOW_TOPLEVEL")
g.gtk_window_set_position(win, "GTK_WIN_POS_CENTER")
g.gtk_window_set_default_size(win, 200, 150);
g.gtk_container_set_border_width(win, 10)

g_signal_connect(win, "delete-event", "GtkCB_ev",
  function(w, ev, p)
    g.gtk_main_quit()
    return true
  end)

-- Create the button and add it to the window.
local but = g.gtk_button_new_with_label("Click me!");
g.gtk_container_add(win, but)

local count = 0
g_signal_connect(but, "clicked", "GtkCB_w",
  function(w, p)
    count = count + 1
    if count == 1 then
      g.gtk_button_set_label(w, "Good boy!")
    elseif count == 2 then
      g.gtk_button_set_label(w, "Once more!")
    else
      g.gtk_main_quit()
    end
  end)

-- Timer example.
local sec = 0
g.g_timeout_add(1000, function(p)
  sec = sec + 1
  print(sec, "seconds since start")
  return true
end, nil)

-- Ready to go.
g.gtk_widget_show_all(win)
g.gtk_main()