Introducing… gulik!

I've been awful quiet on this intertube thingamabob as of late and the three-and-a-half people following poobrains progress might have noticed a distinct lack of activity on that project as well. (upcoming post on that whenever)

I did however not just sit on my ass and laze about. I mean I did that, too, but mainly I was hacking on a new project – gulik, which just reached the first alpha version.

Those of you stalking me on the fediverse may still know it as hugin or nukular but both of those names were scrapped because they collided with other projects.

Now, what the hell is gulik?

gulik is a graphical on-desktop system monitor. Think of gkrellm or the program that frustrated me into starting this project – conky.

gkrellm, about as pretty as it gets

You see, conky offers two completely disparate ways of configuration. With the classic one, you just set one variable called conky.text, which basically acts like a format string on shrooms and lets you place text as well as visualizations like plots. The problem with this is that it always looks like utter crap and suffers from all sorts of horrid issues when you actually want to layout things. Take a look at its default config if you really want to know more about that.

conky, configured via conky.text

The other way to configure it is to use its lua "API" which basically consists of one function called conky_parse into which you pass a string that is allowed to use a subset of conky.text syntax.

You can create some truly beautiful setups with this, but to draw things, you literally have to call the lua bindings for cairo, with absolutely 0 sensible abstractions provided.

conky, configured via lua "API"

This is literally so bad that a whole bunch of people have written their own frameworks on top of it – I'm not even kidding.

The last straw for me was that conky_parse simply returns bullshit values on FreeBSD. Everything works fine with conky.text, but don't you fucking dare to create a sensible layout with conky on FreeBSD!

So, for by now hopefully obvious reasons, I exclaimed

Fuck this, I'm building my own system monitor, with python, cairo and psutil! >:(

One nice thing about this project – besides its scope being way smaller than poobrains whose complexity in comparison is humongous – is that with such competition, it's kind of easy to create something that both looks way better and offers better UX/DX.

I mean, not to rip on them too much, especially since gulik eats way more resources, but it definitely shows that they're a product of the '90s.

Disregard that – actually, gkrellm got started in '99 and torsmo/conky in '04.

gulik lives at phryk evil mad sciences, LLC's new and improved R&D wing.

You can install it via pip using:
pip-3.6 install --user git+https://rnd.phryk.net/phryk-evil-mad-sciences-llc/gulik/.

Currently, documentation doesn't exist, but read on if you want to know more.

Some of guliks highlights:

  • works out of the box, but is completely customizable
  • supplies useful abstractions
  • looks really fucking spiffy
  • separation of concerns with visualization and data collection
  • multi-processed – no freezes if one data collector is blocking
  • live config reload via SIGUSR1, with automatic fallback to previous config if you messed it up
  • netdata support to visualize remote boxes
  • autogenerated legends
  • parameterizable palettes
  • supreme stylability

gulik, in all its current glory

I'm pretty sure I'm not exaggerating with "supreme" here either. The central abstraction is the Visualizer, which you can basically understand as synonymous with "widget" – "visualizer" just seemed like a more accurate name.

Visualizers all have margin and padding, individually settable for each side so you actually have proper control over spacing. Alignable captions are supported as well.

Any visualizer can visualize an arbitrary number of elements. They even come with different combination modes to let you decide whether elements should be shown separately or cumulatively. Blending operators (overlay, difference, lighten, …) can be set for visualizers and captions.

This is not an exhaustive list of the offered styling options, but I think you get the picture. :)

To make things even more better, there are cascading default styles settable through config variables.

For example, PADDING = 42 in the config file will set the padding on all sides of all visualizers to 42 pixels while PADDING_ARC = 23 will override PADDING but only for all Arc visualizers and PADDING_ARC_LEFT = 0 will remove left padding from all Arc visualizers.

This works for pretty much everything, from caption fonts to executable fills and palettes.

To retain explicit styling control, those cascading defaults can be overriden by directly passing the according settings into the constructor of the visualizer in question.

Which brings me to the point that gulik config files are literally just python. There's basically just two things you put in there: ALL_CAPS configuration options and an optional setup function that places all visualizers. It is expected at ~/.config/gulik/config.py, if there isn't one, gulik will revert to using defaults and an autosetup.

One current weakness is that the autosetup pretty much depends on you having over 1000px vertical screen resolution. Fixing this is on my list, but as I said, gulik has just barely arrived at its alpha stage.

If you have a smaller vertical resolution, you'll be forced to create a custom config, so to give you a fighting chance at it, let's look at a specific example of a gulik configuration:

import gulik # should be self-explanatory, we need the gulik lib to use gulik stuff.

FPS = 3 # update 3 times a second
Y = 32 # move gulik below awesomewm top menubar
WIDTH = 400
HEIGHT = 1080 - Y

# make gulik create a NetdataMonitor object for host phlogiston.phryk
# port defaults to 19999, use (hostname, port) tuples for explicit ports
NETDATA_HOSTS = ['phlogiston.phryk'] 


def setup(app):

    # places all the visualizers gulik does by default
    # will break things if you don't have enough vertical resolution
    app.autosetup(width=WIDTH/2)

    # create an extra layout box to the right autosetup stuff
    box = app.box(x=WIDTH/2, width=WIDTH/2) 

    box.place( # automatically place a visualizer in our box
        'netdata-phlogiston.phryk', # "component" identifier, i.e. data source
        gulik.Arc, # the visualizer class to instantiate
        # all parameters below this are passed to gulik.Arc's constructor
        width=box.width,
        height=box.width + 40, # 40px at the bottom for two lines of autolegend
        stroke_width=10,
        elements=[ # elements of netdata-phlogiston.phryk to be visualized
            'system.cpu.nice',
            'system.cpu.system',
            'system.cpu.user',
            'system.cpu.interrupt'
        ],

        # allow captions to be placed in the visualizers padding
        # (as opposed to its inner area)
        caption_placement='padding', 
        captions=[
            {
                'text': 'phlogiston',
                'position': 'left_top',
                'align': 'left_top',
                'font_weight': 'Bold',
                'operator': gulik.Operator.HARD_LIGHT, # blending operator
            },
        ],
        combination='cumulative', 
    )

I'm hoping to change the documentation situation for the better soonish, but if you're running into any problems feel free to post issues or bug me on the fediverse.

Also, before you invest huge amounts of time to build custom configs, be aware that there might be some breaking changes before gulik goes into beta.

If you do however build a custom config, I'd love to get some screenshots! ;)