import tkinter as tk
import PIL.Image
import PIL.ImageTk
import PIL.ImageGrab
import ctypes

# Windowsの高DPI（画面拡大率設定）による座標のズレを防ぐ
try:
    ctypes.windll.shcore.SetProcessDpiAwareness(1)
except Exception:
    try:
        ctypes.windll.user32.SetProcessDPIAware()
    except Exception:
        pass

class DesktopMagnifier:
    def __init__(self, root):
        self.root = root
        self.root.title("デスクトップ拡大鏡")

        # 拡大鏡の初期設定
        self.lens_size = 150   # 初期サイズ: 150px × 150px
        self.zoom_factor = 2.0 # 初期倍率: 2.0倍

        # 最前面表示 & タイトルバー無しの設定
        self.root.attributes("-topmost", True)
        self.root.overrideredirect(True)

        # キャンバスの作成
        self.canvas = tk.Canvas(root, width=self.lens_size, height=self.lens_size, highlightthickness=0)
        self.canvas.pack()

        self.mag_image_id = None
        self.border_id = None
        self.flash_counter = 0  # 枠線を光らせるタイマー用

        # マウスのクリックイベント（右クリックで終了）
        self.root.bind("<Button-3>", lambda e: self.root.quit())

        # マウスが乗ったら自動でキーボード操作を受け付ける
        self.root.bind("<Enter>", lambda e: self.root.focus_force())

        # マウスホイールの回転
        self.root.bind("<MouseWheel>", self.change_zoom)

        # --------------------------------------------------
        # ★ キーボード入力を管理
        # --------------------------------------------------
        self.root.bind("<Key>", self.handle_keypress)

        # リアルタイム更新ループの開始
        self.update_loop()

    # アプリケーションを安全に終了するメソッド
    def close_app(self):
        self.root.destroy()

    # --------------------------------------------------
    # ★ キー判定の修正（環境に左右されない名前判定を導入）
    # --------------------------------------------------
    def handle_keypress(self, event):
        # キーの名前が「Escape」なら確実に終了する
        if event.keysym == "Escape":
            self.close_app()
        # 187: セミコロン/プラスキー, 107: テンキーのプラス
        elif event.keycode in (187, 107) or event.keysym in ("plus", "semicolon"):
            self.increase_lens_size()
        # 189: マイナスキー, 109: テンキーのマイナス
        elif event.keycode in (189, 109) or event.keysym in ("minus", "equal"):
            self.decrease_lens_size()

    # 倍率変更の処理 (1.5倍 〜 8.0倍 の範囲)
    def change_zoom(self, event):
        if event.delta > 0:
            self.zoom_factor = min(8.0, self.zoom_factor + 0.5)
        else:
            self.zoom_factor = max(1.5, self.zoom_factor - 0.5)
        self.trigger_flash()

    # レンズサイズ変更の処理 (100px 〜 500px の範囲)
    def increase_lens_size(self):
        self.lens_size = min(500, self.lens_size + 25)
        self.canvas.config(width=self.lens_size, height=self.lens_size)
        self.trigger_flash()

    def decrease_lens_size(self):
        self.lens_size = max(100, self.lens_size - 25)
        self.canvas.config(width=self.lens_size, height=self.lens_size)
        self.trigger_flash()

    # 操作が成功したときに枠線を赤く光らせるエフェクト
    def trigger_flash(self):
        self.flash_counter = 3

    def update_loop(self):
        cursor_x = self.root.winfo_pointerx()
        cursor_y = self.root.winfo_pointery()

        crop_size = self.lens_size / self.zoom_factor

        left = cursor_x - (crop_size / 2)
        top = cursor_y - (crop_size / 2)
        right = left + crop_size
        bottom = top + crop_size

        try:
            screenshot = PIL.ImageGrab.grab(
                bbox=(int(left), int(top), int(right), int(bottom)),
                all_screens=True
            )

            zoomed = screenshot.resize((self.lens_size, self.lens_size), PIL.Image.Resampling.LANCZOS)

            if self.mag_image_id:
                self.canvas.delete(self.mag_image_id)
            if self.border_id:
                self.canvas.delete(self.border_id)

            self.mag_photo = PIL.ImageTk.PhotoImage(zoomed)
            self.mag_image_id = self.canvas.create_image(0, 0, image=self.mag_photo, anchor=tk.NW)

            if self.flash_counter > 0:
                self.border_id = self.canvas.create_rectangle(
                    0, 0, self.lens_size, self.lens_size,
                    outline="red", width=4
                )
                self.flash_counter -= 1

        except Exception as e:
            pass

        min_offset = (crop_size / 2) + (self.lens_size / 2) + 10

        center_x = cursor_x + min_offset
        center_y = cursor_y + min_offset

        wx = int(center_x - (self.lens_size / 2))
        wy = int(center_y - (self.lens_size / 2))

        self.root.geometry(f"{self.lens_size}x{self.lens_size}+{wx}+{wy}")

        self.root.after(30, self.update_loop)

if __name__ == "__main__":
    root = tk.Tk()
    app = DesktopMagnifier(root)
    root.mainloop()
