After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 760990 - Help calling gkbd_keyboard_drawing_set_groups_levels() from python
Help calling gkbd_keyboard_drawing_set_groups_levels() from python
Status: RESOLVED OBSOLETE
Product: libgnomekbd
Classification: Core
Component: Drawing
unspecified
Other Linux
: Normal normal
: ---
Assigned To: libgnomekbd maintainers
Sergey V. Udaltsov
Depends on:
Blocks:
 
 
Reported: 2016-01-22 15:07 UTC by Antonio Ospite
Modified: 2021-07-05 12:33 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
a python test program to set gdoups and levels (400 bytes, text/x-python)
2016-01-22 15:11 UTC, Antonio Ospite
  Details
a python test program to set groups and levels (495 bytes, text/x-python)
2016-02-24 18:47 UTC, Antonio Ospite
  Details
a javascript example which seems to work (391 bytes, application/javascript)
2016-02-24 18:52 UTC, Antonio Ospite
  Details
Fix calling gkbd_keyboard_drawing_set_groups_levels from other languages (1.91 KB, patch)
2017-11-13 12:43 UTC, Antonio Ospite
none Details | Review

Description Antonio Ospite 2016-01-22 15:07:21 UTC
I am trying to call gkbd_keyboard_drawing_set_groups_levels[1] from pyhton which is Gkbd.KeyboardDrawing.set_groups_levels(), but it looks like GIR is not picking up the correct type for the "groupLevels" parameter.

[1] https://git.gnome.org/browse/libgnomekbd/tree/libgnomekbd/gkbd-keyboard-drawing.c#n2406

pyhton tells that Gkbd.KeyboardDrawing.set_groups_levels() expects a Gkbd.KeyboardDrawingGroupLevel but it really should be a list of them.

So I tried to annotate the function to give a hint for the introspection, like this:

diff --git a/libgnomekbd/gkbd-keyboard-drawing.c b/libgnomekbd/gkbd-keyboard-drawing.c
index c27ab3f..5524c15 100644
--- a/libgnomekbd/gkbd-keyboard-drawing.c
+++ b/libgnomekbd/gkbd-keyboard-drawing.c
@@ -2416,6 +2416,11 @@ gkbd_keyboard_drawing_set_track_config (GkbdKeyboardDrawing * drawing,
                drawing->track_config = 0;
 }

