mini-arcade-native-backend 0.2.3__tar.gz → 0.3.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.
Files changed (26) hide show
  1. mini_arcade_native_backend-0.3.1/.changelog_section +9 -0
  2. {mini_arcade_native_backend-0.2.3 → mini_arcade_native_backend-0.3.1}/.github/workflows/ci.yml +1 -1
  3. mini_arcade_native_backend-0.3.1/.github/workflows/release-finalize.yml +20 -0
  4. mini_arcade_native_backend-0.3.1/.version_to_tag +1 -0
  5. mini_arcade_native_backend-0.3.1/.vscode/settings.json +53 -0
  6. {mini_arcade_native_backend-0.2.3 → mini_arcade_native_backend-0.3.1}/CHANGELOG.md +18 -0
  7. {mini_arcade_native_backend-0.2.3 → mini_arcade_native_backend-0.3.1}/CMakeLists.txt +2 -1
  8. {mini_arcade_native_backend-0.2.3 → mini_arcade_native_backend-0.3.1}/PKG-INFO +1 -1
  9. {mini_arcade_native_backend-0.2.3 → mini_arcade_native_backend-0.3.1}/cpp/bindings.cpp +30 -5
  10. mini_arcade_native_backend-0.3.1/cpp/engine.cpp +307 -0
  11. {mini_arcade_native_backend-0.2.3 → mini_arcade_native_backend-0.3.1}/cpp/engine.h +17 -1
  12. {mini_arcade_native_backend-0.2.3 → mini_arcade_native_backend-0.3.1}/pyproject.toml +1 -1
  13. {mini_arcade_native_backend-0.2.3 → mini_arcade_native_backend-0.3.1}/src/mini_arcade_native_backend/__init__.py +38 -3
  14. mini_arcade_native_backend-0.2.3/.changelog_section +0 -4
  15. mini_arcade_native_backend-0.2.3/.version_to_tag +0 -1
  16. mini_arcade_native_backend-0.2.3/.vscode/settings.json +0 -5
  17. mini_arcade_native_backend-0.2.3/cpp/engine.cpp +0 -153
  18. {mini_arcade_native_backend-0.2.3 → mini_arcade_native_backend-0.3.1}/.github/workflows/create-release-branch.yml +0 -0
  19. {mini_arcade_native_backend-0.2.3 → mini_arcade_native_backend-0.3.1}/.github/workflows/release-publish.yml +0 -0
  20. {mini_arcade_native_backend-0.2.3 → mini_arcade_native_backend-0.3.1}/.gitignore +0 -0
  21. {mini_arcade_native_backend-0.2.3 → mini_arcade_native_backend-0.3.1}/LICENSE +0 -0
  22. {mini_arcade_native_backend-0.2.3 → mini_arcade_native_backend-0.3.1}/README.md +0 -0
  23. {mini_arcade_native_backend-0.2.3 → mini_arcade_native_backend-0.3.1}/examples/native_backend_demo.py +0 -0
  24. {mini_arcade_native_backend-0.2.3 → mini_arcade_native_backend-0.3.1}/poetry.lock +0 -0
  25. {mini_arcade_native_backend-0.2.3 → mini_arcade_native_backend-0.3.1}/poetry.toml +0 -0
  26. {mini_arcade_native_backend-0.2.3 → mini_arcade_native_backend-0.3.1}/tests/test_init.py +0 -0
@@ -0,0 +1,9 @@
1
+ ## [0.3.1] - 2025-12-05
2
+
3
+ ### Added
4
+ - add capture_frame method to Engine and expose it in Python bindings
5
+ - add clear color customization and update draw_rect method
6
+
7
+ ### Other
8
+ - Merge branch 'release/0.3' of https://github.com/alexsc6955/mini-arcade-native-backend into release/0.3
9
+
@@ -19,7 +19,7 @@ jobs:
19
19
  with:
20
20
  project-name: "mini-arcade-native-backend"
