TopHome
<2024-01-18 Thu>techpython

Using game engines to create lock screens

Normally, I use the in-built Gnome lock screen or slock from the suckless group. Slock has a number of nice patches created by the community for extensions like different colors, background images, alternative passwords etc. Other tools in this category include things like i3-lock etc.

But, today, I wanted a simple pomodoro end lock screen, with no password. Instead, I wanted the screen to go away after 10 seconds, a time lock, that can only be opened by waiting.

Usually, I am a fan of minimalism and the suckless approach. That means C code for maximum performance. That comes with its own tradeoff - development time. I don't mind paying the development time upfront, since I end up using the tools I build and customize for years. But, today, today is the kind of day I want to go with simplicity - which is a quality of its own. While browsing for Python based lock tools, I didn't find many, but I did stumble upon this which pleasently surprised me.

The gist of the finding is this: to write a lock manager, you need to play in the GUI framework of the host OS, things like X11 or Wayland. These client libraries are based in C, though bindings exist in other languages, those are usually poorly documented and difficult to weild. Game engines, of all things, come to the rescue. First class game engines exist for higher level languages and games usually are run in full screen mode and lock up all IO!

Now, I wanted to whip something up quick in Python for the usecase I just mentioned, a time lock with no password. I used Pygame as done in the original link and with this blog as a reference, I was able to come up with the solution very quickly in 40 lines of code, reproduced below in whole.

#!/usr/bin/env python3
import pygame
from pygame import constants as pyg_c
import sys

pygame.init()

argv = sys.argv[1:]
if len(argv) != 2:
    print("Usage: tlock.py <path to bg file> <time duration>")
    exit(1)

bgpath = argv[0]
timer = int(argv[1])

screen = pygame.display.set_mode((0, 0), flags=pyg_c.SHOWN | pyg_c.FULLSCREEN, depth=1)
w, h = screen.get_size()

bg = pygame.image.load(bgpath)
font = pygame.font.SysFont("Fantasque Sans Mono", 100)
label = font.render("tlock", 1, "#ffffff")

pygame.mouse.set_visible(False)

for i in range(timer, 0, -1):
    screen.blit(bg, (0, 0))

    sx, sy = w/2 - timer*50/2, 3*h/4
    offset = 5

    screen.blit(label, (1*w/5, 1*h/5))
    pygame.draw.rect(screen, (0, 0, 0), pygame.Rect(sx, sy, 50*i, 50))
    pygame.draw.rect(screen, (255, 255, 255), pygame.Rect(sx+offset, sy+offset, 50*i-2*offset, 50-2*offset))

    pygame.display.update()
    pygame.time.wait(1*1_000)

pygame.quit()

Does exactly what I need it to do: stick it at the end of my pomodoro script and I get a nice shove to get up and away from my laptop. I have found that a simple interruption of even 10 seconds is enough to get you out of the flow.