kolunmi's website

I am a FOSS software developer. You can find me around this space under the name "kolunmi".

1. Contact

You can contact me at kolunmi@posteo.net

2. Links

2.1. Github

2.2. Ko-Fi

3. Some Projects

Name Description
Bazaar New App store for GNOME with 100,000 weekly active users
gtk-crusader-village AIV editor for Stronghold Crusader
cpc-gpu Graphics API Abstraction Library

4. Images

bazaar.png
Figure 1: Bazaar, a few months ago. See the github page for a more up to date screenshot

5. Tutorials

5.1. Using GObject

GObject is the foundation for GTK's types. All objects derive from this class, which grants many tools for plumbing data throughout your program or off to other non-C languages. Using GTK, especially in C, requires a good understanding of the tools provided by GObject.

This tutorial assumes you have working knowledge of C.

5.1.1. Full Example

Personally, I'm much better at learning when I see a full example first. We'll declare and define an object class MyCoolObject, which has prefix My and name CoolObject. Or in snake case (used for methods), prefix my and name cool_object.

  1. my-cool-object.h
    #pragma once
    
    /* Include the GObject library */
    #include <glib-object.h>
    
    /* We need this for the GIcon object interface type, which we'll use later. See
       https://docs.gtk.org/gio/iface.Icon.html */
    #include <gio/gio.h>
    
    /* This macro sets up the necessary inclusion syntax for C declarations if you
       are using this header from C++ */
    G_BEGIN_DECLS
    
    /* Define a macro for retrieving the GType for this object class. GType is
       typedef'd to gsize, or basically size_t. This number will serve as the ID for
       your class in the type system. */
    #define MY_TYPE_COOL_OBJECT (my_cool_object_get_type ())
    
    /* Use this macro to declare your type. The arg meanings are as follows:
    
       MyCoolObject   -> the full pascal case C type name
       my_cool_object -> the same as above except snake case
       MY             -> the macro-style (snake case but capitalized) prefix; as
                         an example, for GtkFileDialog, this would be GTK
       COOL_OBJECT    -> the macro-style name; as an example, for GtkFileDialog,
                         this would be FILE_DIALOG
       GObject        -> the C type of the parent class */
    G_DECLARE_FINAL_TYPE (MyCoolObject, my_cool_object, MY, COOL_OBJECT, GObject)
    
    /* Constructor function */
    MyCoolObject *my_cool_object_new (void);
    
    /* We'll make the class MyCoolObject have two properties, a boolean
       "is-really-cool" and an object "icon" of type GIcon. Properties are cool
       because they give you a ton of tools for synchronizing data across your
       program automatically. Here we declare setters and getters for these
       properties */
    void     my_cool_object_set_is_really_cool (MyCoolObject *self, gboolean is_really_cool);
    gboolean my_cool_object_get_is_really_cool (MyCoolObject *self);
    
    void   my_cool_object_set_icon (MyCoolObject *self, GIcon *icon);
    GIcon *my_cool_object_get_icon (MyCoolObject *self);
    
    /* Let's also set up a regular method for this class. Since this function will
       return a dynamically allocated string, it's best practice to use "dup"
       (duplicate) instead of "get" in the name */
    char *my_cool_object_dup_description (MyCoolObject *self);
    
    G_END_DECLS
    
  2. my-cool-object.c
    /**
     * MyCoolObject:
     *
     * MyCoolObject serves these purposes...
     */
    
    /* This define should come before any inclusions. It will give a domain to the
       GLib logging functions, which is useful for debugging */
    #define G_LOG_DOMAIN "MyCoolObject"
    
    /* Include our header */
    #include "my-cool-object.h"
    
    /* The backing struct for our class definition */
    struct _MyCoolObject
    {
      /* parent_instance MUST be the first field, and it MUST be an instance of the
         parent class of MyCoolObject, which is GObject */
      GObject parent_instance;
    
      /* Storage for our properties */
      gboolean is_really_cool;
      GIcon   *icon;
    };
    
    /* Use this macro to define the type, in a similar manner to how we declared it
    
       MyCoolObject   -> the full pascal case C type name
       my_cool_object -> the same as above except snake case
       G_TYPE_OBJECT  -> the GType of the parent class */
    G_DEFINE_FINAL_TYPE (MyCoolObject, my_cool_object, G_TYPE_OBJECT)
    
    /* These enums will be used to index our property specifications, defined after
       this. PROP_0 is equal to 0 and should not be used. LAST_PROP is only used to
       know how many properties we have. */
    enum
    {
      PROP_0,
    
      PROP_IS_REALLY_COOL,
      PROP_ICON,
    
      LAST_PROP
    };
    
    /* We'll populate this in my_cool_object_class_init () */
    static GParamSpec *props[LAST_PROP] = { 0 };
    
    /* This virtual function is called on the object when its last reference count
       is dropped and it is about to be destroyed. We should release our resources
       here */
    static void
    my_cool_object_dispose (GObject *object)
    {
      /* The MY_COOL_OBJECT () function performs a type check on the object and
         returns NULL if the input object is not of type MyCoolObject */
      MyCoolObject *self = MY_COOL_OBJECT (object);
    
      /* The g_clear_pointer () macro is an easy way to call a destruction function
         on a pointer only if its location is non-NULL, and subsequently set that
         location to NULL to avoid use-after-free bugs.
    
         Since g_object_unref is used so commonly in conjunction with this macro,
         most codebases just use the wrapper macro g_clear_object (), which only
         takes 1 argument, the address of the pointer */
      g_clear_pointer (&self->icon, g_object_unref);
    
      /* Chain up to the parent class */
      G_OBJECT_CLASS (my_cool_object_parent_class)->dispose (object);
    }
    
    /* This virtual function is called when a property value is requested from our
       object. The prop_id will correspond to the enums we defined earlier. Our job
       here is to "fill" the GValue argument with the value we want to give. */
    static void
    my_cool_object_get_property (GObject    *object,
                                 guint       prop_id,
                                 GValue     *value,
                                 GParamSpec *pspec)
    {
      MyCoolObject *self = MY_COOL_OBJECT (object);
    
      switch (prop_id)
        {
        case PROP_IS_REALLY_COOL:
          g_value_set_boolean (value, my_cool_object_get_is_really_cool (self));
          break;
        case PROP_ICON:
          g_value_set_object (value, my_cool_object_get_icon (self));
          break;
        default:
          G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        }
    }
    
    /* This virtual function is called when one of our properties is requested to be
       swapped out with a new value. The prop_id will correspond to the enums we
       defined earlier. Our job here is to read the GValue argument's value and
       store it somehow. Notice that the GValue argument is marked const. */
    static void
    my_cool_object_set_property (GObject      *object,
                                 guint         prop_id,
                                 const GValue *value,
                                 GParamSpec   *pspec)
    {
      MyCoolObject *self = MY_COOL_OBJECT (object);
    
      switch (prop_id)
        {
        case PROP_IS_REALLY_COOL:
          my_cool_object_set_is_really_cool (self, g_value_get_boolean (value));
          break;
        case PROP_ICON:
          my_cool_object_set_icon (self, g_value_get_object (value));
          break;
        default:
          G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        }
    }
    
    /* This is the class initialization function. This will be called only one time
       for the duration of our program, when our type is initialized. You can make
       sure this function has been called for a type by calling
       g_type_ensure(TYPE). */
    static void
    my_cool_object_class_init (MyCoolObjectClass *klass)
    {
      GObjectClass *object_class = G_OBJECT_CLASS (klass);
    
      /* Set the vfuncs we are implementing */
      object_class->set_property = my_cool_object_set_property;
      object_class->get_property = my_cool_object_get_property;
      object_class->dispose      = my_cool_object_dispose;
    
      /* This is where we populate the property specifications. There are many
         variants of "g_param_spec_*" for different types. Take a look at
         <gobject/gparamspecs.h> for a full list of them and their arguments */
      props[PROP_IS_REALLY_COOL] =
        g_param_spec_boolean ("is-really-cool", /* The name of this property */
                              NULL, NULL,
                              FALSE, /* The default value of this property */
                              /* These flags allow you to specify special behavior
                                 for the property. Use G_PARAM_READABLE and
                                 G_PARAM_WRITABLE for a read-only or write-only
                                 property respectively. G_PARAM_STATIC_STRINGS
                                 allows for a slight performance boost by making
                                 GObject aware of the fact that the strings we
                                 passed to this constructor are not dynamically
                                 allocated. G_PARAM_EXPLICIT_NOTIFY lets GObject
                                 know that we will manually emit the "notify" signal
                                 on the object when we want others to know that this
                                 property has changed, either from a setter or some
                                 other event. */
                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
    
      props[PROP_ICON] =
        g_param_spec_object ("icon",
                             NULL, NULL,
                             G_TYPE_ICON,
                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
    
      /* Install the properties on the object class */
      g_object_class_install_properties (object_class, LAST_PROP, props);
    }
    
    /* This function is called when a particular instance of MyCoolObject is
       created. This is where you should initialize any parts of the object that
       doesn't just need to be zeroed out (GObject does this automatically). This
       means initializing mutexes, allocating arrays, etc. */
    static void
    my_cool_object_init (MyCoolObject *self)
    {
      /* Nothing to do... */
    }
    
    /* It is conventional to define a small wrapper around g_object_new() for a
       GObject type */
    MyCoolObject *
    my_cool_object_new (void)
    {
      return g_object_new (MY_TYPE_COOL_OBJECT, NULL);
    }
    
    /* Setter for property "is-really-cool" */
    void
    my_cool_object_set_is_really_cool (MyCoolObject *self,
                                       gboolean is_really_cool){
      /* Verify the pointer argument */
      g_return_if_fail (MY_IS_COOL_OBJECT (self));
    
      /* This is a cute trick you can use to check the equality of booleans in C
         without fear of "trues" being any non-zero value, like if is_really_cool=1
         and self->is_really_cool=5 */
      if (!!is_really_cool == !!self->is_really_cool)
        return;
    
      self->is_really_cool = is_really_cool;
    
      /* Since this property has G_PARAM_EXPLICIT_NOTIFY, we MUST call this if we
         want to notify others that the property has changed */
      g_object_notify_by_pspec (G_OBJECT (self), props[PROP_IS_REALLY_COOL]);
    }
    
    /* Getter for property "is-really-cool" */
    gboolean
    my_cool_object_get_is_really_cool (MyCoolObject *self)
    {
      g_return_val_if_fail (MY_IS_COOL_OBJECT (self), FALSE);
      return self->is_really_cool;
    }
    
    /* Setter for property "icon" */
    void
    my_cool_object_set_icon (MyCoolObject *self,
                             GIcon *icon)
    {
      g_return_if_fail (MY_IS_COOL_OBJECT (self));
    
      if (icon == self->icon)
        return;
    
      g_clear_pointer (&self->icon, g_object_unref);
      if (icon != NULL)
        self->icon = g_object_ref (icon);
    
      g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ICON]);
    }
    
    /* Getter for property "icon" */
    GIcon *
    my_cool_object_get_icon (MyCoolObject *self)
    {
      g_return_val_if_fail (MY_IS_COOL_OBJECT (self), NULL);
      return self->icon;
    }
    
    /* Implement our special method */
    char *
    my_cool_object_dup_description (MyCoolObject *self)
    {
      const char *cool_level_str = NULL;
    
      g_return_val_if_fail (MY_IS_COOL_OBJECT (self), NULL);
    
      if (self->is_really_cool)
        cool_level_str = "REALLY COOL";
      else
        cool_level_str = "cool ig";
    
      if (self->icon != NULL)
        {
          char *icon_str = NULL;
          char *tmp      = NULL;
    
          icon_str = g_icon_to_string (self->icon);
          if (icon_str == NULL)
            return g_strdup (cool_level_str);
    
          tmp = g_strdup_printf ("%s (the icon is %s)", cool_level_str, icon_str);
          g_free (icon_str);
          return tmp;
        }
      else
        return g_strdup (cool_level_str);
    }
    

5.1.2. Design Patterns

TODO

6. Link This Site!

Kolunmi 88x31 Button

To link my site with this cute button my friend ffi made for me, use this html:

<a href="https://kolunmi.net/" target="_blank"><img src="https://kolunmi.net/kolunmi88x31.png" alt="Kolunmi 88x31 Button"></a>

6.1. Some of My Friends :D

ffi 88x31 Button Lumaeris' 88x31 button Xenia 88x31 button

7. Credits

The ice cream art was made by my wonderful friend, Melody.

This website's color palette is inspired by Protesilaos Stavrou's "beach" theme for emacs in his doric-themes package.

Author: Kol

Created: 2026-03-11 Wed 23:18

Validate