21
21
  lint-path: "src/mini_arcade_native_backend"
22
- apt-deps: "libsdl2-dev"
22
+ apt-deps: "libsdl2-dev libsdl2-ttf-dev"
23
23
  secrets:
24
24
  SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
25
25
  SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }}
@@ -0,0 +1,20 @@
1
+ name: Finalize Release
2
+
3
+ permissions:
4
+ contents: write
5
+
6
+ on:
7
+ workflow_dispatch:
8
+ inputs:
9
+ release_branch_suffix:
10
+ description: "Release suffix (e.g. 0.6 for branch release/0.6)"
11
+ required: true
12
+ type: string
13
+
14
+ jobs:
15
+ finalize-release:
16
+ uses: trivox-io/trivox-ci/.github/workflows/finalize-release.yml@main
17
+ with:
18
+ release_branch_suffix: ${{ inputs.release_branch_suffix }}
19
+ main_branch: "main"
20
+ develop_branch: "develop"
@@ -0,0 +1 @@
1
+ 0.3.1
@@ -0,0 +1,53 @@
1
+ {
2
+ "files.associations": {
3
+ "system_error": "cpp",
4
+ "atomic": "cpp",
5
+ "bit": "cpp",
6
+ "cctype": "cpp",
7
+ "charconv": "cpp",
8
+ "clocale": "cpp",
9
+ "cmath": "cpp",
10
+ "compare": "cpp",
11
+ "concepts": "cpp",
12
+ "cstddef": "cpp",
13
+ "cstdint": "cpp",
14
+ "cstdio": "cpp",
15
+ "cstdlib": "cpp",
16
+ "cstring": "cpp",
17
+ "ctime": "cpp",
18
+ "cwchar": "cpp",
19
+ "exception": "cpp",
20
+ "format": "cpp",
21
+ "initializer_list": "cpp",
22
+ "ios": "cpp",
23
+ "iosfwd": "cpp",
24
+ "iostream": "cpp",
25
+ "istream": "cpp",
26
+ "iterator": "cpp",
27
+ "limits": "cpp",
28
+ "locale": "cpp",
29
+ "memory": "cpp",
30
+ "new": "cpp",
31
+ "ostream": "cpp",
32
+ "stdexcept": "cpp",
33
+ "streambuf": "cpp",
34
+ "tuple": "cpp",
35
+ "type_traits": "cpp",
36
+ "typeinfo": "cpp",
37
+ "utility": "cpp",
38
+ "vector": "cpp",
39
+ "xfacet": "cpp",
40
+ "xiosbase": "cpp",
41
+ "xlocale": "cpp",
42
+ "xlocbuf": "cpp",
43
+ "xlocinfo": "cpp",
44
+ "xlocmes": "cpp",
45
+ "xlocmon": "cpp",
46
+ "xlocnum": "cpp",
47
+ "xloctime": "cpp",
48
+ "xmemory": "cpp",
49
+ "xstring": "cpp",
50
+ "xtr1common": "cpp",
51
+ "xutility": "cpp"
52
+ }
53
+ }
@@ -6,6 +6,24 @@ This project adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.3.1] - 2025-12-05
10
+
11
+ ### Added
12
+ - add capture_frame method to Engine and expose it in Python bindings
13
+ - add clear color customization and update draw_rect method
14
+
15
+ ### Other
16
+ - Merge branch 'release/0.3' of https://github.com/alexsc6955/mini-arcade-native-backend into release/0.3
17
+
18
+ ## [0.3.0] - 2025-12-05
19
+
20
+ ### Added
21
+ - add text rendering support with SDL2_ttf integration
22
+
23
+ ### Other
24
+ - Merge pull request #7 from alexsc6955/feature/text_support
25
+ - Merge release/0.2 into develop
26
+
9
27
  ## [0.2.3] - 2025-12-04
10
28
 
11
29
  - Internal changes only.
