File chooser dialogs with FFI

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
User avatar
airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

File chooser dialogs with FFI

Post by airstruck »

I threw together a quick GTK3 file chooser dialog example. I'm wondering if anyone is interested in contributing Windows and Mac examples so we can have a nice simple cross-platform native file chooser dialog library. Please let me know if you're on Windows or Mac and want to help with this. I'm especially interested in figuring out what common functionality is available across platforms (for example, does everything have filters, name-able buttons, stock names/icons for buttons, a choice of whether to allow the user create new directories, etc).

Code: Select all

local ffi = require 'ffi'
local gtk = ffi.load 'gtk-3'
ffi.cdef [[

typedef void GtkDialog;
typedef void GtkWidget;
typedef void GtkWindow;
typedef void GtkFileChooser;

typedef int gint;
typedef char gchar;
typedef bool gboolean;

typedef enum
{
  GTK_FILE_CHOOSER_ACTION_OPEN,
  GTK_FILE_CHOOSER_ACTION_SAVE,
  GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
  GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
} GtkFileChooserAction;

typedef enum
{
  GTK_RESPONSE_NONE         = -1,
  GTK_RESPONSE_REJECT       = -2,
  GTK_RESPONSE_ACCEPT       = -3,
  GTK_RESPONSE_DELETE_EVENT = -4,
  GTK_RESPONSE_OK           = -5,
  GTK_RESPONSE_CANCEL       = -6,
  GTK_RESPONSE_CLOSE        = -7,
  GTK_RESPONSE_YES          = -8,
  GTK_RESPONSE_NO           = -9,
  GTK_RESPONSE_APPLY        = -10,
  GTK_RESPONSE_HELP         = -11
} GtkResponseType;

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

gboolean gtk_events_pending (
    void
);

gboolean gtk_main_iteration (
    void
);

GtkWidget * gtk_file_chooser_dialog_new (
    const gchar *title,
    GtkWindow *parent,
    GtkFileChooserAction action,
    const gchar *first_button_text,
    ...
);

gint gtk_dialog_run (
    GtkDialog *dialog
);

void gtk_widget_destroy (
    GtkWidget *widget
);

gchar * gtk_file_chooser_get_filename (
    GtkFileChooser *chooser
);

]]

local function show (action, button, title)
    gtk.gtk_init(nil, nil)

    local d = gtk.gtk_file_chooser_dialog_new(
        title,
        nil,
        action,
        button, ffi.cast('const gchar *', gtk.GTK_RESPONSE_OK),
        '_Cancel', ffi.cast('const gchar *', gtk.GTK_RESPONSE_CANCEL),
        nil)
        
    local response = gtk.gtk_dialog_run(d)
    local filename = gtk.gtk_file_chooser_get_filename(d)

    gtk.gtk_widget_destroy(d)

    while gtk.gtk_events_pending() do
        gtk.gtk_main_iteration()
    end
    
    if response == gtk.GTK_RESPONSE_OK then
        return filename ~= nil and ffi.string(filename) or nil
    end
end

local function save (title)
    return show(gtk.GTK_FILE_CHOOSER_ACTION_SAVE,
        '_Save', title or 'Save As')
end

local function open (title)
    return show(gtk.GTK_FILE_CHOOSER_ACTION_OPEN,
        '_Open', title or 'Open')
end

return {
    save = save,
    open = open,
}
User avatar
pgimeno
Party member
Posts: 3656
Joined: Sun Oct 18, 2015 2:58 pm

Re: File chooser dialogs with FFI

Post by pgimeno »

Sweet! I can't help with other platforms, sorry, but I've got a question. Currently, the .open() call is blocking; would it be possible to make it non-blocking? Perhaps by using coroutines and callbacks?

I have two other issues. One is that it spits in the terminal: "Gtk-Message: GtkDialog mapped without a transient parent. This is discouraged". The other is that the button borders don't look right (the bottom border is missing, like an off-by-one calculating the height). The latter is most likely on my side, but it'd be nice if you can confirm. Snapshot:

Image
User avatar
airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

Re: File chooser dialogs with FFI

Post by airstruck »

pgimeno wrote:would it be possible to make it non-blocking?
Maybe with Love's threads, not with coroutines as far as I know. Not sure if being dependent on Love's threads is worth it (of course if any Win or Mac users decide to help, it would be a group decision). The blocking doesn't bother me personally, it's more straightforward to use and it seems like you'd usually want these things to be modal in some sense anyway.
Gtk-Message: GtkDialog mapped without a transient parent. This is discouraged
Yeah, it wants the handle to the "parent" window as the second argument to gtk_file_chooser_dialog_new. I think it wants to do things like raise the dialog on top when you raise the parent window, for example. You can probably get that handle through SDL somehow, but it works alright without it (aside from that warning).
button borders don't look right
Hmm, they just look normal on mine. What do GTK file chooser dialogs normally look like on yours? Are you running gnome? If not, does it look better if you run gnome settings daemon?
User avatar
SiENcE
Party member
Posts: 805
Joined: Thu Jul 24, 2008 2:25 pm
Location: Berlin/Germany
Contact:

Re: File chooser dialogs with FFI

Post by SiENcE »

Here you go:

Windows OpenFile Dialogue - (tested on Windows 7 64bit)

Code: Select all

ffi = require"ffi"
bit = require"bit"

collectgarbage("stop")

ffi.cdef[[
  static const int OFN_FILEMUSTEXIST             = 0x1000;
  static const int OFN_NOCHANGEDIR               = 8;
  static const int OFN_PATHMUSTEXIST             = 0x800;

  typedef bool BOOL;
  typedef char CHAR;

  typedef unsigned short WORD; 
  typedef unsigned long DWORD;

  typedef void *PVOID;
  typedef void *LPVOID;
  typedef void *LPOFNHOOKPROC;

  typedef unsigned long HANDLE;
  typedef HANDLE HWND;
  typedef HANDLE HINSTANCE;

  typedef const char *LPCSTR;
  typedef const char *LPCTSTR;

  typedef char *LPSTR;
  typedef char *LPTSTR;

  typedef unsigned long LPARAM;

  typedef struct {
    DWORD         lStructSize;
    HWND          hwndOwner;
    HINSTANCE     hInstance;
    LPCTSTR       lpstrFilter;
    LPTSTR        lpstrCustomFilter;
    DWORD         nMaxCustFilter;
    DWORD         nFilterIndex;
    LPTSTR        lpstrFile;
    DWORD         nMaxFile;
    LPTSTR        lpstrFileTitle;
    DWORD         nMaxFileTitle;
    LPCTSTR       lpstrInitialDir;
    LPCTSTR       lpstrTitle;
    DWORD         flags;
    WORD          nFileOffset;
    WORD          nFileExtension;
    LPCTSTR       lpstrDefExt;
    LPARAM        lCustData;
    LPOFNHOOKPROC lpfnHook;
    LPCTSTR       lpTemplateName;

    LPVOID        pvReserved;
    DWORD         dwReserved;
    DWORD         flagsEx;

  }OPENFILENAME;

  BOOL GetSaveFileNameA( OPENFILENAME lpofn );
  BOOL GetOpenFileNameA( OPENFILENAME *lpofn );
]]
com=ffi.load("comdlg32")

ffi.cdef[[
  DWORD GetLastError(void);
]]
krnl=ffi.load("kernel32")

