OK, I seem to have fumbled something into a working state. At least the
posted messages get properly acted upon in IupLua. Honing and testing is
still badly needed, but I guess I can carry on from here. Thanks for
your help, very much appreciated!

I did something along the lines of the following C++ code snippet:

struct Ihandle;    // opaque IUP handle type

lua["setIupCallback"] =
    [](SessionCache &cache, sol::object obj, sol::this_state L) {
        Ihandle *ih = *(Ihandle **)lua_touserdata(L, 2);
        if (ih) {
            cache.setCallback([ih](std::string s, bool flag) {
                IupPostMessage(ih, s.c_str(), add, 0, nullptr);
            });
        }
    };

For those unfamiliar with C++ and Sol, what I'm doing above is to
register a C++ lambda function with the Lua interpreter as the global
function named "setIupCallback".

From Lua, the function gets called with two parameters, a reference to
a SessionCache object (which is the object that causes the callback to
be called in response to some internal event), and a second parameter
that is the IUP handle of the GUI element whose postmessage_cb shall be
called. I currently handle the second parameter with a call to plain C
function lua_touserdata, but I'm sure there's a proper Sol2 way to do
that which I still need to figure out.

The SessionCache has a member function setCallback to install the
callback. The callback itself is another lambda function that receives a
string and a bool, and calls PostMessage with those two parameters. The
double and the user pointer aren't needed in my case.

From Lua, you do this to install the callback:

setIupCallback(cache, guiElem)

assuming that the variable cache holds a reference to a SessionCache
object, and guiElem is the IupLua handle for the GUI element that is
going to receive calls to its postmessage_cb function.

Proper resource management and error handling is left as an exercise for
the reader. :-)

Cheers
Stefan


Am 27/05/2022 um 06:17 schrieb Eric Wing:
Sometimes events happen in C++-Land that require an update of a GUI
element. I figured that the most sensible way of doing this is to call
IupPostMessage from C++ territory, in whatever thread context this
happens to be, and thus cause a Lua callback to be called in the context
of the GUI main loop the next time it comes around.


Yes, this is the quintessential example case of why IupPostMessage
exists. My personal most frequent use cases for this have been:
- Downloading data over the network on a background thread and then
needing to update the UI when finished.
- Audio playback event callbacks on background threads (and needing to
update UI)
- Computation that takes a long time and I don't want to block the
main UI thread, so I thread it but need to update the GUI state when
it is finished.


I don't know Sol2, but breaking down your situation into the most
basic building blocks, I would just look at this as a classic C/C++
callback pattern. Since C is mostly valid C++, let's just simplify
this further.
Warning: This is typed in email and untested, so there may be bugs:


struct MyAppData
{
        lua_State* luaState;
        Ihandle* someWidget;
        char* callbackString;

};

// This is some hypothetical function that gets invoked when something
happened.
// It is expected to happen on a background thread.
// Presumably, this function will pass you back some information
associated with this event.
// For simplicity, I pretend char* some_string is that data.
// In classic callback function patterns, you also get passed a handle
to some data which you setup earlier, so you can access your
application state without globals.
void SomeCppFunctionOnABackgroundThread(char* some_string, void* user_data)
{
        struct MyAppData* app_data = (struct MyAppData*)user_data;
        app_data->callbackString = some_string; // Just for show. Maybe we
need to make a deep copy depending on how your APIs work.
        IupPostMessage(app_data->someWidget, NULL, 0, 0.0, app_data);
}


// This is the main thread callback for IupPostMessage
void MyCppIupPostMessageMainThreadCallback(Ihandle* ih, char* s, int
i, double d, void* p)
{
        struct MyAppData* app_data = (struct MyAppData*)p;

        // Invoke Lua to do something.

        lua_getglobal(app_data->luaState, "MyLuaIupPostMessageCallback");
        lua_pushstring(lua_state, app_data->callbackString);
        // You could push other arguments to Lua here too, but for this
example, I will just push the string data.
        /* do the call (1 arguments, 0 results) */
        if(lua_pcall(lua_state, 0, 5, 0) != 0)
        {
                printf("Error running function: %s", lua_tostring(lua_state, 
-1));
                lua_pop(L, 1);
        }
}

/*
In Lua somewhere, you could have a global function called
MyLuaIupPostMessageCallback

function MyLuaIupPostMessageCallback(string_data)
        print("string_data", string_data)
        -- Maybe you have global data to access your Iup widgets.
        -- Or maybe you can use IupGetHandle to get the handles by string name.

end

*/


Finally, considering how to integrate Sol2, I would envision that you
have some state for Sol2 that you put into your user_data pointer.
So instead of manually invoking a Lua callback in
MyCppIupPostMessageMainThreadCallback, pull out your Sol2 data
structures from the user_data pointer, and then use Sol2 to invoke the
Lua callback.

-Eric


_______________________________________________
Iup-users mailing list
Iup-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/iup-users

Reply via email to