TL;DR or What is this post about?
In short: it's about me exploring - or at least getting into contact with - a couple of interesting things:
- X Window System APIs via Xlib bindings
- low-level Linux APIs for elevating privilages, checking passwords
- GCC options and some C
- and of course the Nim language
At my work we strongly discourage leaving logged-in accounts and/or unlocked
screens of computers. I happen to agree that locking your computer is a good
habit to have, so I've had no problems with this rule... Up to the point when I
switched from KDE to StumpWM (I wrote about it some time ago,
and this posts) and my
Ctrl + Alt + L stopped working.
The only idea that came to my mind was to use the venerable xscreensaver, but: a) I didn't really need any of the 200+ animations (I just wanted a blank screen) and b) I didn't like how the unlock dialog looked like.
I needed something more lightweight (xscreensaver is ~30k loc of C), simpler and either better looking or without any UI altogether.
slock to the rescue
There's a site called suckless.org where you can find some impressively clean and simple tools, implemented mostly in C. You can read more about their philosophy here, which I recommend as an eye opening experience. Anyway, among the tools being developed there, there is also slock - a very simple and basic screen locker for X. It's 310 lines of code long and it's all pretty straightforward C.
The program suited my needs very well: minimal, fast, and good looking. Well, the last part it got through cheating, as slock simply has no interface at all - but this means there's no ugly unlock dialog, so it's all good.
As I used slock I read through its code a couple of times. It seemed simple and learnable, despite the fact that I knew nothing about X and didn't use C seriously in 15 years. Fast forward to last week and I finally found some free time and decided to learn some Xlib stuff. Re-implementing slock in Nim looked like a good way of learning it: this may sound a bit extreme, but it allowed me to gain exp points in two stats simultaneously - in Xlib usage and Nim knowledge!
Nim is a very interesting language. I'd call it "unorthodox" - not yet
radical, like Lisp or Smalltalk, but also not exactly aligned with Java and
the like. For one tiny example:
return statement in functions is optional
and if omitted, a function returns its last expression's value. That's
rather normal way to go about it; hovewer, in Nim you can also use a special
result and assign to it to have the assigned thing returned.
It looks like this:
At a first glance this may look strange, but consider this in Python:
It's practically an idiom, a very common construct. Nim takes this idiom, adds some sugar, adapts it to the world of static typing and includes in the language itself. We can translate the above Python to Nim with very minor changes:
As I said, it's not a groundbreaking feature, but it is nice and good to have, and it shows that Nim doesn't hesitate much when choosing language features to include. In effect Nim includes many such conveniences, which may be seen both as a strength and as a weakness. While it makes common tasks very easy, it also makes Nim a larger language than some others. It's not bad in itself, rather, depending on how well the features fit together it's harder or easier to remember and use them all. Nim manages well in this area, and also it most definitely is not like C++ with its backwards compatibility problem, so I think even Pythonistas with taste for minimalism will be able to work with Nim.
Being Nimble - the project setup
Many modern languages include some kind of task runner and package manager either as part of stadard distribution or as downloadable packages. Nim has Nimble, which takes care of installing, creating and publishing Nim packages. Assumming that you have Nim already installed, you can install Nimble with:
Note the addition to the
~/.nimble/bin/ is where Nimble
installed itself and where it will place other binaries it installs. Make
sure to have this directory in your
PATH before working with
Creating a project
Creating a project is easy, similar to
slock.nimble is a configuration file
for Nimble; it's written in a
NimScript, which looks like
a very recent development in Nim and it replaces the
previous INI-style config files. This means that many examples and tutorials
on the Internet won't work with this format. The most important
difference is the format of dependencies list for your app: it now has to be
seq. For example, to add x11
library to the project:
The dependencies should be downloaded by Nimble automatically, but you can also dowload them manually, like in the example below. You'll notice that I also install a c2nim package - I will say more about it later.
Workflow and tooling
Nim is a compiled language, but working with it proved to be comparable to working with a dynamic language, mainly thanks to type inference and blazingly fast compiler. You dcan ommit type declarations where they are obvious from the context and Nim will deduce the correct type for you. It's not a full program type inference like in OCaml, but rather a local type inference as used in Scala or modern C++ or Java. Even with this limitation it's immensely useful and reduces code verbosity by a lot.
Compiler speed is important, because it encourages frequent testing. If compilation is going to take a long time you tend to "batch" changes in your code together and only compile and run once in a while instead of after every single change. This, in turn, makes it harder to find and fix regressions if they appear. Dynamic languages work around this issue by rejecting compilation step completely, at the cost of run-time safety and performance. Nim - which is similar to Go in this respect - makes compilation effortless and nearly invisible. The command for compile-and-run looks like this:
The problem with this command is that it doesn't know about Nimble and dependencies, so in reality I used a bit different commands:
While Nim's compiler and Nimble are very good tools, they're not enough to
work comfortably on more complex codebases. Nim acknowledges this and
provides a couple of additional tools for working with code,
However, nimsuggest is rather new and it
SEGFAULTed on me a
couple of times. I used it via - of course - Emacs
with nim-mode, and again,
I encountered a couple of irritating bugs, frequent "Mismatched parens"
errors when trying to jump a word or expression forward. However,
when nimsuggest works, it does a good job, particularly its "Go
to definition" feature works and is very helpful.
Nim's definitive source of documentation is The Index, which does a
surprisingly well as a reference.
Ctrl + f works just as well as search
boxes other documentation sites provide. Nim docs are also nice in that they
link to the source: you get a link to the function implementation beside its
documentation. I like this trend and I'm happy that more documentation is
like this - a quick peek at the source can sometimes save hours of unneeded
In this project, I had to work with Xlib and turns out its
documented in man pages, and the pages were already installed on my system.
I don't remember installing them, so maybe they come with X by default on
Fedora. Anyway, for
Xlib tutorial I used Xlib Programming Manual and for
reference I simply used the man pages: just type, for example,
XFreePixmap and you get XCreatePixmap (3) man
page. The same is true for Linux/POSIX functions - try, for example,
That's it, for now
In the next part I'm going to show some Nim code, translated - both manually and automatically - from C. I'll focus on Nim's language features that C doesn't have and I'll show how they can be used to write shorter, safer and more readable code. In the later, last part I'm going to write about Nim's interop with C and various ways of connecting the two, using Xlib as an example.
- And it seems that JWZ doesn't like people modifying the looks of this dialog, so I didn't even try. ↵
- Very well written C. ↵
- Every power gamer will understand how nice this is! ↵
- But note the lack of unnecessary
returnstatement in Nim. ↵
- I recommend installing from GitHub - it's always
good to have the sources for your compiler and stdlib around, and you won't
get it if you install just the binaries. And
masterbranch is stable: checkout
develbranch for the in-development code. ↵
- This might be because of my own incompetence, of course. ↵