Skip to content

Instantly share code, notes, and snippets.

@adamsvoboda
Created March 25, 2025 16:16
Show Gist options
  • Save adamsvoboda/36c95f21c1f3943b94e3953312d121cc to your computer and use it in GitHub Desktop.
Save adamsvoboda/36c95f21c1f3943b94e3953312d121cc to your computer and use it in GitHub Desktop.
window_shield.py
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