+/**
+ * gkbd_keyboard_drawing_set_groups_levels:
+ * @kbdrawing: a #GkbdKeyboardDrawing
+ * @groupLevels: (array fixed-size=4): List of GkbdKeyboardDrawingGroupLevel
+ */
 void
 gkbd_keyboard_drawing_set_groups_levels (GkbdKeyboardDrawing * drawing,
                                         GkbdKeyboardDrawingGroupLevel *

After this change the python function would accept a list of Gkbd.KeyboardDrawingGroupLevel but the C code still crashes when trying to access it.

Any suggestion on how to set the groups and levels from pyhton?

gkbd_keyboard_drawing_set_groups_levels() is part of the public API, so I ruled out changing the signature for now.

Thanks,
   Antonio
Comment 1 Antonio Ospite 2016-01-22 15:11:07 UTC
Created attachment 319557 [details]
a python test program to set gdoups and levels

The attached program is how I imagine the groups and levels could be set from python, but it does not work even with my change proposed above.

BTW I test the changes with this command line:

LD_LIBRARY_PATH=$PWD/../libgnomekbd/build/lib/ \
GI_TYPELIB_PATH=$PWD/../libgnomekbd/build/lib/girepository-1.0/ \
  ./gkbd_test_set_groups_levels.py

where build/ is a subdirectory containing a local installation of the library which includes the modifications.

Thanks,
   Antonio
Comment 2 Antonio Ospite 2016-02-24 18:47:52 UTC
Created attachment 322274 [details]
a python test program to set groups and levels

An updated python test program which sets the group and levels to 1,1.

Along with the following change to libgnomekbd the example program shows that the C code receives the actual structs where it would expect a pointer to the struct:

  diff --git a/libgnomekbd/gkbd-keyboard-drawing.c b/libgnomekbd/gkbd-keyboard-drawing.c
  index 8cb55d3..9e74a98 100644
  --- a/libgnomekbd/gkbd-keyboard-drawing.c
  +++ b/libgnomekbd/gkbd-keyboard-drawing.c
  @@ -1016,6 +1016,8 @@ draw_key_label (GkbdKeyboardDrawingRenderContext * context,
               glp < GKBD_KEYBOARD_DRAWING_POS_TOTAL; glp++) {
                  if (drawing->groupLevels[glp] == NULL)
                          continue;
  +               fprintf(stderr, "drawing->groupLevels[glp] should be a pointer, instead it's the struct itself\n");
  +               fprintf(stderr, "%016p\n", drawing->groupLevels[glp]);
                  g = drawing->groupLevels[glp]->group;
                  l = drawing->groupLevels[glp]->level;

  @@ -2416,6 +2418,11 @@ gkbd_keyboard_drawing_set_track_config (GkbdKeyboardDrawing * drawing,
                  drawing->track_config = 0;
   }

  +/**
  + * gkbd_keyboard_drawing_set_groups_levels:
  + * @kbdrawing: a #GkbdKeyboardDrawing
  + * @groupLevels: (array fixed-size=4) (transfer container): a list of #GkbdKeyboardDrawingGroupLevel
  + */
   void
   gkbd_keyboard_drawing_set_groups_levels (GkbdKeyboardDrawing * drawing,
                                           GkbdKeyboardDrawingGroupLevel *


In the output we get:

  drawing->groupLevels[glp] should be a pointer, instead it's the struct itself
  0x00000100000001

So maybe there is something going on in the pyhton side of gobject-introspection?
Comment 3 Antonio Ospite 2016-02-24 18:52:37 UTC
Created attachment 322275 [details]
a javascript example which seems to work

Attaching a javascript example which instead seems to work as intended, when run this way after adding the annotations to libgnomekbd:

LD_LIBRARY_PATH=$PWD/../libgnomekbd/build/lib/ \
GI_TYPELIB_PATH=$PWD/../libgnomekbd/build/lib/girepository-1.0/ \
  gjs gkbd_test_set_groups_levels.js

In this case the C code receives pointers to structs.

So calling set_group_levels from gjs seems to work fine, but in all the other languages I tried (python, perl, ruby) it does not.

Can it be gobject introspection that is not mapping an array of pointers to struct correctly in these languages, while it is doing the right thing for javascript?
Comment 4 Bastien Nocera 2016-03-02 13:36:03 UTC
Adding the gobject-introspection maintainers to eventually help out.
Comment 5 Antonio Ospite 2017-11-13 12:43:41 UTC
Created attachment 363501 [details] [review]
Fix calling gkbd_keyboard_drawing_set_groups_levels from other languages

So I looked at this issue again.

It looks like g-ir-scanner (1.54.1-3 from Debian unstable) cannot recognize that the argument is an array of pointer to structs.

In:

void
gkbd_keyboard_drawing_set_groups_levels (GkbdKeyboardDrawing * drawing,
					 GkbdKeyboardDrawingGroupLevel *
					 groupLevels[])

The second parameter gets represented as:

          <parameter name="groupLevels" transfer-ownership="none">
            <type name="KeyboardDrawingGroupLevel"
                  c:type="GkbdKeyboardDrawingGroupLevel*"/>
          </parameter>

Passing annotations does not seem to be enough either:

/**
 * gkbd_keyboard_drawing_set_groups_levels:
 * @groupLevels: (array) (transfer container) (type GkbdKeyboardDrawingGroupLevel**) (element-type GkbdKeyboardDrawingGroupLevel *)
 */
void
gkbd_keyboard_drawing_set_groups_levels (GkbdKeyboardDrawing * drawing,
					 GkbdKeyboardDrawingGroupLevel *
					 groupLevels[])
Results in:

          <parameter name="groupLevels" transfer-ownership="container">
            <array zero-terminated="0" c:type="GkbdKeyboardDrawingGroupLevel*">
              <type name="KeyboardDrawingGroupLevel"/>
            </array>
          </parameter>

Only by changing the function signature I was able to make the gir definition looks as expected and call the method reliably from other languages:

/**
 * gkbd_keyboard_drawing_set_groups_levels:
 * @groupLevels: (array) (transfer container)
 */
void
gkbd_keyboard_drawing_set_groups_levels (GkbdKeyboardDrawing * drawing,
					 GkbdKeyboardDrawingGroupLevel **
					 groupLevels)

which gives:

          <parameter name="groupLevels" transfer-ownership="container">
            <array zero-terminated="0"
                   c:type="GkbdKeyboardDrawingGroupLevel**">
              <type name="KeyboardDrawingGroupLevel"
                    c:type="GkbdKeyboardDrawingGroupLevel*"/>
            </array>
          </parameter>

I still have to make sure that the array elements are in scope when used and that they are in contiguous memory but for a small array this is not asking too much.

If changing the signature is the only way to fix the issue I would take the chance to use a fixed array of four elements for goupLevels as that's what the code assumes in many places anyway.

Any comments? I consider the attached patch still RFC.

Ciao,
   Antonio
Comment 6 Antonio Ospite 2017-11-13 12:52:14 UTC
Note that the second example produces the same result even after fixing the typo (there was a space before the asterisk):

(element-type GkbdKeyboardDrawingGroupLevel*)
Comment 7 Emmanuele Bassi (:ebassi) 2017-11-29 14:06:37 UTC
gobject-introspection doesn't do anything by itself; it just provides an API to dynamically load a shared library, and and API to call symbols into it. The responsibility for marshalling data to and from the C API is left to the language binding calling libgirepository's API.

Assuming that the annotations are correct, if gjs works and pygobject doesn't, then it's likely a pygobject bug.

The annotation:

(array) (transfer container) (type GkbdKeyboardDrawingGroupLevel**) (element-type GkbdKeyboardDrawingGroupLevel *)

is incorrect. The `type` and `element-type` annotations are unnecessary, but GkbdKeyboardDrawingGroupLevel must be a known GType so that language bindings can correctly know how to marshal it.

The (transfer container) annotation is puzzling: does the C API take ownership of the C array but not of its data?

Since the C API expects an array of pointers, then:

  (array)

should be enough. The C API does not provide a length for the C array, so the array must be either NULL-terminated:

  (array null-terminated=1)

 or must be of a fixed size N:

  (array fixed-size=N)
Comment 8 Antonio Ospite 2017-11-29 17:09:21 UTC
(In reply to Emmanuele Bassi (:ebassi) from comment #7)
> gobject-introspection doesn't do anything by itself; it just provides an API
> to dynamically load a shared library, and and API to call symbols into it.
> The responsibility for marshalling data to and from the C API is left to the
> language binding calling libgirepository's API.
>
> Assuming that the annotations are correct, if gjs works and pygobject
> doesn't, then it's likely a pygobject bug.
>

gjs was the only binding I tried which worked without changing the function signature, so I concluded this was because of some "memory bingo": it worked even if the gir file didn't seem to represent the function correctly.

> The annotation:
> 
> (array) (transfer container) (type GkbdKeyboardDrawingGroupLevel**)
> (element-type GkbdKeyboardDrawingGroupLevel *)
> 
> is incorrect. The `type` and `element-type` annotations are unnecessary, but
> GkbdKeyboardDrawingGroupLevel must be a known GType so that language
> bindings can correctly know how to marshal it.
>

An what about plain C types?

GkbdKeyboardDrawingGroupLevel is a C structure with scalar fields, and it looks like it is recognized by g-ir-scanner just fine.

The problems seems to be in the declaration of an array of pointers to it: GkbdKeyboardDrawingGroupLevel *groupLevels[]
Like if the square brackets are not recognized by g-ir-scanner in this case.

That's why I (blindly) tried to overcome this with the "type" and "element-type" annotations. But this opened _another_ problem: the annotations were ignored even if they were there.

But let's not insist on this point, I think the main issue is: having the function signature correctly represented in the gir file.  

As soon as the function signature uses GkbdKeyboardDrawingGroupLevel** instead of  GkbdKeyboardDrawingGroupLevel*[] things seem to start working.

> The (transfer container) annotation is puzzling: does the C API take
> ownership of the C array but not of its data?
>

I'd say so, yes; since it's an array of pointers to structs, I think it makes sense (despite the ugliness of the API) that the array belongs to the function but the elements do not.

> Since the C API expects an array of pointers, then:
> 
>   (array)
> 
> should be enough. The C API does not provide a length for the C array, so
> the array must be either NULL-terminated:
> 
>   (array null-terminated=1)
> 
>  or must be of a fixed size N:
> 
>   (array fixed-size=N)

I agree, and (array fixed-size=4) will work in this case.

The main question is:
Is the current syntax with square brackets supposed to work with g-ir-scanner? Or should the function signature be changed?

I double checked and:
  - using only "(array fixed-size=4)" does not work anywhere

  - using "(array fixed-size=4) (transfer container)" works in gjs (even though the gir file looks still incorrect) but not in the other languages 

  - using "(array fixed-size=4) (transfer container)" and using GkbdKeyboardDrawingGroupLevel** instead of GkbdKeyboardDrawingGroupLevel*[] in the function signature works for gjs and python, but not in perl (but that may be because I don't really know perl...)
Comment 9 GNOME Infrastructure Team 2021-07-05 12:33:51 UTC
GNOME is going to shut down bugzilla.gnome.org in favor of gitlab.gnome.org.
As part of that, we are mass-closing older open tickets in bugzilla.gnome.org
which have not seen updates for a longer time (resources are unfortunately
quite limited so not every ticket can get handled).

If you can still reproduce the situation described in this ticket in a recent
and supported software version, then please follow
  https://wiki.gnome.org/GettingInTouch/BugReportingGuidelines
and create a new ticket at
  https://gitlab.gnome.org/GNOME/libgnomekbd/-/issues/

Thank you for your understanding and your help.