Skip to content

Instantly share code, notes, and snippets.

@supertin
Created July 19, 2020 09:24
Show Gist options
  • Save supertin/2a3e9e9439695030a7e294220d58e7c3 to your computer and use it in GitHub Desktop.
Save supertin/2a3e9e9439695030a7e294220d58e7c3 to your computer and use it in GitHub Desktop.
XScreensaver digital clock hack in Python3
#!/usr/bin/env python3
"""
ClockScreensaver - Show a simple digital clock using XScreensaver
Copyright (C) 2020 Tim Bates
Based on WebScreensaver - Copyright (C) 2012-2017 Lucas Martin-King & Thomas Reifenberger
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Can be run standalone to test. To add to xscreensaver, save somewhere
convenient, and add the full path to your ".xscreensaver" file. Set
command line parameters (colour, size) using xscreensaver's config tool.
"""
import os
import signal
import random
import time
from time import sleep
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GdkX11', '3.0')
from gi.repository import Gtk, Gdk, GdkX11, GLib
class ClockScreensaver(object):
'''
Hopefully a simple digital clock screensaver
'''
def __init__(self, window_id=None, size=190000, colour="blue"):
self.window_id = window_id
# Set the default style variables if blank was passed
if size == None:
self.size = 190000
else:
self.size = size
if colour == None:
self.colour = "blue"
else:
self.colour = colour
#Set a default window size (this is too small for the default font size, but it shouldn't matter)
self.w = 640
self.h = 480
def setup_window(self):
'''Perform some magic (if needed) to set up a Gtk window'''
if self.window_id:
self.win = Gtk.Window(Gtk.WindowType.POPUP)
gdk_display = GdkX11.X11Display.get_default()
self.gdk_win = GdkX11.X11Window.foreign_new_for_display(gdk_display, self.window_id)
# We show the window so we get a Gdk Window,
# then we we can reparent it...
self.win.show()
self.win.get_window().reparent(self.gdk_win, 0, 0)
x, y, w, h = self.gdk_win.get_geometry()
# Make us cover our parent window
self.win.move(0, 0)
self.win.set_default_size(w, h)
self.win.set_size_request(w, h)
self.w, self.h = w, h
else:
self.win = Gtk.Window()
self.win.set_default_size(self.w, self.h)
def setup(self):
'''Do all the things!'''
self.setup_window()
self.win.set_title("Clock Screensaver")
def terminate(*args):
Gtk.main_quit()
self.win.connect('destroy', terminate)
self.win.connect('delete-event', terminate)
signal.signal(signal.SIGINT, signal.SIG_DFL)
signal.signal(signal.SIGTERM, terminate)
# Create a label and set a generic text for debugging purposes (it should change to the time before anything is shown).
self.fixed = Gtk.Fixed()
self.label = Gtk.Label()
self.label.set_markup("<b>CLOCK HERE</b>")
self.label.set_justify(Gtk.Justification.CENTER)
# Now shove the label into a "fixed" frame so it can be repostioned.
self.fixed.put(self.label, 0, 0)
# And add the frame to the window.
self.win.add(self.fixed)
# Set the CSS for the background and set up the CSS settings.
css = b"""
* {
background-color: black;
}
"""
css_provider = Gtk.CssProvider()
css_provider.load_from_data(css)
context = Gtk.StyleContext()
screen = Gdk.Screen.get_default()
context.add_provider_for_screen(screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
#Show it all. Duh.
self.win.show_all()
def updateclock(self):
#Update the actual clock text
self.label.set_markup("<span foreground='" + str(self.colour) + "' size='" + str(self.size) + "'>" + time.strftime("%X") + "</span>")
#Return true to reset the timer (I think - it doesn't repeat without it)
return True
def clocktimer(self):
#Schedule the clock to update roughly every second (1000 milliseconds)
GLib.timeout_add(1000, self.updateclock)
def movelabel(self):
#Move the label to a random space with a 100x600 range. On some screens, this will result in it running off the edge, but
#it works at 1920x1080 on the phone running PostmarketOS that I made this for. Adjust if required I guess.
self.fixed.move(self.label, random.randrange(0, 100, 1), random.randrange(0, 600, 1))
return True
def movetimer(self):
#Schedule random repositioning of the label to prevent burnin
GLib.timeout_add(10000, self.movelabel)
@classmethod
def determine_window_id(cls, win_id=None):
'''Try and get an XID to use as our parent window'''
if not win_id:
win_id = os.getenv('XSCREENSAVER_WINDOW')
if win_id:
win_id = int(win_id, 16)
return win_id
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description='ClockScreensaver: Simple digital clock as your screensaver')
parser.add_argument('-window-id', help='XID of Window to draw on')
parser.add_argument('-size', help='Font size. Default is 190000')
parser.add_argument('-colour', help='Font colour. Yes. English spelling')
args = parser.parse_args()
saver = ClockScreensaver(window_id=ClockScreensaver.determine_window_id(args.window_id), colour=args.colour, size=args.size)
saver.setup()
#Manually update clock text for the first time, then start the clock timer and positioning timer.
saver.updateclock()
saver.clocktimer()
saver.movetimer()
#Start the GTK process - after this, everything must be handled by GTK
Gtk.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment