General Rules#
Different projects might use different styles. General guidelines are:
- Check the project’s
CONTRIBUTING.mdorHACKING.mdfor details - Format code like the surrounding code in the same file
GObject C Based Projects#
For C projects using GObject we mostly use kernel style, but
- Use spaces, never tabs
- Use 2 spaces for indentation
Here are more details:
GTK-style Function Argument Indentation#
If function arguments don’t fit into a single line, use GTK style alignment:
static gboolean
on_key_press_event (GtkWidget *widget,
GdkEvent *event,
gpointer data)Braces#
Everything except functions and structs should have the opening curly brace on the same line.
Good:
if (i < 0) {
...
}Bad:
if (i < 0)
{
...
}Single-line if or else statements don’t need braces, but if either branch uses braces, both
must:
Good:
if (i < 0)
i++;
else
i--;if (i < 0) {
i++;
j++;
} else {
i--;
}if (i < 0) {
i++;
} else {
i--;
j--;
}Bad:
if (i < 0) {
i++;
} else {
i--;
}if (i < 0) {
i++;
j++;
} else
i--;if (i < 0)
i++;
else {
i--;
j--;
}Function calls have a space between function name and invocation:
Good:
visible_child_name = gtk_stack_get_visible_child_name (GTK_STACK (self->stack));Bad:
visible_child_name = gtk_stack_get_visible_child_name(GTK_STACK(self->stack));Header Inclusion Guards#
Guard header inclusion with #pragma once rather than the traditional
#ifndef-#define-#endif trio.
Comment style#
In comments use full sentences with proper capitalization and punctuation. Use C-style comments /* … */, avoid C++ style comments //.
Good:
/* Make sure we don't overflow. */Bad:
/* overflow check */Static functions#
Static functions use the class prefix but it can be dropped to shorten the method name. E.g. with a
type PhoshDialer both work:
Good:
static void
grab_focus_cb (PhoshDialer *dialer, gpointer unused)static void
phosh_dialer_grab_focus_cb (PhoshDialer *dialer, gpointer unused)Note however, that virtual methods like <class_name>_{init,constructed,finalize,dispose} always
use the class prefix. These functions are usually never called directly but only assigned once in
<class_name>_constructed so the longer name is acceptable. This also helps to distinguish virtual
methods from regular private methods.
Self argument#
The first argument is usually the object itself so call it self. E.g. for a static function:
Good:
static gboolean
on_expire (FooButton *self)
{
g_return_val_if_fail (BAR_IS_FOO_BUTTON (self), FALSE);
...
return FALSE;
}And for a public function:
Good:
int
foo_button_get_state (FooButton *self)
{
FooButtonPrivate *priv = foo_button_get_instance_private(self);
g_return_if_fail (FOO_IS_BUTTON (self));
g_return_val_if_fail (FOO_IS_BUTTON (self), -1);
return priv->state;
}Source file layout#
We use one file per GObject named like the GObject lowercase and ‘_’ replaced by ‘-’. So a
hypothetical PhoshThing would go to src/thing.c. If there are likely name clashes, add the
phosh- prefix (e.g. phosh-wayland.c). The individual C files should be structured as (top to
bottom of file):
-
License boilerplate
/* * Copyright (C) year copyright holder * * SPDX-License-Identifier: GPL-3-or-later * Author: you <youremail@example.com> */ -
A log domain, usually the filename with
phosh-prefix#define G_LOG_DOMAIN "phosh-thing" -
#includes: Phosh ones go first, then glib/gtk, then generic C headers. These blocks are separated by newline and each sorted alphabetically:#define G_LOG_DOMAIN "phosh-settings" #include "phosh-config.h" #include "settings.h" #include "shell-priv.h" #include <gio/gdesktopappinfo.h> #include <glib/glib.h> #include <math.h>This helps to detect missing headers in includes.
-
docstring: If you have trouble describing the class concisely, then it might be an indication that it should be split into multiple classes.
/** * PhoshYourThing: * * Short, single line, summary * * A longer description with details that can be * multiline. * * Since: 0.44.0 */ -
property enum
enum { PROP_0, PROP_FOO, PROP_BAR, LAST_PROP }; static GParamSpec *props[LAST_PROP]; -
signal enum
enum { FOO_HAPPENED, BAR_TRIGGERED, N_SIGNALS }; static guint signals[N_SIGNALS]; -
type definitions
typedef struct _PhoshThing { GObject parent; ... } PhoshThing; G_DEFINE_TYPE (PhoshThing, phosh_thing, G_TYPE_OBJECT) -
private methods and callbacks (these can also be placed at convenient locations above
phosh_thing_constructed ()) -
phosh_thing_set_property (). Set properties. If setting a property requires more than a single line prefer adding a setter method, e.g. for thefooproperty, the setter method would bephosh_thing_set_foo (). This is almost always the case as we preferG_PARAM_EXPLICIT_NOTIFY(see below).static void phosh_thing_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { PhoshThing *self = PHOSH_THING (object); switch (property_id) { case PROP_FOO: phosh_thing_set_foo (self, g_value_get_string (value)); break; … default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } -
phosh_thing_get_property () -
phosh_thing_constructed (): Finish object construction. Usually only needed if you need the values of multiple properties passed at object construction time. -
phosh_thing_dispose (): Usually only needed when you need to break reference cycles. Otherwise, preferfinalize. Asdisposecan be run multiple times, useg_clear_*to avoid freeing resources multiple times:static void phosh_thing_dispose (GObject *object) { PhoshThing *self = PHOSH_THING (object); g_cancellable_cancel (self->cancel); g_clear_object (&self->cancel); g_clear_object (&self->bar); g_clear_pointer (&self->foo, g_free); G_OBJECT_CLASS (phosh_thing_parent_class)->dispose (object); } -
phosh_thing_finalize (): Free allocated resources. -
phosh_thing_class_init (): Define properties and signals. For widget templates bind child widgets and signal handlers. -
phosh_thing_init (): Initialize defaults for member variables here. -
phosh_thing_new (): A convenience wrapper aroundg_object_new (). Don’t do further object initialization here but rather do that inphosh_thing_init (),phosh_thing_constructed ()or individual property setters. This ensures that objects can be constructed either via this constructor org_object_new (). -
Public methods, all starting with the object name (i.e.
phosh_thing_).
The reason public methods go at the bottom is that they are declared in the header file and can thus be referenced from anywhere else in the source file.
Derivable Parent Widgets#
If the widget is derivable and accepts a child, then prefer to expose the child
as a property than as its UI child (<child> in UI file). This way we align
with how GTK 4 structures the API and avoid the need to hijack the parent
container’s add/remove method in the derivatives.
For example, a derivable parent widget called PhoshFoo would have the code for
adding and removing child like this:
...
enum {
PROP_0,
PROP_CHILD,
LAST_PROP
};
static GParamSpec *props[LAST_PROP];
typedef struct {
GtkWidget *child;
} PhoshFooPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (PhoshFoo, phosh_foo, GTK_TYPE_BOX);
...
static void
phosh_foo_set_child (PhoshFoo *self, GtkWidget *child)
{
PhoshFooPrivate *priv;
g_return_if_fail (PHOSH_IS_FOO (self));
priv = phosh_foo_get_instance_private (self);
if (priv->child) {
/* Remove the existing child */
}
priv->child = child;
/* child can be NULL, which is used to remove
existing child without replacement. */
if (priv->child) {
/* Add the new child */
}
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_CHILD]);
}For the widgets that take multiple children and adds them to internal widgets,
prefer to implement GtkBuildable interface and expose methods to add and
remove child like phosh_foo_add and phosh_foo_remove.
As an example, a PhoshFoo that can have multiple children, its code would be
like:
...
static void phosh_foo_buildable_init (GtkBuildableIface *iface);
G_DEFINE_TYPE_WITH_CODE (PhoshFoo, phosh_foo,
PHOSH_TYPE_FOO,
G_ADD_PRIVATE (PhoshFoo)
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
phosh_foo_buildable_init))
...
static GtkBuildableIface *parent_buildable_iface;
static void
phosh_foo_buildable_add_child (GtkBuildable *buildable,
GtkBuilder *builder,
GObject *child,
const gchar *type)
{
PhoshFoo *self = PHOSH_FOO (buildable);
/* Check if the child is meant for PhoshFoo by checking child's type etc. */
if (condition) {
phosh_foo_add (self, child);
return;
}
/* The parent is a container itself so chain up */
parent_buildable_iface->add_child (buildable, builder, child, type);
}
static void
phosh_foo_buildable_init (GtkBuildableIface *iface)
{
parent_buildable_iface = g_type_interface_peek_parent (iface);
iface->add_child = phosh_foo_buildable_add_child;
}
...
void
phosh_foo_add (PhoshFoo *self, GtkWidget *child)
{
/* Add the child to self using usual logic. */
...
}The ultimate aim is to do a little heavy-lifting in the widget implementations,
so it is easy to port them to GTK 4. And, as a direct consequence, restrict the
usage of GtkContainer APIs for implementation logic only and expose public
methods to do the same.
CSS Theming#
For custom widgets set the css name using gtk_widget_class_set_css_name ().
There’s no need to set an (additional) style class in the ui file.
Good:
static void
phosh_lockscreen_class_init (PhoshLockscreenClass *klass)
{
…
gtk_widget_class_set_css_name (widget_class, "phosh-lockscreen");
…
}Bad:
<template class="PhoshLockscreen" parent="…">
…
<style>
<class name="phosh-lockscreen"/>
</style>
…
</template>Properties#
Prefix property enum names with PROP_.
Good:
enum {
PROP_0,
PROP_NUMBER,
PROP_SHOW_ACTION_BUTTONS,
PROP_COLUMN_SPACING,
PROP_ROW_SPACING,
PROP_RELIEF,
LAST_PROP
};
static GParamSpec *props[LAST_PROP];Signal Emission on Changed Properties#
Except for G_CONSTRUCT_ONLY properties use G_PARAM_EXPLICIT_NOTIFY and notify
about property changes only when the underlying variable changes value:
static void
on_present_changed (PhoshDockedInfo *self)
{
…
if (self->enabled == enabled)
return;
self->enabled = enabled;
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ENABLED]);
}
static void
phosh_docked_info_class_init (PhoshDockedInfoClass *klass)
{
…
/**
* PhoshDockedInfo:enabled:
*
* Whether docked mode is enabled
*
* Since: 0.44.0
*/
props[PROP_PRESENT] =
g_param_spec_boolean ("present", "", "",
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
…
}This makes sure we minimize the notifications on changed property values.
Property documentation#
Prefer a docstring over filling in the properties’ nick and blurb. See
example above.
Property bindings#
If the state of a property depends on the state of another one prefer
g_object_bind_property () to keep these in sync:
Good:
g_object_bind_property (self, "bar",
self->avatar, "loadable-icon",
G_BINDING_SYNC_CREATE);This has the upside that the binding goes away automatically when either of the two objects gets disposed and also avoids bugs in manual syncing code.
For widgets you can construct the binding via the UI XML:
<object class="PhoshQuickSetting" id="foo_quick_setting">
<property name="sensitive" bind-source="wwaninfo" bind-property="present" bind-flags="sync-create"/>
…
</object>Signals#
Don’t prefix signal enum names with SIGNAL_.
Good:
enum {
SUBMITTED,
DELETED,
SYMBOL_CLICKED,
N_SIGNALS
};
static guint signals[N_SIGNALS];Signals should be documented. Since class_offset, accumulator and
c_marshaller are often unused we put them on a single line.
Good:
/**
* PhoshWwanManager::new-cbm
* @self: The wwan manager
* @message: The message text
* @channel: The channel specifying the source of the CBM
*
* This signal is emitted when a new cell broadcast message is
* received.
*
* Since: 0.44.0
*/
signals[NEW_CBM] = g_signal_new ("new-cbm",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL,
G_TYPE_NONE,
2,
G_TYPE_STRING,
G_TYPE_UINT);In the same source file prefer g_signal_emit over
g_signal_emit_by_name:
Good:
g_signal_emit (self, signals[NEW_CBM], "data", 32);Callbacks#
There are callbacks for signals, async functions, and actions. We
usually have them all start with on_ to make it easy to spot
that these aren’t just methods (and hence have restrictions regarding
their function arguments and return values).
-
Signal handlers are named
on_<signalname>. E.g. the handler for aGListModelsitems-changedsignal would be namedon_items_changed (). To avoid ambiguity one can add the emitter’s name (on_devices_list_box_items_changed ()). -
For
notify::signal handler callbacks acting on property changes we use theon_<property>_changed ()naming. E.g.on_nm_signal_strength_changed (). -
For
GAsyncReadyCallbacks we useon_<async-function>_ready(), e.g. the callback fornm_client_new_asyncthat invokesnm_client_new_finishwould be namedon_nm_client_new_ready (). -
For actions we use
on_<action>_activated, e.g. for ago-backaction we’d useon_go_back_activated (). -
In cases where the signal handler name should express what it does rather than what signal it connects to, we use a
_cbsuffix. This is often the case when we want to use the same signal handler to handle multiple signals. E.g. a callback that updates aGtkStackPagewhen a signal happens would be namedupdate_stack_page_cb ().
API Contracts#
Public (non static) functions must check the input arguments at the
top of the function. This makes it easy to reuse them in other parts
and makes API misuse easy to debug via G_DEBUG=fatal-criticals. You
usually want to check argument types and if the arguments fulfill the
requirements (e.g. if they need to be non-NULL). Public functions
should have doc strings.
Good:
/**
* phosh_foo_set_name:
* @self: The foo
* @name: The name to set
*
* Set Foo's `name`
*/
void
phosh_foo_set_name (PhoshFoo *self, const char *name)
{
GtkWidget *somewidget;
g_return_if_fail (PHOSH_IS_FOO (self));
g_return_if_fail (name != NULL);
…
}GObject introspection annotations#
For the Rust bindings we want to have introspection annotations on public methods. Use a space after the colon:
Good:
* Returns: (transfer none): The generated wisdomBad:
* Returns:(transfer none): The generated wisdomUncrustify#
Several projects ship a uncrustify to ease formatting. The config is not 100% correct so if it breaks above rules trust your style more than uncrustify.
Rust Based Projects#
For Rust projects see if rustfmt is usually used in CI to check the format.
Make sure to use it too during development.
Python Scripts#
Projects that ship Python scripts often have a lint check in CI. If in doubt
formatting with black usually comes close.