From 5df699c547fede7db6e7f2f112aa1affcef50865 Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Wed, 22 Oct 2025 08:25:00 +0200 Subject: [PATCH] working on python bindings --- example/main.py | 93 +++++++++++++++++++++++++++++++++++------ macrolibpy/__init__.py | 95 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 173 insertions(+), 15 deletions(-) diff --git a/example/main.py b/example/main.py index 8d5b080..95ac1e1 100644 --- a/example/main.py +++ b/example/main.py @@ -1,18 +1,85 @@ import macrolibpy as mlpy +import pathlib +import os +import math -if __name__ == '__main__': - mlx = mlpy.Context.create() - win = mlx.new_window(800, 600, "MacroLibX window") +current_path = pathlib.Path(__file__).parent.resolve() - def onevent(ev): - if ev == 0: - mlx.loop_end() +mlx = mlpy.Context.create() +win = mlx.new_window(400, 400, "My window") +mlx.set_fps_goal(60) +logo_png = mlx.new_image_from_file(os.path.join(current_path, "42_logo.png"))[0] +logo_bmp = mlx.new_image_from_file(os.path.join(current_path, "42_logo.bmp"))[0] +logo_jpg = mlx.new_image_from_file(os.path.join(current_path, "42_logo.jpg"))[0] - def onupdate(): - for x in range(0, 50): - for y in range(0, 50): - win.pixel_put(100 + x, 100 + y, 0xff0000ff) +win.pixel_put(200, 10, 0xFF0FFFF) +win.put_image(logo_png, 0, 0) - win.on_event(mlpy.EventType.WINDOW_EVENT, onevent) - mlx.add_loop_hook(onupdate) - mlx.loop() +mlx.set_font_scale(os.path.join(current_path, "font.ttf"), 16.0) +win.string_put(20, 20, 0x0020FFFF, "that text will disappear") + +custom_img = mlx.new_image(100, 100) +i, j, k = 0, 0, 0 +while i < (100 * 100) * 4: + if j >= 100: + j = 0 + k += 1 + if i < 10000 or i > 20000: + custom_img.set_pixel(j, k, (k << 24) | (j << 16) | (i << 8) | 0x99) + i += 4 + j += 1 + +def onevent(ev): + if ev == 0: + mlx.loop_end() + +THRESHOLD = 200 +CIRCLE_RADIUS = 50 +CIRCLE_DIAMETER = CIRCLE_RADIUS + CIRCLE_RADIUS +def onupdate(): + if onupdate.i > THRESHOLD: + win.clear(0x334D4DFF) + win.put_transformed_image(logo_bmp, 220, 40, 0.5, 0.5, onupdate.i) + + if onupdate.i >= THRESHOLD + THRESHOLD / 4: + mlx.set_font_scale("default", 16.0) + else: + mlx.set_font_scale("default", 6.0) + + win.string_put(160, 120, 0xFF2066FF, "this text should be behind") + win.put_image(logo_png, 100, 100) + win.put_image(custom_img, 150, 60) + + mlx.set_font("default") + win.string_put(20, 50, 0xFFFFFFFF, "that's a text") + + color = 0 + for j in range(0, 400): + win.pixel_put(j, j, 0x0000FFFF + (color << 24)) + win.pixel_put(399 - j, j, 0x0000FFFF) + color += (color < 255) + + if onupdate.i < THRESHOLD: + win.put_transformed_image(logo_jpg, 210, 150, 0.5, 2.0, 0.0) + else: + win.put_transformed_image(logo_jpg, 210, 150, abs(math.sin(onupdate.i / 100.0)), abs(math.cos(onupdate.i / 100.0) * 2.0), 0.0) + + mlx.set_font_scale("default", 8.0) + win.string_put(210, 175, 0xFFAF2BFF, "hidden") + + win.pixel_put_region(200, 170, CIRCLE_DIAMETER, CIRCLE_DIAMETER, onupdate.pixels_circle) + + onupdate.i += 1 +onupdate.i = 0 +onupdate.pixels_circle = [pixel for pixel in range(0, CIRCLE_DIAMETER * CIRCLE_DIAMETER)] + +i = 0 +for j in range(0, CIRCLE_DIAMETER): + for k in range(0, CIRCLE_DIAMETER): + if((CIRCLE_RADIUS - j) * (CIRCLE_RADIUS - j) + (CIRCLE_RADIUS - k) * (CIRCLE_RADIUS - k) < CIRCLE_RADIUS * CIRCLE_RADIUS): + onupdate.pixels_circle[i] = 0xA10000FF + ((j * k * i) << 8) + i += 1 + +win.on_event(mlpy.EventType.WINDOW_EVENT, onevent) +mlx.add_loop_hook(onupdate) +mlx.loop() diff --git a/macrolibpy/__init__.py b/macrolibpy/__init__.py index e0417fe..b1a1cff 100644 --- a/macrolibpy/__init__.py +++ b/macrolibpy/__init__.py @@ -80,6 +80,17 @@ ffi.cdef( void mlx_string_put(mlx_context mlx, mlx_window win, int x, int y, mlx_color color, char* str); void mlx_set_font(mlx_context mlx, char* filepath); void mlx_set_font_scale(mlx_context mlx, char* filepath, float scale); + + void mlx_set_window_max_size(mlx_context mlx, mlx_window win, int x, int y); + void mlx_set_window_min_size(mlx_context mlx, mlx_window win, int x, int y); + void mlx_maximise_window(mlx_context mlx, mlx_window win); + void mlx_minimize_window(mlx_context mlx, mlx_window win); + void mlx_restore_window(mlx_context mlx, mlx_window win); + void mlx_pixel_put_array(mlx_context mlx, mlx_window win, int x, int y, mlx_color* pixels, size_t pixels_number); + void mlx_pixel_put_region(mlx_context mlx, mlx_window win, int x, int y, int w, int h, mlx_color* pixels); + void mlx_get_image_region(mlx_context mlx, mlx_image image, int x, int y, int w, int h, mlx_color* dst); + void mlx_set_image_region(mlx_context mlx, mlx_image image, int x, int y, int w, int h, mlx_color* pixels); + void mlx_put_transformed_image_to_window(mlx_context mlx, mlx_window win, mlx_image image, int x, int y, float scale_x, float scale_y, float angle); """ ) @@ -207,6 +218,15 @@ class Context: raise MlxError("mlx_new_image failed") return Image(self, img) + def new_image_from_file(self, path: str) -> Tuple["Image", int, int]: + w = ffi.new("int[]", 1); + h = ffi.new("int[]", 1); + ffi_path = ffi.new("char[]", path.encode('ascii')) + img = lib.mlx_new_image_from_file(self._ctx, ffi_path, w, h) + if img == ffi.NULL: + raise MlxError("mlx_new_image failed") + return (Image(self, img), w, h) + def loop(self) -> None: lib.mlx_loop(self._ctx) @@ -218,6 +238,29 @@ class Context: lib.mlx_destroy_context(self._ctx) self._ctx = ffi.NULL + def mouse_show(self) -> None: + lib.mlx_mouse_show(self._ctx) + + def mouse_hide(self) -> None: + lib.mlx_mouse_hide(self._ctx) + + def mouse_move(self, x: int, y: int) -> None: + lib.mlx_mouse_move(self._ctx, int(x), int(y)) + + def get_mouse_position(self) -> Tuple[int, int]: + x = ffi.new("int[]", 1); + y = ffi.new("int[]", 1); + lib.mlx_mouse_get_pos(self._ctx, x, y) + return (x[0], y[0]) + + def set_font(self, path: str) -> None: + ffi_path = ffi.new("char[]", path.encode('ascii')) + lib.mlx_set_font(self._ctx, ffi_path) + + def set_font_scale(self, path: str, scale: float) -> None: + ffi_path = ffi.new("char[]", path.encode('ascii')) + lib.mlx_set_font_scale(self._ctx, ffi_path, float(scale)) + def __del__(self): try: self.destroy() @@ -235,15 +278,57 @@ class Window: ffi_title = ffi.new("char[]", title.encode('ascii', 'replace')) lib.mlx_set_window_title(self._ctx._ctx, self._win, ffi_title) - def clear(self, rgba: int) -> None: + def set_position(self, x: int, y: int) -> None: + lib.mlx_set_window_position(self._ctx._ctx, self._win, int(x), int(y)) + + def set_size(self, w: int, h: int) -> None: + lib.mlx_set_window_size(self._ctx._ctx, self._win, int(w), int(h)) + + def set_fullscreen(self, enable: bool) -> None: + lib.mlx_set_window_fullscreen(self._ctx._ctx, self._win, bool(enable)) + + def get_position(self) -> Tuple[int, int]: + x = ffi.new("int[]", 1); + y = ffi.new("int[]", 1); + lib.mlx_get_window_position(self._ctx._ctx, self._win, x, y) + return (x[0], y[0]) + + def get_size(self) -> Tuple[int, int]: + w = ffi.new("int[]", 1); + h = ffi.new("int[]", 1); + lib.mlx_get_window_size(self._ctx._ctx, self._win, w, h) + return (w[0], h[0]) + + def get_screen_size(self) -> Tuple[int, int]: + w = ffi.new("int[]", 1); + h = ffi.new("int[]", 1); + lib.mlx_get_screen_size(self._ctx._ctx, self._win, w, h) + return (w[0], h[0]) + + def clear(self, rgba: int = 0) -> None: lib.mlx_clear_window(self._ctx._ctx, self._win, _rgbaToColor(rgba)) def pixel_put(self, x: int, y: int, rgba: int) -> None: lib.mlx_pixel_put(self._ctx._ctx, self._win, int(x), int(y), _rgbaToColor(rgba)) - def put_image(self, image: "Image", x: int = 0, y: int = 0) -> None: + def pixel_put_array(self, x: int, y: int, rgba: list[int]) -> None: + ffi_pixels = ffi.new("mlx_color[]", [_rgbaToColor(pixel) for pixel in rgba]) + lib.mlx_pixel_put_array(self._ctx._ctx, self._win, int(x), int(y), ffi_pixels, int(rgba.len())) + + def pixel_put_region(self, x: int, y: int, w: int, h: int, rgba: list[int]) -> None: + ffi_pixels = ffi.new("mlx_color[]", [_rgbaToColor(pixel) for pixel in rgba]) + lib.mlx_pixel_put_region(self._ctx._ctx, self._win, int(x), int(y), int(w), int(h), ffi_pixels) + + def put_image(self, image: "Image", x: int, y: int) -> None: lib.mlx_put_image_to_window(self._ctx._ctx, self._win, image._img, int(x), int(y)) + def put_transformed_image(self, image: "Image", x: int, y: int, scale_x: float, scale_y: float, angle: float) -> None: + lib.mlx_put_transformed_image_to_window(self._ctx._ctx, self._win, image._img, int(x), int(y), float(scale_x), float(scale_y), float(angle)) + + def string_put(self, x: int, y: int, rgba: int, text: str) -> None: + ffi_text = ffi.new("char[]", text.encode('ascii', 'replace')) + lib.mlx_string_put(self._ctx._ctx, self._win, int(x), int(y), _rgbaToColor(rgba), ffi_text) + def on_event(self, event_type: EventType | int, fn: Callable[[int], None]) -> None: lib.mlx_on_event(self._ctx._ctx, self._win, int(event_type), _makeEventCallback(fn), ffi.NULL) @@ -265,6 +350,12 @@ class Image: self._ctx = ctx self._img = img + def set_pixel(self, x: int, y: int, rgba: int) -> None: + lib.mlx_set_image_pixel(self._ctx._ctx, self._img, int(x), int(y), _rgbaToColor(rgba)) + + def get_pixel(self, x: int, y: int) -> int: + return lib.mlx_get_image_pixel(self._ctx._ctx, self._img, int(x), int(y)) + def destroy(self) -> None: if getattr(self, "_img", None): lib.mlx_destroy_image(self._ctx._ctx, self._img)