@@ -9,6 +9,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
9
9
 
10
10
  find_package(pybind11 CONFIG REQUIRED)
11
11
  find_package(SDL2 CONFIG REQUIRED)
12
+ find_package(SDL2_ttf CONFIG REQUIRED)
12
13
 
13
14
  set(TARGET_NAME _native)
14
15
 
@@ -18,7 +19,7 @@ pybind11_add_module(${TARGET_NAME}
18
19
  cpp/engine.cpp
19
20
  )
20
21
 
21
- target_link_libraries(${TARGET_NAME} PRIVATE SDL2::SDL2)
22
+ target_link_libraries(${TARGET_NAME} PRIVATE SDL2::SDL2 SDL2_ttf::SDL2_ttf)
22
23
 
23
24
  # Install the compiled extension into the Python package directory
24
25
  # so it ends up as mini_arcade_native_backend/_native.*.pyd
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: mini-arcade-native-backend
3
- Version: 0.2.3
3
+ Version: 0.3.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
@@ -25,13 +25,38 @@ PYBIND11_MODULE(_native, m) {
25
25
  py::class_<mini::Engine>(m, "Engine")
26
26
  .def(py::init<>())
27
27
  .def("init", &mini::Engine::init,
28
- py::arg("width"), py::arg("height"), py::arg("title"))
28
+ py::arg("width"), py::arg("height"), py::arg("title"))
29
+
30
+ .def("set_clear_color", &mini::Engine::set_clear_color,
31
+ py::arg("r"), py::arg("g"), py::arg("b"))
32
+
29
33
  .def("begin_frame", &mini::Engine::begin_frame)
30
34
  .def("end_frame", &mini::Engine::end_frame)
35
+
31
36
  .def("draw_rect", &mini::Engine::draw_rect,
32
- py::arg("x"), py::arg("y"), py::arg("w"), py::arg("h"))
37
+ py::arg("x"), py::arg("y"),
38
+ py::arg("w"), py::arg("h"),
39
+ py::arg("r"), py::arg("g"), py::arg("b"))
40
+
33
41
  .def("draw_sprite", &mini::Engine::draw_sprite,
34
- py::arg("texture_id"), py::arg("x"), py::arg("y"),
35
- py::arg("w"), py::arg("h"))
36
- .def("poll_events", &mini::Engine::poll_events);
42
+ py::arg("texture_id"), py::arg("x"), py::arg("y"),
43
+ py::arg("w"), py::arg("h"))
44
+
45
+ .def("load_font", &mini::Engine::load_font,
46
+ py::arg("path"), py::arg("pt_size"))
47
+
48
+ .def(
49
+ "draw_text",
50
+ &mini::Engine::draw_text,
51
+ py::arg("text"),
52
+ py::arg("x"),
53
+ py::arg("y"),
54
+ py::arg("r"),
55
+ py::arg("g"),
56
+ py::arg("b")
57
+ )
58
+ .def("poll_events", &mini::Engine::poll_events)
59
+
60
+ .def("capture_frame", &mini::Engine::capture_frame,
61
+ py::arg("path"));
37
62
  }
