"""
Taskbar magnifier: shows a zoomed view above the taskbar while the mouse is over it.

Created: 2026-10-01 16:00 CEST
Make sure to install libraries in your Python environment first
- Setup:  pip install mss pillow pystray
- Run:    pythonw taskbar_magnifier.py   

Notes: 
- no console window
- quit app via blue magnifier tray icon using right-click > "Quit"
- works only on primary monitor screen

"""

import ctypes
import tkinter as tk
from ctypes import wintypes

import mss
import pystray
from PIL import Image, ImageDraw, ImageTk

ZOOM = 3          # magnification factor
VIEW_W = 600      # width of the magnified window in pixels
GAP = 12          # distance between taskbar and magnified window
INTERVAL_MS = 40  # refresh rate (~25 fps)

ctypes.windll.shcore.SetProcessDpiAwareness(2)  # real pixel coordinates
user32 = ctypes.windll.user32


def taskbar_rect():
    hwnd = user32.FindWindowW("Shell_TrayWnd", None)
    r = wintypes.RECT()
    user32.GetWindowRect(hwnd, ctypes.byref(r))
    return r.left, r.top, r.right, r.bottom


def cursor_pos():
    p = wintypes.POINT()
    user32.GetCursorPos(ctypes.byref(p))
    return p.x, p.y


root = tk.Tk()
root.overrideredirect(True)
root.attributes("-topmost", True)
root.withdraw()
label = tk.Label(root, bd=0)
label.pack()
sct = mss.MSS()
screen_w = user32.GetSystemMetrics(0)
photo = None
quit_requested = False


def make_tray_image():
    img = Image.new("RGBA", (64, 64), (0, 0, 0, 0))
    d = ImageDraw.Draw(img)
    blue = (30, 144, 255, 255)
    d.ellipse((8, 8, 40, 40), outline=blue, width=6)
    d.line((36, 36, 56, 56), fill=blue, width=8)
    return img


def on_quit(icon, item):
    global quit_requested
    quit_requested = True  # tkinter must be closed from its own thread
    icon.stop()


tray = pystray.Icon(
    "taskbar_magnifier",
    make_tray_image(),
    "Taskbar Magnifier",
    menu=pystray.Menu(pystray.MenuItem("Quit", on_quit)),
)
tray.run_detached()


def update():
    global photo
    if quit_requested:
        root.destroy()
        return
    left, top, right, bottom = taskbar_rect()
    cx, cy = cursor_pos()

    if left <= cx < right and top <= cy < bottom:
        src_w = VIEW_W // ZOOM
        src_h = bottom - top
        src_x = min(max(cx - src_w // 2, left), right - src_w)

        shot = sct.grab({"left": src_x, "top": top, "width": src_w, "height": src_h})
        img = Image.frombytes("RGB", shot.size, shot.bgra, "raw", "BGRX")
        img = img.resize((src_w * ZOOM, src_h * ZOOM), Image.LANCZOS)
        photo = ImageTk.PhotoImage(img)
        label.configure(image=photo)

        win_w, win_h = img.size
        win_x = min(max(cx - win_w // 2, 0), screen_w - win_w)
        win_y = top - win_h - GAP
        root.geometry(f"{win_w}x{win_h}+{win_x}+{win_y}")
        root.deiconify()
    else:
        root.withdraw()

    root.after(INTERVAL_MS, update)


update()
try:
    root.mainloop()
finally:
    tray.stop()
