Calling Native Linux APIs

From Xojo Documentation

You can call into Linux APIs to use methods and properties that are not built into the framework by using the Declare command. To create a Declare statement you first need to track down the API you want to use using Linux documentation.

The Linux API is largely based on the C/C++ programming language and makes heavy use of structures.

Window Opacity

As a simple example, the gtk_widget_set_opacity method in libgtk-3 can be used to change the window opacity so that it appears more transparent. This is what the method declaration looks like in the Gnome docs:

gtk_widget_set_opacity (GtkWidget *widget,
                        double opacity);

This tells you it is a method (sub) because the "void" at the beginning indicates it does not return a value. The first parameter is a pointer to a GtkWidget, which in this case is the handle to the Xojo window. The opacity is a Double in the range of 0 to 1. So the above method call translates to a Declare that looks like this:

Declare Sub gtk_widget_set_opacity Lib "libgtk-3" (handle As Integer, opacity As Double)

To set the window to 75% opacity you can call it like this:

gtk_widget_set_opacity(Self.Handle, 0.75)

Because this method is called for a window, you can put it in a Xojo Extension Method to make it easier to call. To do this, create a global method on a module like this:

Public Sub Opacity(Extends w As Window, value As Double)
#If TargetLinux Then
Declare Sub gtk_widget_set_opacity Lib "libgtk-3" (handle As Integer, opacity As Double)
gtk_widget_set_opacity(w, value)
End Sub

Note the use of the "#If TargetLinux" line. This prevents this code from being compiled into Windows or macOS builds of your app where the code could possible crash your app.

This now allows you to have code like this on a window's Open event to center the window:


Default Control Sizes

With the many different Window Managers and themes on Linux, and personal preferences, you can be assured that your UI will look different from one Linux user to the next. The main challenge of being a native Linux app is trying to normalize the UI experience across different platforms (yes, even different Linux distros).

As each platform has their own native default control sizes and such, your app will most likely need to be adjusted accordingly. On Windows and macOS this almost never changes, so thankfully a 22 pixel high PushButton would look just fine on macOS, as it does on Windows. The problem on Linux is that you have different themes that dictate how much padding should go into a control, and the much larger default font size.

Obtaining the default control size

A solution to this problem is to use Linux APs to get the default control size. This information is provided by GTK+ in conjunction with the Window Manager.

To start you first have to ask the Window Manager to let you know when the control has been "realized" (made available) so that you can request its default size. You will typically do that on the Open event of the control. Here is the code, which you can put on the Open event of a PushButton that has been added to a Window:

#If TargetLinux Then
Declare Sub g_signal_connect_data Lib "libgobject-2.0" _
(instance As Integer, signal As CString, _
callback As Ptr, data As Ptr, destroy_data As Ptr, _
connectFlags As Integer)

g_signal_connect_data(PushButton1.Handle, "realize", _
AddressOf RealizeCallback, Nil, Nil, 0)

The actual Declare calls g_signal_connect_data in the libgobject library which takes a reference to the control, the text "realize" to indicate we want to know when the control is available and then the address of a method to call (the callback method) with the information.

So now you need to create the callback method, which is just a shared method on the Window. Here is its code:

Private Shared Sub RealizeCallback(widget As Integer, data As Ptr)
#If TargetLinux Then
Declare Sub gtk_widget_get_preferred_size Lib "libgtk-3" _
(widget As Integer, ByRef minSize As GtkRequisition, ByRef naturalSize As GtkRequisition)
Dim minSize, naturalSize As GtkRequisition
gtk_widget_get_preferred_size(widget, minSize, naturalSize)

For iter As Integer = 0 To Window1.ControlCount - 1
If Window1.Control(iter) IsA RectControl Then
Dim rc As RectControl = RectControl(Window1.Control(iter))
If rc.Handle = widget Then
rc.Width = minSize.Width
rc.Height = minSize.Height
End If
End If
End Sub

This method has a Declare that calls gtk_widget_get_preferred_size in the libgtk-3 library. This method takes a reference to the control and then has two parameters that contain size information. These parameters use the GtkRequisition structure which you will create in a moment. Looking at the code you can see that it is looping through all the controls on the Window and updating any that are RectControls to use the size reported back from the gtk_widget_get_preferred_size Declare call.

The last thing you need to add is the GtkRequisition structure so add a Structure to the Window and call it GtkRequisition, with two properties: Width As Int32 and Height As Int32.

Structure GtkRequisition
Width As Int32
Height As Int32
End Structure

One thing to keep in mind with this technique is that since it is adjusting the size of controls it will alter the look of your layout. You may want to make sure your layout has enough room for control sizes to change (probably they will increase) without causing them to overlap.

Related Information