@@ -0,0 +1,307 @@
1
+ #include "engine.h"
2
+
3
+ #include <stdexcept>
4
+ #include <iostream>
5
+
6
+ namespace mini {
7
+
8
+ Engine::Engine()
9
+ : window_(nullptr),
10
+ renderer_(nullptr),
11
+ initialized_(false),
12
+ font_(nullptr),
13
+ clear_color_{0, 0, 0, 255}
14
+ {
15
+ }
16
+
17
+ Engine::~Engine()
18
+ {
19
+ if (font_ != nullptr) {
20
+ TTF_CloseFont(font_);
21
+ font_ = nullptr;
22
+ }
23
+
24
+ if (renderer_ != nullptr) {
25
+ SDL_DestroyRenderer(renderer_);
26
+ renderer_ = nullptr;
27
+ }
28
+
29
+ if (window_ != nullptr) {
30
+ SDL_DestroyWindow(window_);
31
+ window_ = nullptr;
32
+ }
33
+
34
+ if (initialized_) {
35
+ SDL_Quit();
36
+ initialized_ = false;
37
+ }
38
+ }
39
+
40
+ void Engine::init(int width, int height, const char* title)
41
+ {
42
+ if (initialized_) {
43
+ return; // already initialized
44
+ }
45
+
46
+ if (SDL_Init(SDL_INIT_VIDEO) != 0) {
47
+ throw std::runtime_error(std::string("SDL_Init Error: ") + SDL_GetError());
48
+ }
49
+
50
+ if (TTF_Init() != 0) {
51
+ std::string msg = std::string("TTF_Init Error: ") + TTF_GetError();
52
+ SDL_Quit();
53
+ throw std::runtime_error(msg);
54
+ }
55
+
56
+ window_ = SDL_CreateWindow(
57
+ title,
58
+ SDL_WINDOWPOS_CENTERED,
59
+ SDL_WINDOWPOS_CENTERED,
60
+ width,
61
+ height,
62
+ SDL_WINDOW_SHOWN
63
+ );
64
+
65
+ if (window_ == nullptr) {
66
+ std::string msg = std::string("SDL_CreateWindow Error: ") + SDL_GetError();
67
+ TTF_Quit();
68
+ SDL_Quit();
69
+ throw std::runtime_error(msg);
70
+ }
71
+
72
+ renderer_ = SDL_CreateRenderer(
73
+ window_,
74
+ -1,
75
+ SDL_RENDERER_ACCELERATED
76
+ );
77
+
78
+ if (renderer_ == nullptr) {
79
+ std::string msg = std::string("SDL_CreateRenderer Error: ") + SDL_GetError();
80
+ SDL_DestroyWindow(window_);
81
+ window_ = nullptr;
82
+ TTF_Quit();
83
+ SDL_Quit();
84
+ throw std::runtime_error(msg);
85
+ }
86
+
87
+ initialized_ = true;
88
+ }
89
+
90
+ void Engine::set_clear_color(int r, int g, int b)
91
+ {
92
+ auto clamp = [](int v) {
93
+ if (v < 0) return 0;
94
+ if (v > 255) return 255;
95
+ return v;
96
+ };
97
+
98
+ clear_color_.r = static_cast<Uint8>(clamp(r));
99
+ clear_color_.g = static_cast<Uint8>(clamp(g));
100
+ clear_color_.b = static_cast<Uint8>(clamp(b));
101
+ clear_color_.a = 255;
102
+ }
103
+
104
+ void Engine::begin_frame()
105
+ {
106
+ if (!initialized_ || renderer_ == nullptr) {
107
+ return;
108
+ }
109
+
110
+ // use stored clear color instead of hard-coded black
111
+ SDL_SetRenderDrawColor(
112
+ renderer_,
113
+ clear_color_.r,
114
+ clear_color_.g,
115
+ clear_color_.b,
116
+ clear_color_.a
117
+ );
118
+ SDL_RenderClear(renderer_);
119
+ }
120
+
121
+ void Engine::end_frame()
122
+ {
123
+ if (!initialized_ || renderer_ == nullptr) {
124
+ return;
125
+ }
126
+
127
+ SDL_RenderPresent(renderer_);
128
+ }
129
+
130
+ void Engine::draw_rect(int x, int y, int w, int h, int r, int g, int b)
131
+ {
132
+ if (!initialized_ || renderer_ == nullptr) {
133
+ return;
134
+ }
135
+
136
+ auto clamp = [](int v) {
137
+ if (v < 0) return 0;
138
+ if (v > 255) return 255;
139
+ return v;
140
+ };
141
+
142
+ SDL_Rect rect{ x, y, w, h };
143
+
144
+ SDL_SetRenderDrawColor(
145
+ renderer_,
146
+ static_cast<Uint8>(clamp(r)),
147
+ static_cast<Uint8>(clamp(g)),
148
+ static_cast<Uint8>(clamp(b)),
149
+ 255
150
+ );
151
+ SDL_RenderFillRect(renderer_, &rect);
152
+
153
+ }
154
+
155
+ void Engine::draw_sprite(int /*texture_id*/, int /*x*/, int /*y*/, int /*w*/, int /*h*/)
156
+ {
157
+ // TODO: placeholder for later texture management.
158
+ }
159
+
160
+ // Load a TTF font from file at specified point size.
161
+ void Engine::load_font(const char* path, int pt_size)
162
+ {
163
+ if (!initialized_) {
164
+ throw std::runtime_error("Engine::init must be called before load_font");
165
+ }
166
+
167
+ if (font_ != nullptr) {
168
+ TTF_CloseFont(font_);
169
+ font_ = nullptr;
170
+ }
171
+
172
+ font_ = TTF_OpenFont(path, pt_size);
173
+ if (!font_) {
174
+ std::string msg = std::string("TTF_OpenFont Error: ") + TTF_GetError();
175
+ throw std::runtime_error(msg);
176
+ }
177
+ }
178
+
179
+ // Draw text at specified position.
180
+ void Engine::draw_text(const char* text, int x, int y, int r, int g, int b)
181
+ {
182
+ if (!initialized_ || renderer_ == nullptr || font_ == nullptr) {
183
+ return;
184
+ }
185
+
186
+ // clamp a bit to be safe
187
+ auto clamp = [](int v) {
188
+ if (v < 0) return 0;
189
+ if (v > 255) return 255;
190
+ return v;
191
+ };
192
+
193
+ SDL_Color color = { (Uint8)clamp(r), (Uint8)clamp(g), (Uint8)clamp(b), 255 }; // white text
194
+ SDL_Surface* surface = TTF_RenderUTF8_Blended(font_, text, color);
195
+ if (!surface) {
196
+ std::cerr << "TTF_RenderUTF8_Blended Error: " << TTF_GetError() << std::endl;
197
+ return;
198
+ }
199
+
200
+ SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer_, surface);
201
+ if (!texture) {
202
+ std::cerr << "SDL_CreateTextureFromSurface Error: " << SDL_GetError() << std::endl;
203
+ SDL_FreeSurface(surface);
204
+ return;
205
+ }
206
+
207
+ SDL_Rect dstRect;
208
+ dstRect.x = x;
209
+ dstRect.y = y;
210
+ dstRect.w = surface->w;
211
+ dstRect.h = surface->h;
212
+
213
+ SDL_FreeSurface(surface);
214
+
215
+ SDL_RenderCopy(renderer_, texture, nullptr, &dstRect);
216
+ SDL_DestroyTexture(texture);
217
+ }
218
+
219
+ bool Engine::capture_frame(const char* path)
220
+ {
221
+ if (!initialized_ || renderer_ == nullptr) {
222
+ return false;
223
+ }
224
+
225
+ int width = 0;
226
+ int height = 0;
227
+ if (SDL_GetRendererOutputSize(renderer_, &width, &height) != 0) {
228
+ std::cerr << "SDL_GetRendererOutputSize Error: " << SDL_GetError() << std::endl;
229
+ return false;
230
+ }
231
+
232
+ // Create a surface to hold the pixels (32-bit RGBA)
233
+ SDL_Surface* surface = SDL_CreateRGBSurfaceWithFormat(
234
+ 0,
235
+ width,
236
+ height,
237
+ 32,
238
+ SDL_PIXELFORMAT_ARGB8888
239
+ );
240
+
241
+ if (!surface) {
242
+ std::cerr << "SDL_CreateRGBSurfaceWithFormat Error: " << SDL_GetError() << std::endl;
243
+ return false;
244
+ }
245
+
246
+ // Read pixels from the current render target into the surface
247
+ if (SDL_RenderReadPixels(
248
+ renderer_,
249
+ nullptr, // whole screen
250
+ surface->format->format,
251
+ surface->pixels,
252
+ surface->pitch) != 0)
253
+ {
254
+ std::cerr << "SDL_RenderReadPixels Error: " << SDL_GetError() << std::endl;
255
+ SDL_FreeSurface(surface);
256
+ return false;
257
+ }
258
+
259
+ // Save as BMP (simple, no extra dependencies).
260
+ // Use .bmp extension in the path you pass from Python.
261
+ if (SDL_SaveBMP(surface, path) != 0) {
262
+ std::cerr << "SDL_SaveBMP Error: " << SDL_GetError() << std::endl;
263
+ SDL_FreeSurface(surface);
264
+ return false;
265
+ }
266
+
267
+ SDL_FreeSurface(surface);
268
+ return true;
269
+ }
270
+
271
+ std::vector<Event> Engine::poll_events()
272
+ {
273
+ std::vector<Event> events;
274
+ SDL_Event sdl_event;
275
+
276
+ while (SDL_PollEvent(&sdl_event)) {
277
+ Event ev;
278
+ ev.type = EventType::Unknown;
279
+ ev.key = 0;
280
+
281
+ switch (sdl_event.type) {
282
+ case SDL_QUIT:
283
+ ev.type = EventType::Quit;
284
+ break;
285
+
286
+ case SDL_KEYDOWN:
287
+ ev.type = EventType::KeyDown;
288
+ ev.key = sdl_event.key.keysym.sym;
289
+ break;
290
+
291
+ case SDL_KEYUP:
292
+ ev.type = EventType::KeyUp;
293
+ ev.key = sdl_event.key.keysym.sym;
294
+ break;
295
+
296
+ default:
297
+ ev.type = EventType::Unknown;
298
+ break;
299
+ }
300
+
301
+ events.push_back(ev);
302
+ }
303
+
304
+ return events;
305
+ }
306
+
307
+ } // namespace mini
@@ -1,6 +1,7 @@
1
1
  #pragma once
