[v6.0] Action handler issues

Hi!
So I'm using LVGL with C++.  I have a C++ class which has non-static button callback handlers which need to be called as the handlers need to use the non-static instance members to perform their function.  There are multiple instances of class so that the GUI elements can be repeated.
In order to call the correct NON-STATIC callback handler from LVGL when a button is pressed a static lv_action_t method is called, here's an example.
lv_res_t UI_Thing::btn_click_action(lv_obj_t * btn)
{
    UI_Thing *instance = (UI_Thing *)lv_obj_get_free_ptr(btn);
    return instance->onAction_btnClick(btn);
}

lv_res_t UI_Thing::onAction_btnClick(lv_obj_t * btn)
{
    someInstanceValue++; // do something with the instance member variables...
    return LV_RES_OK;
}

void UI_Thing::createElements(void)
{
    lv_btn_set_action(instanceButton, LV_BTN_ACTION_CLICK, UI_Thing::btn_click_action);
    lv_obj_set_free_ptr(instanceButton, this);
}

class UI_Thing
{
public:
    void createElements(void);

private:
    static lv_res_t btn_click_action(lv_obj_t * btn);

    lv_res_t onAction_btnClick(lv_obj_t * btn);

    int someInstanceValue;
}

This seems fairly obvious stuff up to this point of course and works fine for one button with one action.
The problem, which you probably guessed by now, are:
a) when you want to add another button you have to duplicate both the instance method and static method for each button you want to click.
b) when you want to handle different events from a button (like LONG PRESS and CLICK) you again have to create two methods for each event.
The solution appears to be something like:

use lv_set_free_num() using a different ID for each button.
creating one static handler for each type of event that can occur, that function then lookup an instance method to call based on the lv_object and the free_num obtained from the object.

However, this still has too much duplication for my liking, since you have to create multiple static handlers.  What I want is a solution where there is a single static event handler which is used by all buttons, however this isn't possible as in such a static event handler there is no way to determine what the event is, so I can't say, iterate over a static map of "event + button_id" to "C++ method".
e.g.
EventMappingEntry eventMappings[] = {
    { action: LV_BTN_ACTION_CLICK, button: BUTTON_OK_ID, handler : &UI_Thing::onAction_btnOK_click }
    { action: LV_BTN_ACTION_PR, button: BUTTON_OK_ID, handler : &UI_Thing::onAction_btnOK_pressed }
    { action: LV_BTN_ACTION_REL, button: BUTTON_OK_ID, handler : &UI_Thing::onAction_btnOK_released }
    { action: LV_BTN_ACTION_CLICK, button: BUTTON_CANCEL_ID, handler : &UI_Thing::onAction_btnCancel_click }
    { action: LV_BTN_ACTION_PR, button: BUTTON_CANCEL_ID, handler : &UI_Thing::onAction_btnCancel_pressed }
    { action: LV_BTN_ACTION_REL, button: BUTTON_CANCEL_ID, handler : &UI_Thing::onAction_btnCancel_released }
};

...
    lv_btn_set_action(instanceButton1, LV_BTN_ACTION_CLICK, UI_Thing::handleEvent);
    lv_btn_set_action(instanceButton1, LV_BTN_ACTION_PR, UI_Thing::handleEvent);
    lv_btn_set_action(instanceButton1, LV_BTN_ACTION_REL, UI_Thing::handleEvent);
    lv_obj_set_free_num(instanceButton1, BUTTON_OK_ID);
    lv_obj_set_free_ptr(instanceButton1, this);
    lv_btn_set_action(instanceButton2, LV_BTN_ACTION_CLICK, UI_Thing::handleEvent);
    lv_btn_set_action(instanceButton2, LV_BTN_ACTION_PR, UI_Thing::handleEvent);
    lv_btn_set_action(instanceButton2, LV_BTN_ACTION_REL, UI_Thing::handleEvent);
    lv_obj_set_free_num(instanceButton2, BUTTON_CANCEL_ID);
    lv_obj_set_free_ptr(instanceButton2, this);
...

Also, the 'set_action' calls can be determined by iterating over the eventMappings table, to further reduce code duplication.
In order to do this, the static event handler needs to be given the action in addtion to the lv_obj_t * reference it's already given.
i.e.  something like this:
typedef lv_res_t (*lv_action_t) (struct _lv_obj_t * obj, int action);

and change use action calls in the code in methods like lv_btn_signal() like this:
...
res = ext->actions[LV_BTN_ACTION_PR](btn, LV_BTN_ACTION_PR);
...

Another alternative, is for the action handler to ask the object for the last action, but that requires additional state to be continually held which I don't like.
Thought? Better suggestions?

Hi hydra,
onepossible sulution, because you are using C++, create you own object (classes) for ex. for button (BaseButton, ChildFromBaseButton1 -> override any handlers you need, ChildFromBaseButton2 -> override any handlers you need) ... Then In "class UI_Thing" create you elements with relevant classes. So the solution is in case of C++ use inheritance.

Hi @BesitzeRuf, thanks for your reply.
I don't want to go down the path of creating a wrapper for every type of LV object (yet, if it all).
I think that regardless of any inheritance model, the Button class still needs a way of working out what the Button instance is from the lv_object and still has no way of determining the action without a single handler for each type of action.
I simply want each of my UI elements (e.g. window with 3 buttons), to extend from a base class which can call instance methods.
I'm after something like this:
class UI_EventHandler {
  static lv_res_t handleEvent(lv_obj_t *btn) {
    // get UI_EventHandler instance from button.
    instance->onEvent(btn);
  }
  lv_res_t onEvent(lv_obj_t *btn, int action) {
    // get button id from btn.
    // use mapping table provided by derived class, find appropriate event mapping entry, call instance event handler for btn.
}

class UI_Clock : public UI_EventHandler {
  lv_res_t onAction_time_clicked(lv_obj *) { ... }
  lv_res_t onAction_configure_clicked(lv_obj *) { ... }

  void createElements() {
    // create buttons, set ids, set callback handler for each button to be UI_EventHandler::handleEvent
  }
  static vector<EventMappingEntry> eventMappings = {
    { action: LV_BTN_ACTION_CLICK, id: LABEL_TIME_ID, handler : &onAction_time_clicked},
    { action: LV_BTN_ACTION_CLICK, id: BUTTON_CONFIGURE_ID, handler : &onAction_configure_clicked}
  };
}

The idea is that all I want to define in the UI_Clock class is a static list of events, and their handlers.

As a quick follow up, this is what I currently have to do.
ui_event_handler.h
#pragma once

extern "C" {
    #include "lvgl.h"
}

namespace UI {

class UI_EventHandler;

typedef lv_res_t (UI_EventHandler::*actionHandler)(lv_obj_t *btn);

enum UI_EventID_e {
    UI_EVENT_ON_BUTTON_CLICK,
    UI_EVENT_ON_BUTTON_PRESSED,
    UI_EVENT_ON_BUTTON_RELEASED
};

class EventMappingEntry {
public:
    LV_OBJ_FREE_NUM_TYPE elementId;
    UI_EventID_e eventId;
    actionHandler handler;
};

class UI_EventHandler {
public:

    UI_EventHandler(vector<EventMappingEntry> *_eventMappings) :
        eventMappings(_eventMappings)
    {}

    static lv_res_t onButtonClick(lv_obj_t *btn) {
        UI_EventHandler *instance = (UI_EventHandler *)lv_obj_get_free_ptr(btn);
        return instance->onAction(btn, UI_EVENT_ON_BUTTON_CLICK);
    }

    static lv_res_t onButtonPressed(lv_obj_t *btn) {
        UI_EventHandler *instance = (UI_EventHandler *)lv_obj_get_free_ptr(btn);
        return instance->onAction(btn, UI_EVENT_ON_BUTTON_PRESSED);
    }

    static lv_res_t onButtonPressed(lv_obj_t *btn) {
        UI_EventHandler *instance = (UI_EventHandler *)lv_obj_get_free_ptr(btn);
        return instance->onAction(btn, UI_EVENT_ON_BUTTON_RELEASED);
    }

    lv_res_t onAction(lv_obj_t *btn, UI_EventID_e eventId) {

        LV_OBJ_FREE_NUM_TYPE elementId = lv_obj_get_free_num(btn);

        for (vector<EventMappingEntry>::iterator i = eventMappings->begin(); i != eventMappings->end(); ++i) {
            EventMappingEntry entry = *i;

            if (entry.eventId == eventId && entry.elementId == elementId) {
                return (this->*entry.handler)(btn);
            }
        }
        return LV_RES_OK;
    }

private:
    vector<EventMappingEntry> *eventMappings;
};

} // namespace

example eventMappings and constructor
enum clockConfigureElementId_e {
    UI_CLOCK_CONFIGURE_OK
};

vector<EventMappingEntry> UI_ClockConfigure::uiEventMappings = {
    { {elementId: UI_CLOCK_CONFIGURE_OK, eventId: UI_EVENT_ON_BUTTON_CLICK, handler: (UI::actionHandler)&UI_ClockConfigure::onAction_btnOK_clicked} }
};

UI_ClockConfigure::UI_ClockConfigure() :
    UI_EventHandler(&uiEventMappings)
{
}

example create elements
    lv_obj_t * win = lv_win_create(lv_scr_act(), NULL);
    lv_win_set_title(win, "Set Clock");

    lv_obj_t *btnOK = lv_win_add_btn(win, SYMBOL_OK, UI_ClockConfigure::onButtonClick); // <-- USING STATIC CALLBACK HANDLER IN UI_EventHandler HERE.
    lv_obj_set_free_num(btnOK, UI_CLOCK_CONFIGURE_OK);
    lv_obj_set_free_ptr(btnOK, this);

    lv_win_add_btn(win, SYMBOL_CLOSE, lv_win_close_action);

example handler
lv_res_t UI_ClockConfigure::onAction_btnOK_clicked(lv_obj_t *btn)
{
    // ... get the new time from other member elements
    // ... set the hardware clock

    lv_obj_t * win = lv_win_get_from_btn(btn);
    lv_obj_del(win);

    return LV_RES_INV;
}

Hiya,
As mentioned, I don't want a solution that allows the event handler callback function to ask lvgl for the 'current action type' / lv_btn_get_current_action_type because it means that additional state is held internally in lvgl.  a solution where the data is passed at the time it is needed is better.
using lv_btn_action_t would only be ok in a handler that is only for buttons.  For a generic handler if other controls create call actions then the action value passed must be different from other action values.  i.e. you can't have two action identifiers with the same value unless the action handler code also looks up the object type which would then also have be to kept in the event mappings table.
a more general event handler would be something like:
    static lv_res_t onAction(lv_obj_t *btn, lv_action_id actionId) {
        UI_EventHandler *instance = (UI_EventHandler *)lv_obj_get_free_ptr(btn);
        return instance->onAction(btn, id);
    }

