Page MenuHomePhabricator

Efl.Ui.Selection: efl_ui_selection_get type clipboard not fired
Closed, ResolvedPublic

Description

While testing Efl.Ui.Selection, we found that if efl_ui_selection_get called two times, the callback function which is the 5th parameter of efl_ui_selection_get won't be called at all.
kindly check this example on github

In example there is two buttons, one calls it one time, and the second calls it two times. The callback function will print on stdout.

#define EFL_EO_API_SUPPORT 1
#define EFL_BETA_API_SUPPORT 1
#include <Efl_Ui.h>
#include <Elementary.h>

static Eina_Value
_selection_data_cb(Efl_Ui_Textbox *obj, void *data EINA_UNUSED, const Eina_Value value)
{
   printf("_selection_data_cb called\n");
   return EINA_VALUE_EMPTY;
}

Eina_Array *types = NULL;
static Eina_Array*
_get_types(){
   if (!types)
     {
       types = eina_array_new(1);
       eina_array_push(types, "application/x-elementary-markup");
     }
   return types;
}

static void
_paste_one_click_callback(void *data, const Efl_Event *event EINA_UNUSED)
{
   Eo *btn = (Eo*) data;
   Eina_Future *future;
   future = efl_ui_selection_get(btn, EFL_UI_CNP_BUFFER_COPY_AND_PASTE, 0, eina_array_iterator_new(_get_types()));
   efl_future_then(btn, future, _selection_data_cb);
}

static void
_paste_click_callback(void *data, const Efl_Event *event EINA_UNUSED)
{
   Eo *btn = (Eo*) data;
   Eina_Future *future;
   future = efl_ui_selection_get(btn, EFL_UI_CNP_BUFFER_COPY_AND_PASTE, 0, eina_array_iterator_new(_get_types()));
   efl_future_then(btn, future, _selection_data_cb);
   future = efl_ui_selection_get(btn, EFL_UI_CNP_BUFFER_COPY_AND_PASTE, 0, eina_array_iterator_new(_get_types()));
   efl_future_then(btn, future, _selection_data_cb);
}

