mini-arcade-core 1.2.0__tar.gz → 1.2.1__tar.gz
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.
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/PKG-INFO +1 -1
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/pyproject.toml +1 -1
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/capture/capture_adapter.py +44 -29
- mini_arcade_core-1.2.1/src/mini_arcade_core/runtime/capture/capture_worker.py +174 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/LICENSE +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/README.md +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/__init__.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/backend/__init__.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/backend/backend.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/backend/events.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/backend/keys.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/backend/sdl_map.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/backend/types.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/bus.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/__init__.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/cheats.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/commands.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/game.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/game_config.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/gameplay_settings.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/loop/__init__.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/loop/config.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/loop/hooks.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/loop/runner.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/loop/state.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/managers.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/__init__.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/context.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/effects/__init__.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/effects/base.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/effects/crt.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/effects/registry.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/effects/vignette.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/frame_packet.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/packet.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/passes/__init__.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/passes/base.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/passes/begin_frame.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/passes/end_frame.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/passes/lighting.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/passes/postfx.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/passes/ui.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/passes/world.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/pipeline.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/render_service.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/viewport.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/scenes/__init__.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/scenes/models.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/scenes/scene_manager.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/__init__.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/audio/__init__.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/audio/audio_adapter.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/audio/audio_port.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/capture/__init__.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/capture/capture_port.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/context.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/file/__init__.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/file/file_adapter.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/file/file_port.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/input/__init__.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/input/input_adapter.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/input/input_port.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/input_frame.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/render/__init__.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/render/render_port.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/scene/__init__.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/scene/scene_query_adapter.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/scene/scene_query_port.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/services.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/window/__init__.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/window/window_adapter.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/window/window_port.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/scenes/__init__.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/scenes/autoreg.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/scenes/debug_overlay.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/scenes/registry.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/scenes/sim_scene.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/scenes/systems/__init__.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/scenes/systems/base_system.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/scenes/systems/system_pipeline.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/spaces/__init__.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/spaces/d2/__init__.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/spaces/d2/boundaries2d.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/spaces/d2/collision2d.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/spaces/d2/geometry2d.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/spaces/d2/kinematics2d.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/spaces/d2/physics2d.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/ui/__init__.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/ui/menu.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/utils/__init__.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/utils/deprecated_decorator.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/utils/logging.py +0 -0
- {mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/utils/profiler.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "mini-arcade-core"
|
|
7
|
-
version = "1.2.
|
|
7
|
+
version = "1.2.1"
|
|
8
8
|
description = "Tiny scene-based game loop core for small arcade games."
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "Santiago Rincon", email = "rincores@gmail.com" },
|
|
@@ -8,11 +8,16 @@ from dataclasses import dataclass
|
|
|
8
8
|
from datetime import datetime
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
from typing import Optional
|
|
11
|
+
from uuid import uuid4
|
|
11
12
|
|
|
12
13
|
from PIL import Image
|
|
13
14
|
|
|
14
15
|
from mini_arcade_core.backend import Backend
|
|
15
16
|
from mini_arcade_core.runtime.capture.capture_port import CapturePort
|
|
17
|
+
from mini_arcade_core.runtime.capture.capture_worker import (
|
|
18
|
+
CaptureJob,
|
|
19
|
+
CaptureWorker,
|
|
20
|
+
)
|
|
16
21
|
from mini_arcade_core.utils import logger
|
|
17
22
|
|
|
18
23
|
|
|
@@ -87,9 +92,12 @@ class CaptureAdapter(CapturePort):
|
|
|
87
92
|
self,
|
|
88
93
|
backend: Backend,
|
|
89
94
|
path_builder: Optional[CapturePathBuilder] = None,
|
|
95
|
+
worker: Optional[CaptureWorker] = None,
|
|
90
96
|
):
|
|
91
97
|
self.backend = backend
|
|
92
98
|
self.path_builder = path_builder or CapturePathBuilder()
|
|
99
|
+
self.worker = worker or CaptureWorker()
|
|
100
|
+
self.worker.start()
|
|
93
101
|
|
|
94
102
|
def _bmp_to_image(self, bmp_path: str, out_path: str):
|
|
95
103
|
img = Image.open(bmp_path)
|
|
@@ -98,23 +106,27 @@ class CaptureAdapter(CapturePort):
|
|
|
98
106
|
def screenshot(self, label: str | None = None) -> str:
|
|
99
107
|
label = label or "shot"
|
|
100
108
|
out_path = self.path_builder.build(label)
|
|
101
|
-
|
|
109
|
+
logger.critical(f"Capturing screenshot to: {out_path}")
|
|
102
110
|
|
|
103
|
-
#
|
|
104
|
-
bmp_path = out_path.with_suffix(".bmp")
|
|
111
|
+
# temp BMP next to output (unique)
|
|
112
|
+
bmp_path = out_path.with_suffix(f".{uuid4().hex}.bmp")
|
|
113
|
+
bmp_path.parent.mkdir(parents=True, exist_ok=True)
|
|
105
114
|
|
|
106
|
-
self.backend.capture.bmp(str(bmp_path))
|
|
107
|
-
if not bmp_path.exists():
|
|
108
|
-
raise RuntimeError("Backend capture.bmp
|
|
115
|
+
ok_native = self.backend.capture.bmp(str(bmp_path)) # returns bool
|
|
116
|
+
if not ok_native or not bmp_path.exists():
|
|
117
|
+
raise RuntimeError("Backend capture.bmp failed to create BMP file")
|
|
109
118
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
119
|
+
job_id = uuid4().hex
|
|
120
|
+
ok = self.worker.enqueue(
|
|
121
|
+
CaptureJob(job_id=job_id, out_path=out_path, bmp_path=bmp_path)
|
|
122
|
+
)
|
|
123
|
+
if not ok:
|
|
124
|
+
logger.warning("Screenshot dropped: capture queue full")
|
|
125
|
+
# optional: cleanup temp bmp since we won't process it
|
|
126
|
+
try:
|
|
127
|
+
bmp_path.unlink(missing_ok=True)
|
|
128
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
129
|
+
pass
|
|
118
130
|
|
|
119
131
|
return str(out_path)
|
|
120
132
|
|
|
@@ -127,24 +139,27 @@ class CaptureAdapter(CapturePort):
|
|
|
127
139
|
def screenshot_sim(
|
|
128
140
|
self, run_id: str, frame_index: int, label: str = "frame"
|
|
129
141
|
) -> str:
|
|
130
|
-
"""Screenshot for simulation frames."""
|
|
131
142
|
out_path = self.path_builder.build_sim(run_id, frame_index, label)
|
|
132
|
-
out_path.parent.mkdir(parents=True, exist_ok=True)
|
|
133
|
-
|
|
134
|
-
bmp_path = out_path.with_suffix(".bmp")
|
|
135
|
-
self.backend.capture.bmp(str(bmp_path))
|
|
136
143
|
|
|
137
|
-
|
|
138
|
-
|
|
144
|
+
bmp_path = out_path.with_suffix(f".{uuid4().hex}.bmp")
|
|
145
|
+
bmp_path.parent.mkdir(parents=True, exist_ok=True)
|
|
139
146
|
|
|
140
|
-
self.
|
|
147
|
+
ok_native = self.backend.capture.bmp(str(bmp_path))
|
|
148
|
+
if not ok_native or not bmp_path.exists():
|
|
149
|
+
raise RuntimeError("Backend capture.bmp failed to create BMP file")
|
|
141
150
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
logger.warning(
|
|
148
|
-
|
|
151
|
+
job_id = f"{run_id}:{frame_index}"
|
|
152
|
+
ok = self.worker.enqueue(
|
|
153
|
+
CaptureJob(job_id=job_id, out_path=out_path, bmp_path=bmp_path)
|
|
154
|
+
)
|
|
155
|
+
if not ok:
|
|
156
|
+
logger.warning("Sim screenshot dropped: capture queue full")
|
|
157
|
+
try:
|
|
158
|
+
bmp_path.unlink(missing_ok=True)
|
|
159
|
+
# Justification: Broad exception catch for cleanup
|
|
160
|
+
# pylint: disable=broad-exception-caught
|
|
161
|
+
except Exception:
|
|
162
|
+
pass
|
|
163
|
+
# pylint: enable=broad-exception-caught
|
|
149
164
|
|
|
150
165
|
return str(out_path)
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Capture worker thread for saving screenshots.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from queue import Empty, Queue
|
|
10
|
+
from threading import Event, Thread
|
|
11
|
+
from typing import Callable, Optional
|
|
12
|
+
|
|
13
|
+
from PIL import Image
|
|
14
|
+
|
|
15
|
+
from mini_arcade_core.utils import logger
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class CaptureJob:
|
|
20
|
+
"""
|
|
21
|
+
Job representing a screenshot to be saved.
|
|
22
|
+
|
|
23
|
+
:ivar job_id (str): Unique identifier for the capture job.
|
|
24
|
+
:ivar out_path (Path): Destination path for the saved screenshot.
|
|
25
|
+
:ivar bmp_path (Path): Temporary path of the bitmap image to be saved.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
job_id: str
|
|
29
|
+
out_path: Path
|
|
30
|
+
bmp_path: Path # <-- file-based now
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
class CaptureResult:
|
|
35
|
+
"""
|
|
36
|
+
Result of a completed capture job.
|
|
37
|
+
|
|
38
|
+
:ivar job_id (str): Unique identifier for the capture job.
|
|
39
|
+
:ivar out_path (Path): Destination path where the screenshot was saved.
|
|
40
|
+
:ivar ok (bool): Whether the capture was successful.
|
|
41
|
+
:ivar error (Optional[str]): Error message if the capture failed.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
job_id: str
|
|
45
|
+
out_path: Path
|
|
46
|
+
ok: bool
|
|
47
|
+
error: str | None = None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class WorkerConfig:
|
|
52
|
+
"""
|
|
53
|
+
Configuration options for the CaptureWorker.
|
|
54
|
+
|
|
55
|
+
:ivar queue_size (int): Maximum number of jobs to queue.
|
|
56
|
+
:ivar on_done (Optional[Callable[[CaptureResult], None]]):
|
|
57
|
+
Optional callback invoked when a job is done.
|
|
58
|
+
:ivar name (str): Name of the worker thread.
|
|
59
|
+
:ivar daemon (bool): Whether the thread is a daemon thread.
|
|
60
|
+
:ivar delete_temp (bool): Whether to delete temporary bitmap files after saving.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
queue_size: int = 64
|
|
64
|
+
on_done: Optional[Callable[[CaptureResult], None]] = None
|
|
65
|
+
name: str = "capture-worker"
|
|
66
|
+
daemon: bool = True
|
|
67
|
+
delete_temp: bool = True
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class CaptureWorker:
|
|
71
|
+
"""Capture worker thread for saving screenshots asynchronously."""
|
|
72
|
+
|
|
73
|
+
def __init__(
|
|
74
|
+
self,
|
|
75
|
+
worker_config: WorkerConfig | None = None,
|
|
76
|
+
):
|
|
77
|
+
"""
|
|
78
|
+
:param queue_size: Maximum number of jobs to queue.
|
|
79
|
+
:type queue_size: int
|
|
80
|
+
:param on_done: Optional callback invoked when a job is done.
|
|
81
|
+
:type on_done: Optional[Callable[[CaptureResult], None]]
|
|
82
|
+
:param name: Name of the worker thread.
|
|
83
|
+
:type name: str
|
|
84
|
+
:param daemon: Whether the thread is a daemon thread.
|
|
85
|
+
:type daemon: bool
|
|
86
|
+
:param delete_temp: Whether to delete temporary bitmap files after saving.
|
|
87
|
+
:type delete_temp: bool
|
|
88
|
+
"""
|
|
89
|
+
if worker_config is None:
|
|
90
|
+
worker_config = WorkerConfig()
|
|
91
|
+
self._q: Queue[CaptureJob] = Queue(maxsize=worker_config.queue_size)
|
|
92
|
+
self._stop = Event()
|
|
93
|
+
self._thread = Thread(
|
|
94
|
+
target=self._run,
|
|
95
|
+
name=worker_config.name,
|
|
96
|
+
daemon=worker_config.daemon,
|
|
97
|
+
)
|
|
98
|
+
self._on_done = worker_config.on_done
|
|
99
|
+
self._delete_temp = worker_config.delete_temp
|
|
100
|
+
|
|
101
|
+
def start(self):
|
|
102
|
+
"""Start the capture worker thread."""
|
|
103
|
+
if self._thread.is_alive():
|
|
104
|
+
return
|
|
105
|
+
self._stop.clear()
|
|
106
|
+
self._thread.start()
|
|
107
|
+
|
|
108
|
+
def stop(self):
|
|
109
|
+
"""Stop the capture worker thread."""
|
|
110
|
+
self._stop.set()
|
|
111
|
+
if self._thread.is_alive():
|
|
112
|
+
self._thread.join(timeout=2.0)
|
|
113
|
+
|
|
114
|
+
def enqueue(self, job: CaptureJob) -> bool:
|
|
115
|
+
"""
|
|
116
|
+
Enqueue a capture job.
|
|
117
|
+
|
|
118
|
+
:param job: CaptureJob to enqueue.
|
|
119
|
+
:type job: CaptureJob
|
|
120
|
+
:return: True if the job was enqueued successfully, False otherwise.
|
|
121
|
+
:rtype: bool
|
|
122
|
+
"""
|
|
123
|
+
if self._stop.is_set():
|
|
124
|
+
return False
|
|
125
|
+
try:
|
|
126
|
+
self._q.put_nowait(job)
|
|
127
|
+
return True
|
|
128
|
+
# Justification: Queue.put_nowait can raise a broad exception
|
|
129
|
+
# pylint: disable=broad-exception-caught
|
|
130
|
+
except Exception:
|
|
131
|
+
return False
|
|
132
|
+
# pylint: enable=broad-exception-caught
|
|
133
|
+
|
|
134
|
+
def _run(self):
|
|
135
|
+
while not self._stop.is_set():
|
|
136
|
+
try:
|
|
137
|
+
job = self._q.get(timeout=0.1)
|
|
138
|
+
except Empty:
|
|
139
|
+
continue
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
job.out_path.parent.mkdir(parents=True, exist_ok=True)
|
|
143
|
+
|
|
144
|
+
img = Image.open(str(job.bmp_path))
|
|
145
|
+
img.save(str(job.out_path))
|
|
146
|
+
|
|
147
|
+
if self._delete_temp:
|
|
148
|
+
try:
|
|
149
|
+
job.bmp_path.unlink(missing_ok=True)
|
|
150
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
151
|
+
logger.warning(
|
|
152
|
+
f"Failed to delete temp bmp: {job.bmp_path}"
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
res = CaptureResult(
|
|
156
|
+
job_id=job.job_id, out_path=job.out_path, ok=True
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
except Exception as exc: # pylint: disable=broad-exception-caught
|
|
160
|
+
logger.exception("CaptureWorker failed to save screenshot")
|
|
161
|
+
res = CaptureResult(
|
|
162
|
+
job_id=job.job_id,
|
|
163
|
+
out_path=job.out_path,
|
|
164
|
+
ok=False,
|
|
165
|
+
error=str(exc),
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
if self._on_done:
|
|
169
|
+
try:
|
|
170
|
+
self._on_done(res)
|
|
171
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
172
|
+
logger.warning("CaptureWorker on_done callback failed")
|
|
173
|
+
|
|
174
|
+
self._q.task_done()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/game_config.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/gameplay_settings.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/loop/__init__.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/loop/config.py
RENAMED
|
File without changes
|
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/loop/runner.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/__init__.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/context.py
RENAMED
|
File without changes
|
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/effects/base.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/effects/crt.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/frame_packet.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/packet.py
RENAMED
|
File without changes
|
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/passes/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/passes/ui.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/passes/world.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/pipeline.py
RENAMED
|
File without changes
|
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/render/viewport.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/scenes/__init__.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/engine/scenes/models.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/audio/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/audio/audio_port.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/capture/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/file/__init__.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/file/file_adapter.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/file/file_port.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/input/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/input/input_port.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/input_frame.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/render/__init__.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/render/render_port.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/scene/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/window/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/runtime/window/window_port.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/scenes/debug_overlay.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/scenes/systems/__init__.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/scenes/systems/base_system.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/spaces/d2/__init__.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/spaces/d2/boundaries2d.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/spaces/d2/collision2d.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/spaces/d2/geometry2d.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/spaces/d2/kinematics2d.py
RENAMED
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/spaces/d2/physics2d.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mini_arcade_core-1.2.0 → mini_arcade_core-1.2.1}/src/mini_arcade_core/utils/deprecated_decorator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|