126 lines
3.4 KiB
Python
126 lines
3.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
Globaler Rechtsklick-Paste: Bei Rechtsklick in externen Apps wird
|
|
der Zwischenablage-Inhalt eingefügt (Strg+V).
|
|
Wird von Hauptfenster, Audio-Notiz und Übersetzer genutzt.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import time
|
|
import threading
|
|
import ctypes
|
|
from ctypes import wintypes
|
|
|
|
_user32 = None
|
|
if sys.platform == "win32":
|
|
try:
|
|
_user32 = ctypes.windll.user32
|
|
except Exception:
|
|
pass
|
|
|
|
_HAS_PYNPUT_MOUSE = False
|
|
try:
|
|
from pynput.mouse import Button as MouseButton, Listener as MouseListener
|
|
_HAS_PYNPUT_MOUSE = True
|
|
except ImportError:
|
|
pass
|
|
|
|
|
|
def _is_own_process_window(hwnd: int) -> bool:
|
|
if _user32 is None:
|
|
return False
|
|
try:
|
|
pid = wintypes.DWORD(0)
|
|
_user32.GetWindowThreadProcessId(hwnd, ctypes.byref(pid))
|
|
return int(pid.value) == int(os.getpid())
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
def _send_ctrl_v():
|
|
if _user32 is None:
|
|
return
|
|
try:
|
|
VK_CONTROL = 0x11
|
|
VK_V = 0x56
|
|
_user32.keybd_event(VK_CONTROL, 0, 0, 0)
|
|
time.sleep(0.02)
|
|
_user32.keybd_event(VK_V, 0, 0, 0)
|
|
_user32.keybd_event(VK_V, 0, 2, 0)
|
|
_user32.keybd_event(VK_CONTROL, 0, 2, 0)
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
def _paste_to_hwnd(hwnd: int) -> bool:
|
|
if _user32 is None or not hwnd:
|
|
return False
|
|
try:
|
|
_user32.SetForegroundWindow(hwnd)
|
|
time.sleep(0.04)
|
|
VK_ESCAPE = 0x1B
|
|
_user32.keybd_event(VK_ESCAPE, 0, 0, 0)
|
|
_user32.keybd_event(VK_ESCAPE, 0, 2, 0)
|
|
time.sleep(0.03)
|
|
_send_ctrl_v()
|
|
return True
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
def _run_listener():
|
|
"""Startet den globalen Rechtsklick-Paste-Listener (Windows)."""
|
|
if sys.platform != "win32" or _user32 is None:
|
|
return
|
|
|
|
def on_click(x, y, button, pressed):
|
|
if pressed:
|
|
return
|
|
if button != MouseButton.right:
|
|
return
|
|
try:
|
|
from aza_persistence import is_global_right_click_paste_enabled
|
|
if not is_global_right_click_paste_enabled():
|
|
return
|
|
hwnd = _user32.GetForegroundWindow()
|
|
if _is_own_process_window(hwnd):
|
|
return
|
|
_paste_to_hwnd(hwnd)
|
|
except Exception:
|
|
pass
|
|
|
|
def _polling_fallback():
|
|
VK_RBUTTON = 0x02
|
|
was_down = False
|
|
while True:
|
|
try:
|
|
from aza_persistence import is_global_right_click_paste_enabled
|
|
if not is_global_right_click_paste_enabled():
|
|
time.sleep(0.5)
|
|
continue
|
|
is_down = bool(_user32.GetAsyncKeyState(VK_RBUTTON) & 0x8000)
|
|
if was_down and not is_down:
|
|
hwnd = _user32.GetForegroundWindow()
|
|
if not _is_own_process_window(hwnd):
|
|
_paste_to_hwnd(hwnd)
|
|
was_down = is_down
|
|
except Exception:
|
|
pass
|
|
time.sleep(0.01)
|
|
|
|
if _HAS_PYNPUT_MOUSE:
|
|
try:
|
|
with MouseListener(on_click=on_click) as listener:
|
|
listener.join()
|
|
return
|
|
except Exception:
|
|
pass
|
|
_polling_fallback()
|
|
|
|
|
|
def start_global_right_click_paste_listener():
|
|
"""Startet den Listener in einem Daemon-Thread."""
|
|
t = threading.Thread(target=_run_listener, daemon=True)
|
|
t.start()
|