screenoverlay 0.3.1__py3-none-any.whl → 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
screenoverlay/__init__.py CHANGED
@@ -5,7 +5,7 @@ Provides blur, black, white, and custom color overlays with minimal latency
5
5
 
6
6
  from .overlay import NativeBlurOverlay as Overlay
7
7
 
8
- __version__ = '0.3.1'
8
+ __version__ = '0.4.0'
9
9
  __author__ = 'Pekay'
10
10
  __all__ = ['Overlay']
11
11
 
screenoverlay/overlay.py CHANGED
@@ -14,6 +14,13 @@ import time
14
14
  import atexit
15
15
  import signal
16
16
 
17
+ # Try to import screeninfo for multi-monitor support
18
+ try:
19
+ from screeninfo import get_monitors
20
+ HAS_SCREENINFO = True
21
+ except ImportError:
22
+ HAS_SCREENINFO = False
23
+
17
24
 
18
25
  class NativeBlurOverlay:
19
26
  def __init__(self, mode='blur', blur_strength=3, opacity=0.85, color_tint=(136, 136, 136)):
@@ -55,6 +62,7 @@ class NativeBlurOverlay:
55
62
  self.apply_blur = True
56
63
 
57
64
  self.root = None
65
+ self.windows = [] # List to hold multiple windows for multi-monitor
58
66
  self._timer_id = None
59
67
  self._process = None
60
68
  self._command_queue = None
@@ -141,9 +149,12 @@ class NativeBlurOverlay:
141
149
  def _run_process(self, command_queue):
142
150
  """Run overlay in separate process with command queue"""
143
151
  try:
144
- # Create window
145
- self._create_window()
146
- self.root.withdraw() # Start hidden
152
+ # Create windows for all monitors
153
+ self._create_windows()
154
+
155
+ # Hide all windows initially
156
+ for win in self.windows:
157
+ win.withdraw()
147
158
 
148
159
  # Process commands from queue
149
160
  def check_commands():
@@ -151,10 +162,12 @@ class NativeBlurOverlay:
151
162
  while not command_queue.empty():
152
163
  cmd = command_queue.get_nowait()
153
164
  if cmd == 'show':
154
- self.root.deiconify()
155
- self.root.lift()
165
+ for win in self.windows:
166
+ win.deiconify()
167
+ win.lift()
156
168
  elif cmd == 'hide':
157
- self.root.withdraw()
169
+ for win in self.windows:
170
+ win.withdraw()
158
171
  elif cmd == 'stop':
159
172
  self.root.quit()
160
173
  return
@@ -175,6 +188,67 @@ class NativeBlurOverlay:
175
188
  finally:
176
189
  os._exit(0)
177
190
 
191
+ def _get_monitors(self):
192
+ """Get information about all monitors"""
193
+ if HAS_SCREENINFO:
194
+ try:
195
+ monitors = get_monitors()
196
+ return [(m.x, m.y, m.width, m.height) for m in monitors]
197
+ except:
198
+ pass
199
+
200
+ # Fallback: assume single primary monitor
201
+ root = tk.Tk()
202
+ root.withdraw()
203
+ width = root.winfo_screenwidth()
204
+ height = root.winfo_screenheight()
205
+ root.destroy()
206
+ return [(0, 0, width, height)]
207
+
208
+ def _create_windows(self):
209
+ """Create overlay windows for all monitors"""
210
+ monitors = self._get_monitors()
211
+
212
+ # Create primary root window
213
+ self.root = tk.Tk()
214
+ self.root.overrideredirect(True)
215
+ self.root.attributes('-topmost', True)
216
+
217
+ # Configure primary window for first monitor
218
+ if monitors:
219
+ x, y, width, height = monitors[0]
220
+ self._configure_window(self.root, x, y, width, height)
221
+ self.windows.append(self.root)
222
+
223
+ # Create additional windows for other monitors
224
+ for x, y, width, height in monitors[1:]:
225
+ win = tk.Toplevel(self.root)
226
+ win.overrideredirect(True)
227
+ win.attributes('-topmost', True)
228
+ self._configure_window(win, x, y, width, height)
229
+ self.windows.append(win)
230
+
231
+ def _configure_window(self, window, x, y, width, height):
232
+ """Configure a window with overlay settings"""
233
+ # Set background color (tint)
234
+ bg_color = f'#{self.color_tint[0]:02x}{self.color_tint[1]:02x}{self.color_tint[2]:02x}'
235
+ window.configure(bg=bg_color)
236
+
237
+ # Set opacity
238
+ window.attributes('-alpha', self.opacity)
239
+
240
+ # Position and size
241
+ window.geometry(f"{width}x{height}+{x}+{y}")
242
+
243
+ # Apply native blur effect based on OS (only if mode is 'blur')
244
+ if self.apply_blur:
245
+ self._apply_native_blur_to_window(window)
246
+
247
+ # Bind escape key to exit (only on primary window)
248
+ if window == self.root:
249
+ window.bind('<Escape>', lambda e: self.kill_completely())
250
+ window.focus_set()
251
+
178
252
  def _create_window(self):
