Files
2026-07-23 16:48:58 +02:00

164 lines
5.1 KiB
Python
Executable File

import logging
import win32gui
import win32api
import win32con
import win32clipboard
import threading
import ctypes
import time
from PIL import ImageGrab
_clipboard_thread = None
_hwnd = None # Store the window handle
_callback_update = None
_block_image_once = False
def _get_clipboard_content(enable_image_monitoring=False, enable_file_monitoring=False):
"""
Get the content of the clipboard.
Image:
PNG -> PngImagePlugin.PngImageFile
DIB -> BmpImagePlugin.DibImageFile
PNG, DIB, JPG, etc. -> [file_path1, file_path2, ...]
Text:
CF_UNICODETEXT, CF_TEXT -> str
Files:
CF_HDROP -> (file_path1, file_path2, ...)
"""
# sleep 0.5 to avoid clipboard not ready for read
time.sleep(0.5)
clipboard_type = None
clipboard_content = None
if enable_image_monitoring and win32clipboard.IsClipboardFormatAvailable(
win32con.CF_BITMAP
):
clipboard_type = "image"
clipboard_content = ImageGrab.grabclipboard()
else:
win32clipboard.OpenClipboard()
try:
if win32clipboard.IsClipboardFormatAvailable(win32con.CF_UNICODETEXT):
text = win32clipboard.GetClipboardData(win32con.CF_UNICODETEXT)
clipboard_type = "text"
clipboard_content = text
elif win32clipboard.IsClipboardFormatAvailable(win32con.CF_TEXT):
text_bytes = win32clipboard.GetClipboardData(win32con.CF_TEXT)
text = text_bytes.decode()
clipboard_type = "text"
clipboard_content = text
elif enable_file_monitoring and win32clipboard.IsClipboardFormatAvailable(
win32con.CF_HDROP
):
files = win32clipboard.GetClipboardData(win32con.CF_HDROP)
clipboard_type = "files"
clipboard_content = files
finally:
win32clipboard.CloseClipboard()
return (clipboard_type, clipboard_content)
def _process_message(
hwnd: int,
msg: int,
wparam: int,
lparam: int,
enable_image_monitoring=False,
enable_file_monitoring=False,
):
global _block_image_once
WM_CLIPBOARDUPDATE = 0x031D
if msg == WM_CLIPBOARDUPDATE:
clip = _get_clipboard_content(enable_image_monitoring, enable_file_monitoring)
try:
if clip[0] == "text" and _callback_update:
_callback_update(clip[0], clip[1])
if enable_image_monitoring and clip[0] == "image" and _callback_update:
if _block_image_once:
_block_image_once = False
else:
_callback_update(clip[0], clip[1])
if enable_file_monitoring and clip[0] == "files" and _callback_update:
_callback_update(clip[0], clip[1])
except Exception as e:
logging.error(f"Error processing clipboard update: {e}")
return 0
def _create_window(enable_image_monitoring=False, enable_file_monitoring=False):
global _hwnd
className = "ClipboardHook"
wc = win32gui.WNDCLASS()
wc.lpfnWndProc = lambda hwnd, msg, wparam, lparam: _process_message(
hwnd, msg, wparam, lparam, enable_image_monitoring, enable_file_monitoring
)
wc.lpszClassName = className
wc.hInstance = win32api.GetModuleHandle(None)
class_atom = win32gui.RegisterClass(wc)
_hwnd = win32gui.CreateWindow(
class_atom, className, 0, 0, 0, 0, 0, 0, 0, wc.hInstance, None
)
def _runner(enable_image_monitoring=False, enable_file_monitoring=False):
global _hwnd
_create_window(enable_image_monitoring, enable_file_monitoring)
ctypes.windll.user32.AddClipboardFormatListener(_hwnd)
try:
win32gui.PumpMessages()
finally:
ctypes.windll.user32.RemoveClipboardFormatListener(_hwnd)
win32gui.DestroyWindow(_hwnd)
win32gui.UnregisterClass("ClipboardHook", win32api.GetModuleHandle(None))
def _start(enable_image_monitoring=False, enable_file_monitoring=False):
global _clipboard_thread
if not _clipboard_thread:
_clipboard_thread = threading.Thread(
target=_runner,
args=(enable_image_monitoring, enable_file_monitoring),
daemon=True,
)
_clipboard_thread.start()
def stop():
global _clipboard_thread, _hwnd, _callback_update, _block_image_once
if _clipboard_thread and _hwnd:
win32gui.PostMessage(
_hwnd, win32con.WM_QUIT, 0, 0
) # Send WM_QUIT to the window
_clipboard_thread.join() # Wait for the thread to finish
_clipboard_thread = None
_hwnd = None
_callback_update = None
_block_image_once = False
logging.info("Clipboard monitor stopped")
def wait():
global _clipboard_thread
if _clipboard_thread:
_clipboard_thread.join()
def enable_block_image_once():
global _block_image_once
_block_image_once = True
def on_update(callback, enable_image_monitoring=False, enable_file_monitoring=False):
global _callback_update
_callback_update = callback
_start(enable_image_monitoring, enable_file_monitoring)