2
2
 
3
3
  #include <SDL.h>
4
+ #include <SDL_ttf.h>
4
5
  #include <vector>
5
6
 
6
7
  // A minimal 2D graphics engine binding for Python using SDL.
@@ -29,6 +30,9 @@ namespace mini {
29
30
  // Initialize the engine with a window of given width, height, and title.
30
31
  void init(int width, int height, const char* title);
31
32
 
33
+ // Set the clear color for the screen.
34
+ void set_clear_color(int r, int g, int b);
35
+
32
36
  // Clear the screen to a default color (black) and get ready to draw.
33
37
  void begin_frame();
34
38
 
@@ -36,18 +40,30 @@ namespace mini {
36
40
  void end_frame();
37
41
 
38
42
  // Draw a simple filled rectangle (we'll use a fixed color for now).
39
- void draw_rect(int x, int y, int w, int h);
43
+ void draw_rect(int x, int y, int w, int h, int r, int g, int b);
40
44
 
41
45
  // Sprite drawing stub for later.
42
46
  void draw_sprite(int texture_id, int x, int y, int w, int h);
43
47
 
44
48
  // Poll all pending events and return them.
45
49
  std::vector<Event> poll_events();
50
+
51
+ // Load a TTF font from file at specified point size.
52
+ void load_font(const char* path, int pt_size);
53
+
54
+ // Draw text at specified position.
55
+ void draw_text(const char* text, int x, int y, int r, int g, int b);
56
+
57
+ // Capture the current frame into an image file (BMP for now).
58
+ // Returns true on success, false on failure.
59
+ bool capture_frame(const char* path);
46
60
 
47
61
  private:
48
62
  SDL_Window* window_;
49
63
  SDL_Renderer* renderer_;
50
64
  bool initialized_;
65
+ TTF_Font* font_;
66
+ SDL_Color clear_color_;
51
67
  };
52
68
 
53
69
  } // 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.2.3"
