Created
March 25, 2025 16:16
-
-
Save adamsvoboda/36c95f21c1f3943b94e3953312d121cc to your computer and use it in GitHub Desktop.
window_shield.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
import signal | |
from PyQt5 import QtCore, QtWidgets, QtGui | |
import ctypes | |
# Platform-specific screen capture protection | |
if sys.platform == 'win32': | |
user32, kernel32 = ctypes.windll.user32, ctypes.windll.kernel32 | |
def set_protection(window): | |
result = user32.SetWindowDisplayAffinity(int(window.winId()), 0x00000011) | |
return (True, "Success") if result else (False, f"Error: {kernel32.GetLastError()}") | |
elif sys.platform == 'darwin': | |
try: | |
from Foundation import NSBundle | |
from Cocoa import NSWindow, NSWindowSharingNone | |
from AppKit import NSFloatingWindowLevel, NSApp | |
def set_protection(window): | |
try: | |
success = False | |
for nswindow in NSApp.windows(): | |
try: | |
nswindow.setSharingType_(NSWindowSharingNone) | |
nswindow.setLevel_(NSFloatingWindowLevel) | |
success = True | |
except: pass | |
return (True, "Success") if success else (False, "Failed to protect window") | |
except Exception as e: | |
return False, str(e) | |
except ImportError: | |
def set_protection(window): | |
return False, "PyObjC not properly installed" | |
# Import pynput for global hotkeys | |
try: | |
from pynput import keyboard | |
pynput_available = True | |
except ImportError: | |
pynput_available = False | |
# Thread-safe signal intermediary | |
class HotkeySignals(QtCore.QObject): | |
move_signal = QtCore.pyqtSignal(str) | |
toggle_signal = QtCore.pyqtSignal() | |
class MainWindow(QtWidgets.QWidget): | |
def __init__(self): | |
super().__init__() | |
# Setup window appearance | |
self.setAttribute(QtCore.Qt.WA_TranslucentBackground) | |
self.setStyleSheet("background-color: rgba(50, 50, 50, 180);") | |
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint) | |
self.setAttribute(QtCore.Qt.WA_ShowWithoutActivating) | |
self.setGeometry(100, 100, 300, 200) | |
self.move_step = 10 | |
# Create UI | |
layout = QtWidgets.QVBoxLayout() | |
self.status_label = QtWidgets.QLabel("✓ Hidden from screen capture") | |
self.status_label.setStyleSheet("color: white; font-size: 14px; font-weight: bold;") | |
layout.addWidget(self.status_label) | |
modifier_key = "Alt" if sys.platform == 'win32' else "⌘" | |
self.info_label = QtWidgets.QLabel(f"{modifier_key}+Arrow keys to move • {modifier_key}+B to toggle visibility") | |
self.info_label.setStyleSheet("color: white;") | |
layout.addWidget(self.info_label) | |
self.error_label = QtWidgets.QLabel() | |
self.error_label.setStyleSheet("color: red; font-size: 12px;") | |
self.error_label.setWordWrap(True) | |
self.error_label.hide() | |
layout.addWidget(self.error_label) | |
self.setLayout(layout) | |
# Enable mouse controls and setup hotkeys | |
self.setMouseTracking(True) | |
self.signals = HotkeySignals() | |
self.signals.move_signal.connect(self.move_window) | |
self.signals.toggle_signal.connect(self.toggle_visibility) | |
self.setup_global_hotkeys() | |
def setup_global_hotkeys(self): | |
if not pynput_available: | |
self.error_label.setText("Global hotkeys unavailable: install pynput package") | |
self.error_label.show() | |
return | |
try: | |
self.modifier_key = keyboard.Key.alt if sys.platform == 'win32' else keyboard.Key.cmd | |
self.pressed_keys = set() | |
self.keyboard_listener = keyboard.Listener( | |
on_press=self.on_key_press, | |
on_release=lambda key: self.pressed_keys.discard(key) or True, | |
suppress=False | |
) | |
self.keyboard_listener.start() | |
except: pass | |
def mousePressEvent(self, event): | |
if event.button() == QtCore.Qt.RightButton: | |
self.toggle_visibility() | |
elif event.button() == QtCore.Qt.LeftButton: | |
self.drag_position = event.globalPos() - self.frameGeometry().topLeft() | |
def mouseMoveEvent(self, event): | |
if hasattr(self, 'drag_position') and event.buttons() == QtCore.Qt.LeftButton: | |
self.move(event.globalPos() - self.drag_position) | |
def on_key_press(self, key): | |
try: | |
if key is None: return True | |
self.pressed_keys.add(key) | |
if self.modifier_key in self.pressed_keys: | |
if keyboard.Key.left in self.pressed_keys: | |
self.signals.move_signal.emit("left") | |
elif keyboard.Key.right in self.pressed_keys: | |
self.signals.move_signal.emit("right") | |
elif keyboard.Key.up in self.pressed_keys: | |
self.signals.move_signal.emit("up") | |
elif keyboard.Key.down in self.pressed_keys: | |
self.signals.move_signal.emit("down") | |
# Check for B key | |
elif any((hasattr(k, 'char') and k.char == 'b') or str(k).lower() in ["'b'", "key.b"] | |
for k in self.pressed_keys): | |
self.signals.toggle_signal.emit() | |
except: pass | |
return True | |
def move_window(self, direction): | |
delta = {"left": (-self.move_step, 0), | |
"right": (self.move_step, 0), | |
"up": (0, -self.move_step), | |
"down": (0, self.move_step)}.get(direction, (0, 0)) | |
self.move(self.pos() + QtCore.QPoint(*delta)) | |
def toggle_visibility(self): | |
if self.isVisible(): | |
self.hide() | |
else: | |
flags = self.windowFlags() | |
self.setVisible(True) | |
self.setWindowFlags(flags) | |
self.show() | |
self.raise_() | |
def showEvent(self, event): | |
success, message = set_protection(self) | |
self.raise_() | |
if success: | |
self.status_label.setText("✓ Hidden from screen capture") | |
self.status_label.setStyleSheet("color: white; font-size: 14px; font-weight: bold;") | |
self.error_label.hide() | |
else: | |
self.status_label.setText("✗ Not hidden from screen capture") | |
self.status_label.setStyleSheet("color: red; font-size: 14px; font-weight: bold;") | |
self.error_label.setText(message) | |
self.error_label.show() | |
def closeEvent(self, event): | |
if hasattr(self, 'keyboard_listener') and self.keyboard_listener: | |
try: self.keyboard_listener.stop() | |
except: pass | |
super().closeEvent(event) | |
def signal_handler(sig, frame): | |
"""Handle Ctrl+C to gracefully quit the application""" | |
if QtWidgets.QApplication.instance(): | |
QtWidgets.QApplication.instance().quit() | |
if __name__ == "__main__": | |
# Set up signal handler for Ctrl+C | |
signal.signal(signal.SIGINT, signal_handler) | |
app = QtWidgets.QApplication(sys.argv) | |
window = MainWindow() | |
window.show() | |
# This allows Ctrl+C processing in the main loop | |
timer = QtCore.QTimer() | |
timer.start(100) | |
timer.timeout.connect(lambda: None) | |
sys.exit(app.exec_()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment