HACKING
=======

If you want to contribute to herbstluftwm, this file is for you!

Contributing
------------
Beside writing code you can help herbstluftwm by testing, reporting bugs,
writing/fixing documentation (e.g. by fixing spelling mistakes) or packaging it
for distributions.

Coding style
------------
The coding style is similar to the Linux-kernel style with some changes:

  - Use 4 spaces instead of tabs.
  - Do not add any trailing spaces at the end of a line.
  - Data type names are CamelCase
  - Globals must be prefixed with g_
  - If a function returns success or failure, then encode it in a bool (from
    stdbool.h). Only use main()-like exit codes (0 = success, non zero =
    failure) for commands.
  - Always typedef struct, e.g.:

    typedef struct {
        int         first_member;
        long long   other_member;
    } MyStruct;

Build system
------------
The build system mainly is one Makefile. To check which source files depend on
which header files it uses dependency files src/*.d and ipc-client/*.d (one for
each C source file) which are generated by default.

Note: If you switch a branch, the dependency files are not marked as dirty!
This may cause unresolvable dependencies e.g. if you switch from a branch with
a new header file to a branch without it. So after switching the branch, you
always should remove dependency files by running:

    make cleandeps

Sending patches
---------------
You can use git to make commits and create patches from them via the command
git format-patch. Always specify your full name and a valid e-mail address in
the author field. The commit description must consist two parts, separated by
an empty line:

  - A mandatory short description in imperative form, e.g.: "Do things in this
    or that way". The description must not exceed 50 characters.
  - An optional longer description consisting of full sentences. This is only
    needed if the commit introduces non-trivial changes.

When introducing new features, always

  - add documentation for it in doc/herbstluftwm.txt (or doc/herbstclient.txt).
  - document the change (e.g. "new command ...") in the NEWS file.

You can send those patches to the mailing list[1] or via the irc[2].

[1] hlwm@lists.herbstluftwm.org +
[2] #herbstluftwm on irc.freenode.net

Mailing list
------------
The main mailing list for general development, discussion, release
announcements is:

    hlwm@lists.herbstluftwm.org

You can subscribe by sending a mail with subscribe in the subject to

    hlwm-request@lists.herbstluftwm.org

or by using the web interface at:

    https://lists.schokokeks.org/mailman/listinfo.cgi/hlwm

Debugging with valgrind
-----------------------
If you use tools like valgrind, then it is needed to turn of the memory
optimization settings for glib. So export this before running herbstluftwm
within valgrind:

    export G_SLICE=always-malloc
    export G_DEBUG=gc-friendly

    valgrind --leak-check=full ./herbstluftwm  -c share/autostart


Internal structure
------------------
The main modules (i.e. source file + header file) communicate like this if a
key is pressed or if a command is called via herbstclient:

                 X11
                  |
                  V
              +--------+  key  +-----+ call  +---------+
              |  main  |------>| key |------>| command |
              +--------+ event +-----+       +---------+
                  \                    output /  ^
           IPC-Call\      +------------+<----'  / Execute
            -Window `---->| ipc-server |-------'  IPC-Call
              -Event      +------------+
                                 |
                                 V
                                X11

herbstclient is rather simple:

                 Command-Line-   +------+
                   Arguments --->| main |----.
                                 |      |   X11
                    stdout  <----|      |<---'
                                 +------+

Objects
-------
There is one tree of objects in herbstluftwm to provide an easy access for the
user to many options. The usage is similar to kobjects known from the Linux
kernel. The most important functions are:

  - HSObject* hsobject_create_and_link(HSObject* parent, char* name): Create a
    new child object under parent. If name already exists it will be replaced.
  - void hsobject_unlink_and_destroy(HSObject* parent, HSObject* child): Remove
    all child entries at the parent object and destroys the object afterwards.
  - HSObject* hsobject_root(): Return the root node of the object tree.
  - void hsobject_set_attributes(HSObject* obj, HSAttribute* attributes): Set
    the attributes of an object, attributes is an array terminated by
    ATTRIBUTE_LAST.

For a full list see src/object.h. Each object has a certain set of attributes.
Each attribute has a type, which is defined by the according constructor:

  - ATTRIBUTE_BOOL(N, V, CHANGE): accords to bool
  - ATTRIBUTE_INT(N, V, CHANGE): accords to int
  - ATTRIBUTE_UINT(N, V, CHANGE): accords to unsigned int
  - ATTRIBUTE_STRING(N, V, CHANGE): accords to GString*
  - ATTRIBUTE_CUSTOM(N, V, CHANGE): accords to HSAttributeCustom
  - ATTRIBUTE_CUSTOM_INT(N, V, CHANGE): accords to HSAttributeCustomInt

N is the name of the attribute as displayed to the user, V is the value in the
struct; the macro automatically prepends a '&' to it to get the pointer. To
check if attributes are changed properly (or to do certain actions after a
change), the callback CHANGE is definied:

    GString* (*on_change)  (struct HSAttribute* attr);

It is called after the attribute is changed by the user. If this function
returns something != NULL, this string is put as an error message to the user
and the original value is restored. To mark a attribute as read-only for the
user, give ATTR_READ_ONLY as CHANGE.

The "custom" attributes do not give a pointer to a variable but give a function
that returns the variable. HSAttributeCustom returns a GString,
HSAttributeCustomInt an int.

    typedef void (*HSAttributeCustom)(void* data, GString* output);
    typedef int (*HSAttributeCustomInt)(void* data);

They both get a data-pointer, it is exactly the data-pointer you can give as the
data-member of the HSObject struct. For an example see, add_tag(char*) in
src/tag.c, it covers most cases.


// vim: nowrap ft=asciidoc tw=80