function OpenDialog()
  Ofn=ffi.new("OPENFILENAME")
  ffi.fill(Ofn,ffi.sizeof(Ofn)) --zero fill the structure

  local szFile        = ffi.new("char[260]","\0")
  local hwnd          = ffi.new("HWND",0)

  Ofn.lStructSize     = ffi.sizeof(Ofn)
  Ofn.hwndOwner       = hwnd

  Ofn.lpstrFile       = szFile
  Ofn.nMaxFile        = ffi.sizeof(szFile)

  Ofn.lpstrFilter     = "All\0*.*\0Text\0*.TXT\0"
  Ofn.nFilterIndex    = 1

  Ofn.lpstrFileTitle  = nil
  Ofn.nMaxFileTitle   = 0

  Ofn.lpstrInitialDir = nil
  Ofn.flags           = bit.bor(com.OFN_PATHMUSTEXIST, com.OFN_FILEMUSTEXIST, com.OFN_NOCHANGEDIR)

  print("displaying...")

  if com.GetOpenFileNameA(Ofn) then --luajit converts bool automatically
    print("file->",ffi.string(Ofn.lpstrFile, Ofn.nMaxFile))
  end

  print("lasterror->",krnl.GetLastError())
end

function love.keypressed(key, code)
	if key == 'o' then
		OpenDialog()
	end
end
User avatar
airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

Re: File chooser dialogs with FFI

Post by airstruck »

Man, that was quick. I'll boot up windows and try it out in a little bit, thanks! Just need a Mac example now...
alloyed
Citizen
Posts: 80
Joined: Thu May 28, 2015 8:45 pm
Contact:

Re: File chooser dialogs with FFI

Post by alloyed »

So this is a straight objective-C example:
https://github.com/Alloyed/nativefiledi ... coa.m#L120
I don't own a mac so I can't work on this myself but actually getting working FFI code would probably borrow liberally from this:
https://github.com/luapower/objc

EDIT: And since I already have a C api version of the same thing, I decided to write it up for reference:
https://gist.github.com/Alloyed/f8692f6 ... d8ab742995

You can probably do better that this, because it's a quick wrapper over a random lib I found, but it's a good starting point at least. The API itself blocks for as long as the user takes to choose, so I normally run each command on an async worker and that works fine with a minimum of code.
User avatar
pgimeno
Party member
Posts: 3656
Joined: Sun Oct 18, 2015 2:58 pm

Re: File chooser dialogs with FFI

Post by pgimeno »

airstruck wrote:
pgimeno wrote:would it be possible to make it non-blocking?
Maybe with Love's threads, not with coroutines as far as I know.
I still think that the coroutine idea has some merit. I'll see if I can work out a complete example.

EDIT: Alas, nope. I get "main.lua:31: attempt to yield across C-call boundary" when attempting to yield from a signal handler. Well, was worth a try.
airstruck wrote:
button borders don't look right
Hmm, they just look normal on mine. What do GTK file chooser dialogs normally look like on yours?
I think this is the first GTK3 file dialog I see, so I can't tell :)

GTK2 dialogs show fine.
User avatar
airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

Re: File chooser dialogs with FFI

Post by airstruck »

pgimeno wrote:I still think that the coroutine idea has some merit. I'll see if I can work out a complete example.

EDIT: Alas, nope. I get "main.lua:31: attempt to yield across C-call boundary" when attempting to yield from a signal handler. Well, was worth a try.
Definitely worth a try. Dare we ask for coco?
I think this is the first GTK3 file dialog I see, so I can't tell :).
Want to make a GTK2 dialog? ;)
User avatar
pgimeno
Party member
Posts: 3656
Joined: Sun Oct 18, 2015 2:58 pm

Re: File chooser dialogs with FFI

Post by pgimeno »

airstruck wrote:Want to make a GTK2 dialog? ;)
GTK2 dialogs look normal. Most definitely a problem on my side. Thanks!
User avatar
airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

Re: File chooser dialogs with FFI

Post by airstruck »

I might look at doing a GTK2 backend as well, I don't think the API is much different from GTK3. I wonder if there's a good way to predict whether users will be expecting GTK3 dialogs or GTK2 on a given system. I hope nobody still uses KDE...
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Bing [Bot] and 9 guests