    lv_res_t onAction(lv_obj_t *btn, lv_action_id actionId) {

        LV_OBJ_FREE_NUM_TYPE elementId = lv_obj_get_free_num(btn);

        for (vector<EventMappingEntry>::iterator i = eventMappings->begin(); i != eventMappings->end(); ++i) {
            EventMappingEntry entry = *i;

            if (entry.actionId == actionId && entry.elementId == elementId) {
                return (this->*entry.handler)(btn);
            }
        }
        return LV_RES_OK;
    }

and onButtonClick/onButtonPressed/onButtonReleased would be deleted.

again, i prefer a solution that doesnt require additional state to be held in memory, i.e. for the value that the proposed lv_indev_get_current_event returns.
it will probably be ok if you really dont want to pass the action as a second parameter with the object, however passing the action to the handler while you already know the state and don't have to store anything seems the most logic time and place to do it to me.

In regard to memory usage, you are right. For example, button need to store only one function pointer for all action (instead of 3). Its only drawback is if you want to change only the CLICK action you need to change the whole action. But you can modify the CLICK's behaviour with values of variables so really it's not a big deal.
So I'm convinced. :)
However, as it's an API change it can be only in v6.0. :(

buttons-only isnt a good solution. at the.moment.my example shows buttons, but anywhere a callback to user-code can happen some action reference is required to reduce the code duplication in user-code.
i wouldnt recommend using a define to switch api modes.  the less conditional logic, compile-time or run-time the better.

If you can wait for v6.0 then, yes it would be better to avoid the "dual-API".

@kisvegabor I'm interested in implementing this feature. What is the final API and behavior supposed to be?

@embeddedt Great!
My idea was to add an enum to lv_obj.h:
typedef enum
{
  LV_ACTION_CLICK,
  LV_ACTION_PRESS,
...
} lv_action_type_t;

We should search the general action type, not use only the current button action. For example LV_ACTION_CHANGED can be used by a Text area.
It's only my current idea. We can/should develop/change it as needed.

@kisvegabor That sounds like a reasonable implementation. My only concern is that it might be hard to hide that behind a #define.

I assume then that a new API named lv_obj_set_action would be created?

Hm, it seems like an interesting idea to make the actions to a basic feature. Now buttons handle the calling of basic actions and it's not trivial in all cases. For example Click doesn't have to be called after dragging. What do you think, shall we move these features to lv_obj?
Anyway, I don't see any obvious obstacle to use one generic action function.
To hide it with a define we should turn off all action related API functions and structure members. And use obj->action instead. Maybe it will look messy ang ugly but it need to be tried.

@kisvegabor I think using a generic action function will solve the problems of @hydra and @amirgon. Let's do it this way.
The only question is how do we handle actions that only apply to specific objects? I think we could create a generic LV_ACTION_CHANGED and then leave it to the user to retrieve the change (i.e. for lv_slider the user would have to call lv_slider_get_value).

The only question is how do we handle actions that only apply to specific objects? I think we could create a generic LV_ACTION_CHANGED and then leave it to the user to retrieve the change (i.e. for lv_slider the user would have to call lv_slider_get_value).

@embeddedt
I agree it should be the user's responsibility to know to which object type was the action assigned. It will be still possible to use a generic button action as @amirgon said.

you need 2 user-defined variables for c++, one for the object reference (free_ptr), and one for the action reference.

@kisvegabor

What about my earlier suggestion? You can use free_ptr like this:
static void * ctx[8];
ctx[0] = &x;
ctx[1] = &y;
...
lv_obj_set_free_ptr(obj, ctx);

This is possible, but it requires the user to know exactly how many callbacks are associated with each object, maintain ctx and refer to the correct ctx element in each of his callbacks. It feels like a technical detail we could spare the user.
Also, I think this makes the API less clear (I need to know about lv_obj_set_free_ptr to implement callbacks, this is not immediately obvious).
I see you point about memory usage.
How about solving this by letting the user decide if he wants to pay the extra bytes or not? Just add another macro to lv_conf.h to enable/disable the extra user_data argument. On platforms that have enough RAM (such as ESP32), it can be enabled by default.
I prefer using user_data for Micropython binding support (so user_data would need to be enabled for Micropython, but platforms that support Micropython have enough RAM anyway).

@kisvegabor One more thing, if you worry about RAM.
Even today you need to keep a function pointer for each callback, even if its not used later on. Why not allocate them dynamically (both the function pointer and user_data) when allocating RAM for the object?
The only disadvantage I see is that the user would need to decide which callbacks he would need in advance, when creating the object (for example by a bitmask, you can know how many and which ones are needed).
On the lv_obj struct you can have a flexible array member with entry for each callback that the user actually wants to use.

@kisvegabor @amirgon

Why not allocate them dynamically (both the function pointer and user_data) when allocating RAM for the object?
The only disadvantage I see is that the user would need to decide which callbacks he would need in advance, when creating the object (for example by a bitmask, you can know how many and which ones are needed).
On the lv_obj struct you can have a flexible array member with entry for each callback that the user actually wants to use.

I think that will add too much complexity to the object-specific code. Since we don't know which callbacks the user is going to choose, there will have to be an internal function to determine the index of the callback function. This adds too much indirection and is not worth the 32 bytes saved in my opinion.

@amirgon You are right, now lv_btn has a lv_action_t actions[LV_BTN_ACTION_NUM]; element which is 4 * 4 bytes. However, the new proposal to use only one action and pass the action type will reduce it to 1 * 4 bytes. So it seems to be a good improvement.
In addition, with dynamic memory allocation:

we need a pointer to store the allocated array of functions -> 4 bytes
if you allocate a memory lv_mem it uses 4 extra bytes to store metadata
all the allocated actions need 4 bytes

So the equation is: 8 + (N * 4). So we need 12 bytes for 1 action.
Based on @amirgon's suggestion to make it an option to save the context for actions I suggest having a function like:  lv_obj_set_action_ctx(obj, ACTION_TYPE, &ctx); and its pair: lv_obj_get_action_ctx(obj, ACTION_TYPE);. This way the API can remain the same independently from the settings in lv_conf.h.
What do you think?

we need a pointer to store the allocated array of functions -> 4 bytes

No you don't. That's the whole idea of flexible array member. You allocate it as part of your struct in the same malloc you use to allocate memory for your struct

if you allocate a memory lv_mem it uses 4 extra bytes to store metadata

No extra allocation.

all the allocated actions need 4 bytes

So the equation is: 8 + (N * 4). So we need 12 bytes for 1 action.

No. All you need is a few bytes for the mask. Apart from this you only need memory for the function pointers themselves, according to the number of enabled callbacks.
@kisvegabor I think you didn't understand my suggestion. Please let me know if you want me to clarify it further.

@amirgon Sorry, I really misunderstood your concept. Now it's clear. However, besides what  @embeddedt said ("You can only have one flexible array member per struct"), I'm not sure flexible arrays are really supported by every embedded compiler (even if it's a C99 standard).
Let's summarize what we have so far:

Using 1 action function with an  ACTION_TYPE parameter would be a flexible and memory effective improvement. Do you agree?
We should store contexts (pointers) to every ACTION_TYPE, but ideally:

this feature needs to be optional to save memory
can't be added to every object because it'd consume RAM even is the object not uses actions
using free_ptr with a void * ctx[]  is not intuitive but possible

@amirgon said

I prefer using user_data for Micropython binding support (so user_data would need to be enabled for Micropython, but platforms that support Micropython have enough RAM anyway).

According to it, I think the compromise could be to add an optional void * ctx[LV_ACTION_TYPE_NUM] to lv_obj_t. As you said the whole context thing becomes important on complex systems where the user works with C++ or Micropython.
So the signatures could be:
void lv_obj_set_action(obj, action);               //If CTX feature is disabled
void lv_obj_set_action_ctx(obj, action, &ctx);     //If CTX feautre is enabled

I'd prefer 2 function prototypes for simplicity if the context feature is disabled. The ctx paramter might be confusing for the user who don't want to deal with it.
What do you think?

@kisvegabor

I'm not sure flexible arrays are really supported by every embedded compiler (even if it's a C99 standard).

Actually, before C99 you would write arr[0] instead of arr[] to achieve the same result. Flexible arrays are more common than you think. This would definitely be supported by all C compilers.

Using 1 action function with an  ACTION_TYPE parameter would be a flexible and memory effective improvement. Do you agree?

Yes.

We should store contexts (pointers) to every ACTION_TYPE, but ideally:

this feature needs to be optional to save memory
can't be added to every object because it'd consume RAM even is the object not uses actions

Actually no. With my suggestion an object instance that uses no actions will consume no memory at all except from the bitmask which could be as small as one byte (if 8 action types is enough).
Actually, you won't need to keep even the function pointer you plan to keep if no actions are used, so my suggestion requires less RAM, even if you use a single action function with an  ACTION_TYPE parameter per object and even if you don't enable the ctx option (1 byte for mask vs. 4 bytes for function pointer that is not used, when the action is not used).

According to it, I think the compromise could be to add an optional void * ctx[LV_ACTION_TYPE_NUM] to lv_obj_t

That would be good enough.

I'd prefer 2 function prototypes for simplicity if the context feature is disabled. The ctx paramter might be confusing for the user who don't want to deal with it.

Ok.
You might want to rename it user_data to make it more familiar for the user. I've seen it more often named user_data in different libraries that use callbacks.

@amirgon

Actually, before C99 you would write arr[0] instead of arr[] to achieve the same result. Flexible arrays are more common than you think. This would definitely be supported by all C compilers.

and

Actually, you won't need to keep even the function pointer you plan to keep if no actions are used,

They sound really good. I begin to be convinced about the flexible arrays. Finally a void * action_and_ctx[1] can be used too is [0] is not working.
I suppose the bits of the mask can be set in lv_obj_set_action_ctx on demand. My only concern is if the mask changes in runtime than a lot of reallocation of lv_obj_t and realign of ctx pointers would be required. Of course, it can be implemented but it's not so elegant.
A cleaner solution could be to have only 2 flags:

action_allocated
ctxs_allocated

It could also work with flexible arrays. On lv_obj_set_action lv_obj_t is reallocated to hold an action pointer and action_allocated is set. On lv_obj_set_action_ctx action_allocated and ctxs_allocated are set and lv_obj_t is reallocated to hold an action and all the contexts. It's not so memory effective as your solution but:

still very memory effective
ACTION_CTX_ENABLE define is not required, because it is represented only by 1 bit
simple an clean

To have a common action prototype we can use:
lv_res_t my_action(lv_obj_t * obj, lv_action_type_t type)
{
   void * ctx = lv_obj_get_action_ctx(obj, type);

   ...

   return LV_RES_OK;
}

And, yes user_data is probably more descriptive.
What do you think? @embeddedt I think the proposed solution is less hacky. We can use flexible arrays for one thing (because we can have only one of them). Why not use it for actions?

@kisvegabor

I suppose the bits of the mask can be set in lv_obj_set_action_ctx on demand. My only concern is if the mask changes in runtime than a lot of reallocation of lv_obj_t and realign of ctx pointers would be required. Of course, it can be implemented but it's not so elegant.

I agree reallocations are not so elegant. Originally I though the user would need to specify the action bit mask when calling the create function of an object (another argument to create), so no reallocation would be needed.

It could also work with flexible arrays. On lv_obj_set_action lv_obj_t is reallocated to hold an action pointer and action_allocated is set. On lv_obj_set_action_ctx action_allocated and ctxs_allocated are set and lv_obj_t is reallocated to hold an action and all the contexts.

That would work. But If someone uses ctx on a single action it would cost him RAM to allocate all actions instead of only the action he is interested in.
I'm not against you suggestion, it sounds good. But If you go this far, why not allocate the right number of  ctx, and hold a bitmap instead of a bit?
Do you assume that any platform that would make use of ctx would have enough RAM to spare? Or that the use of ctx would be relatively rare?

To have a common action prototype we can use:
lv_res_t my_action(lv_obj_t * obj, lv_action_type_t type)
{
   void * ctx = lv_obj_get_action_ctx(obj, type);

But why not also pass ctx parameter to my_action? It could be NULL if ctx is not used, and can be ignored by the user if he is not interested in it.
Why ask the user to call lv_obj_get_action_ctx every time in each of his callbacks? (assuming the user is taking advantage of ctx in his callbacks)

@kisvegabor I've done some rethinking about the whole thing.
So you want to have one callback per object, and the callback would have an action_type parameter to specify what was the event that happened, right?
In such case the user should register the callback function exactly once per object, but may specify multiple events that might trigger the callback.
It's not clear to me how the action would be registered.
Will there be a function for it (like lv_btn_set_action) that receives both the callback function and the action type? Or does the registration of the callback happen once, when the object is created or added? What happens if the user wants to specify multiple actions with his callback?  Does he call a function multiple times? Or uses some bit mask?
I'm also having second thoughts about how the callback context should be defined. After looking into it, it seems that at least for Micropython, I would need a context only per callback function, not per action_type. So if we have a single callback per object, a single context might be enough.

@amirgon @embeddedt

Possibly the user could set a callback function and simply ignore events they're not interested in. This resembles how the Win32 API works.

Yes, that was the idea.

I'm also having second thoughts about how the callback context should be defined. After looking into it, it seems that at least for Micropython, I would need a context only per callback function, not per action_type. So if we have a single callback per object, a single context might be enough.

Great news! In this case, I think, free_ptr is intuitive enough. Do you agree?

@kisvegabor

Possibly the user could set a callback function and simply ignore events they're not interested in. This resembles how the Win32 API works.

Yes, that was the idea.

How do you register a callback function? In the create function of the object? As a generic lv_obj function?

 Except from action_type, wouldn't some callbacks require additional parameters?
For example today lv_btnm_action_t callback receives const char *txt parameter. How do you plan to convert it to a uniform callback prototype?

Great news! In this case, I think, free_ptr is intuitive enough. Do you agree?

It is good enough, assuming only one callback function for each object.
Personally I would still keep a user_data pointer for every callback as a rule, since the user might want to use free_ptr for other purposes, but if you believe the usage of user_data is not so common then I guess we could live with free_ptr.

@amirgon

How do you register a callback function? In the create function of the object? As a generic lv_obj function?

Yes, we can use lv_obj_set_action(obj, action).

 Except from action_type, wouldn't some callbacks require additional parameters?
For example today lv_btnm_action_t callback receives const char *txt parameter. How do you plan to convert it to a uniform callback prototype?

An extra parameter is really required in some cases. So this action prototype seems reasonable:
lv_res_t my_action(lv_obj_t * obj, lv_action_type_t type, void * param)

Personally I would still keep a user_data pointer for every callback as a rule, since the user might want to use free_ptr for other purposes, but if you believe the usage of user_data is not so common then I guess we could live with free_ptr.

Now we have LV_OBJ_FREE_NUM_TYPE. Maybe we can add LV_OBJ_FREE_PTR_TYPE to enable the user to use a struct if required. Now the question seems obvious to why not have only one struct as user_data which can hold numbers and pointer as the user's requirements.

@kisvegabor

An extra parameter is really required in some cases. So this action prototype seems reasonable:
lv_res_t my_action(lv_obj_t * obj, lv_action_type_t type, void * param)

That is not so pretty. The user who looks at the function prototype would need to guess how to cast void* instead of deducing it from the function prototype.
In Micropython binding I rely on types to know how to convert between C types and Python types. In case of void*, I would have to leave it to the user to call the conversion function.
How about keeping this information in the lv_obj, and providing a function to access it, instead of passing it as a void* parameter to the callback?

Now we have LV_OBJ_FREE_NUM_TYPE. Maybe we can add LV_OBJ_FREE_PTR_TYPE to enable the user to use a struct if required. Now the question seems obvious to why not have only one struct as user_data which can hold numbers and pointer as the user's requirements.

I like this idea. Instead of a predefined free-num and a free-ptr, let the user define what information (and how much information) he would like to add to the underlying lv-obj struct.

Instead of a predefined free-num and a free-ptr, let the user define what information (and how much information) he would like to add to the underlying lv-obj struct.

How do you "define" this information?

@embeddedt
A typedef n lv_conf.h?
typedef struct {
   uint8_t num;
   void * ptr;
}lv_obj_user_data_t;

@kisvegabor Perfect!

@amirgon  Reaction to https://github.com/littlevgl/lvgl/issues/785#issuecomment-463363341

Every callback function should have a separate registration function, and each registration function should register exactly one callback.

It's important when you set the callback by initializing a struct. In this case, we still can have function(s) to set the callback. It's also good when the user wants to modify the callback later.
E.g. lv_disp_set_flush_cb

The registration function should receive both the function pointer and void *user_data (and can receive other parameters)
The callback function should receive user_data (and can receive other parameters)

When in v6.0 object actions will be generalized on disp will have more callbacks. With one or two display the memory impact of the user_data for each callback is negligible. So this part is ok for me.
What I don't like is the extra parameter in the callback:
void disp_flush (void *user_data, lv_disp_t * disp, lv_area_t * area, lv_color_t * color_p);
This concept of user_data is required very rarely and in most of the cases it's just an extra unused paramter for the user. In a "casual" case one user_data even an int would be enough to identify the caller.
Once the user data is a void * you can point to an array like:
typedef struct {
   void * flush_data;
   void * fill_data;
}lv_disp_user_data_t

This typedef can go even to lv_conf.h and use lv_disp_user_data_t * user_data in lv_disp_drv_t.
Can it work?

@kisvegabor After giving it some more thought, I have another suggestion. (I think you would like it better than the previous one...)
We need a separate user_data for each callback, we need a way  to set it automatically when setting the callback, and access it when the callback is called.
I think we could keep a user_data member per callback on the struct and use some naming convention to identify it and relate it back to the callback.
So I'm thinking of something like:
typedef struct {
    void (*disp_flush)(struct lv_disp_drv_t *context, int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t * color_p);
    void (*disp_fill)(struct lv_disp_drv_t *context, int32_t x1, int32_t y1, int32_t x2, int32_t y2, lv_color_t color);
    void *disp_flush_user_data;
    void *disp_fill_user_data;
    ...
} lv_disp_drv_t;

The important parts here are:

disp_flush_user_data is user data for disp_flush
disp_fill_user_data is the user data for disp_fill
In general, every <callback> member on the struct should have a corresponding void *<callback>_user_data member.
The first parameter of disp_flush and disp_fill callback functions is struct lv_disp_drv_t *context which holds the user data for each callback.
In general every callback will pass a pointer to the struct as the first parameter.

When setting disp_flush in lv_disp_drv_t instance, the Micropython binding will look for disp_flush_user_data member in the same struct and update it automatically to point to the Micropython object that is associated with the callback.
When disp_flush callback is called, Micropython will take the first parameter of the disp_flush callback (which is struct lv_disp_drv_t *context) and will look for disp_flush_user_data in this context.
I hope I'm not missing anything here, but this looks nice and clean, no need for user_data parameter on the callback functions themselves and no need for a special registration function at all (we can keep using something like void lv_disp_drv_register(lv_disp_drv_t *driver) to register the driver).
What do you think?

@amirgon
Sounds very good to me!
Only one thing: my above proposed solution about having a typedef in lv_conf.h like
typedef struct {
   void * flush_data;
   void * fill_data;
}lv_disp_user_data_t

would give more flexibility and still work with your idea. What do you think, can we use it?

Only one thing: my above proposed solution about having a typedef in lv_conf.h like
typedef struct {
   void * flush_data;
   void * fill_data;
}lv_disp_user_data_t

@kisvegabor actually, I was thinking of coupling a callback and its associated user data, and not coupling together user_data of different callbacks.
In my opinion this would be more natural and convenient:
typedef struct {
    disp_flush_t disp_flush;
    void *disp_flush_user_data;

    disp_fill_t disp_fill;
    void *disp_fill_user_data;
    ...
} lv_disp_drv_t;

The reasons are:

There is not special relation between user_data of different callbacks.
When someone adds a callback, with your suggestion he would have to remember there is some separate user_data_t struct he has to update. Easy to forget and miss a user_data for a new callback.
With my suggestion it's easier to see that every callback must be accompanied with a user_data.
It's more generic so it would be easier to parse it with an automatic parser (only need to look for <callback>_user_data member on the same struct). No need for the parser to know the name of the user_data struct (lv_disp_user_data_t in your example).

When someone adds a callback, with your suggestion he would have to remember there is some separate user_data_t struct he has to update. Easy to forget and miss a user_data for a new callback.

Having user_data for every callback is needed only by the Micropython binding so far. So it won't affect the "average" user.

It's more generic so it would be easier to parse it with an automatic parser (only need to look for <callback>_user_data member on the same struct). No need for the parser to know the name of the user_data struct (lv_disp_user_data_t in your example).

In your suggestion you will see disp->drv.disp_fill_user_data
In my suggestion you will see:  disp->drv.user_data.disp_fill
It seems not a big difference to me.
If it could work for you too, I'd prefer the typedef because it gives more freedom. The user can typedef simply an uint8_t id data and use it as disp->drv.user_data.id = 1.

@kisvegabor

Having user_data for every callback is needed only by the Micropython binding so far. So it won't affect the "average" user.

I believe this will be required for any other bindings (at least one that implements callbacks in a generic way). It would also be needed for C++ support, if you would like to support class methods as a callbacks.

In your suggestion you will see disp->drv.disp_fill_user_data
In my suggestion you will see: disp->drv.user_data.disp_fill
It seems not a big difference to me.

Actually there is a difference you might have missed, and it's not in the syntax.
It's in the support for Forward Compatibility, the ability to support new callbacks without changing the code. It stems from the fact the user_data, as you suggest, is part of lvgl configuration.
I'll try to explain, by an example:

Suppose you added to lvgl a new callback called disp->drv.disp_fill2.
Now you build Micropython with the new version of lvgl. With your suggestion you will not see the disp_fill2 callback available automatically. (With my suggestion, you will).
To enable disp_fill2 callback you would have to edit lv_conf.h (or some other configuration file) and change the definition of lv_disp_user_data_t by adding disp_fill2_user_data member to it.
You must edit this configuration file manually because the binding script only creates the C file for wrapping lvgl functions and structs. It does not change lvgl configuration so it does not generate any H file.

So, if you insist on this approach, we have the following options:

Drop Forward Compatibility and manage Micropython's user_data in lvgl micropython bindings repo. A new callback was added? It will not be supported until accepting a PR that updates Micropythons's user_data to support the new callback.
Let the binding script generate the user_data struct for every callback group. This is less favorable and adds complexity because

I don't think the binding script should change lvgl configuration, since the same configuration could also be modified be the user.
It's not generic. How would the binding script know which user_data to create? What happens when we have a new callback group (such as new driver type)?
Today the binding script is simple in the sense that it creates one single C output. It's easy to decide where it is generated, how it is built in the Makefile, etc. Integrating the binding into Micropython is a very simple task.
If the binding script starts to generate H files, it will make the task of integrating it to different Micropython forks more complex.

Require the user to edit a configuration file every time a new callback is added or changed. When integrating the Micropython bindings to a new Micropython fork the user would have to define all user_data structs or copy it from some template.
Keep callbacks as today. Which means - no user data. Only one callback per object or driver is supported.

@kisvegabor If you don't see much of a difference I would go for @amirgon's approach. It's not that different from a user's perspective but from what I can tell it simplifies things a lot.

@amirgon Okay, I'm convinced. Thank you for the detailed explanation.
What I can still imagine to increase flexibility is to typedef the type of disp_fill_user_data. E.g.:
typedef void * lv_disp_user_data_t;   /*Can be the default as it's the most flexible*/

/*OR*/

typedef struct {
  uint8_t id;
  const char * name;
} lv_disp_user_data_t;

/*OR*/

typedef enum {
  DISP_MAIN,
  DISP_AUX,
} lv_disp_user_data_t;

...

typedef struct {
    disp_flush_t disp_flush;
    lv_disp_user_data_t disp_flush_user_data;

    disp_fill_t disp_fill;
    lv_disp_user_data_t disp_fill_user_data;
    ...
} lv_disp_drv_t;

I mean the same approach for all user data.

Awesome! Very happy to see that we found a solution which fit every requirement! :+1:

Having multiple user data for displays and input devices are not big problem because you have only 1-2 display and indput device. However I realized that having multple user data in lv_obj_t for signal_cb, design_cb and action_cb would be really bad in 99% of the cases.
I got an idea. Why not have define like #define LV_USE_MULTIPLE_USER_DATA (or so) in lv_conf.h to have something like this:
typedef struct {
    disp_flush_t disp_flush;
    disp_fill_t disp_fill;

#if LV_USE_MULTIPLE_USER_DATA
    lv_disp_user_data_t disp_flush_user_data;
    lv_disp_user_data_t disp_fill_user_data;
#else
    lv_disp_user_data_t user_data;
#endif
} lv_disp_drv_t;

With this define all the concerns seems to be solved.
What do you think?

@kisvegabor It's possible but I don't like the fact that the callback and its user_data are separated.
So let me suggest something similar that ensures that the per-callback user data won't be forgotten:
#if LV_USE_MULTIPLE_USER_DATA
#define CALLBACK(callback_name) \
callback_name ## _t callback_name; \
callback_name ## _user_data_t callback_name ## _user_data
#else
#define CALLBACK(callback_name) \
callback_name ## _t callback_name
#endif

...

typedef struct {
    CALLBACK(disp_flush);
    CALLBACK(disp_fill);

#if LV_USE_SINGLE_USER_DATA
    lv_disp_drv_user_data_t lv_disp_drv_user_data;
#endif

} lv_disp_drv_t;

btw, I separated the macro to two: LV_USE_MULTIPLE_USER_DATA and LV_USE_SINGLE_USER_DATA, so if someone doesn't want user_data at all, he could set them both to 0.

Your suggestion really looks more robust. However, I'm not afraid about forgetting to add the user data. These things are very core things and changes very rarely.
For better readability, I'd choose the "manual" way.
Disabling the user data is a good idea!

I tested initial implementation of multiple user data and I don't find it useful. On the contrary, I think it's an overkill. As I commented here https://github.com/littlevgl/lvgl/issues/785#issuecomment-465998152:

It would make a little sense if the user data would be passed to the callback directly, but lv_disp_t is being passed instead. So if anybody really needs distinct user data for each callback, she can define

struct lv_disp_user_data_t {
    void *data_for_flush_cb;
    void *data_for_render_cb;
    ...you get the idea...
};

and then access specific user data in the callback through lv_disp_t::driver. But I think the usual scenario is that the driver needs the same user data (typically some low-level handle to underlying graphical context, framebuffer address etc) in all callbacks. Now I need to fill 4 user data for 4 callbacks with the same data. It also means 4 times larger memory footprint. I just think it should be the other way around.

It might be maybe useful for some reason to MicroPython, but that's just one special usage case of LittlevGL. I believe majority of users won't benefit from this and it will put a burden on them.
I have never seen any other framework that would provide distinct user data for each and every callback. Usually you get some handle that you can associate your data with (as with lv_obj_set_free_ptr) and then you receive the handle in callback and you can extract your data from it.
As of C++ (from initial post) one would use this as the user data and then do whatever she wants with it in the callback.

a) when you want to add another button you have to duplicate both the instance method and static method for each button you want to click.
b) when you want to handle different events from a button (like LONG PRESS and CLICK) you again have to create two methods for each event.

