#!/usr/bin/env python3
# SPDX-License-Identifier: WTFPL
# a Qt app that displays an image always-on-top like a sticky note

# /// script
# dependencies = ["PyQt6"]
# ///

import signal
import sys
from argparse import ArgumentParser

from PyQt6.QtCore import Qt, QTimer
from PyQt6.QtGui import QIcon, QKeySequence, QPixmap, QAction
from PyQt6.QtWidgets import (
    QApplication, QLabel, QMenu, QMessageBox, QStyle, QSystemTrayIcon,
)


class Display(QLabel):
    def __init__(self, pix):
        super().__init__()

        self.setPixmap(pix)
        self.setFocusPolicy(Qt.FocusPolicy.NoFocus)
        self.setWindowFlags(
            self.windowFlags()
            | Qt.WindowType.WindowStaysOnTopHint
            | Qt.WindowType.WindowDoesNotAcceptFocus
            | Qt.WindowType.WindowTransparentForInput
            | Qt.WindowType.FramelessWindowHint
            | Qt.WindowType.X11BypassWindowManagerHint
        )
        self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents, True)
        self.setAttribute(Qt.WidgetAttribute.WA_X11DoNotAcceptFocus, True)
        self.setAttribute(Qt.WidgetAttribute.WA_QuitOnClose, True)

    def focusInEvent(self, ev):
        self.clearFocus()


class TrayIcon(QSystemTrayIcon):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.setToolTip("Wallpaper-curtain")

        self.activated.connect(QApplication.instance().quit)

        # mostly useless
        quitAction = QAction("&Quit", self)
        quitAction.setShortcut(QKeySequence("Alt+Q"))
        quitAction.setShortcutContext(Qt.ShortcutContext.ApplicationShortcut)
        quitAction.triggered.connect(QApplication.instance().quit)

        self.menu = QMenu()
        self.menu.addAction(quitAction)
        self.setContextMenu(self.menu)


parser = ArgumentParser(
    description="Wallpaper-curtain shows an image with low-opacity on top of "
    + "other windows and the desktop",
)
parser.add_argument(
    "-o", "--opacity", type=float, default=.5,
    help="Opacity of the image. Value between 0 (fully transparent) and 1 "
    + "(fully opaque). Default: 0.5."
)
parser.add_argument(
    "-m", "--mode", choices=("none", "fit", "cover"), default="none",
    help="Mode to resize images that do not fit desktop dimensions",
)
parser.add_argument(
    "--no-tray-is-fine", action="store_true",
    help="No warning dialog if there's no system tray",
)
parser.add_argument("file", help="Image file to show")


if __name__ == '__main__':
    app = QApplication(sys.argv)

    args = parser.parse_args(app.arguments()[1:])

    pix = QPixmap(args.file)
    if pix.isNull():
        parser.error("invalid image file %r" % args.file)

    screen = app.screens()[0]
    screen_geom = screen.virtualGeometry()
    if args.mode == "fit":
        pix = pix.scaled(
            screen_geom.size(),
            Qt.AspectRatioMode.KeepAspectRatio,
            Qt.TransformationMode.SmoothTransformation
        )
        screen_geom.moveCenter(pix.rect().center())
        pix = pix.copy(screen_geom.intersected(pix.rect()))
    elif args.mode == "cover":
        pix = pix.scaled(
            screen_geom.size(),
            Qt.AspectRatioMode.KeepAspectRatioByExpanding,
            Qt.TransformationMode.SmoothTransformation
        )
        screen_geom.moveCenter(pix.rect().center())
        pix = pix.copy(screen_geom.intersected(pix.rect()))

    has_tray = TrayIcon.isSystemTrayAvailable()
    if not (has_tray or args.no_tray_is_fine):
        button = QMessageBox.question(
            None,
            "Warning",
            "No system tray is available. This app can only be quit by killing the process. Continue?",
        )
        if button == QMessageBox.StandardButton.No:
            sys.exit("No system tray, quitting")

    if has_tray:
        tray = TrayIcon()
        tray.setIcon(QIcon(pix))
        tray.setIcon(app.style().standardIcon(QStyle.StandardPixmap.SP_DialogCancelButton))
        tray.show()

        def show_message():
            tray.showMessage(
                "Wallpaper-curtain",
                "Click here to quit curtain",
                QIcon(),
            )

        QTimer.singleShot(200, show_message)

    disp = Display(pix)
    disp.setWindowTitle("Wallpaper-curtain: %r" % args.file)
    disp.setWindowOpacity(args.opacity)
    disp.resize(pix.size())
    disp.showFullScreen()

    # Qt catches SIGINT but we want to quit easily
    signal.signal(signal.SIGINT, signal.SIG_DFL)

    app.exec()
