screenoverlay 0.3.1__py3-none-any.whl → 0.4.1__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,9 +14,16 @@ 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
- def __init__(self, mode='blur', blur_strength=3, opacity=0.85, color_tint=(136, 136, 136)):
26
+ def __init__(self, mode='blur', blur_strength=3, opacity=0.85, color_tint=(136, 136, 136), all_screens=True):
20
27
  """
21
28
  Initialize native overlay
22
29
 
@@ -29,9 +36,11 @@ class NativeBlurOverlay:
29
36
  - blur_strength (int): How blurred/obscured (1-5, only for mode='blur')
30
37
  - opacity (float): Window opacity (0.0 to 1.0)
31
38
  - color_tint (tuple): RGB color tint (0-255)
39
+ - all_screens (bool): If True, blur all monitors. If False, only blur primary monitor (default: True)
32
40
  """
33
41
  self.mode = mode.lower()
34
42
  self.blur_strength = max(1, min(5, blur_strength))
43
+ self.all_screens = all_screens
35
44
 
36
45
  # Apply mode-specific settings
37
46
  if self.mode == 'black':
@@ -55,6 +64,7 @@ class NativeBlurOverlay:
55
64
  self.apply_blur = True
56
65
 
57
66
  self.root = None
67
+ self.windows = [] # List to hold multiple windows for multi-monitor
58
68
  self._timer_id = None
59
69
  self._process = None
60
70
  self._command_queue = None
@@ -141,9 +151,12 @@ class NativeBlurOverlay:
141
151
  def _run_process(self, command_queue):
142
152
  """Run overlay in separate process with command queue"""
143
153
  try:
144
- # Create window
145
- self._create_window()
146
- self.root.withdraw() # Start hidden
154
+ # Create windows for all monitors
155
+ self._create_windows()
156
+
157
+ # Hide all windows initially
158
+ for win in self.windows:
159
+ win.withdraw()
147
160
 
148
161
  # Process commands from queue
149
162
  def check_commands():
@@ -151,10 +164,12 @@ class NativeBlurOverlay:
151
164
  while not command_queue.empty():
152
165
  cmd = command_queue.get_nowait()
153
166
  if cmd == 'show':
154
- self.root.deiconify()
155
- self.root.lift()
167
+ for win in self.windows:
168
+ win.deiconify()
169
+ win.lift()
156
170
  elif cmd == 'hide':
157
- self.root.withdraw()
171
+ for win in self.windows:
172
+ win.withdraw()
158
173
  elif cmd == 'stop':
159
174
  self.root.quit()
160
175
  return
@@ -175,6 +190,71 @@ class NativeBlurOverlay:
175
190
  finally:
176
191
  os._exit(0)
177
192
 
193
+ def _get_monitors(self):
194
+ """Get information about all monitors"""
195
+ if HAS_SCREENINFO:
196
+ try:
197
+ monitors = get_monitors()
198
+ return [(m.x, m.y, m.width, m.height) for m in monitors]
199
+ except:
200
+ pass
201
+
202
+ # Fallback: assume single primary monitor
203
+ root = tk.Tk()
204
+ root.withdraw()
205
+ width = root.winfo_screenwidth()
206
+ height = root.winfo_screenheight()
207
+ root.destroy()
208
+ return [(0, 0, width, height)]
209
+
210
+ def _create_windows(self):
211
+ """Create overlay windows for all monitors (or just primary if all_screens=False)"""
212
+ monitors = self._get_monitors()
213
+
214
+ # If all_screens is False, only use primary monitor
215
+ if not self.all_screens:
216
+ monitors = monitors[:1] # Only keep first monitor
217
+
218
+ # Create primary root window
219
+ self.root = tk.Tk()
220
+ self.root.overrideredirect(True)
221
+ self.root.attributes('-topmost', True)
222
+
223
+ # Configure primary window for first monitor
224
+ if monitors:
225
+ x, y, width, height = monitors[0]
226
+ self._configure_window(self.root, x, y, width, height)
227
+ self.windows.append(self.root)
228
+
229
+ # Create additional windows for other monitors (only if all_screens=True)
230
+ for x, y, width, height in monitors[1:]:
231
+ win = tk.Toplevel(self.root)
232
+ win.overrideredirect(True)
233
+ win.attributes('-topmost', True)
234
+ self._configure_window(win, x, y, width, height)
235
+ self.windows.append(win)
236
+
237
+ def _configure_window(self, window, x, y, width, height):
238
+ """Configure a window with overlay settings"""
239
+ # Set background color (tint)
240
+ bg_color = f'#{self.color_tint[0]:02x}{self.color_tint[1]:02x}{self.color_tint[2]:02x}'
241
+ window.configure(bg=bg_color)
242
+
243
+ # Set opacity
244
+ window.attributes('-alpha', self.opacity)
245
+
246
+ # Position and size
247
+ window.geometry(f"{width}x{height}+{x}+{y}")
248
+
249
+ # Apply native blur effect based on OS (only if mode is 'blur')
250
+ if self.apply_blur:
251
+ self._apply_native_blur_to_window(window)
252
+
253
+ # Bind escape key to exit (only on primary window)
254
+ if window == self.root:
255
+ window.bind('<Escape>', lambda e: self.kill_completely())
256
+ window.focus_set()
257
+
178
258
  def _create_window(self):