static void
_quit_cb(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
{
   efl_exit(0);
}

EAPI_MAIN void
efl_main(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
{
   Eo *win, *box;

   win = efl_add(EFL_UI_WIN_CLASS, efl_main_loop_get(),
                  efl_text_set(efl_added, "Hello world"),
                  efl_ui_win_autodel_set(efl_added, EINA_TRUE));
   efl_event_callback_add(win, EFL_UI_WIN_EVENT_DELETE_REQUEST, _quit_cb, NULL);
   efl_gfx_entity_size_set(win, EINA_SIZE2D(400, 240));

   box = efl_add(EFL_UI_BOX_CLASS, win,
                 efl_content_set(win, efl_added),
                 efl_ui_layout_orientation_set(efl_added, EFL_UI_LAYOUT_ORIENTATION_VERTICAL));

   Eo *btn = efl_add(EFL_UI_BUTTON_CLASS, box,
            efl_text_set(efl_added, "Paste one time"),
            efl_gfx_hint_weight_set(efl_added, EFL_GFX_HINT_EXPAND, 0.1),
            efl_event_callback_add(efl_added, EFL_INPUT_EVENT_CLICKED, _paste_one_click_callback, efl_added),
            efl_pack(box, efl_added));
   efl_add(EFL_UI_BUTTON_CLASS, box,
            efl_text_set(efl_added, "Paste two times (This is bug)"),
            efl_gfx_hint_weight_set(efl_added, EFL_GFX_HINT_EXPAND, 0.1),
            efl_event_callback_add(efl_added, EFL_INPUT_EVENT_CLICKED, _paste_click_callback, btn),
            efl_pack(box, efl_added));

   Eina_Future *future;

   future = efl_ui_selection_get(btn, EFL_UI_CNP_BUFFER_COPY_AND_PASTE, 0, eina_array_iterator_new(_get_types()));
   efl_future_then(btn, future, _selection_data_cb);
}
EFL_MAIN()
a.srour created this task.Jan 2 2020, 6:28 AM
ali.alzyod updated the task description. (Show Details)Jan 5 2020, 9:03 AM

Well, that is how selection and copy and paste does work. Selection can only be requested once. Reason for this is, the sender client might want to delete the selection after its delivered once, (like pasting passwords only once etc.) So this is intended. However, the second selection requesting should be probebly a error message, or the first should deliver 0 bytes in the data_cb, i dont know yet.

@bu5hm4n Please note that if you request twice, none of the callback will be called (not even once)

I think it will be nice if we have synchronous version of this method, to get clipboard data without the need of callback

That is not possible, the getting of clipboard data is always asynchronous. (As it involves asking another process etc.)

That is not possible, the getting of clipboard data is always asynchronous. (As it involves asking another process etc.)

Some toolkit provides nice synchronize functionality (https://developer.apple.com/documentation/appkit/nspasteboard?language=objc) ,
Even with current implementation we can workaround creating synchronous version from this one.

There are a lot of cases we can benefit from this call, for example, user programmatically paste something two or three times after each other.

You cannot compare that at all. On OSX you can "buffer" the content of the clipboard in your app side, the counter side cannot react to random transmissions of provided data. However, X11&wayland will do that, so this is not possible, as requesting the content of the clipboard *does* involve *always* talking to the other process, which cannot be sanely sychronous, as we have to run the mainloop in order to read the fd's.

You cannot compare that at all. On OSX you can "buffer" the content of the clipboard in your app side, the counter side cannot react to random transmissions of provided data. However, X11&wayland will do that, so this is not possible, as requesting the content of the clipboard *does* involve *always* talking to the other process, which cannot be sanely sychronous, as we have to run the mainloop in order to read the fd's.

of course, it is possible even on X11, please check https://doc.qt.io/archives/qt-4.8/qclipboard.html, I am looking at stuff from toolkit perspective. anyway I do not want to start long discussion about this synchronize version.

Let go back to the main issue, which is requesting the clipboard data twice will give you no data, this is surely a bug

bu5hm4n removed a subscriber: bu5hm4n.Jan 8 2020, 2:21 AM

To be honest, you keep acting like you know it better. It was not me that tried to talk about off topic things here, and my patience for dealing with this kind of communication is at 0.

Last reply in here, QTs xcb implementation does run another process, Clipboard manager, which "steals" all the clipboard data, once a process has announced ownership. Ask yourself, with the goals of EFL if that is what we want or not :)

herb added a subscriber: herb.Jan 8 2020, 2:49 AM

I checked this codes on my desktop and it is reproduced with x11 environment.
In case of pasting twice, it is blocked by below codes.

Ex)
efl_selection_manager.c:
static Eina_Bool _efl_sel_manager_x11_selection_notify(void *udata, int type EINA_UNUSED, void *event)
{

...
if (!sel->asked) return ECORE_CALLBACK_PASS_ON;
...

}

If remove the code, the _selection_data_cb callback function will be called twice when trying to pasting twice.
However I think this looks intentional codes to prevent pasting twice.
and It seems to be related to the following patch

commit e3b6f5a959b020d68abd4c0363afff7183e6cb8a
efl selection manager - avoid multiple selection get callbacks for req

@bu5hm4n I am sorry if you feel that from my comment, and you are right I open a side discussion, and then find out it will slow down the main topic of this task because it effect Paste behavior in Text Classes.
What I was trying to say not possible ambiguous why it is not possible (is it because of implementation limitation or as you suggested EFL Goals, with toolkit you can always do some magic to make things happened (thank you for the QT notes)).
Again I am very sorry

@herb Thank you a lot for investigate it, and provide all these details.

@raster can you please have a look at this task. it seems pasting from clipboard twice will prevent any callback to be emitted

@bu5hm4n, Will this issue resolved with new clipboard implementation

ali.alzyod updated the task description. (Show Details)Mar 30 2020, 3:35 AM

Currently we have following error printed

ERR<10727>:eina_safety ../src/modules/ecore_evas/engines/x/ecore_evas_x.c:3928 _ecore_evas_x_selection_notify() safety check failed: !edata->selection_data[selection].later_conversion is false
bu5hm4n closed this task as Resolved.Mar 30 2020, 4:38 AM
bu5hm4n claimed this task.

Right now your testcode has a little bug, you need to listen to the resolving of the promise *and* the rejection.

#define EFL_EO_API_SUPPORT 1
#define EFL_BETA_API_SUPPORT 1
#include <Efl_Ui.h>
#include <Elementary.h>

static Eina_Value
_selection_rejected_data_cb(Efl_Ui_Textbox *obj, void *data EINA_UNUSED, const Eina_Value value)
{
   printf("_selection_rejected_data_cb called\n");
   return EINA_VALUE_EMPTY;
}

static Eina_Value
_selection_data_cb(Efl_Ui_Textbox *obj, void *data EINA_UNUSED, const Eina_Value value)
{
   printf("_selection_data_cb called\n");
   return EINA_VALUE_EMPTY;
}

Eina_Array *types = NULL;
static Eina_Array*
_get_types(){
   if (!types)
     {
       types = eina_array_new(1);
       eina_array_push(types, "application/x-elementary-markup");
     }
   return types;
}

static void
_paste_one_click_callback(void *data, const Efl_Event *event EINA_UNUSED)
{
   Eo *btn = (Eo*) data;
   Eina_Future *future;
   future = efl_ui_selection_get(btn, EFL_UI_CNP_BUFFER_COPY_AND_PASTE, 0, eina_array_iterator_new(_get_types()));
   efl_future_then(btn, future, _selection_data_cb, _selection_rejected_data_cb);
}

static void
_paste_click_callback(void *data, const Efl_Event *event EINA_UNUSED)
{
   Eo *btn = (Eo*) data;
   Eina_Future *future;
   future = efl_ui_selection_get(btn, EFL_UI_CNP_BUFFER_COPY_AND_PASTE, 0, eina_array_iterator_new(_get_types()));
   efl_future_then(btn, future, _selection_data_cb, _selection_rejected_data_cb);
   future = efl_ui_selection_get(btn, EFL_UI_CNP_BUFFER_COPY_AND_PASTE, 0, eina_array_iterator_new(_get_types()));
   efl_future_then(btn, future, _selection_data_cb, _selection_rejected_data_cb);
}

static void
_quit_cb(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
{
   efl_exit(0);
}

EAPI_MAIN void
efl_main(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
{
   Eo *win, *box;

   win = efl_add(EFL_UI_WIN_CLASS, efl_main_loop_get(),
                  efl_text_set(efl_added, "Hello world"),
                  efl_ui_win_autodel_set(efl_added, EINA_TRUE));
   efl_event_callback_add(win, EFL_UI_WIN_EVENT_DELETE_REQUEST, _quit_cb, NULL);
   efl_gfx_entity_size_set(win, EINA_SIZE2D(400, 240));

   box = efl_add(EFL_UI_BOX_CLASS, win,
                 efl_content_set(win, efl_added),
                 efl_ui_layout_orientation_set(efl_added, EFL_UI_LAYOUT_ORIENTATION_VERTICAL));

   Eo *btn = efl_add(EFL_UI_BUTTON_CLASS, box,
            efl_text_set(efl_added, "Paste one time"),
            efl_gfx_hint_weight_set(efl_added, EFL_GFX_HINT_EXPAND, 0.1),
            efl_event_callback_add(efl_added, EFL_INPUT_EVENT_CLICKED, _paste_one_click_callback, efl_added),
            efl_pack(box, efl_added));
   efl_add(EFL_UI_BUTTON_CLASS, box,
            efl_text_set(efl_added, "Paste two times (This is bug)"),
            efl_gfx_hint_weight_set(efl_added, EFL_GFX_HINT_EXPAND, 0.1),
            efl_event_callback_add(efl_added, EFL_INPUT_EVENT_CLICKED, _paste_click_callback, btn),
            efl_pack(box, efl_added));

   Eina_Future *future;

   future = efl_ui_selection_get(btn, EFL_UI_CNP_BUFFER_COPY_AND_PASTE, 0, eina_array_iterator_new(_get_types()));
   efl_future_then(btn, future, _selection_data_cb, _selection_rejected_data_cb);
}
EFL_MAIN()

This way, the code prints that the first promise got rejected and the second one got fullfilled. The ERR you are seeing is due to the inability of our abstraction to ignore a requested request, which is IMO not a bug.
All in all, the usecase seems to work now as expected.