mirror of
https://github.com/seekrs/MacroLibX.git
synced 2026-01-12 07:03:34 +00:00
Compare commits
3 Commits
v2.2.0
...
152-add-py
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5df699c547 | ||
|
|
0d0d2d4451 | ||
|
|
73d31c196e |
2
.github/workflows/greetings.yml
vendored
2
.github/workflows/greetings.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
|||||||
issues: write
|
issues: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/first-interaction@v2
|
- uses: actions/first-interaction@v1
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
issue-message: |
|
issue-message: |
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -23,3 +23,5 @@
|
|||||||
objs/
|
objs/
|
||||||
build/
|
build/
|
||||||
example/Test
|
example/Test
|
||||||
|
macrolibpy/__pycache__
|
||||||
|
example/__pycache__
|
||||||
|
|||||||
6
Makefile
6
Makefile
@@ -38,9 +38,6 @@ GCH = runtime/Includes/PreCompiled.h.gch
|
|||||||
CCH = runtime/Includes/PreCompiled.h.pch
|
CCH = runtime/Includes/PreCompiled.h.pch
|
||||||
PCH =
|
PCH =
|
||||||
|
|
||||||
# Personal path, should be overriden with env var
|
|
||||||
UNIT_TESTS_PATH = ../UnitTester/build/Bin/linux_x86_64/MacroUnitTest
|
|
||||||
|
|
||||||
NZSLC ?= nzslc
|
NZSLC ?= nzslc
|
||||||
|
|
||||||
ifeq ($(TOOLCHAIN), gcc)
|
ifeq ($(TOOLCHAIN), gcc)
|
||||||
@@ -164,9 +161,6 @@ clean-shaders:
|
|||||||
|
|
||||||
shaders: clean-shaders $(SPVS)
|
shaders: clean-shaders $(SPVS)
|
||||||
|
|
||||||
tests: debug
|
|
||||||
@$(UNIT_TESTS_PATH) --headless --path="./$(NAME)"
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@$(RM) $(OBJ_DIR)
|
@$(RM) $(OBJ_DIR)
|
||||||
@printf "Cleaned $(_BOLD)$(OBJ_DIR)$(_RESET)\n"
|
@printf "Cleaned $(_BOLD)$(OBJ_DIR)$(_RESET)\n"
|
||||||
|
|||||||
85
example/main.py
git.filemode.normal_file
85
example/main.py
git.filemode.normal_file
@@ -0,0 +1,85 @@
|
|||||||
|
import macrolibpy as mlpy
|
||||||
|
import pathlib
|
||||||
|
import os
|
||||||
|
import math
|
||||||
|
|
||||||
|
current_path = pathlib.Path(__file__).parent.resolve()
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
win.pixel_put(200, 10, 0xFF0FFFF)
|
||||||
|
win.put_image(logo_png, 0, 0)
|
||||||
|
|
||||||
|
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()
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
|
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2023/11/10 08:49:17 by maldavid #+# #+# */
|
/* Created: 2023/11/10 08:49:17 by maldavid #+# #+# */
|
||||||
/* Updated: 2025/10/22 13:38:43 by maldavid ### ########.fr */
|
/* Updated: 2025/03/12 22:01:07 by maldavid ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
@@ -206,7 +206,7 @@
|
|||||||
|
|
||||||
typedef void (*mlx_function)(void);
|
typedef void (*mlx_function)(void);
|
||||||
|
|
||||||
#define MLX_VERSION MLX_MAKE_VERSION(2, 2, 0)
|
#define MLX_VERSION MLX_MAKE_VERSION(2, 0, 0)
|
||||||
#define MLX_TARGET_VULKAN_API_VERSION MLX_MAKE_VERSION(1, 0, 0)
|
#define MLX_TARGET_VULKAN_API_VERSION MLX_MAKE_VERSION(1, 0, 0)
|
||||||
|
|
||||||
// Checking common assumptions
|
// Checking common assumptions
|
||||||
|
|||||||
368
macrolibpy/__init__.py
git.filemode.normal_file
368
macrolibpy/__init__.py
git.filemode.normal_file
@@ -0,0 +1,368 @@
|
|||||||
|
"""
|
||||||
|
macrolibpy — thin, Pythonic bindings over MacroLibX.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
from enum import IntEnum
|
||||||
|
from typing import Callable, Optional, Any, List
|
||||||
|
import os, sys
|
||||||
|
from cffi import FFI
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
__all__ = ["Context", "Window", "Image", "Event"]
|
||||||
|
|
||||||
|
ffi = FFI()
|
||||||
|
|
||||||
|
# NOTE: This is a curated declaration set focused on the core API
|
||||||
|
ffi.cdef(
|
||||||
|
r"""
|
||||||
|
typedef unsigned char uint8_t;
|
||||||
|
typedef unsigned int uint32_t;
|
||||||
|
|
||||||
|
typedef void (*mlx_function)(void);
|
||||||
|
typedef struct mlx_context_handler* mlx_context;
|
||||||
|
typedef struct mlx_window_handler* mlx_window;
|
||||||
|
typedef struct mlx_image_handler* mlx_image;
|
||||||
|
|
||||||
|
typedef struct mlx_color
|
||||||
|
{
|
||||||
|
uint32_t rgba;
|
||||||
|
} mlx_color;
|
||||||
|
|
||||||
|
typedef enum mlx_event_type
|
||||||
|
{
|
||||||
|
MLX_KEYDOWN = 0,
|
||||||
|
MLX_KEYUP = 1,
|
||||||
|
MLX_MOUSEDOWN = 2,
|
||||||
|
MLX_MOUSEUP = 3,
|
||||||
|
MLX_MOUSEWHEEL = 4,
|
||||||
|
MLX_WINDOW_EVENT = 5
|
||||||
|
} mlx_event_type;
|
||||||
|
|
||||||
|
typedef struct mlx_window_create_info
|
||||||
|
{
|
||||||
|
mlx_image render_target;
|
||||||
|
const char* title;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
_Bool is_fullscreen;
|
||||||
|
_Bool is_resizable;
|
||||||
|
} mlx_window_create_info;
|
||||||
|
|
||||||
|
mlx_context mlx_init();
|
||||||
|
void mlx_set_fps_goal(mlx_context mlx, int fps);
|
||||||
|
void mlx_destroy_context(mlx_context mlx);
|
||||||
|
mlx_window mlx_new_window(mlx_context mlx, const mlx_window_create_info* info);
|
||||||
|
void mlx_destroy_window(mlx_context mlx, mlx_window win);
|
||||||
|
void mlx_set_window_position(mlx_context mlx, mlx_window win, int x, int y);
|
||||||
|
void mlx_set_window_size(mlx_context mlx, mlx_window win, int width, int height);
|
||||||
|
void mlx_set_window_title(mlx_context mlx, mlx_window win, const char* title);
|
||||||
|
void mlx_set_window_fullscreen(mlx_context mlx, mlx_window win, _Bool enable);
|
||||||
|
void mlx_get_window_position(mlx_context mlx, mlx_window win, int* x, int* y);
|
||||||
|
void mlx_get_window_size(mlx_context mlx, mlx_window win, int* x, int* y);
|
||||||
|
void mlx_clear_window(mlx_context mlx, mlx_window win, mlx_color color);
|
||||||
|
void mlx_get_screen_size(mlx_context mlx, mlx_window win, int* w, int* h);
|
||||||
|
void mlx_add_loop_hook(mlx_context mlx, void(*f)(void*), void* param);
|
||||||
|
void mlx_loop(mlx_context mlx);
|
||||||
|
void mlx_loop_end(mlx_context mlx);
|
||||||
|
void mlx_mouse_show(mlx_context mlx);
|
||||||
|
void mlx_mouse_hide(mlx_context mlx);
|
||||||
|
void mlx_mouse_move(mlx_context mlx, mlx_window win, int x, int y);
|
||||||
|
void mlx_mouse_get_pos(mlx_context mlx, int* x, int* y);
|
||||||
|
void mlx_on_event(mlx_context mlx, mlx_window win, mlx_event_type event, void(*f)(int, void*), void* param);
|
||||||
|
void mlx_pixel_put(mlx_context mlx, mlx_window win, int x, int y, mlx_color color);
|
||||||
|
mlx_image mlx_new_image(mlx_context mlx, int width, int height);
|
||||||
|
mlx_image mlx_new_image_from_file(mlx_context mlx, char* filename, int* width, int* height);
|
||||||
|
void mlx_destroy_image(mlx_context mlx, mlx_image image);
|
||||||
|
mlx_color mlx_get_image_pixel(mlx_context mlx, mlx_image image, int x, int y);
|
||||||
|
void mlx_set_image_pixel(mlx_context mlx, mlx_image image, int x, int y, mlx_color color);
|
||||||
|
void mlx_put_image_to_window(mlx_context mlx, mlx_window win, mlx_image image, int x, int y);
|
||||||
|
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);
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
def _candidateLibNames() -> List[str]:
|
||||||
|
if sys.platform.startswith("linux"):
|
||||||
|
return ["libmlx.so"]
|
||||||
|
if sys.platform == "darwin":
|
||||||
|
return ["libmlx.dylib"]
|
||||||
|
if sys.platform.startswith("win"):
|
||||||
|
# depending on build system, either of these may exist
|
||||||
|
return ["mlx.dll", "libmlx.dll"]
|
||||||
|
return []
|
||||||
|
|
||||||
|
def _candidateSdl2Names() -> List[str]:
|
||||||
|
if sys.platform.startswith("linux"):
|
||||||
|
# Try common SONAMEs (distro dependent)
|
||||||
|
return ["libSDL2-2.0.so.0", "libSDL2.so.0", "libSDL2.so"]
|
||||||
|
if sys.platform == "darwin":
|
||||||
|
return ["libSDL2-2.0.0.dylib", "libSDL2.dylib"]
|
||||||
|
if sys.platform.startswith("win"):
|
||||||
|
return ["SDL2.dll"]
|
||||||
|
return []
|
||||||
|
|
||||||
|
def _loadLibrary(candidates: List[str]):
|
||||||
|
last_err: Optional[BaseException] = None
|
||||||
|
RTLD_NOW = 2
|
||||||
|
RTLD_GLOBAL = 0x100
|
||||||
|
for path in candidates:
|
||||||
|
try:
|
||||||
|
return ffi.dlopen(path, RTLD_NOW | RTLD_GLOBAL)
|
||||||
|
except OSError as e:
|
||||||
|
last_err = e
|
||||||
|
raise OSError(
|
||||||
|
f"Could not load library:\n"
|
||||||
|
f"\tTried: {candidates}.\n"
|
||||||
|
f"\tLast error: {last_err}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _dlopenMlx():
|
||||||
|
_loadLibrary(_candidateSdl2Names())
|
||||||
|
candidates: List[str] = []
|
||||||
|
for name in _candidateLibNames():
|
||||||
|
candidates.append(os.path.join(pathlib.Path(__file__).parent.parent.resolve(), name))
|
||||||
|
return _loadLibrary(candidates)
|
||||||
|
|
||||||
|
lib = _dlopenMlx()
|
||||||
|
|
||||||
|
class EventType(IntEnum):
|
||||||
|
KEYDOWN = 0
|
||||||
|
KEYUP = 1
|
||||||
|
MOUSEDOWN = 2
|
||||||
|
MOUSEUP = 3
|
||||||
|
MOUSEWHEEL = 4
|
||||||
|
WINDOW_EVENT = 5
|
||||||
|
|
||||||
|
class MlxError(RuntimeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Keep Python references to cffi callbacks alive
|
||||||
|
_alive_callbacks: List[Any] = []
|
||||||
|
|
||||||
|
def _makeLoopCallback(pyfunc: Callable[[], None]):
|
||||||
|
@ffi.callback("void(void*)")
|
||||||
|
def cfunc(userdata):
|
||||||
|
pyfunc()
|
||||||
|
_alive_callbacks.append(cfunc)
|
||||||
|
return cfunc
|
||||||
|
|
||||||
|
def _makeEventCallback(pyfunc: Callable[[int], None]):
|
||||||
|
@ffi.callback("void(int, void*)")
|
||||||
|
def cfunc(event, userdata):
|
||||||
|
pyfunc(int(event))
|
||||||
|
_alive_callbacks.append(cfunc)
|
||||||
|
return cfunc
|
||||||
|
|
||||||
|
def _rgbaToColor(rgba: int):
|
||||||
|
c = ffi.new("mlx_color*")
|
||||||
|
c[0].rgba = int(rgba) & 0xFFFFFFFF
|
||||||
|
return c[0]
|
||||||
|
|
||||||
|
class Context:
|
||||||
|
__slots__ = ("_ctx",)
|
||||||
|
|
||||||
|
def __init__(self, _ptr) -> None:
|
||||||
|
self._ctx = _ptr
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(cls) -> "Context":
|
||||||
|
ctx = lib.mlx_init()
|
||||||
|
if ctx == ffi.NULL:
|
||||||
|
raise MlxError("mlx_init failed")
|
||||||
|
return cls(ctx)
|
||||||
|
|
||||||
|
def set_fps_goal(self, fps: int) -> None:
|
||||||
|
lib.mlx_set_fps_goal(self._ctx, int(fps))
|
||||||
|
|
||||||
|
def add_loop_hook(self, fn: Callable[[], None]) -> None:
|
||||||
|
lib.mlx_add_loop_hook(self._ctx, _makeLoopCallback(fn), ffi.NULL)
|
||||||
|
|
||||||
|
def new_window(
|
||||||
|
self,
|
||||||
|
width: int,
|
||||||
|
height: int,
|
||||||
|
title: str,
|
||||||
|
resizable: bool = True,
|
||||||
|
fullscreen: bool = False,
|
||||||
|
) -> "Window":
|
||||||
|
ffi_title = ffi.new("char[]", title.encode('ascii', 'replace'))
|
||||||
|
|
||||||
|
info = ffi.new("mlx_window_create_info*")
|
||||||
|
info.render_target = ffi.NULL
|
||||||
|
info.title = ffi_title
|
||||||
|
info.width = int(width)
|
||||||
|
info.height = int(height)
|
||||||
|
info.is_fullscreen = bool(fullscreen)
|
||||||
|
info.is_resizable = bool(resizable)
|
||||||
|
win = lib.mlx_new_window(self._ctx, info)
|
||||||
|
if win == ffi.NULL:
|
||||||
|
raise MlxError("mlx_new_window failed")
|
||||||
|
return Window(self, win)
|
||||||
|
|
||||||
|
def new_image(self, width: int, height: int) -> "Image":
|
||||||
|
img = lib.mlx_new_image(self._ctx, int(width), int(height))
|
||||||
|
if img == ffi.NULL:
|
||||||
|
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)
|
||||||
|
|
||||||
|
def loop_end(self) -> None:
|
||||||
|
lib.mlx_loop_end(self._ctx)
|
||||||
|
|
||||||
|
def destroy(self) -> None:
|
||||||
|
if getattr(self, "_ctx", None):
|
||||||
|
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()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Window:
|
||||||
|
__slots__ = ("_ctx", "_win")
|
||||||
|
|
||||||
|
def __init__(self, ctx: Context, win) -> None:
|
||||||
|
self._ctx = ctx
|
||||||
|
self._win = win
|
||||||
|
|
||||||
|
def set_title(self, title: str) -> None:
|
||||||
|
ffi_title = ffi.new("char[]", title.encode('ascii', 'replace'))
|
||||||
|
lib.mlx_set_window_title(self._ctx._ctx, self._win, ffi_title)
|
||||||
|
|
||||||
|
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 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)
|
||||||
|
|
||||||
|
def destroy(self) -> None:
|
||||||
|
if getattr(self, "_win", None):
|
||||||
|
lib.mlx_destroy_window(self._ctx._ctx, self._win)
|
||||||
|
self._win = ffi.NULL
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
try:
|
||||||
|
self.destroy()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Image:
|
||||||
|
__slots__ = ("_ctx", "_img")
|
||||||
|
|
||||||
|
def __init__(self, ctx: Context, img) -> None:
|
||||||
|
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)
|
||||||
|
self._img = ffi.NULL
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
try:
|
||||||
|
self.destroy()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
@@ -50,7 +50,7 @@ namespace mlx
|
|||||||
~DescriptorPoolManager() = default;
|
~DescriptorPoolManager() = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::unique_ptr<DescriptorPool>> m_pools;
|
std::vector<DescriptorPool> m_pools;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DescriptorSet : public std::enable_shared_from_this<DescriptorSet>
|
class DescriptorSet : public std::enable_shared_from_this<DescriptorSet>
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ namespace mlx
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Swapchain m_swapchain;
|
Swapchain m_swapchain;
|
||||||
std::vector<VkSemaphore> m_render_finished_semaphores;
|
|
||||||
std::array<VkSemaphore, MAX_FRAMES_IN_FLIGHT> m_image_available_semaphores;
|
std::array<VkSemaphore, MAX_FRAMES_IN_FLIGHT> m_image_available_semaphores;
|
||||||
|
std::array<VkSemaphore, MAX_FRAMES_IN_FLIGHT> m_render_finished_semaphores;
|
||||||
std::array<VkCommandBuffer, MAX_FRAMES_IN_FLIGHT> m_cmd_buffers;
|
std::array<VkCommandBuffer, MAX_FRAMES_IN_FLIGHT> m_cmd_buffers;
|
||||||
std::array<VkFence, MAX_FRAMES_IN_FLIGHT> m_cmd_fences;
|
std::array<VkFence, MAX_FRAMES_IN_FLIGHT> m_cmd_fences;
|
||||||
NonOwningPtr<Window> p_window;
|
NonOwningPtr<Window> p_window;
|
||||||
|
|||||||
@@ -114,8 +114,11 @@ namespace mlx
|
|||||||
|
|
||||||
void DescriptorPool::ReturnDescriptorSet(std::shared_ptr<DescriptorSet> set)
|
void DescriptorPool::ReturnDescriptorSet(std::shared_ptr<DescriptorSet> set)
|
||||||
{
|
{
|
||||||
|
//std::size_t i = 0;
|
||||||
auto it = std::find_if(m_used_sets.begin(), m_used_sets.end(), [&](const std::shared_ptr<DescriptorSet>& rhs_set)
|
auto it = std::find_if(m_used_sets.begin(), m_used_sets.end(), [&](const std::shared_ptr<DescriptorSet>& rhs_set)
|
||||||
{
|
{
|
||||||
|
//i++;
|
||||||
|
//std::cout << m_used_sets.size() << " " << i << std::endl;
|
||||||
return set == rhs_set;
|
return set == rhs_set;
|
||||||
});
|
});
|
||||||
if(it == m_used_sets.end())
|
if(it == m_used_sets.end())
|
||||||
@@ -129,18 +132,18 @@ namespace mlx
|
|||||||
MLX_PROFILE_FUNCTION();
|
MLX_PROFILE_FUNCTION();
|
||||||
for(auto& pool : m_pools)
|
for(auto& pool : m_pools)
|
||||||
{
|
{
|
||||||
if(pool->GetNumberOfSetsAllocated() < MAX_SETS_PER_POOL)
|
if(pool.GetNumberOfSetsAllocated() < MAX_SETS_PER_POOL)
|
||||||
return *pool;
|
return pool;
|
||||||
}
|
}
|
||||||
m_pools.emplace_back(std::make_unique<DescriptorPool>())->Init();
|
m_pools.emplace_back().Init();
|
||||||
return *m_pools.back();
|
return m_pools.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DescriptorPoolManager::Destroy()
|
void DescriptorPoolManager::Destroy()
|
||||||
{
|
{
|
||||||
MLX_PROFILE_FUNCTION();
|
MLX_PROFILE_FUNCTION();
|
||||||
for(auto& pool : m_pools)
|
for(auto& pool : m_pools)
|
||||||
pool->Destroy();
|
pool.Destroy();
|
||||||
m_pools.clear();
|
m_pools.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,42 +20,29 @@ namespace mlx
|
|||||||
MLX_PROFILE_FUNCTION();
|
MLX_PROFILE_FUNCTION();
|
||||||
p_window = window;
|
p_window = window;
|
||||||
m_swapchain.Init(p_window);
|
m_swapchain.Init(p_window);
|
||||||
Init(NonOwningPtr<Texture>{ nullptr });
|
for(std::size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
|
||||||
|
{
|
||||||
|
m_image_available_semaphores[i] = kvfCreateSemaphore(RenderCore::Get().GetDevice());
|
||||||
|
DebugLog("Vulkan: image available semaphore created");
|
||||||
|
m_render_finished_semaphores[i] = kvfCreateSemaphore(RenderCore::Get().GetDevice());
|
||||||
|
DebugLog("Vulkan: render finished semaphore created");
|
||||||
|
m_cmd_buffers[i] = kvfCreateCommandBuffer(RenderCore::Get().GetDevice());
|
||||||
|
DebugLog("Vulkan: command buffer created");
|
||||||
|
m_cmd_fences[i] = kvfCreateFence(RenderCore::Get().GetDevice());
|
||||||
|
DebugLog("Vulkan: fence created");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::Init(NonOwningPtr<Texture> render_target)
|
void Renderer::Init(NonOwningPtr<Texture> render_target)
|
||||||
{
|
{
|
||||||
MLX_PROFILE_FUNCTION();
|
MLX_PROFILE_FUNCTION();
|
||||||
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
|
p_render_target = render_target;
|
||||||
{
|
|
||||||
if(event.What() == Event::ResizeEventCode)
|
|
||||||
{
|
|
||||||
for(std::size_t i = 0; i < m_render_finished_semaphores.size(); i++)
|
|
||||||
{
|
|
||||||
kvfDestroySemaphore(RenderCore::Get().GetDevice(), m_render_finished_semaphores[i]);
|
|
||||||
DebugLog("Vulkan: render finished semaphore destroyed");
|
|
||||||
}
|
|
||||||
m_render_finished_semaphores.clear();
|
|
||||||
for(std::size_t i = 0; i < (p_window ? m_swapchain.GetImagesCount() : 1); i++)
|
|
||||||
{
|
|
||||||
m_render_finished_semaphores.push_back(kvfCreateSemaphore(RenderCore::Get().GetDevice()));
|
|
||||||
DebugLog("Vulkan: render finished semaphore created");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
EventBus::RegisterListener({ functor, "mlx_renderer_" + std::to_string(reinterpret_cast<std::uintptr_t>(this)) });
|
|
||||||
|
|
||||||
if(render_target)
|
|
||||||
p_render_target = render_target;
|
|
||||||
for(std::size_t i = 0; i < (p_window ? m_swapchain.GetImagesCount() : 1); i++)
|
|
||||||
{
|
|
||||||
m_render_finished_semaphores.push_back(kvfCreateSemaphore(RenderCore::Get().GetDevice()));
|
|
||||||
DebugLog("Vulkan: render finished semaphore created");
|
|
||||||
}
|
|
||||||
for(std::size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
|
for(std::size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
|
||||||
{
|
{
|
||||||
m_image_available_semaphores[i] = kvfCreateSemaphore(RenderCore::Get().GetDevice());
|
m_image_available_semaphores[i] = kvfCreateSemaphore(RenderCore::Get().GetDevice());
|
||||||
DebugLog("Vulkan: image available semaphore created");
|
DebugLog("Vulkan: image available semaphore created");
|
||||||
|
m_render_finished_semaphores[i] = kvfCreateSemaphore(RenderCore::Get().GetDevice());
|
||||||
|
DebugLog("Vulkan: render finished semaphore created");
|
||||||
m_cmd_buffers[i] = kvfCreateCommandBuffer(RenderCore::Get().GetDevice());
|
m_cmd_buffers[i] = kvfCreateCommandBuffer(RenderCore::Get().GetDevice());
|
||||||
DebugLog("Vulkan: command buffer created");
|
DebugLog("Vulkan: command buffer created");
|
||||||
m_cmd_fences[i] = kvfCreateFence(RenderCore::Get().GetDevice());
|
m_cmd_fences[i] = kvfCreateFence(RenderCore::Get().GetDevice());
|
||||||
@@ -79,15 +66,14 @@ namespace mlx
|
|||||||
void Renderer::EndFrame()
|
void Renderer::EndFrame()
|
||||||
{
|
{
|
||||||
MLX_PROFILE_FUNCTION();
|
MLX_PROFILE_FUNCTION();
|
||||||
std::size_t render_finished_index = (p_window ? m_swapchain.GetImageIndex() : 0);
|
|
||||||
VkPipelineStageFlags wait_stages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
|
VkPipelineStageFlags wait_stages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
|
||||||
kvfEndCommandBuffer(m_cmd_buffers[m_current_frame_index]);
|
kvfEndCommandBuffer(m_cmd_buffers[m_current_frame_index]);
|
||||||
if(p_window)
|
if(p_window)
|
||||||
kvfSubmitCommandBuffer(RenderCore::Get().GetDevice(), m_cmd_buffers[m_current_frame_index], KVF_GRAPHICS_QUEUE, m_render_finished_semaphores[render_finished_index], m_image_available_semaphores[m_current_frame_index], m_cmd_fences[m_current_frame_index], wait_stages);
|
kvfSubmitCommandBuffer(RenderCore::Get().GetDevice(), m_cmd_buffers[m_current_frame_index], KVF_GRAPHICS_QUEUE, m_render_finished_semaphores[m_current_frame_index], m_image_available_semaphores[m_current_frame_index], m_cmd_fences[m_current_frame_index], wait_stages);
|
||||||
else
|
else
|
||||||
kvfSubmitCommandBuffer(RenderCore::Get().GetDevice(), m_cmd_buffers[m_current_frame_index], KVF_GRAPHICS_QUEUE, VK_NULL_HANDLE, VK_NULL_HANDLE, m_cmd_fences[m_current_frame_index], wait_stages);
|
kvfSubmitCommandBuffer(RenderCore::Get().GetDevice(), m_cmd_buffers[m_current_frame_index], KVF_GRAPHICS_QUEUE, VK_NULL_HANDLE, VK_NULL_HANDLE, m_cmd_fences[m_current_frame_index], wait_stages);
|
||||||
if(p_window)
|
if(p_window)
|
||||||
m_swapchain.Present(m_render_finished_semaphores[render_finished_index]);
|
m_swapchain.Present(m_render_finished_semaphores[m_current_frame_index]);
|
||||||
m_current_frame_index = (m_current_frame_index + 1) % MAX_FRAMES_IN_FLIGHT;
|
m_current_frame_index = (m_current_frame_index + 1) % MAX_FRAMES_IN_FLIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,16 +81,12 @@ namespace mlx
|
|||||||
{
|
{
|
||||||
MLX_PROFILE_FUNCTION();
|
MLX_PROFILE_FUNCTION();
|
||||||
RenderCore::Get().WaitDeviceIdle();
|
RenderCore::Get().WaitDeviceIdle();
|
||||||
for(std::size_t i = 0; i < m_render_finished_semaphores.size(); i++)
|
|
||||||
{
|
|
||||||
kvfDestroySemaphore(RenderCore::Get().GetDevice(), m_render_finished_semaphores[i]);
|
|
||||||
DebugLog("Vulkan: render finished semaphore destroyed");
|
|
||||||
}
|
|
||||||
m_render_finished_semaphores.clear();
|
|
||||||
for(std::size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
|
for(std::size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
|
||||||
{
|
{
|
||||||
kvfDestroySemaphore(RenderCore::Get().GetDevice(), m_image_available_semaphores[i]);
|
kvfDestroySemaphore(RenderCore::Get().GetDevice(), m_image_available_semaphores[i]);
|
||||||
DebugLog("Vulkan: image available semaphore destroyed");
|
DebugLog("Vulkan: image available semaphore destroyed");
|
||||||
|
kvfDestroySemaphore(RenderCore::Get().GetDevice(), m_render_finished_semaphores[i]);
|
||||||
|
DebugLog("Vulkan: render finished semaphore destroyed");
|
||||||
kvfDestroyCommandBuffer(RenderCore::Get().GetDevice(), m_cmd_buffers[i]);
|
kvfDestroyCommandBuffer(RenderCore::Get().GetDevice(), m_cmd_buffers[i]);
|
||||||
DebugLog("Vulkan: command buffer destroyed");
|
DebugLog("Vulkan: command buffer destroyed");
|
||||||
kvfDestroyFence(RenderCore::Get().GetDevice(), m_cmd_fences[i]);
|
kvfDestroyFence(RenderCore::Get().GetDevice(), m_cmd_fences[i]);
|
||||||
|
|||||||
Reference in New Issue
Block a user