Initial thoughts
Well, I've been using a tiling window manager for some weeks, but still there's something I'm not comfortable with; the autolocker. I'm using xautolock(1), but it haven't implemented signal handling (USR1 to lock screen now, etc), so I'm thinking about creating my own.
Initial planning
So I'll write my own replacement for xautolock(1). What should the new software do:
- Watch X11 idle time (should have an interface)
- Listen USR{1,2} signals
- Send desktop notifications
- Parse arguments from command line
- Low start times
- Have as few dependencies as possible
I started looking on the xautolock(1) source code to see what did it do to check the idle time, so I found this line. I know it's not the official source, but this is enough. Now it's time to look on the man(1)
pages for XScreenSaver(3)
. Ok, there's a library with the things I need, so I should use a language that can call C functions with a reasonable overhead.
Here comes the inevitable choose the right language. My possible choices were:
-
Javascript
-> My first programming language. I thought it first, but as the last resort. -
C
-> The best language to call C functions from 😄. But I'm not so proficient on that lang. (Now I'm more confident writing things with it). Also, what about threads, I should learn how to use them, and it looks so complicated. Maybe another time. -
Java
-> I've written tons of (shitty) programs with it, but I don't and won't use them. Java was discarded from the beginning. -
Rust
-> A beautiful project to get started with Rust, but maybe it would not be a production-grade software. -
Golang
-> I've learning it but I haven't used it on a formal project. On the tests, some concepts caught my attention like simple concurrency with goroutines and channels. Also has a simple calling between C and Go
So golang
is the chosen one. Now it's time to design it.
Let's take the above steps from initial planning.
- X11 libraries docs are available under their man page, and functions are callable with cgo.
- Signal catching is easy with
os/signal
andsyscall
. - Desktop notifications can be achieved with go-notify.
- Parsing arguments from the command line is possible, but the standard library package is too basic. I ended up with
github.com/akamensky/argparse
. - Golang has a runtime statically linked to the final binary, but the startup times are very low and doesn't have a hard impact on the performance.
- Well, the dependencies. LibX11 for
XOpenDisplay(3)
andxscrnsaver
extension to get idle time info. Also depends on libnotify. Some distributions split the libraries and headers. Headers are needed then compiling.
Now, let's start. take ...
hmm, how do I name this thing? Sorry for the low effort name, but it's called goautolock(1)
. Ok, take goautolock&&git init
. (take
is a function defined on oh-my-zsh that runs mkdir
with all parameters and does cd on the last one).
Writing it
With the initial version I faced my first race conditions. Those weren't causing unexpected behavior, but go expects memory accesses to be synced, so I improved some features to convert some flags to channels. This change improved the performance (e.g. waiting 2 seconds at max to lock screen after SIGUSR1 vs locking immediately after getting a message on a channel).
That didn't fix all of the race conditions caused by the initial design, so a recent version implemented the mutexes provided by the go standard library.
It suffered many changes from the first time written. Take a look at the functions calling C libraries.
First time written:
Now looks like this:
Note the calls to C.free
to fix memory leaks.
When I got into signal handling, I thought: "There are 2 user defined signals without any special meaning, one of them will call the locker immediately, but ¿what about the other one?" So, I decided to stole a concept from LineageOS (formerly Cyanogen MOD) where you could add caffeine to prevent looking the screen for a time. The implementation on goautolock(1)
is simple:
- Each SIGUSR2 adds X seconds to the lock delay. Take X like the value of
--caffeine
option (defaulting to 10). - After time is elapsed and screen is locked, this extra delay is set to 0.
Writing experience with golang
The first impression I had is that golang has a very limited set of options (coming from JavaScript that allows you to do whatever you want). And the error handling (split on panic and error returns) was chaotic at the first glance, but when used to it, the experience is the most consistent I had.
Also, it has some syntactic sugar like prepending defer
to a expression to make it run after the actual function has been finished. This allows you to free C variables not managed by the GC just after the allocation, so you don't have to find the original declaration when you see a call to C.free
.
Documentation
godoc
is a very powerful utility for generating docs from source code, but this is a user utility, so the docs should be on the standard man(1)
. This was the most difficult part, using groff(7)
to create a man page. There are a lot of macros to format the output. Now the result is available at the /docs
folder on project (links on the bottom).
Packaging
This is an utility for Linux, each distro has a different packaging system (I won't use flatpak or spam snap for this).
The first thing I did was to build a release binary on the GitLab pipeline.
On the other hand, I want goautolock to be updated on my laptop when I do pakku -Syu
, so I uploaded it to the AUR. It was so easy using ldd
and namcap
and testing with makepkg
on a clean chroot.
The other distro I use is Fedora. This gave me some headaches.
- Package names aren't the same nor similar to ArchLinux.
- Devel packages are separated from the main ones.
- Which utility should I use to create a RPM following a spec file? rpkg? a Makefile? fedpkg? tito?
- The AUR counterpart (saving the distances) on Fedora is the COPR. Let's start a repo!
- Wait, I want a RPM, what the hell is a SRPM?
After figuring out I should make another repo in Fedora Pagure to upload the spec and source tarball, I started using rpkg to build the srpm. And after a lot of failed builds it's finally uploaded to Fedora COPR. Then I started to pack some other projects, so everybody can easily use them.
Links
And here's the link to the project and some others if you want to contribute or propose something:
- GitLab
- AUR for the fellow Arch users
- Pagure.io
- Fedora COPR
Top comments (0)