Not true. You can use either inheritance or "events" (as C++ does not provide events as language construct, you can use e.g. std::function and lambdas).
class BaseButton
{
private:
    lv_obj_t *_obj;

public:
    Button()
    {
        _obj = lv_btn_create();
        lv_obj_set_free_ptr(_obj, this);
        lv_obj_set_action(_obj, LV_BTN_ACTION_CLICK, Button::ClickActionStatic);
    }

private:
    static lv_res_t ClickActionStatic(lv_obj_t * obj)
    {
        BaseButton *that = reinterpret_cast<BaseButton *>(lv_obj_get_free_ptr(obj);
        return that->OnClick();
    }

protected:
    virtual lv_res_t OnClick() = 0;
};

class FirstButton : public BaseButton
{
protected:
    virtual lv_res_t OnClick() override
    {
        ...FirstButton clicked....
    }
}

class SecondButton : public BaseButton
{
protected:
    virtual lv_res_t OnClick() override
    {
        ...SecondButton clicked....
    }
}

or (maybe more ellegant):
class Button
{
private:
    lv_obj_t *_obj;
    std::function<lv_res_t(void)> _click;

    static lv_res_t ClickActionStatic(lv_obj_t * obj)
    {
        BaseButton *that = reinterpret_cast<BaseButton *>(lv_obj_get_free_ptr(obj);
        return that->_click();
    }

public:
    void OnClick(std::function<lv_res_t(void)> handler)
    {
        _click = handler;
    }
};

Button btn;
btn.OnClick([]() {
    ...btn clicked...
});

I still thinks we don't need multiple user data at all but should they be supported then make it opt-in as @kisvegabor suggested with LV_USE_MULTIPLE_USER_DATA.

Your suggestion really looks more robust. However, I'm not afraid about forgetting to add the user data. These things are very core things and changes very rarely.

@kisvegabor
If this changes very rarely, we could drop Forward Compatibility. As I already wrote on a previous post:

So, if you insist on this approach, we have the following options:

Drop Forward Compatibility and manage Micropython's user_data in lvgl micropython bindings repo. A new callback was added? It will not be supported until accepting a PR that updates Micropythons's user_data to support the new callback.

But does it really happens very rarely? Can't we expect almost every new lvgl component to contain some new callback?
I see a strong resistance to the user_data per-callback from @manison side, and I can see his point.
In many cases a single user_data per struct is enough.
So why do we need user_data per-callback in Micropython?
Because we can't predict how the callback would be defined by the user. In case of Micropython, the user is free to define the callback however he wants, for example, as a Python Closure! I expect this to be very common, and you can see an example of this on the demo script. In such case every callback needs to be associated with a different user_data.
A user_data per-callback could be defined:

In LittlevGL sources (with my suggestion of adding user_data per-callback next to each callback), or
On the Micropython Bindings configuration (by setting the ...user_data_t struct to contain user_data for each callback),

In both cases, when you add a new callback you need to actively add a user_data per callback, somewhere.
This is also true when adding a new lvgl component that contains some new callback.
To be honest, there is another option, which is more complex and expensive on the Micropython side.
The script could analyze (when parsing lvgl sources) which are all the callbacks per struct, and try to allocate user_data dynamically (when?) as an array of pointers to each per-callback user_data. I'm still not sure how difficult would it be to implement it this way, but it's worth mentioning that this option also exists. Recall I want to avoid changing lvgl configuration so I would not like to create the ...user_data_t structs on lvgl configuration (on a header file), that's why I need to allocate user_data dynamically in this case.

However, no matter what approach is selected, there is one thing which is the most important requirement for Micropython bindings:
A strict naming convention to identify the user_data for any callback.
gen_mpy.py analyzes lvgl headers in a generic way. It's not aware of what callbacks lvgl has, but it identifies them when it sees "a pointer to a function" function parameter or struct member.
Now - it needs to find the corresponding user_data. How does it called? free_ptr?? <callback>_user_data?  Where can it be found?
My point is - there needs to be a convention for the user_data name and location so an automatic script could identify it for any existing or future callback.

I see a strong resistance to the user_data per-callback from @manison side, and I can see his point.
In many cases a single user_data per struct is enough.

True, I'm against this idea 😃 I don't want to sound rough but if this complicates things for MicroPython then let it be complex on MicroPython side and do not complicate things for majority of other users. I really don't know the process MicroPython uses for bindings and would be happy to learn more to understand it. On the other hand as I already mentioned above I don't know any other framework providing the luxury of separate user data per callback. How does MicroPython bind in this situation? Can't you use C++ model I mentioned above, i.e. saving instance pointer as your user data?

Recall I want to avoid changing lvgl configuration so I would not like to create the ...user_data_t structs on lvgl configuration (on a header file), that's why I need to allocate user_data dynamically in this case.

Can't you generate definitions into separate header file that would then be included from the config header? That way you wouldn't modify the config itself.

gen_mpy.py analyzes lvgl headers in a generic way.

This is your script or is it from MicroPython's distribution?

...there needs to be a convention for the user_data name and location so an automatic script could identify it for any existing or future callback.

This is doable. There can be also some kind of annotation in comment (just throwing ideas).

I might be opening another can of worms here, but I have an idea... can we come up with some standard macro for declaring a callback inside a struct that will also generate a user data variable beside it? The macro itself would probably look very ugly, but here is a rough example from the top of my head:
#define LV_CALLBACK(type) lv_##type##_cb_t type##_cb; void * type##_user_data;

typedef void (*lv_disp_flush_cb_t)(lv_area_t *area_p, int x1, int y1, int x2, int y2);

typedef struct {
        LV_CALLBACK(disp_flush);
} lv_some_struct_t;

That macro would then generate the following code:
typedef struct {
        lv_disp_flush_cb_t disp_flush_cb;
        void * disp_flush_user_data;
} lv_some_struct_t;

I'm not sure if @amirgon's script is sophisticated enough to detect the use of the LV_CALLBACK macro but that might be solution to get the best of both worlds.

True, I'm against this idea  I don't want to sound rough but if this complicates things for MicroPython then let it be complex on MicroPython side and do not complicate things for majority of other users.

@manison I believe we can find common grounds, for example by configuring either a single user_data or user_data-per-callback in lv_conf.h.

I really don't know the process MicroPython uses for bindings and would be happy to learn more to understand it

I provided a lot of explanations on this thread and on #557. After reading them please let me know specifically what you don't understand and I'll be happy to clarify.

How does MicroPython bind in this situation? Can't you use C++ model I mentioned above, i.e. saving instance pointer as your user data?

No, I don't think you got the idea. It is unrelated to C++.

Can't you generate definitions into separate header file that would then be included from the config header? That way you wouldn't modify the config itself.

It may be possible but it complicates things. We already discussed this on this thread on the posts above.

This is your script or is it from MicroPython's distribution?

This is my script. Micropython does not provide a way to automatically bind to external libraries.

This is doable. There can be also some kind of annotation in comment (just throwing ideas).

The library we use to parse C headers works well only on preprocessed code, so no comments annotations are available.
We would need some naming convention on the identifier names themselves.
There was a lot of discussions on this subject on #557, please have a look there.

I might be opening another can of worms here, but I have an idea... can we come up with some standard macro for declaring a callback inside a struct that will also generate a user data variable beside it?

@embeddedt could you clarify how this is different from my suggestion here?

@amirgon Oops, that's my fault. I didn't realize you had suggested that before.
I think your idea is cleaner than adding it manually.

I prefer to add the callback and user data explicitly because it increases the readability. If the user checks the implementation of lv_disp_drv_t he will clearly see what is there.
I don't think too much new callbacks will be added. The new object features shall use the one generic action callback. Probably new action types (e.g. LV_ACTION_CLICK) will be added instead.
By manually adding user_data forward compatibility is still ensured for Micropython. Just care must be taken to not forget to add the user data. But I think it's not an issue because of rare changes.
I'm ok with having multiple user data if it makes binding easier. However if all user data/callback is used by micropython, no user data remains for the actual user to store an ID or something.  So we should have a define to enable user data for each callback and other to enable a simple user data. Do you agree?

@kisvegabor

I prefer to add the callback and user data explicitly because it increases the readability. If the user checks the implementation of lv_disp_drv_t he will clearly see what is there.

Ok.

I don't think too much new callbacks will be added. The new object features shall use the one generic action callback. Probably new action types (e.g. LV_ACTION_CLICK) will be added instead.

That's good!

By manually adding user_data forward compatibility is still ensured for Micropython. Just care must be taken to not forget to add the user data. But I think it's not an issue because of rare changes.

Yes. And care must also be taken to keep naming conventions, and name the user_data fields correctly.
That's because Micropython bindings would rely on naming convention to find the right user_data for each callback.
So if a callback is called, for example, flush_cb, its user_data must be called flush_user_data and nothing else. From a quick look at lv_hal_disp.h on multi-disp branch, it looks ok.

I'm ok with having multiple user data if it makes binding easier. However if all user data/callback is used by micropython, no user data remains for the actual user to store an ID or something. So we should have a define to enable user data for each callback and other to enable a simple user data. Do you agree?

Do you mean two independent defines LV_USE_MULTIPLE_USER_DATA and LV_USE_SINGLE_USER_DATA as I suggested in a previous post?
I think it would be a good idea, to let the user configure whether he wants

A single user_data per struct
A user_data per callback
No user_data at all.

This can be achieved by these defines.
But I don't see a need for both single user_data per struct and user_data per callback, at least not for Micropython.
Once you associate a Micropython object with a callback, you can keep all extra data (ID or something) in that Micropython object. So the Micropython user will not need it, the same way he wouldn't need free_ptr for lvgl object. (free_ptr is also used internally by Micropython Bindings, to associate lvgl object with Micropython object).

@kisvegabor
Looking at  lv_hal_disp.h on multi-disp branch, I see that we need two steps to get from the callback to the user_data:
void (*flush_cb)(struct _disp_t * disp, const lv_area_t * area, lv_color_t * color_p);

flush_cb receives struct _disp_t * disp. So the callback needs to access disp->driver.flush_user_data.

We know disp, this is the first parameter to the callback, by convention.
We know flush_user_data, this is the user_data for flush_cb by convention.

But what is the convention for the driver field?
I'm trying to think how to generalize this for any callback. A callback doesn't have to be on a "driver", it could be an object callback for example, there we will not have a "driver" member to access.
So the question is:
Given a "generic" callback, whether it's a driver callback or an object callback or any other callback:
void (*some_callback_cb)(struct s_t *s, ...)

What is the convention to access user_data on the callback? (s->[???].some_callback_user_data)

But I don't see a need for both single user_data per struct and user_data per callback, at least not for Micropython.
Once you associate a Micropython object with a callback, you can keep all extra data (ID or something) in that Micropython object. So the Micropython user will not need it, the same way he wouldn't need free_ptr for lvgl object. (free_ptr is also used internally by Micropython Bindings, to associate lvgl object with Micropython object).

I see, I really forget that in (Micro)python it's that simple. :)
However, making the two user_data declaration independent is easy and increases flexibility without any disadvantages. So I say why not!?

Looking at lv_hal_disp.h on multi-disp branch, I see that we need two steps to get from the callback to the user_data:

That's true and really messes up things. I've realized that it's fine to pass the drivers as a parameter. lv_disp/indev_t are containing only run-time data. The calling indev or disp can be get by lv_indev_get_act() and lv_refr_get_disp_rendering() in case if they are required for some reason.
I update the multi-disp branch accordingly.

@kisvegabor

However, making the two user_data declaration independent is easy and increases flexibility without any disadvantages. So I say why not!?

Sure, I agree there are no disadvantages.

I've realized that it's fine to pass the drivers as a parameter

That's better now :smile:
btw I couldn't find where lv_disp_user_data_t is defined. It's void* by default, right?

I merged the multi-disp and other feature branches to dev-6.0. These types are added to lv_conf_templ.h And defined to void * by lv_conf_checker.h if not defined.

Just for the record, there is another problem with callbacks described here.
Summarizing it, today in the Micropython Binding callbacks are not called immediately, but are scheduled for execution later on.
This is because:

There may be another python code executing right now, we can't start executing the callback python code before it finishes execution
Why not take a lock and wait for the other code to finish? Because the architecture we are running on is not necessarily multithreaded. It may not be possible to stop and wait for the other python code to finish.

What it means, is that the Micropython callback is not necessarily called immediately. It may be called sometime in the near future, after the C callback already returned.
So such "scheduled" callback cannot:

Return a meaningful value to the caller of the callback.
Change some data that will immediately be used by the caller of the callback (like input device, where the callback updates coordinates and lvgl uses these coordinates)

Possible solutions:

Solve it in Micropython Bindings when multithreading is supported. Take a lock and return from the C callback only after the Micropython callback was called. This means that "non-scheduled" callbacks will only be supported on multithreaded architectures.
Make sure lvgl calls a callback only when it is possible to immediately execute Micropython code.
Change the way callbacks are implemented. We can design a callback that instead of immediately returning a value or changing some data that the caller needs, calls a function when it's done to let the caller know that the result is ready. Possible but complicates things, so probably less favorable option.

I'm still not sure what is the best way to solve this.

I provided a lot of explanations on this thread and on #557. After reading them please let me know specifically what you don't understand and I'll be happy to clarify.

Thank you for the link, it provided me with a lot of useful information and insight.
Drivers
Regarding the display drivers there was some initial discussion about potential performance issues:

https://github.com/littlevgl/lvgl/issues/557#issuecomment-439882894 …performance overhead of handling the low-level display code through Python might be too high. What do you think?

https://github.com/littlevgl/lvgl/issues/557#issuecomment-440040632 …My guess is that rending would take most of the cycles (and rendering remains in C)…

and agreement that this should remain in C:

https://github.com/littlevgl/lvgl/issues/557#issuecomment-440165975 I agree, disp_flush should remain a low level C function.

But now the display (and input) drivers are accessible from Python and we have multiple user data support in lv_disp_drv_t (and lv_indev_t too). What has changed? Is it because there are some existing pure Python display drivers?
Actions
Regarding the actions I believe the usual technique employed in these situations is called thunking (aka trampoline, also concept known in higher languages as closure). The thunk would allow you to inject additional argument(s) to the action (or any other) callback. It involves allocating (executable) memory at runtime and generating executable code on the fly.
So instead of calling the dispatcher callback statically

it would be called through the thunk in fact

For example
STATIC mp_obj_t mp_lv_btn_set_action(size_t n_args, const mp_obj_t *args)
{
    lv_obj_t *btn = mp_to_lv(args[0]);
    lv_btn_action_t type = (uint8_t)mp_obj_get_int(args[1]);
-   set_action(args[0], args[2]);
-   lv_action_t action = &lv_action_t_callback;
+   lv_action_t action = create_thunk(args[2]); /*Use the address of the dynamically allocated thunk instead of the static callback*/
    lv_btn_set_action(btn, type, action);
    return mp_const_none;
}

The simple thunk for Linux (System V) amd64 ABI could be constructed as follows:

The specification says that 1st integral argument is being passed in RDI register ad 2nd in RSI. So in case of the lv_action_t callback, the first (and only) obj argument is in RDI.
Our thunk will add second argument (into RSI) that will be the mp_obj_t representing the Python-side callback.
We then jump to our dispatcher callback lv_action_t_callback which now (thanks to the thunk) receives two arguments.

lv_action_t create_thunk(mp_obj_t action)
{
    uint8_t *thunk_code = malloc_x(sizeof(thunk));
    /* Load the 2nd arg register (RSI) with the Python object */
    /* mov rsi, imm64 */
    thunk_code[0] = 0x48; thunk_code[1] = 0xBE;
    *(uint64_t*)&thunk_code[2] = (uint64_t)action;
    /* Jump to the dispatcher callback */
    /* mov rax, imm64 */
    thunk_code[10] = 0x48; thunk_code[11] = 0xB8;
    *(uint64_t*)&thunk_code[12] = (uint64_t)&lv_action_t_callback;
    /* jmp rax */
    *thunk_code[20] = 0xFF; *thunk_code[21] = 0xE0;
    return (lv_action_t)thunk_code;
}

-STATIC lv_res_t lv_action_t_callback(struct _lv_obj_t* arg0)
+STATIC lv_res_t lv_action_t_callback(struct _lv_obj_t* arg0, mp_obj_t *action)
{
    mp_obj_t args[1];
    args[0] = lv_to_mp((void*)arg0);
-   mp_obj_t action = mp_to_lv_action(args[0]);
    mp_obj_t arg_list = mp_obj_new_list(1, args);
    bool schedule_result = mp_sched_schedule(action, arg_list);
    return schedule_result? LV_RES_OK: LV_RES_INV;
}

PROS

This way you wouldn't need the user_data at all and it could be even used by Python user code.
You can create universal thunk that will cover all callback types (lv_action_t, lv_signal_t, driver callbacks) and have single dispatcher callback (i.e. less ROM usage).

CONS

Thunks are not portable and there would have to be a distinct port for each supported platform. This is however done anyway. And I can see MicroPython repository even does have dependency on Foreign Function Interface Library (libffi) and it might be probably used for that (take a look at the closure API - ffi_closure_alloc etc).

However is this even a thing? Once the generic actions (#785) will be implemented there will be just single action callback covering all action types, am I right?
Anyway I like the idea of USE_LV_USER_DATA_SINGLE and USE_LV_USER_DATA_MULTI, it makes all of us happy 😃
OT: Parsing the header files
Regarding the annotation (discussion around here https://github.com/littlevgl/lvgl/issues/557#issuecomment-450739249). I can't see you would have considered annotating the arguments directly, something that is used on Windows for static code analysis (https://docs.microsoft.com/en-us/visualstudio/code-quality/annotating-function-parameters-and-return-values), e.g.
void lv_obj_set_style(_In_ lv_obj_t * obj, _In_opt_ lv_style_t * style);
void lv_line_set_points(_In_ lv_obj_t * line, _In_reads_(point_num) const lv_point_t * point_a, _In_ uint16_t point_num);
void lv_label_set_text(_In_ lv_obj_t * line, _In_z_ const char *text);

Thank you for the link, it provided me with a lot of useful information and insight.

@manison, Thank you for going over this thread. It shows you got into the details and have some insights. I'm always happy to hear feedback on my work!

But now the display (and input) drivers are accessible from Python and we have multiple user data support in lv_disp_drv_t (and lv_indev_t too). What has changed? Is it because there are some existing pure Python display drivers?

There are a few aspects for this.

Generic callbacks - my line of though is to allow any callback to be accessible in Micropython, because I would like to have this flexibility for existing and future callbacks. In a way, the discussion over the display driver callbacks is a random example of callback. My goal is to provide Micropython access to it, as well as to any other callback, even lv_log_register_print callback for that matter.
Pure drivers - apparently there is some user demand for pure Micropython driver. On this discussion I was asked to provide something like this for Input driver. In current lvgl version I would have to write some driver ad-hoc. If generic callbacks were already supported - I would not have to do anything!
Surprisingly, the same person is also using pure python driver for his display on his projects, including register access, spi device writes etc. things I would instinctively do in C. And he claims he got really decent performance with it! So why not enable this option for the users, if they want?
Interface for native driver - Even if the driver is implemented in C, I would like to let the user select the driver he wants and configure it in Micropython. So when I enable callbacks in Micropython, they might be used only to delegate the call to the user's native driver and not necessarily implement the driver logic in pure python.

Regarding the actions I believe the usual technique employed in these situations is called thunking (aka trampoline, also concept known in higher languages as closure).

First, thank you for the detailed explanations, diagrams and examples!
I agree that a possible solution to the user_data issue is thunking, it could allow passing the Micropython object pointer to the callback without an explicit user_data.
However, I see several problems with this approach:

As you mentioned, it's architecture specific and not portable. I wish there was a generic way to achieve this in C, but there isn't.
I would like to avoid architecture specific code in lvgl Micropython bindings. The goal is to allow it to work anywhere, even on architectures that are not yet supported by Micropython (Forward Compatibility)
It's not always possible. As you mentioned, for implementing thunk we must allocate and write to executable memory at runtime. This option may not be available on some architectures, for example in case of advanced ARM with MPU configured to limit writes to executable memory.
libffi is used in Micropython only on the unix port. There has been multiple requests over the time to enable ffi support on other platforms, such as ESP32, but the response was usually that this is not supported. Here are some examples: this, this, this.
There may be real reasons why ffi is not enabled on ports other than unix, or it might be lack of interest from the Micropython maintainers to support this. In any case, at present time, it does not look like a viable portable solution for implementing thunks on Micropython.

However is this even a thing? Once the generic actions (#785) will be implemented there will be just single action callback covering all action types, am I right?

Yes, and in this case we will have one user_data because there is only one callback. But this is just one example of callback.
Callbacks are used on other contexts such as drivers, file system, animations, image loading etc.
Again - I'm looking for a generic solution for callbacks. That's the thing. Even for callbacks that will only be added to lvgl in the future (Forward Compatibility).

Anyway I like the idea of USE_LV_USER_DATA_SINGLE and USE_LV_USER_DATA_MULTI, it makes all of us happy

Of course! I told you we could find common grounds.

Regarding the annotation (discussion around here #557 (comment)). I can't see you would have considered annotating the arguments directly, something that is used on Windows for static code analysis

Actually the whole annotation thing was driven more from the side of @rreilink for the Python (not Micropython) bindings. He is not using gc so he needs to know who allocates memory for structs, who is responsible to release the memory etc.
In Micropython it's simpler since we don't really care, Micropython gc will take care of memory deallocation.
On Micropython I'm relying on naming conventions which lvgl has anyway, such as _create suffix for object constructors, lv_<obj>_t for object names etc.
In any case, I don't see how we can directly annotate arguments in a way that is:

Working on any C compiler
Survives preprocessing (I think that Microsoft's SAL don't survive preprocessing). I need to parse the sources only after the standard C preprocessor finished.
Does not add ROM or RAM or cycles
Readable and easy to use

On the other hand, I don't need annotations now so this is not something I really gave a lot of though of.

@amirgon thank you for summing this all up nicely 👍 Keep up the good work.

@manison @amirgon I've also learned a lot from your conversation! :+1:
@amirgon
Delayed callback execution
So there are two ways:

Let the execution delayed and handle it in LittlevGL
Somehow solve the "in-place" execution

Obviously, the first option would be better. But it can cause issues in the drivers:

in indev read callback the library wants to get back the result immediately. The python driver still can be implemented with a C "backend" which can be simply called by LittlevGL.
It's worse with the display driver because the library waits for lv_flush_ready. If disp_flush is not called there lv_flush_ready won't be called as well and the execution stops at while(drv->flushing);

It seems I can make the actions work without having a return value.

Delayed callback execution
So there are two ways:

Let the execution delayed and handle it in LittlevGL
Somehow solve the "in-place" execution

@kisvegabor
I think I solved this issue.
The source of the problem was:

There may be another python code executing right now, we can't start executing the callback python code before it finishes execution

This required scheduling the callback instead  of calling it immediately.
However, under the assumption that micropython, lvgl and the display/indev drivers are all running on the same thread, the problem doesn't exist since no other python code could be executing when the callback is called.
So - as long as this constraint holds, I think we can call the callback immediately without any problem (actually I tested it right now and it looks fine!).

Very happy to hear it!

So - as long as this constraint holds, I think we can call the callback immediately without any problem

I think it's not a big constraint and it will came naturally on embedded systems (not unix with SDL)

The new event system is implemented in dev-6.0. If you find bugs with it please report them in a new issue.
Thank you very much for your contribution. This update will make a lot of things easier.


RFC: Refactor of the playbook package

Hi!
I opened this PR in order to propose a small refactor of the playbook package. The idea is make getting the text of the playbook and parsing it more orthogonal, in order to support both new playbook providers (e.g. REST, ZooKeeper, etcd) and different formats (e.g. JSON, TOML).
If you are OK with the changes, I plan to add support for getting the playbooks from a REST interface. The GET request would accept both JSON and YAML, and automatically parse the playbook depending on the Content-Type of the response.

Also, another possible change in the future (probably not for series of patches) would be to try to load/parse all the queries right after the playbook is loaded: this would help to isolate any provider-related stuff, and could be a bit more stable (e.g. the connection to Consul might not be available during a later step, or the query itself might have changed).

Hey @andrioni - on the second point, we have a ticket for that: #22

This looks cool to me @andrioni ! I like the approach of refactoring to support multiple discrete back-ends... @jbeemster thoughts when you are back?

Nice! With this and #22, I'll get back to working on a REST interface. Right now I'm thinking about an API pretty identical to the consul one, re-using the -playbook and the -sqlroot arguments.

Sounds good @andrioni ! Thanks for your contributions


Thanks a lot for sharing the code! I have some questions about part segment.

Are the best parameters is the default parameters?Also I noticed that in the classification you use the default initialization, but in this task you use the xavier initialization method, is there any intuitive explanation for this? I hope to get your reply, maybe the parameter settings. Thank you very much for your contribution!

@xindeng98

yes.
I just followed previous works like GDANet for part segmentation task.
Please let me know if you have any further questions.

@xindeng98

yes.
I just followed previous works like GDANet for part segmentation task.
Please let me know if you have any further questions.

Thank you for such a quick reply! Can I get the training log of the part segment from you? I want to quickly judge whether my idea is feasible according to the comparison of loss descent speed.

@xindeng98 Sure. You can check commit:d2b8dbaa. The log files are provided here: [anonymous google drive]

Thanks a lot.


[OpenAPI Hub] Create new API version fails to create GitHub tree

Creating a new API version copies current base version to create a new tree. This step uses GitHub API createTree to create GitHub tree in target branch. We have noticed that this steps randomly fails without much info and failure is originated from GitHub side and retry has always succeeded. This issue has been filed to improve the logging when this issue happens as well as to try and add a retry within backend call so we don't have to ask user to retry again.

This was already completed with a change in OpenAPIHub to resolve GH secondary rate limiting error.


allow router mode to be set in config

This enables you to set router.mode to null, which will make generate work with cordova as it doesn't support history mode.

Codecov Report

Merging #272 into master will not change coverage.
The diff coverage is n/a.

@@          Coverage Diff          @@
##           master   #272   +/-   ##
=====================================
  Coverage     100%   100%
=====================================
  Files          11     11
  Lines         418    418
=====================================
  Hits          418    418

Impacted Files
Coverage Δ

lib/nuxt.js
100% <ø> (ø)
:white_check_mark:

lib/build.js
100% <ø> (ø)
:white_check_mark:

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update da0bba8...88dbe00. Read the comment docs.

Great, I was thinking of adding this option @cj
Thank you!


Add Sync send support in jBallerina

Purpose

Add sync send support for jBallerina.

Remarks

This does not support receiver or sender panic related scenarios.

Check List

[x] Read the Contributing Guide
[ ] Required Balo version update
[ ] Updated Change Log
[ ] Checked Tooling Support (#)
[ ] Added necessary tests

[x] Unit Tests
[ ] Spec Conformance Tests
[ ] Integration Tests
[ ] Ballerina By Example Tests

[ ] Increased Test Coverage
[ ] Added necessary documentation

[ ] API documentation
[ ] Module documentation in Module.md files
[ ] Ballerina By Examples

Codecov Report

Merging #15306 into master will decrease coverage by <.01%.
The diff coverage is 0%.

@@             Coverage Diff              @@
##             master   #15306      +/-   ##
============================================
- Coverage     23.22%   23.22%   -0.01%
  Complexity     5095     5095
============================================
  Files          1343     1343
  Lines         67978    67996      +18
  Branches       9280     9281       +1
============================================
  Hits          15789    15789
- Misses        50437    50455      +18
  Partials       1752     1752

Impacted Files
Coverage Δ
Complexity Δ

...allerinalang/compiler/bir/model/BIRTerminator.java
0% <0%> (ø)
0 <0> (ø)
:arrow_down:

...va/org/wso2/ballerinalang/compiler/bir/BIRGen.java
0% <0%> (ø)
0 <0> (ø)
:arrow_down:

...lang/compiler/bir/writer/BIRInstructionWriter.java
0% <0%> (ø)
0 <0> (ø)
:arrow_down:

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0090394...d1368d4. Read the comment docs.


Migration to bem-xjst 8

@remnev are you ok with the PR?

How about the rest of blocks? I mean google-analytics, yandex-metrica ?

Thank you!
Is it going to be a major fix? Right?

Is it going to be a major fix?

it's backward compatible for bem-xjst 7+ so most likely it should work fine for all the users. but if there are some with bem-xjst < 7, they have to update.
it's up to you to decide if it's a major update.


Would it be possible to parse a localized date?

This will create an error, but it would be nice if it would just work.
Date::setLocale('nl');
$d = Date::parse('-1 day')->format('l j F Y H:i:s'); // $d = 'zondag 22 februari 2015 09:37:23'
Date::createFromFormat('l j F Y H:i:s', $d)->toDateTimeString()

The error is, of course:
InvalidArgumentException in Carbon.php line 385:
A textual day could not be found
A textual month could not be found

Would it be possible to get this to work?

This feature was added to v3.

Awesome, thank you so much!


Extremely invalid, but please can I know the skin off this?

Sorry for making an issue that has nothing to do with this, even add an 'invalid' label to this but:
What is the skin that Crystal used in the screenshot?

Can I please know please! And again, sorry for invalidness.

megabyte locks issue for collaborators only

Haha lmfao @Luka967

@megabyte
XD

Find out yourself at https://github.com/ItzLevvie/Agario-skins/releases/download/474/premiumSkins.zip

Multi-Ogar needs a forum or at least a redit page

Nice

Lol. All Agar.io premium-skins can be found here: http://agario-skins.org/premium-skins

Lol all I wanted to know was the skin name.
And it's Dr.Cosmo's.
Thanks @ARBCells


"Search / Cuisine" drop down on home page should be alphabetically sorted or logically sorted

The "Search / Cuisine" drop down on home page should be alphabetically sorted or logically sorted?
There are also some items in there that don't make sense, for example one of the option in the drop down is 6901

This is done.


Allow it to work with other services

Pinboard follows the delicious API, as do other services.
It would be great if this app could work with those other services.
One app that worked with both:

https://aki-null.net/shiori/

Are there specific services you'd like to see supported?

Good question! The most popular I know of are:

linkace
shaarli
linkding

Thanks. I'll definitely explore adding these. Are you making regular use of Bookmarks and, if so, is there one that would be valuable to you?

Hi, I do make use of it (as I transition away from Pinboard).
I had to self-sign the macOS app installed through your pkg but otherwise I like it.
I'll file some issues/requests with my thoughts!


Fuzz embedded type with an array

Hi. Thanks for the awesome library. It helped me a lot!
Anyway, I found an issue which I don't know how to solve.  I am trying to fuzz the structure provided by gochimp,
gochimp3.CampaignResponse
It has embed type
withLinks
type withLinks struct {
	Link []Link `json:"_link"`
}

When I do gofakeit.Struct(&gochimp3.CampaignResponse{}) it left array of a links equals nil
(*gochimp3.CampaignResponse)(0xc0001aca00)({
 withLinks: (gochimp3.withLinks) {
  Link: ([]gochimp3.Link) <nil>
 },
 ID: (string) (len=19) "gowubpqygkqyqygtnvo",
 WebID: (uint) 0,
 Type: (string) (len=19) "gyxpllrycezdzkxsrxu",
 CreateTime: (string) (len=19) "obbzezktpgxwjvbzinn",
 ArchiveUrl: (string) (len=19) "gfrpzstvzlruupgvbqa",
 LongArchiveUrl: (string) (len=19) "jiuhtemmhcbpgpmobrx",
// ...

Any help is very appreciated.

Looks like withLinks is a private struct. Gofakeit(in fact no other package) does not have access to unexpected fields. Even if the subfield of the private field is public. Im open to hearing if someone can access a public field via a sub private field but thats my current understanding.

Problem is that somehow another library (github.com/alecthomas/jsonschema) generates jsonschema with private fields.
Wonder how they do that? And can gofakeit do the same?
So my fuzzied object does not match the schema which was generated for it.
sch := jsonschema.Reflect(gochimp3.CampaignResponse{})
data, _ := sch.MarshalJSON()

 "CampaignResponse": {
      "required": [
        "_link",
...

Holy crap! I figured it out. Ill do a release shortly.

Did a release v6.1.1
Test it out and let me know if it works for your gochimp implementation.

It works now! Thank you very much!
(*gochimp3.CampaignResponse)(0xc0001c8a00)({
 withLinks: (gochimp3.withLinks) {
  Link: ([]gochimp3.Link) (len=2 cap=2) {
   (gochimp3.Link) {
    Rel: (string) (len=9) "BBCAAYbEH",
    Href: (string) (len=4) "KLdd",
    Method: (string) (len=4) "zQCt",
    TargetSchema: (string) (len=8) "BwfHUAhB",
    Schema: (string) (len=8) "RauJedZN"
   },
   (gochimp3.Link) {
    Rel: (string) (len=4) "FFeF",
    Href: (string) (len=8) "pxuFdGDe",

Nice! Glad to hear. I better see you staring the repository!!! 😄


window.close() does nothing

I set a callback in the discovery TokenScript which calls window.close() when the user has already claimed the tokens. This gives a null webview

Solved in #912


Question.  Release candence of ZeroCode updating the currency of internal dependencies.

Hi,
I work for Watson Health in IBM and we are reviewing ZeroCode for our requirements.
I have a question to your release cadence and your plans to keep the version of third party open source jars current.   With critical issues often appearing in the third party supply chain, keeping current is important from a security stance and to allow  ZeroCode to continue to work with other software, e.g. Spring Boot.
Do you plan to release new versions of ZeroCode moving to the most current version of dependent jars?  If a request is made through this channel to update dependencies to the current version can you give an example of the timeline.
The status of the last releases here are not up-to-date https://github.com/authorjapps/zerocode/wiki/Zerocode-release-frequency-and-schedule
https://mvnrepository.com/artifact/org.jsmart/zerocode-tdd - 1 release in 2021.
Example of an issue I hit.
I tried to get ZeroCode to work with the latest Spring Boot version, version 2.6.3 .  There is a clashing jar file version, the higher version of the jackson library 2.13.1 in Spring Boot 2.6.3 is not compatible with ZeroCode, you get an exception saying 'Step cannot be created.'.   I can continue as I have removed Spring Boot from my spike of ZeroCode.
Example of  dependencies for ZeroCode.

jackson-dataformat-yaml-2.9.8.jar - Dec, 2018
jackson-databind-2.10.0.jar - Sep, 2019

Hi. We do plan to update dependencies. Im not sure about the release date though. You can always exclude whatever dependency is "problematic". Btw, please join our  Slack if you need to discuss some other related issue to your project.

One related question on full Junit 5 integration.
Do you have plans to support Junit 5 for all test scenario types, just load testing is supported at present if I understand your docs correctly.


[Bug report] Stuck on running gradle task

Version
3.2.2
Platforms
Android
Device Model
Pixel 7
flutter info
[√] Flutter (Channel stable, 3.22.1, on Microsoft Windows [Version 10.0.22635.3936], locale en-US)
    • Flutter version 3.22.1 on channel stable at E:\flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision a14f74ff3a (9 weeks ago), 2024-05-22 11:08:21 -0500
    • Engine revision 55eae6864b
    • Dart version 3.4.1
    • DevTools version 2.34.3

[√] Windows Version (Installed version of Windows is version 10 or higher)

[!] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    • Android SDK at E:\Android Studios\Android Sdk\Sdk
    X cmdline-tools component is missing
      Run `path/to/sdkmanager --install "cmdline-tools;latest"`
      See https://developer.android.com/studio/command-line for more details.
    X Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/docs/get-started/install/windows#android-setup for more details.

[√] Chrome - develop for the web
    • Chrome at C:\Program Files\Google\Chrome\Application\chrome.exe

[√] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.8.0)
    • Visual Studio at E:\VIsual Studio Environment\VS
    • Visual Studio Community 2022 version 17.8.34309.116
    • Windows 10 SDK version 10.0.22621.0

[√] Android Studio (version 2024.1)
    • Android Studio at C:\Program Files\Android\Android Studio
    • Flutter plugin can be installed from:
       https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
       https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 17.0.10+0--11609105)

[√] VS Code (version 1.91.1)
    • VS Code at C:\Users\user\AppData\Local\Programs\Microsoft VS Code
    • Flutter extension version 3.92.0

[√] Connected device (4 available)
    • sdk gphone64 x86 64 (mobile) • emulator-5554 • android-x64    • Android 14 (API 34) (emulator)
    • Windows (desktop)            • windows       • windows-x64    • Microsoft Windows [Version 10.0.22635.3936]
    • Chrome (web)                 • chrome        • web-javascript • Google Chrome 126.0.6478.185
    • Edge (web)                   • edge          • web-javascript • Microsoft Edge 127.0.2651.49

How to reproduce?
Stuck on ```
Launching lib\main.dart on sdk gphone64 x86 64 in debug mode...
Running Gradle task 'assembleDebug'... when photo_manager: 3.2.2 library is installed

### Logs

_No response_

### Example code (optional)

```Dart
name: example
version: 1.0.0+1
publish_to: none
description: example
environment:
  sdk: '>=3.4.1 <4.0.0'

dependencies:
  cupertino_icons: ^1.0.6
  get: 4.6.6
  get_storage: 2.1.1
  reorderable_grid_view: 2.2.8
  photo_manager: 3.2.2
  photo_manager_image_provider: 2.1.1
  flutter:
    sdk: flutter

dev_dependencies:
  flutter_lints: ^3.0.0
  flutter_test:
    sdk: flutter

flutter:
  uses-material-design: true

Contact
No response

[!] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    • Android SDK at E:\Android Studios\Android Sdk\Sdk
    X cmdline-tools component is missing
      Run `path/to/sdkmanager --install "cmdline-tools;latest"`
      See https://developer.android.com/studio/command-line for more details.
    X Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/docs/get-started/install/windows#android-setup for more details.

Your doctor's result tells that you don't have a valid Android setup. Consider solving that first before you make further steps. Also, a long duration with assembleDebug usually means there are something wrong with the network connection to the Gradle dependencies.

[!] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    • Android SDK at E:\Android Studios\Android Sdk\Sdk
    X cmdline-tools component is missing
      Run `path/to/sdkmanager --install "cmdline-tools;latest"`
      See https://developer.android.com/studio/command-line for more details.
    X Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/docs/get-started/install/windows#android-setup for more details.

Your doctor's result tells that you don't have a valid Android setup. Consider solving that first before you make further steps. Also, a long duration with assembleDebug usually means there are something wrong with the network connection to the Gradle dependencies.

https://github.com/fluttercandies/flutter_photo_manager/issues/1167#issuecomment-2250673425

[!] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    • Android SDK at E:\Android Studios\Android Sdk\Sdk
    X cmdline-tools component is missing
      Run `path/to/sdkmanager --install "cmdline-tools;latest"`
      See https://developer.android.com/studio/command-line for more details.
    X Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/docs/get-started/install/windows#android-setup for more details.

Your doctor's result tells that you don't have a valid Android setup. Consider solving that first before you make further steps. Also, a long duration with assembleDebug usually means there are something wrong with the network connection to the Gradle dependencies.

But i dont think something wrong with my setup or network, its run immediately when i remove this library.

We have a workflow to ensure you can build the APK with the code. https://github.com/fluttercandies/flutter_photo_manager/actions/workflows/build-example-apk.yml
You can try to run the example of the package and see if it works.

We have a workflow to ensure you can build the APK with the code. https://github.com/fluttercandies/flutter_photo_manager/actions/workflows/build-example-apk.yml You can try to run the example of the package and see if it works.

Yeah now worked, thanks for helping


If an instance of a game/program runs can't run anyother game/program until it's closed.

If I run a game/program and want to run a other game/program I need to first close the already running game/program and after that the game/program that I wanted to run will run, but if I tried opening multiple game/program all of them would start running once the first game/program is closed.

Is there a way to change this? I don't think it works correctly because if a game is running and I launch two other games if I close the first game the other two games start running, which shouldn't happen.

umu-launcher has an option to change this.
Last time I tried I had problems with it.
I'll test it again and if I mange to make it work properly, I'll change it.


Move debug-related Webpack plugins to debugConfig

Noticed that having the webpack DuplicationPlugin in the base config was leading to errors when running yarn link:
｢wdm｣: Error: ELOOP: too many symbolic links encountered, open '/Users/cn/Sites/artsy/force/node_modules/@artsy/reaction/node_modules/@artsy/reaction/node_modules/@artsy/reaction/node_modules/@artsy/reaction/node_modules/@artsy/reaction/node_modules/@artsy/reaction/node_modules/@artsy/reaction/node_modules/@artsy/reaction/node_modules/@artsy/reaction/node_modules/@artsy/reaction/node_modules/@artsy/reaction/node_modules/@artsy/reaction/node_modules/@artsy/reaction/node_modules/@artsy/reaction/node_modules/@artsy/reaction/node_modules/<EMAIL_ADDRESS>ℹ ｢wdm｣: wait until bundle finished: /

This moves things into a debugConfig, and also imports it to be used in the isCI config

Looks good :+1:


Item Whitelist

Looking at issue #29 I thought that it might be helpful to approach the issue by using a whitelist. While I'm not entirely sure it would resolve any issues, it was brought up that the whitelist idea would be great as it's own issue/enhancement. This issue outlines the thoughts for the whitelist option.
As a user, it would be nice for me to be able to specify items that are ALWAYS sent through the already existing webhooks. I believe that the following picture represents what would be expected from such an option.

Text box allowing the user to enter a comma separated list with items that should always be sent via webhooks
A label above the textbox which explains what the textbox is (whitelist might not be the best wording here but illustrates my point)
The hover text for the label in 2) should be something along the lines of "Items which always get sent to discord via webooks regardless of all other rarity/price options selected"
A checkbox to enable the use of whitelisted items in the text box above (this may not be needed but I'm not sure if it would make it easier. Based on the ignore list, I suspect it probably isn't required) The main reason this would be nice is if someone has a huge whitelist (like me) and would like the option to turn it on/off without deleted/copy+pasting every time to disable/enable respectively.

I agree with everything except 4. I think we should use the option if it's filled in.

Yeah, it's basically implied that, if you have items entered in the text box, they will be used. If there is nothing there, then nothing will be used.

Will be added in the next release, need to add some more changes because of the plugin breaking with the next RL update.
Closing this issue thanks to the PR of @GerjanWielink


Docs: fix Image attribute type and default value

Please make sure these boxes are checked before submitting your PR, thank you!

[x] Make sure you follow Element's contributing guide (中文 | English | Español | Français).
[x] Make sure you are merging your commits to dev branch.
[x] Add some descriptions and refer relative issues for you PR.

Please resolve the conflicts ~

Please resolve the conflicts ~

Solved.


Object accessibility (Access = Public)

Hi Bert,
I want to hear if it would be possible for you to change the accessibility of the codeunit 82574 "ADLSE External Events" to Public, so that it becomes possible to subscribe to the events in this codeunit?
And if the table 82560 "ADLSE Setup" could also become accessible it would be great, but it is not as important to me than getting access to the events in the codeunit.
Best regards,
Randi

Hi @RandiJakobsen for the ADLSE External Events I think it isn't an issue.
But why do you want to get the table ADLSE Setup open? I'm not a fan of that one.

Hi Bert,
We are running BC 14 on-prem and are due to technical reasons not able to make a connection between BC and Power Automate, so I want to trigger the Fabric Notebook by subscribing to MyBusinessOnExportFinished event. In my subscription I will make the API Request (POST https://api.fabric.microsoft.com/v1/workspaces/4f8a8c2e-86b0-405d-a7e5-a4c84214f1dd/items/bd4ac8cf-67bf-4ae1-a4cb-321844f8d019/jobs/instances?jobType=RunNotebook)
I will need two new fields to hold the "wokspace GUID" and "notebook GUID", and if the ADLSE Setup table were open for extension I would have added the fields here, but I can easily make a new table holding these two fields.
I fully understand that you will not change the accessibility of the "ADLSE Setup" table, and that is no problem.
And perfect that you will change the accessibility of the "ADLSE External Events" codeunit.

@RandiJakobsen thanks for the approval.
It is now merged into the master.


Incorrect indentation reported from 7.29.0

We have a particular issue after upgrading eslint-plugin-react to 7.29.0 from 7.28.0: indentation doesn't seem to be counted correctly in some cases, resulting in false positives. Take this code snippet:
    case 'multiChoice':
      for (let i = 0; i < obj.options.length; i += 1) {
        options[i] = new Option(obj.options[i].description, parse(obj.options[i].option));
      }
      return new MultiChoiceNode(obj.question, // This is line 34 in the original file
        obj.questionKey,
        options,
        parse(obj.singleOpening),
        parse(obj.multipleOpening),
        parse(obj.singleClosing),
        parse(obj.multipleClosing));

Running the linter on this file with 7.28.0 causes no problem, but from 7.29.0 it results in the following error:
TemplateParser.js:34:7: Expected indentation of 6 space characters but found 8. [Error/react/jsx-indent]
Note that the indentation in line 34 is indeed 6 spaces, not 8 as the error message reports.

I've noticed the same issue. I was just trying to create a simple repo to reproduce the issue, but haven't had much luck yet. I did a little digging to see what changed, and found it was https://github.com/yannickcr/eslint-plugin-react/pull/620 which fixed an issue with aligning the closing parens in jsx return statements, but seems to also be flagging correct non-jsx returns as incorrect. I suspect the issue is related to this change, and the fact that non-jsx functions are being evaluated there as well.

@quiram just to clarify, despite the line number in the error saying 34, the error is actually complaining because line 40 is not  also indented at 6. I believe what you're seeing is what I was seeing, the rule is treating your return as if it were returning JSX where the closing paren should line up with the return statement.
A better description for this issue might be "jsx-indent reporting error on non-jsx return"

Sorry it took me a minute, but here's a simple repo that reproduces the issue, if you change the package.json to pin to 7.28.0 the error no longer appears.
https://github.com/job13er/jsx-indent-bug

Thanks @job13er for looking at this so quickly and for providing the sample repo, I tried for a bit but JavaScript is not my forte (minor thing: the repo says 7.49.0, but I think it's 7.29.0).

Update: actually, you can probably resolve your issue just by formatting that return statement differently:

Odd, I tried that and the linter just put the closing parenthesis back to the previous line. However, I think your theory makes sense: another thing that I tried was to increase the indentation of the whole block so the first line does indeed have 8 spaces of indentation, and when running eslint with autofix it would only fix the last line, so there is something definitely there.
For now what I'm doing is skipping 7.29.0 by modifying my dependency definition like this:
"eslint-plugin-react": ">7.20.0 <7.29.0 || ^7.30.0",

@quiram You're quite right, I fixed the reference in the repo :)
I'm just pinning to 7.28.0 explicitly as a workaround in the meantime. Just to point it out, your versioning there assumes that a future 7.30.0 will resolve the issue. I'm not sure that's a safe assumption.

@job13er you're right that 7.30.0 may not fix the problem, but if it doesn't I'll find out soon enough 😄
We have a nightly build to update to the latest minor version of all our npm dependencies; if 7.30.0 fixes it then we're sorted, if not then I'll bump the version.

It wouldn't be 7.30.0, since that's a minor version; i'll fix it in v7.29.1.

v7.29.1 is released.

Thanks for the quick turnaround! :)