179
253
  """Internal method to create and configure the Tkinter window"""
180
254
  self.root = tk.Tk()
@@ -214,25 +288,33 @@ class NativeBlurOverlay:
214
288
  self.root.mainloop()
215
289
 
216
290
  def _apply_native_blur(self):
217
- """Apply OS-native backdrop blur effect"""
291
+ """Apply OS-native backdrop blur effect to root window (legacy method)"""
292
+ self._apply_native_blur_to_window(self.root)
293
+
294
+ def _apply_native_blur_to_window(self, window):
295
+ """Apply OS-native backdrop blur effect to a specific window"""
218
296
  system = platform.system()
219
297
 
220
298
  if system == 'Darwin': # macOS
221
- self._apply_macos_blur()
299
+ self._apply_macos_blur_to_window(window)
222
300
  elif system == 'Windows':
223
- self._apply_windows_blur()
301
+ self._apply_windows_blur_to_window(window)
224
302
  elif system == 'Linux':
225
- self._apply_linux_blur()
303
+ self._apply_linux_blur_to_window(window)
226
304
 
227
305
  def _apply_macos_blur(self):
228
- """Apply macOS NSVisualEffectView blur"""
306
+ """Apply macOS NSVisualEffectView blur (legacy method)"""
307
+ self._apply_macos_blur_to_window(self.root)
308
+
309
+ def _apply_macos_blur_to_window(self, window):
310
+ """Apply macOS NSVisualEffectView blur to a specific window"""
229
311
  try:
230
312
  from Cocoa import NSView, NSVisualEffectView
231
313
  from Cocoa import NSVisualEffectBlendingModeBehindWindow, NSVisualEffectMaterialDark
232
314
  import objc
233
315
 
234
316
  # Get the Tk window's NSWindow
235
- window_id = self.root.winfo_id()
317
+ window_id = window.winfo_id()
236
318
 
237
319
  # Create NSVisualEffectView
238
320
  # Note: This requires pyobjc-framework-Cocoa
@@ -270,7 +352,11 @@ class NativeBlurOverlay:
270
352
  print(f"macOS blur effect failed: {e}")
271
353
 
272
354
  def _apply_windows_blur(self):
273
- """Apply Windows Acrylic/Blur effect"""
355
+ """Apply Windows Acrylic/Blur effect (legacy method)"""
356
+ self._apply_windows_blur_to_window(self.root)
357
+
358
+ def _apply_windows_blur_to_window(self, window):
359
+ """Apply Windows Acrylic/Blur effect to a specific window"""
274
360
  try:
275
361
  import ctypes
276
362
  from ctypes import wintypes
@@ -278,10 +364,10 @@ class NativeBlurOverlay:
278
364
  # Get window handle - try multiple methods
279
365
  try:
280
366
  # Method 1: Direct window ID
281
- hwnd = self.root.winfo_id()
367
+ hwnd = window.winfo_id()
282
368
  except:
283
369
  # Method 2: Get parent window
284
- hwnd = ctypes.windll.user32.GetParent(self.root.winfo_id())
370
+ hwnd = ctypes.windll.user32.GetParent(window.winfo_id())
285
371
 
286
372
  if not hwnd:
287
373
  print("Could not get window handle for blur effect")
@@ -332,7 +418,11 @@ class NativeBlurOverlay:
332
418
  print("Overlay will work but without native blur effect")
333
419
 
334
420
  def _apply_linux_blur(self):
335
- """Apply Linux compositor blur (X11/Wayland)"""
421
+ """Apply Linux compositor blur (X11/Wayland) (legacy method)"""
422
+ self._apply_linux_blur_to_window(self.root)
423
+
424
+ def _apply_linux_blur_to_window(self, window):
425
+ """Apply Linux compositor blur (X11/Wayland) to a specific window"""
336
426
  try:
337
427
  # Linux blur depends on compositor (KWin, Mutter, etc.)
338
428
  # Most compositors respect window transparency and apply blur automatically
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: screenoverlay
3
- Version: 0.3.1
3
+ Version: 0.4.0
4
4
  Summary: Cross-platform screen overlay with blur, black, white, and custom modes
5
5
  Home-page: https://github.com/pekay-ai/screenoverlay
6
6
  Author: Pekay
@@ -25,6 +25,7 @@ Requires-Python: >=3.7
25
25
  Description-Content-Type: text/markdown
26
26
  License-File: LICENSE
27
27
  Requires-Dist: pyobjc-framework-Cocoa; platform_system == "Darwin"
28
+ Requires-Dist: screeninfo
28
29
  Provides-Extra: dev
29
30
  Requires-Dist: pytest; extra == "dev"
30
31
  Requires-Dist: twine; extra == "dev"
@@ -70,6 +71,7 @@ We're releasing it as **open-source (MIT)** because we believe privacy tools sho
70
71
 
71
72
  - 🎭 **4 Overlay Modes** - blur, black, white, custom colors
72
73
  - ⚡ **Ultra Fast** - <50ms startup, native OS blur effects
74
+ - 🖥️ **Multi-Monitor Support** - Automatically blurs ALL screens simultaneously
73
75
  - 🔒 **No Permissions** - No screen recording access required
74
76
  - 🌍 **Cross-Platform** - macOS, Windows, Linux
75
77
  - 🎯 **Simple API** - One line of code to activate
@@ -384,13 +386,15 @@ Overlay(
384
386
 
385
387
  - **Method:** Native OS window effects (no screen capture)
386
388
  - **Permissions:** None required (works without screen recording access)
387
- - **Memory:** Minimal footprint
389
+ - **Memory:** Minimal footprint (~10 MB per screen)
388
390
  - **Process Model:** Separate process with queue-based messaging
391
+ - **Multi-Monitor:** Automatically detects and covers all screens
389
392
 
390
393
  **Why so fast?** Unlike traditional screen capture approaches (400-1000ms), we use:
391
394
  1. Native OS-level window blur effects (no image processing)
392
395
  2. Persistent subprocess with `withdraw()`/`deiconify()` toggling
393
396
  3. Queue-based messaging for instant communication
397
+ 4. One window per monitor (all controlled simultaneously)
394
398
 
395
399
  This makes `show()` and `hide()` nearly **10,000x faster** than recreating the overlay each time!
396
400
 
@@ -0,0 +1,7 @@
1
+ screenoverlay/__init__.py,sha256=NX7qDYscRGygfi9p6HJC_gT876L8wNpX1f01sS1mxPc,256
2
+ screenoverlay/overlay.py,sha256=h3z8tl33oB-i4q91Zoc7-mqjypnbYOtZNi5fQKmeoI8,17843
3
+ screenoverlay-0.4.0.dist-info/licenses/LICENSE,sha256=QlEjK4tuMjNEYVlvzaIhxfsCeU8hcGZyuT85cm1YChE,1084
4
+ screenoverlay-0.4.0.dist-info/METADATA,sha256=6XJrI12_VPcULWDkiq69itu6THJW9dhCXD3jAEz63v4,15143
5
+ screenoverlay-0.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
6
+ screenoverlay-0.4.0.dist-info/top_level.txt,sha256=kfPL07o_kJ-mlb14Ps2zp_tIYnD8GfsSXlbDxDF6Eic,14
7
+ screenoverlay-0.4.0.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- screenoverlay/__init__.py,sha256=29uNmyZ483sH3HLk6u-yJ4v5beNOEjbvd5s7SRtKsE0,256
2
- screenoverlay/overlay.py,sha256=f5NlalqC81tm5c1g9FEfZhCqgwaJzFDvO5dNPT1uNJU,14326
3
- screenoverlay-0.3.1.dist-info/licenses/LICENSE,sha256=QlEjK4tuMjNEYVlvzaIhxfsCeU8hcGZyuT85cm1YChE,1084
4
- screenoverlay-0.3.1.dist-info/METADATA,sha256=Kxip-tXj48r2tN_Ihu5gLMrbU9P6ULX7PQZQYvB8diw,14888
5
- screenoverlay-0.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
6
- screenoverlay-0.3.1.dist-info/top_level.txt,sha256=kfPL07o_kJ-mlb14Ps2zp_tIYnD8GfsSXlbDxDF6Eic,14
7
- screenoverlay-0.3.1.dist-info/RECORD,,