Private
Public Access
1
0

new: dev: initialize the GTK client #11

This commit is contained in:
2026-04-28 08:28:51 +02:00
parent 199bb7e525
commit 6484133199
29 changed files with 2788 additions and 0 deletions

View File

@@ -0,0 +1,108 @@
"""
This file is part of the SplendidBear Websites' projects.
Copyright (c) 2026 @ www.splendidbear.org
For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
"""
from __future__ import annotations
import io
import logging
import gi
gi.require_version("GdkPixbuf", "2.0")
gi.require_version("Gst", "1.0")
from gi.repository import GdkPixbuf, Gst
from mineseeker import config
from mineseeker.api import client
from mineseeker.constants import IMAGE_NAMES, SOUND_NAMES
log = logging.getLogger(__name__)
# ---------------------------------------------------------------------------
# Image cache { filename: GdkPixbuf.Pixbuf }
# ---------------------------------------------------------------------------
_images: dict[str, GdkPixbuf.Pixbuf] = {}
def load_images(cell_size: int = 40) -> None:
"""
Fetch all game images from the server and cache them as GdkPixbuf.Pixbuf.
Call once at startup (blocking; run in a thread if you want a splash screen).
"""
for name in IMAGE_NAMES:
url = f"{config.BASE_URL}/images/{name}"
try:
resp = client.get_session().get(url, timeout=10)
resp.raise_for_status()
loader = GdkPixbuf.PixbufLoader()
loader.write(resp.content)
loader.close()
pixbuf = loader.get_pixbuf()
# Scale to the cell size used by the grid widget
pixbuf = pixbuf.scale_simple(cell_size, cell_size, GdkPixbuf.InterpType.BILINEAR)
_images[name] = pixbuf
except Exception as exc:
log.warning("Could not load image %s: %s", name, exc)
def get_image(name: str) -> GdkPixbuf.Pixbuf | None:
"""Return a cached Pixbuf by filename, or None if not loaded."""
return _images.get(name)
def get_image_or_fallback(name: str, fallback: str) -> GdkPixbuf.Pixbuf | None:
return _images.get(name) or _images.get(fallback)
# ---------------------------------------------------------------------------
# Sound — via GStreamer playbin
# ---------------------------------------------------------------------------
_sounds: dict[str, str] = {} # { key: URI }
def load_sounds() -> None:
"""
Build the URI map for the six game sound effects.
GStreamer will stream them on-demand from the server.
"""
Gst.init(None)
for filename in SOUND_NAMES:
key = filename.split(".")[0] # "click", "bomb", etc.
_sounds[key] = f"{config.BASE_URL}/sound/{filename}"
def play_sound(key: str) -> None:
"""
Play a sound by key ("click", "mine", "warning", "bomb", "won", "starting").
Each call spawns a fresh GStreamer playbin — fire-and-forget.
"""
uri = _sounds.get(key)
if not uri:
return
try:
player = Gst.ElementFactory.make("playbin", None)
if player is None:
return
player.set_property("uri", uri)
player.set_state(Gst.State.PLAYING)
# Connect to bus to clean up after playback
bus = player.get_bus()
bus.add_signal_watch()
def _on_message(bus, msg, player=player):
if msg.type in (Gst.MessageType.EOS, Gst.MessageType.ERROR):
player.set_state(Gst.State.NULL)
bus.remove_signal_watch()
return True
bus.connect("message", _on_message)
except Exception as exc:
log.debug("Sound play failed (%s): %s", key, exc)