11
+ version = "0.3.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" },
@@ -45,8 +45,10 @@ _NATIVE_TO_CORE = {
45
45
  class NativeBackend(Backend):
46
46
  """Adapter that makes the C++ Engine usable as a mini-arcade backend."""
47
47
 
48
- def __init__(self):
48
+ def __init__(self, font_path: str | None = None, font_size: int = 24):
49
49
  self._engine = native.Engine()
50
+ self._font_path = font_path
51
+ self._font_size = font_size
50
52
 
51
53
  def init(self, width: int, height: int, title: str):
52
54
  """
@@ -63,6 +65,13 @@ class NativeBackend(Backend):
63
65
  """
64
66
  self._engine.init(width, height, title)
65
67
 
68
+ # Load font if provided
69
+ if self._font_path is not None:
70
+ self._engine.load_font(self._font_path, self._font_size)
71
+
72
+ def set_clear_color(self, r: int, g: int, b: int) -> None:
73
+ self._engine.set_clear_color(int(r), int(g), int(b))
74
+
66
75
  def poll_events(self) -> list[Event]:
67
76
  """
68
77
  Poll for events from the backend and return them as a list of Event objects.
@@ -85,7 +94,14 @@ class NativeBackend(Backend):
85
94
  """End the current frame for rendering."""
86
95
  self._engine.end_frame()
87
96
 
88
- def draw_rect(self, x: int, y: int, w: int, h: int):
97
+ def draw_rect(
98
+ self,
99
+ x: int,
100
+ y: int,
101
+ w: int,
102
+ h: int,
103
+ color: tuple[int, int, int] = (255, 255, 255),
104
+ ):
89
105
  """
90
106
  Draw a rectangle at the specified position with given width and height.
91
107
 
@@ -101,4 +117,23 @@ class NativeBackend(Backend):
101
117
  :param h: Height of the rectangle.
102
118
  :type h: int
103
119
  """
104
- self._engine.draw_rect(x, y, w, h)
120
+ r, g, b = color
121
+ self._engine.draw_rect(x, y, w, h, int(r), int(g), int(b))
122
+
123
+ def draw_text(
124
+ self,
125
+ x: int,
126
+ y: int,
127
+ text: str,
128
+ color: tuple[int, int, int] = (255, 255, 255),
129
+ ) -> None:
130
+ """
131
+ Draw text at the given position using the loaded font.
132
+ If no font is loaded, this is a no-op.
133
+ """
134
+ # We rely on C++ side to no-op if font is missing
135
+ r, g, b = color
136
+ self._engine.draw_text(text, x, y, int(r), int(g), int(b))
137
+
138
+ def capture_frame(self, path: str) -> bool:
139
+ return self._engine.capture_frame(path)
@@ -1,4 +0,0 @@
1
- ## [0.2.3] - 2025-12-04
2
-
3
- - Internal changes only.
4
-
@@ -1 +0,0 @@
1
- 0.2.3
@@ -1,5 +0,0 @@
1
- {
2
- "files.associations": {
3
- "system_error": "cpp"
4
- }
5
- }
@@ -1,153 +0,0 @@
1
- #include "engine.h"
2
-
3
- #include <stdexcept>
4
- #include <iostream>
5
-
6
- namespace mini {
7
-
8
- Engine::Engine()
9
- : window_(nullptr),
10
- renderer_(nullptr),
11
- initialized_(false)
12
- {
13
- }
14
-
15
- Engine::~Engine()
16
- {
17
- if (renderer_ != nullptr) {
18
- SDL_DestroyRenderer(renderer_);
19
- renderer_ = nullptr;
20
- }
21
-
22
- if (window_ != nullptr) {
23
- SDL_DestroyWindow(window_);
24
- window_ = nullptr;
25
- }
26
-
27
- if (initialized_) {
28
- SDL_Quit();
29
- initialized_ = false;
30
- }
31
- }
32
-
33
- void Engine::init(int width, int height, const char* title)
34
- {
35
- if (initialized_) {
36
- return; // already initialized
37
- }
38
-
39
- if (SDL_Init(SDL_INIT_VIDEO) != 0) {
40
- throw std::runtime_error(std::string("SDL_Init Error: ") + SDL_GetError());
41
- }
42
-
43
- window_ = SDL_CreateWindow(
44
- title,
45
- SDL_WINDOWPOS_CENTERED,
46
- SDL_WINDOWPOS_CENTERED,
47
- width,
48
- height,
49
- SDL_WINDOW_SHOWN
50
- );
51
-
52
- if (window_ == nullptr) {
53
- std::string msg = std::string("SDL_CreateWindow Error: ") + SDL_GetError();
54
- SDL_Quit();
55
- throw std::runtime_error(msg);
56
- }
57
-
58
- renderer_ = SDL_CreateRenderer(
59
- window_,
60
- -1,
61
- SDL_RENDERER_ACCELERATED
62
- );
63
-
64
- if (renderer_ == nullptr) {
65
- std::string msg = std::string("SDL_CreateRenderer Error: ") + SDL_GetError();
66
- SDL_DestroyWindow(window_);
67
- window_ = nullptr;
68
- SDL_Quit();
69
- throw std::runtime_error(msg);
70
- }
71
-
72
- initialized_ = true;
73
- }
74
-
75
- void Engine::begin_frame()
76
- {
77
- if (!initialized_ || renderer_ == nullptr) {
78
- return;
79
- }
80
-
81
- // Clear to black
82
- SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 255);
83
- SDL_RenderClear(renderer_);
84
- }
85
-
86
- void Engine::end_frame()
87
- {
88
- if (!initialized_ || renderer_ == nullptr) {
89
- return;
90
- }
91
-
92
- SDL_RenderPresent(renderer_);
93
- }
94
-
95
- void Engine::draw_rect(int x, int y, int w, int h)
96
- {
97
- if (!initialized_ || renderer_ == nullptr) {
98
- return;
99
- }
100
-
101
- SDL_Rect rect;
102
- rect.x = x;
103
- rect.y = y;
104
- rect.w = w;
105
- rect.h = h;
106
-
107
- // White rectangle for now (you can parameterize later).
108
- SDL_SetRenderDrawColor(renderer_, 255, 255, 255, 255);
109
- SDL_RenderFillRect(renderer_, &rect);
110
- }
111
-
112
- void Engine::draw_sprite(int /*texture_id*/, int /*x*/, int /*y*/, int /*w*/, int /*h*/)
113
- {
114
- // TODO: placeholder for later texture management.
115
- }
116
-
117
- std::vector<Event> Engine::poll_events()
118
- {
119
- std::vector<Event> events;
120
- SDL_Event sdl_event;
121
-
122
- while (SDL_PollEvent(&sdl_event)) {
123
- Event ev;
124
- ev.type = EventType::Unknown;
125
- ev.key = 0;
126
-
127
- switch (sdl_event.type) {
128
- case SDL_QUIT:
129
- ev.type = EventType::Quit;
130
- break;
131
-
132
- case SDL_KEYDOWN:
133
- ev.type = EventType::KeyDown;
134
- ev.key = sdl_event.key.keysym.sym;
135
- break;
136
-
137
- case SDL_KEYUP:
138
- ev.type = EventType::KeyUp;
139
- ev.key = sdl_event.key.keysym.sym;
140
- break;
141
-
142
- default:
143
- ev.type = EventType::Unknown;
144
- break;
145
- }
146
-
147
- events.push_back(ev);
148
- }
149
-
150
- return events;
151
- }
152
-
153
- } // namespace mini