Files
Classroom_Desktop/scripts/classroom-dock
T

347 lines
7.1 KiB
Python
Executable File

#!/usr/bin/env python3
import subprocess
from datetime import datetime
from pathlib import Path
import gi
gi.require_version("Gtk", "3.0")
gi.require_version("GtkLayerShell", "0.1")
from gi.repository import Gtk, Gdk, GdkPixbuf, GLib, GtkLayerShell
CONFIG_FILE = Path("/etc/ekklesia-classroom/device.conf")
CSS_FILE = Path("/etc/ekklesia-classroom/dock/style.css")
ICON_DIR = Path("/etc/ekklesia-classroom/icons")
def read_device_config():
config = {}
if not CONFIG_FILE.exists():
return config
for raw_line in CONFIG_FILE.read_text().splitlines():
line = raw_line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
config[key.strip()] = value.strip()
return config
def enabled(config, key, default=False):
if key not in config:
return default
return config[key].lower() in {
"1",
"true",
"yes",
"on",
"enabled",
}
def launch(command):
subprocess.Popen(command, start_new_session=True)
def notify(title, message):
launch([
"notify-send",
"--app-name=Ekklesia Classroom",
title,
message,
])
def themed_icon(*names):
theme = Gtk.IconTheme.get_default()
for name in names:
if theme.has_icon(name):
return Gtk.Image.new_from_icon_name(
name,
Gtk.IconSize.DIALOG,
)
return Gtk.Image.new_from_icon_name(
"application-x-executable",
Gtk.IconSize.DIALOG,
)
def custom_icon(filename, size):
path = ICON_DIR / filename
if not path.exists():
return None
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
str(path),
size,
size,
True,
)
return Gtk.Image.new_from_pixbuf(pixbuf)
class ClassroomShell(Gtk.Window):
def __init__(self):
super().__init__()
screen = self.get_screen()
visual = screen.get_rgba_visual()
if visual is not None:
self.set_visual(visual)
self.set_app_paintable(True)
self.set_name("classroom-shell")
self.set_decorated(False)
self.set_resizable(False)
self.config = read_device_config()
GtkLayerShell.init_for_window(self)
GtkLayerShell.set_layer(
self,
GtkLayerShell.Layer.TOP,
)
GtkLayerShell.set_anchor(
self,
GtkLayerShell.Edge.BOTTOM,
True,
)
GtkLayerShell.set_anchor(
self,
GtkLayerShell.Edge.LEFT,
True,
)
GtkLayerShell.set_anchor(
self,
GtkLayerShell.Edge.RIGHT,
True,
)
GtkLayerShell.set_exclusive_zone(self, 76)
GtkLayerShell.set_keyboard_interactivity(
self,
False,
)
layout = Gtk.Overlay()
layout.set_size_request(-1, 76)
dock = self.build_dock()
dock_align = Gtk.Alignment(
xalign=0.5,
yalign=0.5,
xscale=0,
yscale=0,
)
dock_align.add(dock)
layout.add(dock_align)
self.clock = Gtk.Label()
self.clock.get_style_context().add_class("clock")
clock_align = Gtk.Alignment(
xalign=1.0,
yalign=0.5,
xscale=0,
yscale=0,
)
clock_align.set_padding(
0,
0,
0,
20,
)
clock_align.add(self.clock)
layout.add_overlay(clock_align)
layout.set_overlay_pass_through(clock_align, True)
self.add(layout)
self.update_time()
GLib.timeout_add_seconds(
15,
self.update_time,
)
def build_dock(self):
dock = Gtk.Box(
orientation=Gtk.Orientation.HORIZONTAL,
spacing=10,
)
dock.get_style_context().add_class("dock")
dock.pack_start(
self.make_button(
self.browser_icon(),
lambda *_: launch(
["/usr/local/bin/classroom-browser"]
),
),
False,
False,
0,
)
if enabled(
self.config,
"DVD_ENABLED",
False,
):
dock.pack_start(
self.make_button(
self.dvd_icon(),
lambda *_: notify(
"DVD",
"DVD support is being prepared.",
),
),
False,
False,
0,
)
dock.pack_start(
self.make_button(
self.sanctuary_icon(),
lambda *_: notify(
"Sanctuary",
"Sanctuary live view is being prepared.",
),
),
False,
False,
0,
)
return dock
def make_button(self, image, callback):
button = Gtk.Button()
button.set_relief(Gtk.ReliefStyle.NONE)
button.get_style_context().add_class(
"dock-button"
)
button.add(image)
button.connect("clicked", callback)
return button
def browser_icon(self):
image = custom_icon(
"browser.svg",
28,
)
if image is not None:
return image
image = themed_icon(
"web-browser-symbolic",
"internet-web-browser-symbolic",
)
image.set_pixel_size(28)
return image
def dvd_icon(self):
image = custom_icon(
"dvd.svg",
40,
)
if image is not None:
return image
image = themed_icon(
"media-optical-symbolic",
"media-optical",
)
image.set_pixel_size(28)
return image
def sanctuary_icon(self):
is_live = enabled(
self.config,
"SANCTUARY_LIVE",
False,
)
filename = (
"live.svg"
if is_live
else "live-off.svg"
)
image = custom_icon(
filename,
38,
)
if image is not None:
return image
image = themed_icon(
"video-display-symbolic",
"video-display",
)
image.set_pixel_size(28)
return image
def update_time(self):
self.clock.set_text(
datetime.now().strftime("%-I:%M %p")
)
return True
def load_css():
provider = Gtk.CssProvider()
provider.load_from_path(
str(CSS_FILE)
)
screen = Gdk.Screen.get_default()
Gtk.StyleContext.add_provider_for_screen(
screen,
provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION,
)
def main():
load_css()
shell = ClassroomShell()
shell.connect(
"destroy",
Gtk.main_quit,
)
shell.show_all()
Gtk.main()
if __name__ == "__main__":
main()