179
259
  """Internal method to create and configure the Tkinter window"""
180
260
  self.root = tk.Tk()
@@ -205,7 +285,7 @@ class NativeBlurOverlay:
205
285
 
206
286
  def activate(self, duration=5):
207
287
  """Show native blur overlay and exit after duration"""
208
- self._create_window()
288
+ self._create_windows() # Use multi-monitor aware method
209
289
 
210
290
  # Auto-exit timer
211
291
  self._timer_id = self.root.after(int(duration * 1000), self.kill_completely)
@@ -214,25 +294,33 @@ class NativeBlurOverlay:
214
294
  self.root.mainloop()
215
295
 
216
296
  def _apply_native_blur(self):
217
- """Apply OS-native backdrop blur effect"""
297
+ """Apply OS-native backdrop blur effect to root window (legacy method)"""
298
+ self._apply_native_blur_to_window(self.root)
299
+
300
+ def _apply_native_blur_to_window(self, window):
301
+ """Apply OS-native backdrop blur effect to a specific window"""
218
302
  system = platform.system()
219
303
 
220
304
  if system == 'Darwin': # macOS
221
- self._apply_macos_blur()
305
+ self._apply_macos_blur_to_window(window)
222
306
  elif system == 'Windows':
223
- self._apply_windows_blur()
307
+ self._apply_windows_blur_to_window(window)
224
308
  elif system == 'Linux':
225
- self._apply_linux_blur()
309
+ self._apply_linux_blur_to_window(window)
226
310
 
227
311
  def _apply_macos_blur(self):
228
- """Apply macOS NSVisualEffectView blur"""
312
+ """Apply macOS NSVisualEffectView blur (legacy method)"""
313
+ self._apply_macos_blur_to_window(self.root)
314
+
315
+ def _apply_macos_blur_to_window(self, window):
316
+ """Apply macOS NSVisualEffectView blur to a specific window"""
229
317
  try:
230
318
  from Cocoa import NSView, NSVisualEffectView
231
319
  from Cocoa import NSVisualEffectBlendingModeBehindWindow, NSVisualEffectMaterialDark
232
320
  import objc
233
321
 
234
322
  # Get the Tk window's NSWindow
235
- window_id = self.root.winfo_id()
323
+ window_id = window.winfo_id()
236
324
 
237
325
  # Create NSVisualEffectView
238
326
  # Note: This requires pyobjc-framework-Cocoa
@@ -270,7 +358,11 @@ class NativeBlurOverlay:
270
358
  print(f"macOS blur effect failed: {e}")
271
359
 
272
360
  def _apply_windows_blur(self):
273
- """Apply Windows Acrylic/Blur effect"""
361
+ """Apply Windows Acrylic/Blur effect (legacy method)"""
362
+ self._apply_windows_blur_to_window(self.root)
363
+
364
+ def _apply_windows_blur_to_window(self, window):
365
+ """Apply Windows Acrylic/Blur effect to a specific window"""
274
366
  try:
275
367
  import ctypes
276
368
  from ctypes import wintypes
@@ -278,10 +370,10 @@ class NativeBlurOverlay:
278
370
  # Get window handle - try multiple methods
279
371
  try:
280
372
  # Method 1: Direct window ID
281
- hwnd = self.root.winfo_id()
373
+ hwnd = window.winfo_id()
282
374
  except:
283
375
  # Method 2: Get parent window
284
- hwnd = ctypes.windll.user32.GetParent(self.root.winfo_id())
376
+ hwnd = ctypes.windll.user32.GetParent(window.winfo_id())
285
377
 
286
378
  if not hwnd:
287
379
  print("Could not get window handle for blur effect")
@@ -332,7 +424,11 @@ class NativeBlurOverlay:
332
424
  print("Overlay will work but without native blur effect")
333
425
 
334
426
  def _apply_linux_blur(self):
