Download XScreenSaver. In your binary you can’t use glfw to create window, use GLX instead, because you have to hook up to the virtual root window.
As of Ubuntu 11.10 screensaver server isn’t placed in the distro (from that moment on it supports only screen blanking), to enjoy graphical screensavers we’ve got to install it for ourselves:
After installing, place where screensavers are stored is /usr/lib/xscreensaver,
where listing shows some default ones:
They’re ordinary executable files:
…and when running one of them they create a window with a screensaver displayed:
That’s great, or that’s more what I thought, because as I dropped there one of my OpenGL programs (I put them on my Github) naively thinking that every arbitrary binary can be set up as a screensaver I ran:
Which launches tool for choosing & setting up a screensaver from /usr/lib/xscreensaver:
As I eventually selected my program (it’s called Screensaver on the list above), it occured to me that there are 2 problems.
- My program does not show in the little squared window of xscreensaver-demo when selected; it just runs in a new window, unlike screensavers shipped with package.
- When 1 minute passes and XScreenSaver launches my own screensaver, all what I see are logs from my screensaver on some black screen, not the window it was supposed to create (as I’ve seen for a splitsecond when moved a mouse, window was indeed created, but it wasn’t floating on top of the others despite hints I passed to glfw, this black screen was shadowing it).
What these problems have in common?
There must be some parent-window to hook up when launching my screensaver, so it wouldn’t just run in a new window but rather take a handle from another process. Looks like I can’t just select any arbitrary program and expect it to work as a screensaver, pity.
A look at the ‘Root window’ Wikipedia article confirmed my assumptions:
The virtual root window is also used by XScreenSaver: when the screensaver is activated, this program creates a virtual root window, places it at the top of all other windows, and calls one of its hacks (modules), which finds the virtual root window and draws in it.
Down the rabbit hole
I needed to get some example code of screensavers that are shipped within the XScreenSaver or any other working artifacts. I got a clever, concise example from Github:
and it actually worked as other examples!
So that’s the way that RobertZenz did it in his lavanet screensaver:
- He included a header called vroot.h, which is an abbreviation from virtual root window. Root window is the window which is the bottom-most, it’s a parent to every other window. As Wikipedia states:
In the X Window System, every window is contained within another window, called its parent. This makes the windows form a hierarchy. The root window is the root of this hierarchy. It is as large as the screen, and all other windows are either children or descendants of it.
File’s content is 106 lines, more than a half of it is description which I’ll just put here for clarity because it describes what it does better than I would:
- In lavanet.c, vroot.h is used it this way:
Rest of the code is GLX calls and lavanet logics which is not important for us.
OK! Looks like I can’t just make a new window with glfw.
I need to get that root window first.
At this point I hoped that there’s a way in glfw to create a native X11 Window, configure it (with vroot.h) and pass it to the glfw, since glfw exposes some native calls:
…but I was wrong. There’s just no way. To get GLFWwindow object, you’ve got to call glfwCreateWindow and it’s the only way.
There’s even an issue opened on Github in 2013 which was active through the years and the last answer is from 20 days before I wrote this post:
It was exactly the same problem I was facing, but feature that would provide passing native handle is still not shipped.
Since I used glfw only for convenience (it abstracts creating window so developer wouldn’t bother writing platform-specific branches) I could use GLX to get that native window handle.
GLX is like an interface for OpenGL calls that can talk with an X11 server.
As Windows has its own windowing system, it has its own equivalent of GLX, called WGL.
If you’re getting confused, reffer to the Wikipedia and this answer on StackOverflow:
The glfw way (code before)
My previous code I used to create windows with glfw looked like this:
Not much, right? There were some glfw calls on program start:
But that’s all.
The GLX way (code after)
I got window handle exactly the same way as in the lavanet example.
Then, in my main loop, I couldn’t do anymore:
So I replaced it with:
(I also change classname to WrapperWindow so it wouldn’t conflict with X11 window).
I had a window, but I still needed to register a graphical context. And that’s where this very helpful post came:
So the class after changes looked like that:
As it was inside my opengl-playground CMake project that displayed textured cubes, I simply built it and copied resulting binary into /usr/lib/xscreensaver/.
Then, typed xscreensaver-demo, selected my screensaver and could preview it – it worked.
Looks like it’s not that hard to make a screensaver for X11, just make sure you create a native window by your own, the rest is just ordinary OpenGL.
I created a separate CMake project with this X11 screensaver afterwards and put it on Github, so you could try it for yourself. For clarity, I cut the logics, so it only fills screen with colours every some amount of time. OpenGL and X11 are the only dependencies.
By the way, this time I used a service called carbon:
It saved my time – generates images from source code.
Sources (or just interesting related stuff to read)
How to make an X11 screensaver with python:
5 thoughts on “[X11] Writing X11 screensaver with C++ & OpenGL”
I think I miss what in “modern” context for GLX makes it modern … but it’s great you put all that together ^_^
Thank you! 🙂 Yeah, GLX gave me a headache.
Nice post! It looks like xscreensaver uses the root window. Looking at the documentation for the protocol extension https://www.x.org/releases/X11R7.7/doc/scrnsaverproto/saver.html it looks like this is an internal screensaver. I’m guessing that if you setup an external screensaver it can use a different window. I’m not sure how that works with xscreensaver but you may be able to do this without xscreensaver. An external screensaver might be easier to implement using other frameworks. You just need the window id.
LikeLiked by 1 person
Thanks! I didn’t try 3rd party screensavers, having option to just pass native window handle would remove lots of boilerplate, as from what I recall SDL/GLFW expose API for getting it.
Speaking of the X server, a week ago I released a video, large part of which concerns the X, it may interest you: