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
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
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.
- 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
- 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!
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>
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.