335
- """Apply Linux compositor blur (X11/Wayland)"""
427
+ """Apply Linux compositor blur (X11/Wayland) (legacy method)"""
428
+ self._apply_linux_blur_to_window(self.root)
429
+
430
+ def _apply_linux_blur_to_window(self, window):
431
+ """Apply Linux compositor blur (X11/Wayland) to a specific window"""
336
432
  try:
337
433
  # Linux blur depends on compositor (KWin, Mutter, etc.)
338
434
  # 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.1
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
 
@@ -480,11 +484,63 @@ Contributions are welcome! Here's how you can help:
480
484
 
481
485
  ---
482
486
 
483
- ## 📄 License
487
+ ## 📄 Licensing
484
488
 
485
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
489
+ ScreenOverlay uses a **dual-license model** similar to xlwings:
486
490
 
487
- **Why MIT?** We believe privacy tools should be freely accessible to everyone. Use it in your personal projects, integrate it into your commercial applications, or fork it to create something entirely new. No restrictions, no complications.
491
+ ### 🆓 Non-Commercial Use (Free)
492
+
493
+ Free for individuals and non-commercial purposes:
494
+
495
+ ✅ Personal projects
496
+ ✅ Educational use
497
+ ✅ Academic research
498
+ ✅ Open source projects (OSI-approved licenses)
499
+ ✅ Non-profit organizations
500
+ ✅ Evaluation and testing
501
+
502
+ **No license key needed** - just `pip install screenoverlay` and start using it!
503
+
504
+ ### 💼 Commercial Use (License Required)
505
+
506
+ A commercial license is required if you use ScreenOverlay:
507
+
508
+ 💼 At a company or for commercial purposes
509
+ 🏢 In a commercial product or service
510
+ 💰 For client work or revenue-generating activities
511
+ 🔧 In any business context
512
+
513
+ #### Pricing:
514
+
515
+ | License Type | Price | Use Case |
516
+ |--------------|-------|----------|
517
+ | 👨‍💻 **Developer** | $149/year | Single developer |
518
+ | 👥 **Team** | $699/year | Up to 5 developers |
519
+ | 🏢 **Enterprise** | Custom | Unlimited developers + priority support |
520
+
521
+ **All commercial licenses include:**
522
+
523
+ ✅ Commercial use rights
524
+ ✅ Priority email support
525
+ ✅ Perpetual license for purchased version
526
+ ✅ 1 year of updates
527
+
528
+ **[Purchase License](mailto:ppnicky@gmail.com?subject=ScreenOverlay%20Commercial%20License) | [Contact Sales](mailto:ppnicky@gmail.com?subject=ScreenOverlay%20Enterprise%20Inquiry)**
529
+
530
+ ### ❓ Which License Do I Need?
531
+
532
+ **Simple rule:** If you're using it in a business/commercial context, you need a commercial license.
533
+
534
+ | Scenario | License Needed |
535
+ |----------|----------------|
536
+ | Personal side project (no revenue) | 🆓 Non-Commercial |
537
+ | Learning Python at home | 🆓 Non-Commercial |
538
+ | University research project | 🆓 Non-Commercial |
539
+ | Open source project (MIT, GPL, etc.) | 🆓 Non-Commercial |
540
+ | Using at your company/job | 💼 Commercial |
541
+ | Building a SaaS product | 💼 Commercial |
542
+ | Freelance client work | 💼 Commercial |
543
+ | Integrating into commercial software | 💼 Commercial |
488
544
 
489
545
  ---
490
546
 
@@ -0,0 +1,7 @@
1
+ screenoverlay/__init__.py,sha256=NX7qDYscRGygfi9p6HJC_gT876L8wNpX1f01sS1mxPc,256
2
+ screenoverlay/overlay.py,sha256=rLVjc2NGG5TffVg8nZ-PkZ6PftZ8krERavX4ZjguE78,18276
3
+ screenoverlay-0.4.1.dist-info/licenses/LICENSE,sha256=QlEjK4tuMjNEYVlvzaIhxfsCeU8hcGZyuT85cm1YChE,1084
4
+ screenoverlay-0.4.1.dist-info/METADATA,sha256=OVWAHek0ALWLywqb5f4NoGRFF2msyFBoO_Bu8kwFWvY,16746
5
+ screenoverlay-0.4.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
6
+ screenoverlay-0.4.1.dist-info/top_level.txt,sha256=kfPL07o_kJ-mlb14Ps2zp_tIYnD8GfsSXlbDxDF6Eic,14
7
+ screenoverlay-0.4.1.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,,