mini-arcade-native-backend 0.3.3__tar.gz → 0.4.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_native_backend-0.3.3 → mini_arcade_native_backend-0.4.1}/.github/workflows/release-publish.yml +2 -1
- {mini_arcade_native_backend-0.3.3 → mini_arcade_native_backend-0.4.1}/CHANGELOG.md +24 -0
- {mini_arcade_native_backend-0.3.3 → mini_arcade_native_backend-0.4.1}/PKG-INFO +2 -2
- {mini_arcade_native_backend-0.3.3 → mini_arcade_native_backend-0.4.1}/cpp/bindings.cpp +22 -2
- {mini_arcade_native_backend-0.3.3 → mini_arcade_native_backend-0.4.1}/cpp/engine.cpp +80 -26
- {mini_arcade_native_backend-0.3.3 → mini_arcade_native_backend-0.4.1}/cpp/engine.h +43 -10
- {mini_arcade_native_backend-0.3.3 → mini_arcade_native_backend-0.4.1}/pyproject.toml +2 -2
- {mini_arcade_native_backend-0.3.3 → mini_arcade_native_backend-0.4.1}/src/mini_arcade_native_backend/__init__.py +78 -8
- {mini_arcade_native_backend-0.3.3 → mini_arcade_native_backend-0.4.1}/tests/test_init.py +83 -14
- mini_arcade_native_backend-0.3.3/.changelog_section +0 -6
- mini_arcade_native_backend-0.3.3/.version_to_tag +0 -1
- {mini_arcade_native_backend-0.3.3 → mini_arcade_native_backend-0.4.1}/.github/workflows/ci.yml +0 -0
- {mini_arcade_native_backend-0.3.3 → mini_arcade_native_backend-0.4.1}/.github/workflows/create-release-branch.yml +0 -0
- {mini_arcade_native_backend-0.3.3 → mini_arcade_native_backend-0.4.1}/.github/workflows/release-finalize.yml +0 -0
- {mini_arcade_native_backend-0.3.3 → mini_arcade_native_backend-0.4.1}/.gitignore +0 -0
- {mini_arcade_native_backend-0.3.3 → mini_arcade_native_backend-0.4.1}/.vscode/settings.json +0 -0
- {mini_arcade_native_backend-0.3.3 → mini_arcade_native_backend-0.4.1}/CMakeLists.txt +0 -0
- {mini_arcade_native_backend-0.3.3 → mini_arcade_native_backend-0.4.1}/LICENSE +0 -0
- {mini_arcade_native_backend-0.3.3 → mini_arcade_native_backend-0.4.1}/README.md +0 -0
- {mini_arcade_native_backend-0.3.3 → mini_arcade_native_backend-0.4.1}/examples/native_backend_demo.py +0 -0
- {mini_arcade_native_backend-0.3.3 → mini_arcade_native_backend-0.4.1}/poetry.lock +0 -0
- {mini_arcade_native_backend-0.3.3 → mini_arcade_native_backend-0.4.1}/poetry.toml +0 -0
|
@@ -17,8 +17,9 @@ jobs:
|
|
|
17
17
|
with:
|
|
18
18
|
release_branch: ${{ inputs.release_branch }}
|
|
19
19
|
version_file: "pyproject.toml"
|
|
20
|
-
apt-deps: "libsdl2-dev"
|
|
20
|
+
apt-deps: "libsdl2-dev libsdl2-ttf-dev"
|
|
21
21
|
poetry-build-args: "-f sdist"
|
|
22
|
+
build_wheels: true
|
|
22
23
|
secrets:
|
|
23
24
|
PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
|
|
24
25
|
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
|
|
@@ -6,6 +6,30 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.4.1] - 2025-12-15
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- extend event handling with additional mouse and window events in engine and bindings
|
|
13
|
+
- enhance font handling by returning font ID and updating draw_text signature
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- update mini-arcade-core dependency version to ensure compatibility
|
|
17
|
+
|
|
18
|
+
### Other
|
|
19
|
+
- Merge release/0.3 into develop
|
|
20
|
+
|
|
21
|
+
## [0.4.0] - 2025-12-15
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
- extend event handling with additional mouse and window events in engine and bindings
|
|
25
|
+
- enhance font handling by returning font ID and updating draw_text signature
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- update mini-arcade-core dependency version to ensure compatibility
|
|
29
|
+
|
|
30
|
+
### Other
|
|
31
|
+
- Merge release/0.3 into develop
|
|
32
|
+
|
|
9
33
|
## [0.3.3] - 2025-12-05
|
|
10
34
|
|
|
11
35
|
### Other
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: mini-arcade-native-backend
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Native SDL2 backend for mini-arcade-core using SDL2 + pybind11.
|
|
5
5
|
Author-Email: Santiago Rincon <rincores@gmail.com>
|
|
6
6
|
License: Copyright (c) 2025 Santiago Rincón
|
|
@@ -24,7 +24,7 @@ License: Copyright (c) 2025 Santiago Rincón
|
|
|
24
24
|
SOFTWARE.
|
|
25
25
|
|
|
26
26
|
Requires-Python: <3.12,>=3.9
|
|
27
|
-
Requires-Dist: mini-arcade-core
|
|
27
|
+
Requires-Dist: mini-arcade-core~=0.9
|
|
28
28
|
Provides-Extra: dev
|
|
29
29
|
Requires-Dist: pytest~=8.3; extra == "dev"
|
|
30
30
|
Requires-Dist: pytest-cov~=6.0; extra == "dev"
|
|
@@ -14,12 +14,31 @@ PYBIND11_MODULE(_native, m) {
|
|
|
14
14
|
.value("Quit", mini::EventType::Quit)
|
|
15
15
|
.value("KeyDown", mini::EventType::KeyDown)
|
|
16
16
|
.value("KeyUp", mini::EventType::KeyUp)
|
|
17
|
+
.value("MouseMotion", mini::EventType::MouseMotion)
|
|
18
|
+
.value("MouseButtonDown", mini::EventType::MouseButtonDown)
|
|
19
|
+
.value("MouseButtonUp", mini::EventType::MouseButtonUp)
|
|
20
|
+
.value("MouseWheel", mini::EventType::MouseWheel)
|
|
21
|
+
.value("WindowResized", mini::EventType::WindowResized)
|
|
22
|
+
.value("TextInput", mini::EventType::TextInput)
|
|
17
23
|
.export_values();
|
|
18
24
|
|
|
19
25
|
// Bind the Event struct
|
|
20
26
|
py::class_<mini::Event>(m, "Event")
|
|
21
27
|
.def_readonly("type", &mini::Event::type)
|
|
22
|
-
.def_readonly("key", &mini::Event::key)
|
|
28
|
+
.def_readonly("key", &mini::Event::key)
|
|
29
|
+
.def_readonly("scancode", &mini::Event::scancode)
|
|
30
|
+
.def_readonly("mod", &mini::Event::mod)
|
|
31
|
+
.def_readonly("repeat", &mini::Event::repeat)
|
|
32
|
+
.def_readonly("x", &mini::Event::x)
|
|
33
|
+
.def_readonly("y", &mini::Event::y)
|
|
34
|
+
.def_readonly("dx", &mini::Event::dx)
|
|
35
|
+
.def_readonly("dy", &mini::Event::dy)
|
|
36
|
+
.def_readonly("button", &mini::Event::button)
|
|
37
|
+
.def_readonly("wheel_x", &mini::Event::wheel_x)
|
|
38
|
+
.def_readonly("wheel_y", &mini::Event::wheel_y)
|
|
39
|
+
.def_readonly("width", &mini::Event::width)
|
|
40
|
+
.def_readonly("height", &mini::Event::height)
|
|
41
|
+
.def_readonly("text", &mini::Event::text);
|
|
23
42
|
|
|
24
43
|
// Bind the Engine class
|
|
25
44
|
py::class_<mini::Engine>(m, "Engine")
|
|
@@ -58,7 +77,8 @@ PYBIND11_MODULE(_native, m) {
|
|
|
58
77
|
py::arg("y"),
|
|
59
78
|
py::arg("r"),
|
|
60
79
|
py::arg("g"),
|
|
61
|
-
py::arg("b")
|
|
80
|
+
py::arg("b"),
|
|
81
|
+
py::arg("font_id") = -1
|
|
62
82
|
)
|
|
63
83
|
.def("poll_events", &mini::Engine::poll_events)
|
|
64
84
|
|
|
@@ -10,7 +10,8 @@ namespace mini {
|
|
|
10
10
|
renderer_(nullptr),
|
|
11
11
|
initialized_(false),
|
|
12
12
|
font_(nullptr),
|
|
13
|
-
clear_color_{0, 0, 0, 255}
|
|
13
|
+
clear_color_{0, 0, 0, 255},
|
|
14
|
+
default_font_id_(-1)
|
|
14
15
|
{
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -87,6 +88,8 @@ namespace mini {
|
|
|
87
88
|
// Enable alpha blending for RGBA drawing
|
|
88
89
|
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND);
|
|
89
90
|
|
|
91
|
+
SDL_StartTextInput(); // <--- needed for SDL_TEXTINPUT
|
|
92
|
+
|
|
90
93
|
initialized_ = true;
|
|
91
94
|
}
|
|
92
95
|
|
|
@@ -161,40 +164,47 @@ namespace mini {
|
|
|
161
164
|
}
|
|
162
165
|
|
|
163
166
|
// Load a TTF font from file at specified point size.
|
|
164
|
-
|
|
167
|
+
int Engine::load_font(const char* path, int pt_size)
|
|
165
168
|
{
|
|
166
169
|
if (!initialized_) {
|
|
167
170
|
throw std::runtime_error("Engine::init must be called before load_font");
|
|
168
171
|
}
|
|
169
172
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
+
TTF_Font* f = TTF_OpenFont(path, pt_size);
|
|
174
|
+
if (!f) {
|
|
175
|
+
throw std::runtime_error(std::string("TTF_OpenFont Error: ") + TTF_GetError());
|
|
173
176
|
}
|
|
174
177
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
178
|
+
fonts_.push_back(f);
|
|
179
|
+
int id = static_cast<int>(fonts_.size() - 1);
|
|
180
|
+
|
|
181
|
+
// first loaded font becomes default (good default behavior)
|
|
182
|
+
if (default_font_id_ < 0) {
|
|
183
|
+
default_font_id_ = id;
|
|
179
184
|
}
|
|
185
|
+
|
|
186
|
+
return id;
|
|
180
187
|
}
|
|
181
188
|
|
|
182
189
|
// Draw text at specified position.
|
|
183
|
-
void Engine::draw_text(const char* text, int x, int y, int r, int g, int b)
|
|
190
|
+
void Engine::draw_text(const char* text, int x, int y, int r, int g, int b, int font_id)
|
|
184
191
|
{
|
|
185
|
-
if (!initialized_ || renderer_ == nullptr
|
|
186
|
-
|
|
187
|
-
|
|
192
|
+
if (!initialized_ || renderer_ == nullptr) return;
|
|
193
|
+
|
|
194
|
+
int idx = (font_id >= 0) ? font_id : default_font_id_;
|
|
195
|
+
if (idx < 0 || idx >= (int)fonts_.size() || fonts_[idx] == nullptr) return;
|
|
196
|
+
|
|
197
|
+
TTF_Font* font = fonts_[idx];
|
|
188
198
|
|
|
189
|
-
// clamp a bit to be safe
|
|
190
199
|
auto clamp = [](int v) {
|
|
191
200
|
if (v < 0) return 0;
|
|
192
201
|
if (v > 255) return 255;
|
|
193
202
|
return v;
|
|
194
203
|
};
|
|
195
204
|
|
|
196
|
-
SDL_Color color = { (Uint8)clamp(r), (Uint8)clamp(g), (Uint8)clamp(b), 255 };
|
|
197
|
-
|
|
205
|
+
SDL_Color color = { (Uint8)clamp(r), (Uint8)clamp(g), (Uint8)clamp(b), 255 };
|
|
206
|
+
|
|
207
|
+
SDL_Surface* surface = TTF_RenderUTF8_Blended(font, text, color);
|
|
198
208
|
if (!surface) {
|
|
199
209
|
std::cerr << "TTF_RenderUTF8_Blended Error: " << TTF_GetError() << std::endl;
|
|
200
210
|
return;
|
|
@@ -207,12 +217,7 @@ namespace mini {
|
|
|
207
217
|
return;
|
|
208
218
|
}
|
|
209
219
|
|
|
210
|
-
SDL_Rect dstRect;
|
|
211
|
-
dstRect.x = x;
|
|
212
|
-
dstRect.y = y;
|
|
213
|
-
dstRect.w = surface->w;
|
|
214
|
-
dstRect.h = surface->h;
|
|
215
|
-
|
|
220
|
+
SDL_Rect dstRect{ x, y, surface->w, surface->h };
|
|
216
221
|
SDL_FreeSurface(surface);
|
|
217
222
|
|
|
218
223
|
SDL_RenderCopy(renderer_, texture, nullptr, &dstRect);
|
|
@@ -243,7 +248,6 @@ namespace mini {
|
|
|
243
248
|
SDL_RenderFillRect(renderer_, &rect);
|
|
244
249
|
}
|
|
245
250
|
|
|
246
|
-
|
|
247
251
|
bool Engine::capture_frame(const char* path)
|
|
248
252
|
{
|
|
249
253
|
if (!initialized_ || renderer_ == nullptr) {
|
|
@@ -303,8 +307,6 @@ namespace mini {
|
|
|
303
307
|
|
|
304
308
|
while (SDL_PollEvent(&sdl_event)) {
|
|
305
309
|
Event ev;
|
|
306
|
-
ev.type = EventType::Unknown;
|
|
307
|
-
ev.key = 0;
|
|
308
310
|
|
|
309
311
|
switch (sdl_event.type) {
|
|
310
312
|
case SDL_QUIT:
|
|
@@ -314,11 +316,63 @@ namespace mini {
|
|
|
314
316
|
case SDL_KEYDOWN:
|
|
315
317
|
ev.type = EventType::KeyDown;
|
|
316
318
|
ev.key = sdl_event.key.keysym.sym;
|
|
319
|
+
ev.scancode = (int)sdl_event.key.keysym.scancode;
|
|
320
|
+
ev.mod = (int)sdl_event.key.keysym.mod;
|
|
321
|
+
ev.repeat = (int)sdl_event.key.repeat;
|
|
317
322
|
break;
|
|
318
323
|
|
|
319
324
|
case SDL_KEYUP:
|
|
320
325
|
ev.type = EventType::KeyUp;
|
|
321
326
|
ev.key = sdl_event.key.keysym.sym;
|
|
327
|
+
ev.scancode = (int)sdl_event.key.keysym.scancode;
|
|
328
|
+
ev.mod = (int)sdl_event.key.keysym.mod;
|
|
329
|
+
ev.repeat = 0;
|
|
330
|
+
break;
|
|
331
|
+
|
|
332
|
+
case SDL_MOUSEMOTION:
|
|
333
|
+
ev.type = EventType::MouseMotion;
|
|
334
|
+
ev.x = sdl_event.motion.x;
|
|
335
|
+
ev.y = sdl_event.motion.y;
|
|
336
|
+
ev.dx = sdl_event.motion.xrel;
|
|
337
|
+
ev.dy = sdl_event.motion.yrel;
|
|
338
|
+
break;
|
|
339
|
+
|
|
340
|
+
case SDL_MOUSEBUTTONDOWN:
|
|
341
|
+
ev.type = EventType::MouseButtonDown;
|
|
342
|
+
ev.button = (int)sdl_event.button.button;
|
|
343
|
+
ev.x = sdl_event.button.x;
|
|
344
|
+
ev.y = sdl_event.button.y;
|
|
345
|
+
break;
|
|
346
|
+
|
|
347
|
+
case SDL_MOUSEBUTTONUP:
|
|
348
|
+
ev.type = EventType::MouseButtonUp;
|
|
349
|
+
ev.button = (int)sdl_event.button.button;
|
|
350
|
+
ev.x = sdl_event.button.x;
|
|
351
|
+
ev.y = sdl_event.button.y;
|
|
352
|
+
break;
|
|
353
|
+
|
|
354
|
+
case SDL_MOUSEWHEEL:
|
|
355
|
+
ev.type = EventType::MouseWheel;
|
|
356
|
+
ev.wheel_x = sdl_event.wheel.x;
|
|
357
|
+
ev.wheel_y = sdl_event.wheel.y;
|
|
358
|
+
// If you want "natural" direction handling, you can flip based on sdl_event.wheel.direction
|
|
359
|
+
break;
|
|
360
|
+
|
|
361
|
+
case SDL_TEXTINPUT:
|
|
362
|
+
ev.type = EventType::TextInput;
|
|
363
|
+
ev.text = sdl_event.text.text; // UTF-8
|
|
364
|
+
break;
|
|
365
|
+
|
|
366
|
+
case SDL_WINDOWEVENT:
|
|
367
|
+
if (sdl_event.window.event == SDL_WINDOWEVENT_RESIZED ||
|
|
368
|
+
sdl_event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
|
|
369
|
+
{
|
|
370
|
+
ev.type = EventType::WindowResized;
|
|
371
|
+
ev.width = sdl_event.window.data1;
|
|
372
|
+
ev.height = sdl_event.window.data2;
|
|
373
|
+
} else {
|
|
374
|
+
continue; // ignore other window events
|
|
375
|
+
}
|
|
322
376
|
break;
|
|
323
377
|
|
|
324
378
|
default:
|
|
@@ -326,7 +380,7 @@ namespace mini {
|
|
|
326
380
|
break;
|
|
327
381
|
}
|
|
328
382
|
|
|
329
|
-
events.push_back(ev);
|
|
383
|
+
events.push_back(std::move(ev));
|
|
330
384
|
}
|
|
331
385
|
|
|
332
386
|
return events;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#include <SDL.h>
|
|
4
4
|
#include <SDL_ttf.h>
|
|
5
5
|
#include <vector>
|
|
6
|
+
#include <string>
|
|
6
7
|
|
|
7
8
|
// A minimal 2D graphics engine binding for Python using SDL.
|
|
8
9
|
namespace mini {
|
|
@@ -11,14 +12,44 @@ namespace mini {
|
|
|
11
12
|
enum class EventType {
|
|
12
13
|
Unknown = 0,
|
|
13
14
|
Quit,
|
|
15
|
+
|
|
14
16
|
KeyDown,
|
|
15
|
-
KeyUp
|
|
17
|
+
KeyUp,
|
|
18
|
+
|
|
19
|
+
MouseMotion,
|
|
20
|
+
MouseButtonDown,
|
|
21
|
+
MouseButtonUp,
|
|
22
|
+
MouseWheel,
|
|
23
|
+
|
|
24
|
+
WindowResized,
|
|
25
|
+
TextInput
|
|
16
26
|
};
|
|
17
27
|
|
|
18
28
|
// A simple event structure to pass events to Python.
|
|
19
29
|
struct Event {
|
|
20
|
-
EventType type;
|
|
21
|
-
|
|
30
|
+
EventType type = EventType::Unknown;
|
|
31
|
+
|
|
32
|
+
// Keyboard
|
|
33
|
+
int key = 0; // SDL_Keycode
|
|
34
|
+
int scancode = 0; // SDL_Scancode
|
|
35
|
+
int mod = 0; // SDL_Keymod bitmask
|
|
36
|
+
int repeat = 0; // 1 if key repeat, else 0
|
|
37
|
+
|
|
38
|
+
// Mouse
|
|
39
|
+
int x = 0;
|
|
40
|
+
int y = 0;
|
|
41
|
+
int dx = 0; // motion relative
|
|
42
|
+
int dy = 0;
|
|
43
|
+
int button = 0; // SDL_BUTTON_LEFT etc.
|
|
44
|
+
int wheel_x = 0;
|
|
45
|
+
int wheel_y = 0;
|
|
46
|
+
|
|
47
|
+
// Window
|
|
48
|
+
int width = 0;
|
|
49
|
+
int height = 0;
|
|
50
|
+
|
|
51
|
+
// Text input
|
|
52
|
+
std::string text;
|
|
22
53
|
};
|
|
23
54
|
|
|
24
55
|
// The main engine class that wraps SDL functionality.
|
|
@@ -49,10 +80,10 @@ namespace mini {
|
|
|
49
80
|
std::vector<Event> poll_events();
|
|
50
81
|
|
|
51
82
|
// Load a TTF font from file at specified point size.
|
|
52
|
-
|
|
83
|
+
int load_font(const char* path, int pt_size);
|
|
53
84
|
|
|
54
85
|
// Draw text at specified position.
|
|
55
|
-
void draw_text(const char* text, int x, int y, int r, int g, int b);
|
|
86
|
+
void draw_text(const char* text, int x, int y, int r, int g, int b, int font_id = -1);
|
|
56
87
|
|
|
57
88
|
// Capture the current frame into an image file (BMP for now).
|
|
58
89
|
// Returns true on success, false on failure.
|
|
@@ -62,11 +93,13 @@ namespace mini {
|
|
|
62
93
|
void draw_rect_rgba(int x, int y, int w, int h, int r, int g, int b, int a);
|
|
63
94
|
|
|
64
95
|
private:
|
|
65
|
-
SDL_Window* window_;
|
|
66
|
-
SDL_Renderer* renderer_;
|
|
67
|
-
bool initialized_;
|
|
68
|
-
TTF_Font* font_;
|
|
69
|
-
SDL_Color clear_color_;
|
|
96
|
+
SDL_Window* window_; // The main application window.
|
|
97
|
+
SDL_Renderer* renderer_; // The renderer for drawing.
|
|
98
|
+
bool initialized_; // Whether the engine has been initialized.
|
|
99
|
+
TTF_Font* font_; // The current font for text rendering.
|
|
100
|
+
SDL_Color clear_color_; // The clear color for the screen.
|
|
101
|
+
std::vector<TTF_Font*> fonts_; // Loaded fonts.
|
|
102
|
+
int default_font_id_; // Default font index.
|
|
70
103
|
};
|
|
71
104
|
|
|
72
105
|
} // namespace mini
|
|
@@ -8,7 +8,7 @@ build-backend = "scikit_build_core.build"
|
|
|
8
8
|
|
|
9
9
|
[project]
|
|
10
10
|
name = "mini-arcade-native-backend"
|
|
11
|
-
version = "0.
|
|
11
|
+
version = "0.4.1"
|
|
12
12
|
description = "Native SDL2 backend for mini-arcade-core using SDL2 + pybind11."
|
|
13
13
|
authors = [
|
|
14
14
|
{ name = "Santiago Rincon", email = "rincores@gmail.com" },
|
|
@@ -17,7 +17,7 @@ readme = "README.md"
|
|
|
17
17
|
requires-python = ">=3.9,<3.12"
|
|
18
18
|
license = { file = "LICENSE" }
|
|
19
19
|
dependencies = [
|
|
20
|
-
"mini-arcade-core
|
|
20
|
+
"mini-arcade-core~=0.9",
|
|
21
21
|
]
|
|
22
22
|
|
|
23
23
|
[project.optional-dependencies]
|
|
@@ -6,17 +6,31 @@ from __future__ import annotations
|
|
|
6
6
|
|
|
7
7
|
import os
|
|
8
8
|
import sys
|
|
9
|
+
from pathlib import Path
|
|
9
10
|
|
|
10
11
|
# --- 1) Make sure Windows can find SDL2.dll when using vcpkg ------------------
|
|
11
12
|
|
|
12
13
|
if sys.platform == "win32":
|
|
14
|
+
# a) If running as a frozen PyInstaller exe (e.g. DejaBounce.exe),
|
|
15
|
+
# SDL2.dll will live next to the executable. Add that dir.
|
|
16
|
+
if getattr(sys, "frozen", False):
|
|
17
|
+
exe_dir = Path(sys.executable).resolve().parent
|
|
18
|
+
try:
|
|
19
|
+
os.add_dll_directory(str(exe_dir))
|
|
20
|
+
except (FileNotFoundError, OSError):
|
|
21
|
+
# If this somehow fails, we still try other fallbacks.
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
# b) Dev / vcpkg fallback: use VCPKG_ROOT if available.
|
|
13
25
|
vcpkg_root = os.environ.get("VCPKG_ROOT")
|
|
14
26
|
if vcpkg_root:
|
|
15
27
|
# Typical vcpkg layout: <VCPKG_ROOT>/installed/x64-windows/bin/SDL2.dll
|
|
16
28
|
sdl_bin = os.path.join(vcpkg_root, "installed", "x64-windows", "bin")
|
|
17
29
|
if os.path.isdir(sdl_bin):
|
|
18
|
-
|
|
19
|
-
|
|
30
|
+
try:
|
|
31
|
+
os.add_dll_directory(sdl_bin)
|
|
32
|
+
except (FileNotFoundError, OSError):
|
|
33
|
+
pass
|
|
20
34
|
|
|
21
35
|
# --- 2) Now import native extension and core types ----------------------------
|
|
22
36
|
|
|
@@ -39,6 +53,12 @@ _NATIVE_TO_CORE = {
|
|
|
39
53
|
native.EventType.Quit: EventType.QUIT,
|
|
40
54
|
native.EventType.KeyDown: EventType.KEYDOWN,
|
|
41
55
|
native.EventType.KeyUp: EventType.KEYUP,
|
|
56
|
+
native.EventType.MouseMotion: EventType.MOUSEMOTION,
|
|
57
|
+
native.EventType.MouseButtonDown: EventType.MOUSEBUTTONDOWN,
|
|
58
|
+
native.EventType.MouseButtonUp: EventType.MOUSEBUTTONUP,
|
|
59
|
+
native.EventType.MouseWheel: EventType.MOUSEWHEEL,
|
|
60
|
+
native.EventType.WindowResized: EventType.WINDOWRESIZED,
|
|
61
|
+
native.EventType.TextInput: EventType.TEXTINPUT,
|
|
42
62
|
}
|
|
43
63
|
|
|
44
64
|
|
|
@@ -56,6 +76,7 @@ class NativeBackend(Backend):
|
|
|
56
76
|
self._engine = native.Engine()
|
|
57
77
|
self._font_path = font_path
|
|
58
78
|
self._font_size = font_size
|
|
79
|
+
self._default_font_id: int | None = None
|
|
59
80
|
|
|
60
81
|
def init(self, width: int, height: int, title: str):
|
|
61
82
|
"""
|
|
@@ -74,7 +95,9 @@ class NativeBackend(Backend):
|
|
|
74
95
|
|
|
75
96
|
# Load font if provided
|
|
76
97
|
if self._font_path is not None:
|
|
77
|
-
self._engine.load_font(
|
|
98
|
+
self._default_font_id = self._engine.load_font(
|
|
99
|
+
self._font_path, self._font_size
|
|
100
|
+
)
|
|
78
101
|
|
|
79
102
|
def set_clear_color(self, r: int, g: int, b: int):
|
|
80
103
|
"""
|
|
@@ -91,6 +114,8 @@ class NativeBackend(Backend):
|
|
|
91
114
|
"""
|
|
92
115
|
self._engine.set_clear_color(int(r), int(g), int(b))
|
|
93
116
|
|
|
117
|
+
# Justification: Many local variables needed for event mapping
|
|
118
|
+
# pylint: disable=too-many-locals
|
|
94
119
|
def poll_events(self) -> list[Event]:
|
|
95
120
|
"""
|
|
96
121
|
Poll for events from the backend and return them as a list of Event objects.
|
|
@@ -98,12 +123,54 @@ class NativeBackend(Backend):
|
|
|
98
123
|
:return: List of Event objects representing the polled events.
|
|
99
124
|
:rtype: list[Event]
|
|
100
125
|
"""
|
|
101
|
-
|
|
126
|
+
out: list[Event] = []
|
|
102
127
|
for ev in self._engine.poll_events():
|
|
103
|
-
|
|
128
|
+
etype = _NATIVE_TO_CORE.get(ev.type, EventType.UNKNOWN)
|
|
129
|
+
|
|
130
|
+
# "0 means not present" convention from C++ side
|
|
104
131
|
key = ev.key if getattr(ev, "key", 0) != 0 else None
|
|
105
|
-
|
|
106
|
-
|
|
132
|
+
|
|
133
|
+
x = getattr(ev, "x", 0) or None
|
|
134
|
+
y = getattr(ev, "y", 0) or None
|
|
135
|
+
dx = getattr(ev, "dx", 0) or None
|
|
136
|
+
dy = getattr(ev, "dy", 0) or None
|
|
137
|
+
button = getattr(ev, "button", 0) or None
|
|
138
|
+
|
|
139
|
+
wheel_x = getattr(ev, "wheel_x", 0)
|
|
140
|
+
wheel_y = getattr(ev, "wheel_y", 0)
|
|
141
|
+
wheel = (wheel_x, wheel_y) if (wheel_x or wheel_y) else None
|
|
142
|
+
|
|
143
|
+
w = getattr(ev, "width", 0)
|
|
144
|
+
h = getattr(ev, "height", 0)
|
|
145
|
+
size = (w, h) if (w and h) else None
|
|
146
|
+
|
|
147
|
+
text = getattr(ev, "text", "") or None
|
|
148
|
+
|
|
149
|
+
scancode = getattr(ev, "scancode", 0) or None
|
|
150
|
+
mod = getattr(ev, "mod", 0) or None
|
|
151
|
+
repeat_raw = getattr(ev, "repeat", 0)
|
|
152
|
+
repeat = bool(repeat_raw) if repeat_raw else None
|
|
153
|
+
|
|
154
|
+
out.append(
|
|
155
|
+
Event(
|
|
156
|
+
type=etype,
|
|
157
|
+
key=key,
|
|
158
|
+
x=x,
|
|
159
|
+
y=y,
|
|
160
|
+
dx=dx,
|
|
161
|
+
dy=dy,
|
|
162
|
+
button=button,
|
|
163
|
+
wheel=wheel,
|
|
164
|
+
size=size,
|
|
165
|
+
text=text,
|
|
166
|
+
scancode=scancode,
|
|
167
|
+
mod=mod,
|
|
168
|
+
repeat=repeat,
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
return out
|
|
172
|
+
|
|
173
|
+
# pylint: enable=too-many-locals
|
|
107
174
|
|
|
108
175
|
def begin_frame(self):
|
|
109
176
|
"""Begin a new frame for rendering."""
|
|
@@ -178,7 +245,10 @@ class NativeBackend(Backend):
|
|
|
178
245
|
"""
|
|
179
246
|
# We rely on C++ side to no-op if font is missing
|
|
180
247
|
r, g, b = color
|
|
181
|
-
|
|
248
|
+
font_id = (
|
|
249
|
+
self._default_font_id if self._default_font_id is not None else -1
|
|
250
|
+
)
|
|
251
|
+
self._engine.draw_text(text, x, y, int(r), int(g), int(b), font_id)
|
|
182
252
|
|
|
183
253
|
def capture_frame(self, path: str | None = None) -> bool:
|
|
184
254
|
"""
|
|
@@ -18,18 +18,33 @@ def setup_fake_core_and_native(monkeypatch):
|
|
|
18
18
|
class FakeBackend:
|
|
19
19
|
"""Minimal base class just to satisfy inheritance."""
|
|
20
20
|
|
|
21
|
-
pass
|
|
22
|
-
|
|
23
21
|
class FakeCoreEventType:
|
|
24
22
|
UNKNOWN = "core_unknown"
|
|
25
23
|
QUIT = "core_quit"
|
|
26
24
|
KEYDOWN = "core_keydown"
|
|
27
25
|
KEYUP = "core_keyup"
|
|
26
|
+
MOUSEMOTION = "core_mousemotion"
|
|
27
|
+
MOUSEBUTTONDOWN = "core_mousebuttondown"
|
|
28
|
+
MOUSEBUTTONUP = "core_mousebuttonup"
|
|
29
|
+
MOUSEWHEEL = "core_mousewheel"
|
|
30
|
+
WINDOWRESIZED = "core_windowresized"
|
|
31
|
+
TEXTINPUT = "core_textinput"
|
|
28
32
|
|
|
29
33
|
@dataclass
|
|
30
34
|
class FakeEvent:
|
|
31
35
|
type: object
|
|
32
36
|
key: object = None
|
|
37
|
+
x: object = None
|
|
38
|
+
y: object = None
|
|
39
|
+
dx: object = None
|
|
40
|
+
dy: object = None
|
|
41
|
+
button: object = None
|
|
42
|
+
wheel: object = None
|
|
43
|
+
size: object = None
|
|
44
|
+
text: object = None
|
|
45
|
+
scancode: object = None
|
|
46
|
+
mod: object = None
|
|
47
|
+
repeat: object = None
|
|
33
48
|
|
|
34
49
|
fake_core.Backend = FakeBackend
|
|
35
50
|
fake_core.Event = FakeEvent
|
|
@@ -45,6 +60,12 @@ def setup_fake_core_and_native(monkeypatch):
|
|
|
45
60
|
Quit = "native_quit"
|
|
46
61
|
KeyDown = "native_keydown"
|
|
47
62
|
KeyUp = "native_keyup"
|
|
63
|
+
MouseMotion = "native_mousemotion"
|
|
64
|
+
MouseButtonDown = "native_mousebuttondown"
|
|
65
|
+
MouseButtonUp = "native_mousebuttonup"
|
|
66
|
+
MouseWheel = "native_mousewheel"
|
|
67
|
+
WindowResized = "native_windowresized"
|
|
68
|
+
TextInput = "native_textinput"
|
|
48
69
|
|
|
49
70
|
class FakeEngine:
|
|
50
71
|
def __init__(self):
|
|
@@ -226,36 +247,84 @@ def test_poll_events_maps_native_events_to_core_events_and_keys(
|
|
|
226
247
|
backend_module,
|
|
227
248
|
):
|
|
228
249
|
pkg, fake_core, fake_native = backend_module
|
|
229
|
-
|
|
230
250
|
backend = pkg.NativeBackend()
|
|
231
251
|
|
|
232
|
-
# Create fake native events
|
|
233
252
|
@dataclass
|
|
234
253
|
class FakeNativeEvent:
|
|
235
254
|
type: object
|
|
236
255
|
key: int = 0
|
|
256
|
+
x: int = 0
|
|
257
|
+
y: int = 0
|
|
258
|
+
dx: int = 0
|
|
259
|
+
dy: int = 0
|
|
260
|
+
button: int = 0
|
|
261
|
+
wheel_x: int = 0
|
|
262
|
+
wheel_y: int = 0
|
|
263
|
+
width: int = 0
|
|
264
|
+
height: int = 0
|
|
265
|
+
text: str = ""
|
|
266
|
+
scancode: int = 0
|
|
267
|
+
mod: int = 0
|
|
268
|
+
repeat: int = 0
|
|
237
269
|
|
|
238
270
|
engine = backend._engine
|
|
239
271
|
engine._events_to_return = [
|
|
240
|
-
FakeNativeEvent(fake_native.EventType.Quit
|
|
272
|
+
FakeNativeEvent(fake_native.EventType.Quit), # -> QUIT
|
|
241
273
|
FakeNativeEvent(
|
|
242
|
-
fake_native.EventType.KeyDown, 32
|
|
274
|
+
fake_native.EventType.KeyDown, key=32
|
|
243
275
|
), # -> KEYDOWN, key=32
|
|
244
|
-
FakeNativeEvent(
|
|
276
|
+
FakeNativeEvent(
|
|
277
|
+
fake_native.EventType.MouseMotion, x=10, y=20, dx=1, dy=-2
|
|
278
|
+
),
|
|
279
|
+
FakeNativeEvent(
|
|
280
|
+
fake_native.EventType.MouseButtonDown, x=5, y=6, button=1
|
|
281
|
+
),
|
|
282
|
+
FakeNativeEvent(
|
|
283
|
+
fake_native.EventType.MouseWheel, wheel_x=0, wheel_y=-1
|
|
284
|
+
),
|
|
285
|
+
FakeNativeEvent(
|
|
286
|
+
fake_native.EventType.WindowResized, width=800, height=600
|
|
287
|
+
),
|
|
288
|
+
FakeNativeEvent(fake_native.EventType.TextInput, text="á"),
|
|
289
|
+
FakeNativeEvent("something_unknown", key=10), # -> UNKNOWN, key=10
|
|
245
290
|
]
|
|
246
291
|
|
|
247
292
|
events = backend.poll_events()
|
|
293
|
+
assert len(events) == 8
|
|
248
294
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
# 1) Quit event, key==0 -> key should become None
|
|
295
|
+
# Quit: key 0 -> None
|
|
252
296
|
assert events[0].type == fake_core.EventType.QUIT
|
|
253
297
|
assert events[0].key is None
|
|
254
298
|
|
|
255
|
-
#
|
|
299
|
+
# KeyDown: key passes through
|
|
256
300
|
assert events[1].type == fake_core.EventType.KEYDOWN
|
|
257
301
|
assert events[1].key == 32
|
|
258
302
|
|
|
259
|
-
#
|
|
260
|
-
assert events[2].type == fake_core.EventType.
|
|
261
|
-
assert events[2].
|
|
303
|
+
# MouseMotion: x/y/dx/dy mapped (0 becomes None; here non-zero)
|
|
304
|
+
assert events[2].type == fake_core.EventType.MOUSEMOTION
|
|
305
|
+
assert events[2].x == 10
|
|
306
|
+
assert events[2].y == 20
|
|
307
|
+
assert events[2].dx == 1
|
|
308
|
+
assert events[2].dy == -2
|
|
309
|
+
|
|
310
|
+
# MouseButtonDown
|
|
311
|
+
assert events[3].type == fake_core.EventType.MOUSEBUTTONDOWN
|
|
312
|
+
assert events[3].x == 5
|
|
313
|
+
assert events[3].y == 6
|
|
314
|
+
assert events[3].button == 1
|
|
315
|
+
|
|
316
|
+
# MouseWheel -> wheel tuple
|
|
317
|
+
assert events[4].type == fake_core.EventType.MOUSEWHEEL
|
|
318
|
+
assert events[4].wheel == (0, -1)
|
|
319
|
+
|
|
320
|
+
# WindowResized -> size tuple
|
|
321
|
+
assert events[5].type == fake_core.EventType.WINDOWRESIZED
|
|
322
|
+
assert events[5].size == (800, 600)
|
|
323
|
+
|
|
324
|
+
# TextInput -> text
|
|
325
|
+
assert events[6].type == fake_core.EventType.TEXTINPUT
|
|
326
|
+
assert events[6].text == "á"
|
|
327
|
+
|
|
328
|
+
# Unknown
|
|
329
|
+
assert events[7].type == fake_core.EventType.UNKNOWN
|
|
330
|
+
assert events[7].key == 10
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.3.3
|
{mini_arcade_native_backend-0.3.3 → mini_arcade_native_backend-0.4.1}/.github/workflows/ci.yml
RENAMED
|
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
|