Interfacing Raku to Gnome GTK+

Implementation details

  • The native objects wrapped in Raku classes are mostly not visible to the user, but if they do, their types always start wit N-. E.g. N-GObject, N-GValue, etc. This is not yet done everywhere.
    • To retrieve a native object, call ._get-native-object().
    • To set a native object, call .set-native-object(). Both methods are mostly not used by user software and are only of use internally.
  • The FALLBACK() method defined in Gnome::GObject::Object is called if a method is not found. This makes it possible to search for the defined native subroutines in the class and inherited classes. It calls the _fallback() method, which starts with the class at the bottom and working its way up until the subroutine is found. Each _fallback() method is calling callsame() when a sub is not found yet. The resulting subroutine address is returned and processed with the test-call() functions from Gnome::N::X. Thrown exceptions are handled by the function test-catch-exception() from the same module.

  • All classes deriving from Gnome::N::TopLevelClassSupport know about the :native-object(…) named attribute when instantiating a widget class. This is used when the result of another native sub returns a N-GObject. In most cases a Raku object can be provided. The method will retrieve the native object from the given Raku object.

  • Classes inheriting from Gnome::GObject::Object also recognize the named argument :build-id(…) which is used to get a N-GObject from a Gnome::Gtk3::Builder object. It does something like $builder.get-object(…). A builder must be initialized and loaded with a GUI description before to be useful. This option works for all child classes too if those classes are managed by Gnome::Gtk3::Builder.

    An example to see both named arguments in use is when cleaning a list box;

    # instantiate a list box using the :build-id argument
    my Gnome::Gtk3::ListBox $list-box .= new(:build-id<someListBox>);
    loop {
      # Keep the index 0, entries will shift up after removal
      my $nw = $list-box.get-row-at-index(0);
      last unless $nw.defined;
    
      # Instantiate a container object using the :widget argument
      my Gnome::Gtk3::ListBoxRow $row .= new(:native-object($nw));
      $row.gtk-widget-destroy;
    }
    
  • Wrapped subs
    • The C functions can only return simple values like int32, num64, etc or Pointer to the values or structures. This can be handled by Raku and is not a problem. However, many subs are defined so that the values are returned in a Pointer argument and Raku users must handle that properly by giving a real location instead of a constant. Also this can be done properly, simple types need a rw trait and structures and arrays are already given by pointer. But to make things a bit comfortable, those functions are converted to return the values normally. Many subs still need to be converted to show this behavior!.

      So the definition of the sub is changed like so;

      sub gtk_range_get_range_rect ( N-GObject $range --> GdkRectangle ) {
        _gtk_range_get_range_rect( $range, my GdkRectangle $rectangle .= new);
        $rectangle
      }
      
      sub _gtk_range_get_range_rect ( N-GObject $range, GdkRectangle $rectangle )
        is native(&gtk-lib)
        is symbol('gtk_range_get_range_rect')
        { * }
      

      Now we can do

      my GdkRectangle $rectangle = $range.get-range-rect();
      
    • The same situation arises when a native sub wants to return more than one value. Again this is solved by creating a wrapper around the native sub, the arguments can be provided locally and after the call, the wrapper returns a list of values. also here, many subs still need to be converted to show this behavior!

      An example from Gnome::Gdk3::Window to get the coordinates of a window;

      sub gdk_window_get_position ( N-GObject $window --> List ) {
        _gdk_window_get_position( $window, my int32 $x, my int32 $y);
        ( $x, $y)
      }
      
      sub _gdk_window_get_position (
        N-GObject $window, int32 $x is rw, int32 $y is rw
      ) is native(&gdk-lib)
        is symbol('gdk_window_get_position')
        { * }
      

      To use it one can write the following

      my Int ( $x, $y) = $w.get-position;
      
    • There is no Boolean type in C. All Booleans are int32 and only 0 (False) or 1 (True) is used. One can use True and False when needed as an argument, but it is not possible to store an int32 back into a boolean. So the next definition;
      sub gtk_widget_set_visible ( N-GObject $widget, int32 $visible )
        is native(&gtk-lib)
        { * }
      

      can be used as;

      $button.gtk_widget_set_visible(True);
      

      Documentation and examples mentioning the use of 0 and 1, must be rewritten to show True and False where possible.

      Testing the returned ‘boolean’ can be done using if/else and no extra changes are needed. However, to store it, the value must be coersed into Bool if one needs it that way.

      sub gtk_widget_get_visible ( N-GObject $widget --> int32 )
        is native(&gtk-lib)
        { * }
      

      Use when testing

      if !$button.gtk_widget_get_visible {
        $button.gtk_widget_set_visible(True);
      }
      

      Use following when storing it in a boolean.

      my Bool $is-visible = $button.gtk_widget_get_visible.Bool;
      

      To wrap a sub like above is a bit too much so it is not done.

    • Subs are also wrapped to cope with subroutines which have variable argument lists.

    • In the mean time, doing benchmark tests, using a method for those wrappers showed a good speedup of 8 to 10 times. The module generator will now generate a method side by side to the native sub declaration enabling the possible convertions mentioned above. Older modules will also get these methods for the speedup and to simplify the interface. The downside is that debugging with Gnome::N::debug(:on); will not show much when invoking these methods.