#!/usr/bin/env python3
# 5 taps in the top-right corner of the screen close Chromium (kiosk).
# Uses wlr-layer-shell to create an overlay surface above all windows (including fullscreen).
# Falls back to a regular GTK window on X11.
# Run from session autostart. Requires: python3, PyGObject (Gtk3), gir1.2-gtklayershell-0.1.

import logging
import subprocess
import sys

LOG_TAG = "five-tap"
logging.basicConfig(
    stream=sys.stderr,
    level=logging.DEBUG,
    format=f"{LOG_TAG}: %(message)s",
)
log = logging.getLogger(LOG_TAG)

try:
    import gi
    gi.require_version("Gdk", "3.0")
    gi.require_version("Gtk", "3.0")
    from gi.repository import Gdk, Gtk, GLib
except Exception as e:
    log.error("need PyGObject Gtk: %s", e)
    sys.exit(1)

HAS_LAYER_SHELL = False
try:
    gi.require_version("GtkLayerShell", "0.1")
    from gi.repository import GtkLayerShell
    HAS_LAYER_SHELL = True
except Exception:
    log.warning("GtkLayerShell not available, falling back to regular window")

CORNER_SIZE = 80
TAP_WINDOW_SEC = 2.0
CHROMIUM_KILL_CMD = ["pkill", "-f", "chromium"]


def on_button_press(widget, event, data):
    count, reset_timer = data
    count[0] += 1
    log.debug("tap %d of 5 at (%.0f, %.0f)", count[0], event.x_root, event.y_root)
    if reset_timer[0]:
        GLib.source_remove(reset_timer[0])
    if count[0] >= 5:
        count[0] = 0
        log.info("5 taps reached — killing chromium")
        try:
            result = subprocess.run(CHROMIUM_KILL_CMD, timeout=2, capture_output=True)
            log.info("pkill returncode=%d", result.returncode)
        except Exception as e:
            log.error("pkill failed: %s", e)
        if reset_timer[0]:
            GLib.source_remove(reset_timer[0])
        reset_timer[0] = None
        return
    def reset():
        log.debug("tap count reset (%.1fs timeout)", TAP_WINDOW_SEC)
        count[0] = 0
        reset_timer[0] = None
        return False
    reset_timer[0] = GLib.timeout_add(int(TAP_WINDOW_SEC * 1000), reset)
    return False


def get_screen_size():
    display = Gdk.Display.get_default()
    if not display:
        log.warning("no display found, using fallback 800x1280")
        return 800, 1280
    monitor = display.get_monitor(0) if display.get_n_monitors() else None
    if monitor:
        geom = monitor.get_geometry()
        log.info("screen size: %dx%d", geom.width, geom.height)
        return geom.width, geom.height
    log.warning("no monitor found, using fallback 800x1280")
    return 800, 1280


def make_window_layer_shell(win):
    """Anchor an overlay surface to the top-right corner via wlr-layer-shell."""
    GtkLayerShell.init_for_window(win)
    GtkLayerShell.set_layer(win, GtkLayerShell.Layer.OVERLAY)
    GtkLayerShell.set_anchor(win, GtkLayerShell.Edge.TOP, True)
    GtkLayerShell.set_anchor(win, GtkLayerShell.Edge.RIGHT, True)
    GtkLayerShell.set_keyboard_mode(win, GtkLayerShell.KeyboardMode.NONE)
    log.info("layer-shell overlay anchored top-right (%dx%d)", CORNER_SIZE, CORNER_SIZE)


def make_window_fallback(win):
    """X11 fallback: position with move() and hope keep-above works."""
    win.set_keep_above(True)
    win.realize()
    w, _h = get_screen_size()
    x = max(0, w - CORNER_SIZE)
    win.move(x, 0)
    log.info("X11 fallback: window positioned at x=%d y=0", x)


def main():
    win = Gtk.Window()
    win.set_decorated(False)
    win.set_resizable(False)
    win.set_default_size(CORNER_SIZE, CORNER_SIZE)
    win.set_skip_taskbar_hint(True)
    win.set_skip_pager_hint(True)
    win.set_accept_focus(False)
    win.set_focus_on_map(False)

    # Make the window nearly invisible
    screen = win.get_screen()
    visual = screen.get_rgba_visual()
    if visual:
        win.set_visual(visual)
    win.set_app_paintable(True)
    win.connect("draw", lambda w, cr: (cr.set_source_rgba(0, 0, 0, 0.01), cr.paint()))

    if HAS_LAYER_SHELL and GtkLayerShell.is_supported():
        make_window_layer_shell(win)
    else:
        make_window_fallback(win)

    win.connect("destroy", Gtk.main_quit)

    count = [0]
    reset_timer = [None]
    win.connect("button-press-event", on_button_press, (count, reset_timer))
    win.set_events(
        win.get_events()
        | Gdk.EventMask.BUTTON_PRESS_MASK
        | Gdk.EventMask.TOUCH_MASK
    )

    win.show_all()
    log.info("listening for taps (5 within %.1fs to kill chromium)", TAP_WINDOW_SEC)
    Gtk.main()


if __name__ == "__main__":
    main()
