4 Hugues Ross - Blog: Playing with Cairo
Hugues Ross

11/26/17

Playing with Cairo

This week, I took a break from Singularity to mess around with my desktop a bit. In the process of that, I finally sat down and tried using the Cairo vector graphics library.

The Goal

Right now, I'm using a fork of the i3lock utility to lock my PC. Honestly, I'm not thrilled by how it looks. It basically just displays a simple indicator over an image you provide (or a blurred version of your desktop), and that's it. Here's an example from the Github page:
Lately, I've been thinking that the overall "circle with some text in it" design it looks kinda tacky. Unfortunately, I don't know of many other screen-locking options. So, I thought I'd make a small fork that provides a more interesting overlay. I have a lot of ideas for fun designs and overlays that I could use to replace the old design, but I decided to start with something basic:
Note: Colors and fonts aren't final. Just needed something to put in this mockup
I figured that a simple static design would would be pretty easy to swap in, and then I could look into some crazier ideas like dynamic overlays and procedural generation.

Current Progress

To get a new overlay working, the obvious first step is to figure out how the old overlay works. After some poking in the code, it looks like i3lock renders its overlay via Cairo, so I finally decided to finally learn the API.

Cairo is one of those things that I've known about for years, but never really had a good excuse to play with. This mini-project gave me the perfect opportunity, so I decided to make a little playground application where I could mess around. To make things extra-simple, I also did it in Vala. I'm still not sure if I want to try and hook vala-generated code up to the final product, but it's a big help in the short term. After a couple hours of playing around, I made a function to draw those colored parallelograms (which I called lozenges in code, because only a sadist would casually drop a word of that length into a function name):

   53     public void draw_lozenge(int x, int y, int width, int height, int spacing, float[] ratio)
   54         requires(ratio.length >= 3)
   55     {
   56         ctx.move_to(x, y);
   57         ctx.new_path();
   58         ctx.line_to(x + (width * ratio[0]), y);
   59         ctx.line_to(x + (width * ratio[0]) - (height), y + height);
   60         ctx.line_to(x, y + height);
   61         ctx.line_to(x, y);
   62         ctx.close_path();
   63         ctx.set_source_rgba (147.0f / 255, 87.0f / 255, 57.0f / 255, 1);
   64         ctx.fill ();
   65 
   66         ctx.new_path();
   67         ctx.move_to(x + (width * ratio[0]) + spacing, y);
   68         ctx.line_to(x + (width * ratio[0]) + spacing + (width * ratio[1]), y);
   69         ctx.line_to(x + (width * ratio[0]) + spacing - (height) + (width * ratio[1]), y + height);
   70         ctx.line_to(x + (width * ratio[0]) + spacing - (height), y + height);
   71         ctx.set_source_rgba (214.0f / 255, 165.0f / 255, 122.0f / 255, 1);
   72         ctx.fill ();
   73 
   74 
   75         ctx.new_path();
   76         ctx.move_to(x + (width * ratio[0]) + (width * ratio[1]) + (spacing * 2), y);
   77         ctx.line_to(x + (width * ratio[0]) + (width * ratio[1]) + (spacing * 2) + (width * ratio[2]), y);
   78         ctx.line_to(x + (width * ratio[0]) + (width * ratio[1]) + (spacing * 2) - (height) + (width * ratio[2]), y + height);
   79         ctx.line_to(x + (width * ratio[0]) + (width * ratio[1]) + (spacing * 2) - (height), y + height);
   80         ctx.set_source_rgba (161.0f / 255, 124.0f / 255, 91.0f / 255, 1);
   81         ctx.fill ();
   82     }

I am 100% certain that this code can be simplified further and made more generic, but for the moment I'm happy enough with the result.

Besides the missing text, this is almost exactly the same. The big difference is that whereas the previous image was painstakingly hand-made, this new version is the result of 2 simple function calls.

What's Next?

Naturally, the next steps are to add text and get the new design into i3lock. Cairo provides some text-rendering methods (i3lock uses these already), and I believe there's also a library called PangoCairo that adds rich text/formatting/markup via Pango. I haven't tried either of these approaches yet, but I will soon enough.

Once I have a working i3lock with a custom design, I'll post again and look at some of the interesting options that this opens up for me.
Post a Comment