screenoverlay 0.3.0__py3-none-any.whl → 0.3.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 +1 -1
- screenoverlay/overlay.py +120 -26
- {screenoverlay-0.3.0.dist-info → screenoverlay-0.3.1.dist-info}/METADATA +57 -24
- screenoverlay-0.3.1.dist-info/RECORD +7 -0
- screenoverlay-0.3.0.dist-info/RECORD +0 -7
- {screenoverlay-0.3.0.dist-info → screenoverlay-0.3.1.dist-info}/WHEEL +0 -0
- {screenoverlay-0.3.0.dist-info → screenoverlay-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {screenoverlay-0.3.0.dist-info → screenoverlay-0.3.1.dist-info}/top_level.txt +0 -0
screenoverlay/__init__.py
CHANGED
screenoverlay/overlay.py
CHANGED
|
@@ -8,6 +8,11 @@ import tkinter as tk
|
|
|
8
8
|
import platform
|
|
9
9
|
import sys
|
|
10
10
|
import os
|
|
11
|
+
import threading
|
|
12
|
+
from multiprocessing import Process, Queue
|
|
13
|
+
import time
|
|
14
|
+
import atexit
|
|
15
|
+
import signal
|
|
11
16
|
|
|
12
17
|
|
|
13
18
|
class NativeBlurOverlay:
|
|
@@ -51,37 +56,124 @@ class NativeBlurOverlay:
|
|
|
51
56
|
|
|
52
57
|
self.root = None
|
|
53
58
|
self._timer_id = None
|
|
59
|
+
self._process = None
|
|
60
|
+
self._command_queue = None
|
|
61
|
+
|
|
62
|
+
# Register cleanup on exit to prevent orphaned processes
|
|
63
|
+
atexit.register(self._cleanup_on_exit)
|
|
64
|
+
|
|
65
|
+
def _cleanup_on_exit(self):
|
|
66
|
+
"""Cleanup overlay process on program exit"""
|
|
67
|
+
if self._process is not None and self._process.is_alive():
|
|
68
|
+
try:
|
|
69
|
+
# Try graceful stop first
|
|
70
|
+
if self._command_queue is not None:
|
|
71
|
+
try:
|
|
72
|
+
self._command_queue.put('stop')
|
|
73
|
+
except:
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
# Wait briefly
|
|
77
|
+
self._process.join(timeout=0.5)
|
|
78
|
+
|
|
79
|
+
# Force kill if still alive
|
|
80
|
+
if self._process.is_alive():
|
|
81
|
+
self._process.terminate()
|
|
82
|
+
self._process.join(timeout=0.5)
|
|
83
|
+
|
|
84
|
+
# Last resort - force kill
|
|
85
|
+
if self._process.is_alive():
|
|
86
|
+
self._process.kill()
|
|
87
|
+
except:
|
|
88
|
+
pass
|
|
54
89
|
|
|
55
90
|
def start(self):
|
|
56
91
|
"""
|
|
57
|
-
Start the overlay
|
|
92
|
+
Start the overlay process with show/hide control.
|
|
93
|
+
Call this once at app startup.
|
|
58
94
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
#
|
|
95
|
+
After calling start(), use show() and hide() to control visibility instantly.
|
|
96
|
+
|
|
97
|
+
Example for ScreenStop:
|
|
98
|
+
overlay = Overlay(mode='blur', blur_strength=4)
|
|
99
|
+
overlay.start() # Initialize (call once)
|
|
100
|
+
|
|
101
|
+
overlay.show() # Show overlay (instant)
|
|
102
|
+
time.sleep(2)
|
|
103
|
+
overlay.hide() # Hide overlay (instant)
|
|
104
|
+
overlay.show() # Show again
|
|
105
|
+
|
|
106
|
+
overlay.stop() # Cleanup when done
|
|
64
107
|
"""
|
|
65
|
-
self.
|
|
66
|
-
|
|
108
|
+
if self._process is not None:
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
self._command_queue = Queue()
|
|
112
|
+
self._process = Process(target=self._run_process, args=(self._command_queue,), daemon=True)
|
|
113
|
+
self._process.start()
|
|
114
|
+
|
|
115
|
+
# Wait a bit for process to initialize
|
|
116
|
+
time.sleep(0.3)
|
|
117
|
+
|
|
118
|
+
def show(self):
|
|
119
|
+
"""Show the overlay (instant, ~1ms)"""
|
|
120
|
+
if self._command_queue is not None:
|
|
121
|
+
self._command_queue.put('show')
|
|
122
|
+
|
|
123
|
+
def hide(self):
|
|
124
|
+
"""Hide the overlay (instant, ~1ms)"""
|
|
125
|
+
if self._command_queue is not None:
|
|
126
|
+
self._command_queue.put('hide')
|
|
67
127
|
|
|
68
128
|
def stop(self):
|
|
69
|
-
"""Stop and
|
|
70
|
-
if self.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
129
|
+
"""Stop and cleanup the overlay completely"""
|
|
130
|
+
if self._command_queue is not None:
|
|
131
|
+
self._command_queue.put('stop')
|
|
132
|
+
|
|
133
|
+
if self._process is not None:
|
|
134
|
+
self._process.join(timeout=2.0)
|
|
135
|
+
if self._process.is_alive():
|
|
136
|
+
self._process.terminate()
|
|
137
|
+
self._process = None
|
|
138
|
+
|
|
139
|
+
self._command_queue = None
|
|
140
|
+
|
|
141
|
+
def _run_process(self, command_queue):
|
|
142
|
+
"""Run overlay in separate process with command queue"""
|
|
143
|
+
try:
|
|
144
|
+
# Create window
|
|
145
|
+
self._create_window()
|
|
146
|
+
self.root.withdraw() # Start hidden
|
|
147
|
+
|
|
148
|
+
# Process commands from queue
|
|
149
|
+
def check_commands():
|
|
150
|
+
try:
|
|
151
|
+
while not command_queue.empty():
|
|
152
|
+
cmd = command_queue.get_nowait()
|
|
153
|
+
if cmd == 'show':
|
|
154
|
+
self.root.deiconify()
|
|
155
|
+
self.root.lift()
|
|
156
|
+
elif cmd == 'hide':
|
|
157
|
+
self.root.withdraw()
|
|
158
|
+
elif cmd == 'stop':
|
|
159
|
+
self.root.quit()
|
|
160
|
+
return
|
|
161
|
+
except:
|
|
162
|
+
pass
|
|
163
|
+
|
|
164
|
+
# Check again in 10ms
|
|
165
|
+
self.root.after(10, check_commands)
|
|
166
|
+
|
|
167
|
+
# Start command checker
|
|
168
|
+
check_commands()
|
|
169
|
+
|
|
170
|
+
# Run mainloop
|
|
171
|
+
self.root.mainloop()
|
|
172
|
+
|
|
173
|
+
except Exception as e:
|
|
174
|
+
print(f"Overlay process error: {e}")
|
|
175
|
+
finally:
|
|
176
|
+
os._exit(0)
|
|
85
177
|
|
|
86
178
|
def _create_window(self):
|
|
87
179
|
"""Internal method to create and configure the Tkinter window"""
|
|
@@ -254,7 +346,7 @@ class NativeBlurOverlay:
|
|
|
254
346
|
print(f"Linux blur effect hint failed: {e}")
|
|
255
347
|
|
|
256
348
|
def kill_completely(self):
|
|
257
|
-
"""Exit the overlay completely"""
|
|
349
|
+
"""Exit the overlay completely (for activate() backward compatibility)"""
|
|
258
350
|
try:
|
|
259
351
|
if self.root:
|
|
260
352
|
self.root.quit()
|
|
@@ -262,7 +354,9 @@ class NativeBlurOverlay:
|
|
|
262
354
|
except:
|
|
263
355
|
pass
|
|
264
356
|
|
|
265
|
-
os._exit(
|
|
357
|
+
# Only call os._exit if we're in activate() mode (has timer)
|
|
358
|
+
if self._timer_id is not None:
|
|
359
|
+
os._exit(0)
|
|
266
360
|
|
|
267
361
|
|
|
268
362
|
if __name__ == "__main__":
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: screenoverlay
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.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
|
|
@@ -109,27 +109,33 @@ Overlay(mode='custom', color_tint=(255, 100, 100), opacity=0.7).activate()
|
|
|
109
109
|
|
|
110
110
|
Press `ESC` to dismiss the overlay early.
|
|
111
111
|
|
|
112
|
-
###
|
|
112
|
+
### Instant Show/Hide Control ⭐ **NEW**
|
|
113
113
|
|
|
114
|
-
For
|
|
114
|
+
For real-time applications that need instant toggling with **zero latency** (like ScreenStop):
|
|
115
115
|
|
|
116
116
|
```python
|
|
117
117
|
from screenoverlay import Overlay
|
|
118
|
-
|
|
118
|
+
import time
|
|
119
|
+
|
|
120
|
+
# Initialize once (one-time ~300ms setup)
|
|
121
|
+
overlay = Overlay(mode='blur', blur_strength=4)
|
|
122
|
+
overlay.start()
|
|
119
123
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
124
|
+
# Then show/hide instantly (~0.1ms each)
|
|
125
|
+
overlay.show() # Overlay appears instantly
|
|
126
|
+
time.sleep(2)
|
|
127
|
+
overlay.hide() # Overlay disappears instantly
|
|
123
128
|
|
|
124
|
-
#
|
|
125
|
-
|
|
126
|
-
|
|
129
|
+
overlay.show() # Show again - still instant!
|
|
130
|
+
time.sleep(2)
|
|
131
|
+
overlay.hide()
|
|
127
132
|
|
|
128
|
-
#
|
|
129
|
-
|
|
130
|
-
overlay_process.join()
|
|
133
|
+
# Cleanup when done
|
|
134
|
+
overlay.stop()
|
|
131
135
|
```
|
|
132
136
|
|
|
137
|
+
**Performance:** `show()` and `hide()` take **~0.1ms** - virtually instant! Perfect for real-time control.
|
|
138
|
+
|
|
133
139
|
**See [`examples/`](examples/) folder for more use cases!**
|
|
134
140
|
|
|
135
141
|
---
|
|
@@ -243,32 +249,45 @@ overlay.activate(duration=5)
|
|
|
243
249
|
|
|
244
250
|
**Note:** Press `ESC` to dismiss early.
|
|
245
251
|
|
|
246
|
-
### `start()` Method
|
|
252
|
+
### `start()` Method ⭐ **NEW**
|
|
247
253
|
|
|
248
|
-
|
|
254
|
+
Initialize the overlay process for instant show/hide control.
|
|
249
255
|
|
|
250
256
|
```python
|
|
251
257
|
overlay.start()
|
|
252
258
|
```
|
|
253
259
|
|
|
254
|
-
**Important:**
|
|
260
|
+
**Important:** Call this once at app startup. It creates a background process (~300ms). After initialization, use `show()` and `hide()` for instant toggling.
|
|
261
|
+
|
|
262
|
+
### `show()` Method ⭐ **NEW**
|
|
263
|
+
|
|
264
|
+
Show the overlay instantly (~0.1ms).
|
|
265
|
+
|
|
266
|
+
```python
|
|
267
|
+
overlay.show()
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
**Performance:** Virtually instant - no subprocess creation, just a queue message.
|
|
271
|
+
|
|
272
|
+
### `hide()` Method ⭐ **NEW**
|
|
273
|
+
|
|
274
|
+
Hide the overlay instantly (~0.1ms).
|
|
255
275
|
|
|
256
276
|
```python
|
|
257
|
-
|
|
258
|
-
p = Process(target=overlay.start)
|
|
259
|
-
p.start()
|
|
260
|
-
# Later: p.terminate()
|
|
277
|
+
overlay.hide()
|
|
261
278
|
```
|
|
262
279
|
|
|
280
|
+
**Performance:** Even faster than `show()` - typically <0.1ms.
|
|
281
|
+
|
|
263
282
|
### `stop()` Method
|
|
264
283
|
|
|
265
|
-
Stop and
|
|
284
|
+
Stop and cleanup the overlay process.
|
|
266
285
|
|
|
267
286
|
```python
|
|
268
287
|
overlay.stop()
|
|
269
288
|
```
|
|
270
289
|
|
|
271
|
-
**Note:**
|
|
290
|
+
**Note:** Call this when your application exits to gracefully terminate the overlay process.
|
|
272
291
|
|
|
273
292
|
---
|
|
274
293
|
|
|
@@ -355,12 +374,25 @@ Overlay(
|
|
|
355
374
|
|
|
356
375
|
## ⚡ Performance
|
|
357
376
|
|
|
358
|
-
|
|
377
|
+
### Latency Benchmarks ⭐ **UPDATED**
|
|
378
|
+
|
|
379
|
+
- **activate() (duration-based):** <50ms startup
|
|
380
|
+
- **start() (one-time init):** ~300ms (creates subprocess)
|
|
381
|
+
- **show() / hide():** **~0.1ms** (virtually instant!)
|
|
382
|
+
|
|
383
|
+
### How It Works
|
|
384
|
+
|
|
359
385
|
- **Method:** Native OS window effects (no screen capture)
|
|
360
386
|
- **Permissions:** None required (works without screen recording access)
|
|
361
387
|
- **Memory:** Minimal footprint
|
|
388
|
+
- **Process Model:** Separate process with queue-based messaging
|
|
389
|
+
|
|
390
|
+
**Why so fast?** Unlike traditional screen capture approaches (400-1000ms), we use:
|
|
391
|
+
1. Native OS-level window blur effects (no image processing)
|
|
392
|
+
2. Persistent subprocess with `withdraw()`/`deiconify()` toggling
|
|
393
|
+
3. Queue-based messaging for instant communication
|
|
362
394
|
|
|
363
|
-
|
|
395
|
+
This makes `show()` and `hide()` nearly **10,000x faster** than recreating the overlay each time!
|
|
364
396
|
|
|
365
397
|
---
|
|
366
398
|
|
|
@@ -420,6 +452,7 @@ Check out the [`examples/`](examples/) directory for complete working examples:
|
|
|
420
452
|
|
|
421
453
|
- **`basic_duration.py`** - Simple blur overlay with fixed duration
|
|
422
454
|
- **`black_screen.py`** - Privacy blackout screen
|
|
455
|
+
- **`show_hide_control.py`** ⭐ **NEW** - Instant show/hide toggling (~0.1ms)
|
|
423
456
|
- **`start_stop_control.py`** - Manual control with multiprocessing
|
|
424
457
|
- **`custom_color.py`** - Custom colored overlay
|
|
425
458
|
|
|
@@ -0,0 +1,7 @@
|
|
|
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,,
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
screenoverlay/__init__.py,sha256=3Dj4hc8poOLtYXw3boa_wl7SC5mQ31OPocfz7uOAGkE,256
|
|
2
|
-
screenoverlay/overlay.py,sha256=vqf516zJKzUuslGPIhljKxSVBIIWHkVJSXMRjK4sgA0,10956
|
|
3
|
-
screenoverlay-0.3.0.dist-info/licenses/LICENSE,sha256=QlEjK4tuMjNEYVlvzaIhxfsCeU8hcGZyuT85cm1YChE,1084
|
|
4
|
-
screenoverlay-0.3.0.dist-info/METADATA,sha256=5A3RMRlGyi6JTGfb0fByhEv55usJp5q-6huXPf6kN5o,13861
|
|
5
|
-
screenoverlay-0.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
6
|
-
screenoverlay-0.3.0.dist-info/top_level.txt,sha256=kfPL07o_kJ-mlb14Ps2zp_tIYnD8GfsSXlbDxDF6Eic,14
|
|
7
|
-
screenoverlay-0.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|