124 lines
3.7 KiB
Python
124 lines
3.7 KiB
Python
"""
|
|
Clipboard management with 60-second auto-clear.
|
|
Uses xclip/xdotool for SteamOS (KDE Plasma).
|
|
"""
|
|
import asyncio
|
|
import subprocess
|
|
import time
|
|
from typing import Optional
|
|
|
|
# Clipboard state
|
|
_clipboard_timers: dict = {}
|
|
_clipboard_contents: dict = {}
|
|
|
|
|
|
async def copy_to_clipboard(text: str, clear_after: int = 60, label: str = "default") -> dict:
|
|
"""Copy text to clipboard with auto-clear after specified seconds."""
|
|
global _clipboard_timers, _clipboard_contents
|
|
|
|
try:
|
|
# Cancel any existing timer for this label
|
|
if label in _clipboard_timers:
|
|
_clipboard_timers[label].cancel()
|
|
|
|
# Store the text
|
|
_clipboard_contents[label] = text
|
|
|
|
# Copy to clipboard using xclip
|
|
process = await asyncio.create_subprocess_exec(
|
|
"xclip", "-selection", "clipboard",
|
|
stdin=asyncio.subprocess.PIPE,
|
|
stdout=asyncio.subprocess.PIPE,
|
|
stderr=asyncio.subprocess.PIPE,
|
|
)
|
|
await process.communicate(input=text.encode("utf-8"))
|
|
|
|
if process.returncode != 0:
|
|
# Fallback to xsel if xclip not available
|
|
process = await asyncio.create_subprocess_exec(
|
|
"xsel", "--clipboard", "--input",
|
|
stdin=asyncio.subprocess.PIPE,
|
|
stdout=asyncio.subprocess.PIPE,
|
|
stderr=asyncio.subprocess.PIPE,
|
|
)
|
|
await process.communicate(input=text.encode("utf-8"))
|
|
|
|
if process.returncode != 0:
|
|
return {"success": False, "error": "No clipboard tool available (xclip or xsel)"}
|
|
|
|
# Set up auto-clear timer
|
|
async def clear_clipboard():
|
|
await asyncio.sleep(clear_after)
|
|
await clear_clipboard_label(label)
|
|
|
|
timer = asyncio.create_task(clear_clipboard())
|
|
_clipboard_timers[label] = timer
|
|
|
|
return {
|
|
"success": True,
|
|
"clear_after": clear_after,
|
|
"expires_at": int(time.time()) + clear_after,
|
|
}
|
|
|
|
except FileNotFoundError:
|
|
return {"success": False, "error": "xclip/xsel not found. Install with: pacman -S xclip"}
|
|
except Exception as e:
|
|
return {"success": False, "error": str(e)}
|
|
|
|
|
|
async def clear_clipboard_label(label: str = "default") -> dict:
|
|
"""Clear the clipboard for a specific label."""
|
|
global _clipboard_timers, _clipboard_contents
|
|
|
|
# Cancel timer if exists
|
|
if label in _clipboard_timers:
|
|
_clipboard_timers[label].cancel()
|
|
del _clipboard_timers[label]
|
|
|
|
# Clear clipboard content
|
|
if label in _clipboard_contents:
|
|
del _clipboard_contents[label]
|
|
|
|
# Clear the actual clipboard
|
|
try:
|
|
process = await asyncio.create_subprocess_exec(
|
|
"xclip", "-selection", "clipboard",
|
|
stdin=asyncio.subprocess.PIPE,
|
|
stdout=asyncio.subprocess.PIPE,
|
|
stderr=asyncio.subprocess.PIPE,
|
|
)
|
|
await process.communicate(input=b"")
|
|
except FileNotFoundError:
|
|
pass
|
|
|
|
return {"success": True}
|
|
|
|
|
|
async def clear_all_clipboards() -> dict:
|
|
"""Clear all clipboard contents and cancel all timers."""
|
|
global _clipboard_timers, _clipboard_contents
|
|
|
|
for label in list(_clipboard_timers.keys()):
|
|
_clipboard_timers[label].cancel()
|
|
|
|
_clipboard_timers.clear()
|
|
_clipboard_contents.clear()
|
|
|
|
await clear_clipboard_label("default")
|
|
|
|
return {"success": True}
|
|
|
|
|
|
def get_clipboard_status(label: str = "default") -> dict:
|
|
"""Get the status of clipboard content for a label."""
|
|
global _clipboard_contents, _clipboard_timers
|
|
|
|
if label not in _clipboard_contents:
|
|
return {"has_content": False}
|
|
|
|
return {
|
|
"has_content": True,
|
|
"label": label,
|
|
"has_timer": label in _clipboard_timers,
|
|
}
|