Загрузка данных


import tkinter as tk
from tkinter import colorchooser
import ctypes
from ctypes import wintypes
import threading
import time

import win32api
import win32gui
import win32con

import keyboard
from pynput import mouse

# --- Подключение Win32 функций через ctypes для надежности ---
user32 = ctypes.windll.user32
gdi32 = ctypes.windll.gdi32

# Установка DPI Awareness (важно для координат)
try:
    user32.SetProcessDpiAwarenessContext(ctypes.c_void_p(-4)) # Per Monitor Awareness V2
except Exception:
    try:
        ctypes.windll.shcore.SetProcessDpiAwareness(2)
    except Exception:
        user32.SetProcessDPIAware()

class GameOverlayApp:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("Settings")
        self.root.geometry("320x190")
        self.root.attributes("-topmost", True)
        self.root.resizable(False, False)
        
        # Протокол закрытия основного окна
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
        
        self.state = 'IDLE' 
        self.points = []
        self.hotkey = 'f4'
        
        self.line_thickness = tk.IntVar(value=3)
        self.line_color = '#00FF00' # Ярко-зеленый по умолчанию
        self.line_id = None
        self.mouse_listener = None
        
        self.setup_ui()
        self.setup_overlay_win32()
        
        keyboard.add_hotkey(self.hotkey, self.on_hotkey_pressed)

    def setup_ui(self):
        frame = tk.Frame(self.root, padx=10, pady=10)
        frame.pack(fill=tk.BOTH, expand=True)

        tk.Label(frame, text="Толщина линии:").grid(row=0, column=0, sticky="w", pady=5)
        tk.Spinbox(frame, from_=1, to=20, textvariable=self.line_thickness, width=5).grid(row=0, column=1, sticky="w", pady=5)

        tk.Label(frame, text="Цвет линии:").grid(row=1, column=0, sticky="w", pady=5)
        self.btn_color = tk.Button(frame, bg=self.line_color, width=5, command=self.choose_color)
        self.btn_color.grid(row=1, column=1, sticky="w", pady=5)

        tk.Label(frame, text="Горячая клавиша:").grid(row=2, column=0, sticky="w", pady=5)
        self.btn_rebind = tk.Button(frame, text=f"Изменить ({self.hotkey})", command=self.rebind_hotkey)
        self.btn_rebind.grid(row=2, column=1, sticky="ew", pady=5)

        self.lbl_status = tk.Label(frame, text="Статус: Ожидание", fg="black", font=("Arial", 9, "bold"))
        self.lbl_status.grid(row=3, column=0, columnspan=2, pady=10)

    def setup_overlay_win32(self):
        """Создание оверлея с использованием хардкорного Win32 API для прозрачности."""
        self.overlay = tk.Toplevel(self.root)
        self.overlay.overrideredirect(True)
        self.overlay.attributes("-topmost", True)
        
        # Используем ЧЕРНЫЙ фон как ключ прозрачности. 
        # Рисовать черным цветом на оверлее будет нельзя (он станет прозрачным).
        self.bg_color_key = '#000000' 
        self.overlay.config(bg=self.bg_color_key)
        
        # Размеры виртуального экрана
        self.v_width = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN)
        self.v_height = win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN)
        self.v_x = win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN)
        self.v_y = win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN)
        
        self.overlay.geometry(f"{self.v_width}x{self.v_height}+{self.v_x}+{self.v_y}")
        
        self.canvas = tk.Canvas(self.overlay, width=self.v_width, height=self.v_height, 
                                bg=self.bg_color_key, highlightthickness=0)
        self.canvas.pack(fill=tk.BOTH, expand=True)
        
        # Ждем, пока Tkinter создаст окно реально в ОС
        self.overlay.update_idletasks()
        
        # Получаем HWND (дескриптор окна)
        self.hwnd = user32.GetParent(self.overlay.winfo_id())
        
        # --- ПРИМЕНЕНИЕ СТИЛЕЙ WIN32 (WS_EX_LAYERED и WS_EX_TRANSPARENT) ---
        # Получаем текущие расширенные стили
        ex_style = user32.GetWindowLongW(self.hwnd, win32con.GWL_EXSTYLE)
        
        # Накладываем стиль Многослойного окна (LAYERED) и Прозрачного для мыши (TRANSPARENT)
        new_ex_style = ex_style | win32con.WS_EX_LAYERED | win32con.WS_EX_TRANSPARENT
        user32.SetWindowLongW(self.hwnd, win32con.GWL_EXSTYLE, new_ex_style)
        
        # Устанавливаем Ключ Прозрачности (ColorKey). 
        # Все пиксели цвета self.bg_color_key (черные) станут на 100% прозрачными визуально и для мыши.
        # RGB ключ для черного: 0, 0, 0
        user32.SetLayeredWindowAttributes(self.hwnd, 0x000000, 0, win32con.LWA_COLORKEY)
        
        # Принудительное обновление стилей
        user32.SetWindowPos(self.hwnd, win32con.HWND_TOPMOST, 0,0,0,0, 
                             win32con.SWP_NOMOVE | win32con.SWP_NOSIZE | win32con.SWP_FRAMECHANGED | win32con.SWP_SHOWWINDOW)

    # --- Остальная логика без изменений ---
    def choose_color(self):
        color = colorchooser.askcolor(initialcolor=self.line_color, title="Выберите цвет")
        if color[1]:
            if color[1] == '#000000': # Запрет черного цвета
                self.line_color = '#000001' # Почти черный, но не ключ
            else:
                self.line_color = color[1]
            self.btn_color.config(bg=self.line_color)
            if self.state == 'DRAWN':
                self.draw_line() # Обновить цвет если уже нарисована

    def rebind_hotkey(self):
        self.btn_rebind.config(text="Нажмите клавишу...", state=tk.DISABLED)
        keyboard.unhook_all_hotkeys()
        threading.Thread(target=self._wait_for_new_hotkey, daemon=True).start()

    def _wait_for_new_hotkey(self):
        new_key = keyboard.read_key(suppress=False)
        time.sleep(0.3) 
        self.hotkey = new_key
        keyboard.add_hotkey(self.hotkey, self.on_hotkey_pressed)
        self.root.after(0, self._finish_rebind, new_key)

    def _finish_rebind(self, new_key):
        self.btn_rebind.config(text=f"Изменить ({new_key})", state=tk.NORMAL)

    def on_hotkey_pressed(self):
        self.root.after(0, self.handle_state_machine)

    def handle_state_machine(self):
        if self.state == 'IDLE':
            self.start_listening()
        elif self.state == 'LISTENING':
            self.stop_listening()
        elif self.state == 'DRAWN':
            self.clear_line()

    def start_listening(self):
        self.state = 'LISTENING'
        self.points = []
        self.lbl_status.config(text="Статус: Кликните 2 раза ЛКМ", fg="blue")
        # Глобальный пассивный хук
        self.mouse_listener = mouse.Listener(on_click=self.on_mouse_click)
        self.mouse_listener.start()

    def stop_listening(self):
        self.state = 'IDLE'
        if self.mouse_listener:
            self.mouse_listener.stop()
            self.mouse_listener = None
        self.lbl_status.config(text="Статус: Ожидание", fg="black")

    def on_mouse_click(self, x, y, button, pressed):
        if button == mouse.Button.left and pressed:
            # Предотвращаем клик по самому окну настроек
            try:
                if user32.GetForegroundWindow() == self.root.winfo_id():
                    return
            except: pass

            self.points.append((x, y))
            if len(self.points) == 2:
                self.root.after(0, self.draw_line)
                return False 

    def draw_line(self):
        self.state = 'DRAWN'
        if self.line_id:
            self.canvas.delete(self.line_id)
            
        p1, p2 = self.points
        # Коррекция координат под вирт. экран
        c_x1, c_y1 = p1[0] - self.v_x, p1[1] - self.v_y
        c_x2, c_y2 = p2[0] - self.v_x, p2[1] - self.v_y

        self.line_id = self.canvas.create_line(
            c_x1, c_y1, c_x2, c_y2, 
            fill=self.line_color, 
            width=self.line_thickness.get(),
            capstyle=tk.ROUND
        )
        self.lbl_status.config(text="Статус: Линия ОК", fg="green")
        self.stop_listening()

    def clear_line(self):
        self.state = 'IDLE'
        if self.line_id:
            self.canvas.delete(self.line_id)
            self.line_id = None
        self.lbl_status.config(text="Статус: Ожидание", fg="black")

    def on_closing(self):
        keyboard.unhook_all_hotkeys()
        self.root.destroy()

    def run(self):
        self.root.mainloop()

if __name__ == "__main__":
    app = GameOverlayApp()
    app.run()