mirror of
https://github.com/nicbarker/clay.git
synced 2025-04-18 12:18:03 +00:00
Compare commits
9 Commits
8182956478
...
595ff514a4
Author | SHA1 | Date | |
---|---|---|---|
|
595ff514a4 | ||
|
eb46688b82 | ||
|
87efc49f52 | ||
|
38bb241ced | ||
|
f4933c6669 | ||
|
61ba36753b | ||
|
4f4605eff9 | ||
|
7c65f31f46 | ||
|
01025e9157 |
7
.gitignore
vendored
7
.gitignore
vendored
@ -4,4 +4,9 @@ cmake-build-release/
|
||||
.idea/
|
||||
node_modules/
|
||||
*.dSYM
|
||||
.vs/
|
||||
.vs/
|
||||
bindings/odin/clay-odin/tmp/
|
||||
|
||||
generator/__pycache__/
|
||||
|
||||
generator/generators/__pycache__/
|
||||
|
@ -9,6 +9,8 @@ option(CLAY_INCLUDE_CPP_EXAMPLE "Build C++ example" OFF)
|
||||
option(CLAY_INCLUDE_RAYLIB_EXAMPLES "Build raylib examples" OFF)
|
||||
option(CLAY_INCLUDE_SDL2_EXAMPLES "Build SDL 2 examples" OFF)
|
||||
option(CLAY_INCLUDE_SDL3_EXAMPLES "Build SDL 3 examples" OFF)
|
||||
option(CLAY_INCLUDE_WIN32_GDI_EXAMPLES "Build Win32 GDI examples" OFF)
|
||||
option(CLAY_INCLUDE_SOKOL_EXAMPLES "Build Sokol examples" OFF)
|
||||
|
||||
message(STATUS "CLAY_INCLUDE_DEMOS: ${CLAY_INCLUDE_DEMOS}")
|
||||
|
||||
@ -36,6 +38,16 @@ endif ()
|
||||
if(NOT MSVC AND (CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_SDL3_EXAMPLES))
|
||||
add_subdirectory("examples/SDL3-simple-demo")
|
||||
endif()
|
||||
if(CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_SOKOL_EXAMPLES)
|
||||
add_subdirectory("examples/sokol-video-demo")
|
||||
add_subdirectory("examples/sokol-corner-radius")
|
||||
endif()
|
||||
|
||||
if(WIN32) # Build only for Win or Wine
|
||||
if(CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_WIN32_GDI_EXAMPLES)
|
||||
add_subdirectory("examples/win32_gdi")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# add_subdirectory("examples/cairo-pdf-rendering") Some issue with github actions populating cairo, disable for now
|
||||
|
||||
|
10
examples/sokol-corner-radius/CMakeLists.txt
Normal file
10
examples/sokol-corner-radius/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
cmake_minimum_required(VERSION 3.27)
|
||||
project(sokol_corner_radius C)
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL Windows)
|
||||
add_executable(sokol_corner_radius WIN32 main.c)
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT sokol_corner_radius)
|
||||
else()
|
||||
add_executable(sokol_corner_radius main.c)
|
||||
endif()
|
||||
target_link_libraries(sokol_corner_radius PUBLIC sokol)
|
110
examples/sokol-corner-radius/main.c
Normal file
110
examples/sokol-corner-radius/main.c
Normal file
@ -0,0 +1,110 @@
|
||||
#include "sokol_app.h"
|
||||
#include "sokol_gfx.h"
|
||||
#include "sokol_glue.h"
|
||||
#include "sokol_log.h"
|
||||
|
||||
#define CLAY_IMPLEMENTATION
|
||||
#include "../../clay.h"
|
||||
|
||||
#include "util/sokol_gl.h"
|
||||
#include "fontstash.h"
|
||||
#include "util/sokol_fontstash.h"
|
||||
#define SOKOL_CLAY_IMPL
|
||||
#include "../../renderers/sokol/sokol_clay.h"
|
||||
|
||||
static void init() {
|
||||
sg_setup(&(sg_desc){
|
||||
.environment = sglue_environment(),
|
||||
.logger.func = slog_func,
|
||||
});
|
||||
sgl_setup(&(sgl_desc_t){
|
||||
.logger.func = slog_func,
|
||||
});
|
||||
sclay_setup();
|
||||
uint64_t totalMemorySize = Clay_MinMemorySize();
|
||||
Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));
|
||||
Clay_Initialize(clayMemory, (Clay_Dimensions){ (float)sapp_width(), (float)sapp_height() }, (Clay_ErrorHandler){0});
|
||||
Clay_SetMeasureTextFunction(sclay_measure_text, NULL);
|
||||
}
|
||||
|
||||
Clay_RenderCommandArray CornerRadiusTest(){
|
||||
Clay_BeginLayout();
|
||||
Clay_Sizing layoutExpand = {
|
||||
.width = CLAY_SIZING_GROW(0),
|
||||
.height = CLAY_SIZING_GROW(0)
|
||||
};
|
||||
CLAY({ .id = CLAY_ID("OuterContainer"),
|
||||
.backgroundColor = {43, 41, 51, 255},
|
||||
.layout = {
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||
.sizing = layoutExpand,
|
||||
.padding = {0, 0, 20, 20},
|
||||
.childGap = 20
|
||||
}
|
||||
}) {
|
||||
for(int i = 0; i < 6; ++i){
|
||||
CLAY({ .id = CLAY_IDI("Row", i),
|
||||
.layout = {
|
||||
.layoutDirection = CLAY_LEFT_TO_RIGHT,
|
||||
.sizing = layoutExpand,
|
||||
.padding = {20, 20, 0, 0},
|
||||
.childGap = 20
|
||||
}
|
||||
}) {
|
||||
for(int j = 0; j < 6; ++j){
|
||||
CLAY({ .id = CLAY_IDI("Tile", i*6+j),
|
||||
.backgroundColor = {120, 140, 255, 128},
|
||||
.cornerRadius = {(i%3)*15, (j%3)*15, (i/2)*15, (j/2)*15},
|
||||
.border = {
|
||||
.color = {120, 140, 255, 255},
|
||||
.width = {3, 9, 6, 12, 0},
|
||||
},
|
||||
.layout = { .sizing = layoutExpand }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Clay_EndLayout();
|
||||
}
|
||||
|
||||
static void frame() {
|
||||
sclay_new_frame();
|
||||
Clay_RenderCommandArray renderCommands = CornerRadiusTest();
|
||||
|
||||
sg_begin_pass(&(sg_pass){ .swapchain = sglue_swapchain() });
|
||||
sgl_matrix_mode_modelview();
|
||||
sgl_load_identity();
|
||||
sclay_render(renderCommands, NULL);
|
||||
sgl_draw();
|
||||
sg_end_pass();
|
||||
sg_commit();
|
||||
}
|
||||
|
||||
static void event(const sapp_event *ev) {
|
||||
if(ev->type == SAPP_EVENTTYPE_KEY_DOWN && ev->key_code == SAPP_KEYCODE_D){
|
||||
Clay_SetDebugModeEnabled(true);
|
||||
} else {
|
||||
sclay_handle_event(ev);
|
||||
}
|
||||
}
|
||||
|
||||
static void cleanup() {
|
||||
sclay_shutdown();
|
||||
sgl_shutdown();
|
||||
sg_shutdown();
|
||||
}
|
||||
|
||||
sapp_desc sokol_main(int argc, char **argv) {
|
||||
return (sapp_desc){
|
||||
.init_cb = init,
|
||||
.frame_cb = frame,
|
||||
.event_cb = event,
|
||||
.cleanup_cb = cleanup,
|
||||
.window_title = "Clay - Corner Radius Test",
|
||||
.width = 800,
|
||||
.height = 600,
|
||||
.icon.sokol_default = true,
|
||||
.logger.func = slog_func,
|
||||
};
|
||||
}
|
71
examples/sokol-video-demo/CMakeLists.txt
Normal file
71
examples/sokol-video-demo/CMakeLists.txt
Normal file
@ -0,0 +1,71 @@
|
||||
cmake_minimum_required(VERSION 3.27)
|
||||
project(sokol_video_demo C)
|
||||
|
||||
include(FetchContent)
|
||||
set(FETCHCONTENT_QUIET FALSE)
|
||||
|
||||
# Linux -pthread shenanigans
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL Linux)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
endif()
|
||||
|
||||
FetchContent_Declare(
|
||||
fontstash
|
||||
GIT_REPOSITORY "https://github.com/memononen/fontstash.git"
|
||||
GIT_TAG "b5ddc9741061343740d85d636d782ed3e07cf7be"
|
||||
GIT_PROGRESS TRUE
|
||||
GIT_SHALLOW TRUE
|
||||
)
|
||||
FetchContent_MakeAvailable(fontstash)
|
||||
|
||||
FetchContent_Declare(
|
||||
sokol
|
||||
GIT_REPOSITORY "https://github.com/floooh/sokol.git"
|
||||
GIT_TAG "da9de496f938b7575eff7f01ab774d77469bd390"
|
||||
GIT_PROGRESS TRUE
|
||||
GIT_SHALLOW TRUE
|
||||
)
|
||||
FetchContent_MakeAvailable(sokol)
|
||||
set(sokol_HEADERS
|
||||
${sokol_SOURCE_DIR}/sokol_app.h
|
||||
${sokol_SOURCE_DIR}/sokol_gfx.h
|
||||
${sokol_SOURCE_DIR}/sokol_glue.h
|
||||
${sokol_SOURCE_DIR}/sokol_log.h
|
||||
${sokol_SOURCE_DIR}/util/sokol_gl.h
|
||||
${fontstash_SOURCE_DIR}/src/fontstash.h
|
||||
${sokol_SOURCE_DIR}/util/sokol_fontstash.h)
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL Darwin)
|
||||
add_library(sokol STATIC sokol.c ${sokol_HEADERS})
|
||||
target_compile_options(sokol PRIVATE -x objective-c)
|
||||
target_link_libraries(sokol PUBLIC
|
||||
"-framework QuartzCore"
|
||||
"-framework Cocoa"
|
||||
"-framework MetalKit"
|
||||
"-framework Metal")
|
||||
else()
|
||||
add_library(sokol STATIC sokol.c ${sokol_HEADERS})
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL Linux)
|
||||
target_compile_definitions(sokol PRIVATE SOKOL_GLCORE=1)
|
||||
target_link_libraries(sokol INTERFACE X11 Xi Xcursor GL dl m)
|
||||
target_link_libraries(sokol PUBLIC Threads::Threads)
|
||||
endif()
|
||||
endif()
|
||||
target_include_directories(sokol INTERFACE ${sokol_SOURCE_DIR} ${fontstash_SOURCE_DIR}/src
|
||||
PRIVATE ${sokol_SOURCE_DIR} ${fontstash_SOURCE_DIR}/src)
|
||||
|
||||
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL Windows)
|
||||
add_executable(sokol_video_demo WIN32 main.c)
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT sokol_video_demo)
|
||||
else()
|
||||
add_executable(sokol_video_demo main.c)
|
||||
endif()
|
||||
target_link_libraries(sokol_video_demo PUBLIC sokol)
|
||||
|
||||
add_custom_command(
|
||||
TARGET sokol_video_demo POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/resources
|
||||
${CMAKE_CURRENT_BINARY_DIR}/resources)
|
77
examples/sokol-video-demo/main.c
Normal file
77
examples/sokol-video-demo/main.c
Normal file
@ -0,0 +1,77 @@
|
||||
#include "sokol_app.h"
|
||||
#include "sokol_gfx.h"
|
||||
#include "sokol_glue.h"
|
||||
#include "sokol_log.h"
|
||||
|
||||
#define CLAY_IMPLEMENTATION
|
||||
#include "../../clay.h"
|
||||
|
||||
#include "util/sokol_gl.h"
|
||||
#include "fontstash.h"
|
||||
#include "util/sokol_fontstash.h"
|
||||
#define SOKOL_CLAY_IMPL
|
||||
#include "../../renderers/sokol/sokol_clay.h"
|
||||
|
||||
#include "../shared-layouts/clay-video-demo.c"
|
||||
|
||||
static ClayVideoDemo_Data demoData;
|
||||
static sclay_font_t fonts[1];
|
||||
|
||||
static void init() {
|
||||
sg_setup(&(sg_desc){
|
||||
.environment = sglue_environment(),
|
||||
.logger.func = slog_func,
|
||||
});
|
||||
sgl_setup(&(sgl_desc_t){
|
||||
.logger.func = slog_func,
|
||||
});
|
||||
sclay_setup();
|
||||
uint64_t totalMemorySize = Clay_MinMemorySize();
|
||||
Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));
|
||||
Clay_Initialize(clayMemory, (Clay_Dimensions){ (float)sapp_width(), (float)sapp_height() }, (Clay_ErrorHandler){0});
|
||||
fonts[FONT_ID_BODY_16] = sclay_add_font("resources/Roboto-Regular.ttf");
|
||||
Clay_SetMeasureTextFunction(sclay_measure_text, &fonts);
|
||||
demoData = ClayVideoDemo_Initialize();
|
||||
}
|
||||
|
||||
static void frame() {
|
||||
sclay_new_frame();
|
||||
Clay_RenderCommandArray renderCommands = ClayVideoDemo_CreateLayout(&demoData);
|
||||
|
||||
sg_begin_pass(&(sg_pass){ .swapchain = sglue_swapchain() });
|
||||
sgl_matrix_mode_modelview();
|
||||
sgl_load_identity();
|
||||
sclay_render(renderCommands, fonts);
|
||||
sgl_draw();
|
||||
sg_end_pass();
|
||||
sg_commit();
|
||||
}
|
||||
|
||||
static void event(const sapp_event *ev) {
|
||||
if(ev->type == SAPP_EVENTTYPE_KEY_DOWN && ev->key_code == SAPP_KEYCODE_D){
|
||||
Clay_SetDebugModeEnabled(true);
|
||||
} else {
|
||||
sclay_handle_event(ev);
|
||||
}
|
||||
}
|
||||
|
||||
static void cleanup() {
|
||||
sclay_shutdown();
|
||||
sgl_shutdown();
|
||||
sg_shutdown();
|
||||
}
|
||||
|
||||
sapp_desc sokol_main(int argc, char **argv) {
|
||||
return (sapp_desc){
|
||||
.init_cb = init,
|
||||
.frame_cb = frame,
|
||||
.event_cb = event,
|
||||
.cleanup_cb = cleanup,
|
||||
.window_title = "Clay - Sokol Renderer Example",
|
||||
.width = 800,
|
||||
.height = 600,
|
||||
.high_dpi = true,
|
||||
.icon.sokol_default = true,
|
||||
.logger.func = slog_func,
|
||||
};
|
||||
}
|
BIN
examples/sokol-video-demo/resources/Roboto-Regular.ttf
Normal file
BIN
examples/sokol-video-demo/resources/Roboto-Regular.ttf
Normal file
Binary file not shown.
24
examples/sokol-video-demo/sokol.c
Normal file
24
examples/sokol-video-demo/sokol.c
Normal file
@ -0,0 +1,24 @@
|
||||
#define SOKOL_IMPL
|
||||
#if defined(_WIN32)
|
||||
#define SOKOL_D3D11
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
#define SOKOL_GLES2
|
||||
#elif defined(__APPLE__)
|
||||
#define SOKOL_METAL
|
||||
#else
|
||||
#define SOKOL_GLCORE33
|
||||
#endif
|
||||
#include "sokol_app.h"
|
||||
#include "sokol_gfx.h"
|
||||
#include "sokol_time.h"
|
||||
#include "sokol_fetch.h"
|
||||
#include "sokol_glue.h"
|
||||
#include "sokol_log.h"
|
||||
|
||||
#include "util/sokol_gl.h"
|
||||
#include <stdio.h> // fontstash requires this
|
||||
#include <stdlib.h> // fontstash requires this
|
||||
#define FONTSTASH_IMPLEMENTATION
|
||||
#include "fontstash.h"
|
||||
#define SOKOL_FONTSTASH_IMPL
|
||||
#include "util/sokol_fontstash.h"
|
15
examples/win32_gdi/CMakeLists.txt
Normal file
15
examples/win32_gdi/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
cmake_minimum_required(VERSION 3.27)
|
||||
project(win32_gdi C)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
|
||||
add_executable(win32_gdi WIN32 main.c)
|
||||
|
||||
target_compile_options(win32_gdi PUBLIC)
|
||||
target_include_directories(win32_gdi PUBLIC .)
|
||||
|
||||
add_custom_command(
|
||||
TARGET win32_gdi POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/resources
|
||||
${CMAKE_CURRENT_BINARY_DIR}/resources)
|
@ -25,6 +25,14 @@ void CenterWindow(HWND hWnd);
|
||||
|
||||
long lastMsgTime = 0;
|
||||
bool ui_debug_mode;
|
||||
HFONT fonts[1];
|
||||
|
||||
#ifndef RECTWIDTH
|
||||
#define RECTWIDTH(rc) ((rc).right - (rc).left)
|
||||
#endif
|
||||
#ifndef RECTHEIGHT
|
||||
#define RECTHEIGHT(rc) ((rc).bottom - (rc).top)
|
||||
#endif
|
||||
|
||||
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
@ -113,7 +121,7 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
case WM_PAINT:
|
||||
{
|
||||
Clay_RenderCommandArray renderCommands = ClayVideoDemo_CreateLayout(&demo_data);
|
||||
Clay_Win32_Render(hwnd, renderCommands);
|
||||
Clay_Win32_Render(hwnd, renderCommands, fonts);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -151,7 +159,12 @@ int APIENTRY WinMain(
|
||||
uint64_t clayRequiredMemory = Clay_MinMemorySize();
|
||||
Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(clayRequiredMemory, malloc(clayRequiredMemory));
|
||||
Clay_Initialize(clayMemory, (Clay_Dimensions){.width = 800, .height = 600}, (Clay_ErrorHandler){HandleClayErrors}); // This final argument is new since the video was published
|
||||
Clay_SetMeasureTextFunction(Clay_Win32_MeasureText, NULL);
|
||||
|
||||
Clay_Win32_SetRendererFlags(CLAYGDI_RF_ALPHABLEND | CLAYGDI_RF_SMOOTHCORNERS);
|
||||
|
||||
// Initialize clay fonts and text drawing
|
||||
fonts[FONT_ID_BODY_16] = Clay_Win32_SimpleCreateFont("resources/Roboto-Regular.ttf", "Roboto", -11, FW_NORMAL);
|
||||
Clay_SetMeasureTextFunction(Clay_Win32_MeasureText, fonts);
|
||||
|
||||
ZeroMemory(&wc, sizeof wc);
|
||||
wc.hInstance = hInstance;
|
||||
@ -165,6 +178,10 @@ int APIENTRY WinMain(
|
||||
if (FALSE == RegisterClass(&wc))
|
||||
return 0;
|
||||
|
||||
// Calculate window rectangle by given client size
|
||||
// TODO: AdjustWindowRectExForDpi for DPI support
|
||||
RECT rcWindow = { .right = 800, .bottom = 600 };
|
||||
AdjustWindowRect(&rcWindow, WS_OVERLAPPEDWINDOW, FALSE);
|
||||
|
||||
hwnd = CreateWindow(
|
||||
szAppName,
|
||||
@ -172,8 +189,8 @@ int APIENTRY WinMain(
|
||||
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
800, // CW_USEDEFAULT,
|
||||
600, // CW_USEDEFAULT,
|
||||
RECTWIDTH(rcWindow), // CW_USEDEFAULT,
|
||||
RECTHEIGHT(rcWindow), // CW_USEDEFAULT,
|
||||
0,
|
||||
0,
|
||||
hInstance,
|
||||
|
BIN
examples/win32_gdi/resources/Roboto-Regular.ttf
Normal file
BIN
examples/win32_gdi/resources/Roboto-Regular.ttf
Normal file
Binary file not shown.
0
generator/__init__.py
Normal file
0
generator/__init__.py
Normal file
83
generator/cli.py
Normal file
83
generator/cli.py
Normal file
@ -0,0 +1,83 @@
|
||||
import argparse
|
||||
import logging
|
||||
import json
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from generators.base_generator import BaseGenerator
|
||||
from generators.odin_generator import OdinGenerator
|
||||
from parser import parse_headers
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
GeneratorMap = dict[str, type[BaseGenerator]]
|
||||
GENERATORS = {
|
||||
'odin': OdinGenerator,
|
||||
}
|
||||
|
||||
def main() -> None:
|
||||
arg_parser = argparse.ArgumentParser(description='Generate clay bindings')
|
||||
|
||||
# Directories
|
||||
arg_parser.add_argument('input_files', nargs='+', type=str, help='Input header files')
|
||||
arg_parser.add_argument('--output-dir', type=str, help='Output directory', required=True)
|
||||
arg_parser.add_argument('--tmp-dir', type=str, help='Temporary directory')
|
||||
|
||||
# Generators
|
||||
arg_parser.add_argument('--generator', type=str, choices=list(GENERATORS.keys()), help='Generators to run', required=True)
|
||||
|
||||
# Logging
|
||||
arg_parser.add_argument('--verbose', action='store_true', help='Verbose logging')
|
||||
|
||||
args = arg_parser.parse_args()
|
||||
|
||||
log_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
log_handler = logging.StreamHandler()
|
||||
log_handler.setFormatter(log_formatter)
|
||||
if args.verbose:
|
||||
logging.basicConfig(level=logging.DEBUG, handlers=[log_handler])
|
||||
else:
|
||||
logging.basicConfig(level=logging.INFO, handlers=[log_handler])
|
||||
|
||||
output_dir = Path(args.output_dir)
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if args.tmp_dir:
|
||||
tmp_dir = Path(args.tmp_dir)
|
||||
else:
|
||||
tmp_dir = output_dir / 'tmp'
|
||||
tmp_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
fake_libc_include_path = Path(__file__).parent / 'fake_libc_include'
|
||||
input_files = list(fake_libc_include_path.glob('*.h')) + [Path(f) for f in args.input_files]
|
||||
|
||||
logger.info(f'Input files: {input_files}')
|
||||
logger.info(f'Output directory: {output_dir}')
|
||||
logger.info(f'Temporary directory: {tmp_dir}')
|
||||
logger.info(f'Generator: {args.generator}')
|
||||
|
||||
logger.info('Parsing headers')
|
||||
extracted_symbols = parse_headers(input_files, tmp_dir)
|
||||
with open(tmp_dir / 'extracted_symbols.json', 'w') as f:
|
||||
f.write(json.dumps({
|
||||
'structs': extracted_symbols.structs,
|
||||
'enums': extracted_symbols.enums,
|
||||
'functions': extracted_symbols.functions,
|
||||
}, indent=2))
|
||||
|
||||
logger.info('Generating bindings')
|
||||
generator = GENERATORS[args.generator](extracted_symbols)
|
||||
generator.generate()
|
||||
logger.debug(f'Generated bindings:')
|
||||
# for file_name, content in generator.get_outputs().items():
|
||||
# logger.debug(f'{file_name}:')
|
||||
# logger.debug(content)
|
||||
# logger.debug('\n')
|
||||
|
||||
tmp_outputs_dir = tmp_dir / 'generated'
|
||||
tmp_outputs_dir.mkdir(parents=True, exist_ok=True)
|
||||
generator.write_outputs(tmp_outputs_dir)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
262
generator/fake_libc_include/_fake_defines.h
Normal file
262
generator/fake_libc_include/_fake_defines.h
Normal file
@ -0,0 +1,262 @@
|
||||
#ifndef _FAKE_DEFINES_H
|
||||
#define _FAKE_DEFINES_H
|
||||
|
||||
#define NULL 0
|
||||
#define BUFSIZ 1024
|
||||
#define FOPEN_MAX 20
|
||||
#define FILENAME_MAX 1024
|
||||
|
||||
#ifndef SEEK_SET
|
||||
#define SEEK_SET 0 /* set file offset to offset */
|
||||
#endif
|
||||
#ifndef SEEK_CUR
|
||||
#define SEEK_CUR 1 /* set file offset to current plus offset */
|
||||
#endif
|
||||
#ifndef SEEK_END
|
||||
#define SEEK_END 2 /* set file offset to EOF plus offset */
|
||||
#endif
|
||||
|
||||
#define __LITTLE_ENDIAN 1234
|
||||
#define LITTLE_ENDIAN __LITTLE_ENDIAN
|
||||
#define __BIG_ENDIAN 4321
|
||||
#define BIG_ENDIAN __BIG_ENDIAN
|
||||
#define __BYTE_ORDER __LITTLE_ENDIAN
|
||||
#define BYTE_ORDER __BYTE_ORDER
|
||||
|
||||
#define EXIT_FAILURE 1
|
||||
#define EXIT_SUCCESS 0
|
||||
|
||||
#define SCHAR_MIN -128
|
||||
#define SCHAR_MAX 127
|
||||
#define CHAR_MIN -128
|
||||
#define CHAR_MAX 127
|
||||
#define UCHAR_MAX 255
|
||||
#define SHRT_MIN -32768
|
||||
#define SHRT_MAX 32767
|
||||
#define USHRT_MAX 65535
|
||||
#define INT_MIN -2147483648
|
||||
#define INT_MAX 2147483647
|
||||
#define UINT_MAX 4294967295U
|
||||
#define LONG_MIN -9223372036854775808L
|
||||
#define LONG_MAX 9223372036854775807L
|
||||
#define ULONG_MAX 18446744073709551615UL
|
||||
#define RAND_MAX 32767
|
||||
|
||||
/* C99 inttypes.h defines */
|
||||
#define PRId8 "d"
|
||||
#define PRIi8 "i"
|
||||
#define PRIo8 "o"
|
||||
#define PRIu8 "u"
|
||||
#define PRIx8 "x"
|
||||
#define PRIX8 "X"
|
||||
#define PRId16 "d"
|
||||
#define PRIi16 "i"
|
||||
#define PRIo16 "o"
|
||||
#define PRIu16 "u"
|
||||
#define PRIx16 "x"
|
||||
#define PRIX16 "X"
|
||||
#define PRId32 "d"
|
||||
#define PRIi32 "i"
|
||||
#define PRIo32 "o"
|
||||
#define PRIu32 "u"
|
||||
#define PRIx32 "x"
|
||||
#define PRIX32 "X"
|
||||
#define PRId64 "d"
|
||||
#define PRIi64 "i"
|
||||
#define PRIo64 "o"
|
||||
#define PRIu64 "u"
|
||||
#define PRIx64 "x"
|
||||
#define PRIX64 "X"
|
||||
#define PRIdLEAST8 "d"
|
||||
#define PRIiLEAST8 "i"
|
||||
#define PRIoLEAST8 "o"
|
||||
#define PRIuLEAST8 "u"
|
||||
#define PRIxLEAST8 "x"
|
||||
#define PRIXLEAST8 "X"
|
||||
#define PRIdLEAST16 "d"
|
||||
#define PRIiLEAST16 "i"
|
||||
#define PRIoLEAST16 "o"
|
||||
#define PRIuLEAST16 "u"
|
||||
#define PRIxLEAST16 "x"
|
||||
#define PRIXLEAST16 "X"
|
||||
#define PRIdLEAST32 "d"
|
||||
#define PRIiLEAST32 "i"
|
||||
#define PRIoLEAST32 "o"
|
||||
#define PRIuLEAST32 "u"
|
||||
#define PRIxLEAST32 "x"
|
||||
#define PRIXLEAST32 "X"
|
||||
#define PRIdLEAST64 "d"
|
||||
#define PRIiLEAST64 "i"
|
||||
#define PRIoLEAST64 "o"
|
||||
#define PRIuLEAST64 "u"
|
||||
#define PRIxLEAST64 "x"
|
||||
#define PRIXLEAST64 "X"
|
||||
#define PRIdFAST8 "d"
|
||||
#define PRIiFAST8 "i"
|
||||
#define PRIoFAST8 "o"
|
||||
#define PRIuFAST8 "u"
|
||||
#define PRIxFAST8 "x"
|
||||
#define PRIXFAST8 "X"
|
||||
#define PRIdFAST16 "d"
|
||||
#define PRIiFAST16 "i"
|
||||
#define PRIoFAST16 "o"
|
||||
#define PRIuFAST16 "u"
|
||||
#define PRIxFAST16 "x"
|
||||
#define PRIXFAST16 "X"
|
||||
#define PRIdFAST32 "d"
|
||||
#define PRIiFAST32 "i"
|
||||
#define PRIoFAST32 "o"
|
||||
#define PRIuFAST32 "u"
|
||||
#define PRIxFAST32 "x"
|
||||
#define PRIXFAST32 "X"
|
||||
#define PRIdFAST64 "d"
|
||||
#define PRIiFAST64 "i"
|
||||
#define PRIoFAST64 "o"
|
||||
#define PRIuFAST64 "u"
|
||||
#define PRIxFAST64 "x"
|
||||
#define PRIXFAST64 "X"
|
||||
#define PRIdPTR "d"
|
||||
#define PRIiPTR "i"
|
||||
#define PRIoPTR "o"
|
||||
#define PRIuPTR "u"
|
||||
#define PRIxPTR "x"
|
||||
#define PRIXPTR "X"
|
||||
#define PRIdMAX "d"
|
||||
#define PRIiMAX "i"
|
||||
#define PRIoMAX "o"
|
||||
#define PRIuMAX "u"
|
||||
#define PRIxMAX "x"
|
||||
#define PRIXMAX "X"
|
||||
#define SCNd8 "d"
|
||||
#define SCNi8 "i"
|
||||
#define SCNo8 "o"
|
||||
#define SCNu8 "u"
|
||||
#define SCNx8 "x"
|
||||
#define SCNd16 "d"
|
||||
#define SCNi16 "i"
|
||||
#define SCNo16 "o"
|
||||
#define SCNu16 "u"
|
||||
#define SCNx16 "x"
|
||||
#define SCNd32 "d"
|
||||
#define SCNi32 "i"
|
||||
#define SCNo32 "o"
|
||||
#define SCNu32 "u"
|
||||
#define SCNx32 "x"
|
||||
#define SCNd64 "d"
|
||||
#define SCNi64 "i"
|
||||
#define SCNo64 "o"
|
||||
#define SCNu64 "u"
|
||||
#define SCNx64 "x"
|
||||
#define SCNdLEAST8 "d"
|
||||
#define SCNiLEAST8 "i"
|
||||
#define SCNoLEAST8 "o"
|
||||
#define SCNuLEAST8 "u"
|
||||
#define SCNxLEAST8 "x"
|
||||
#define SCNdLEAST16 "d"
|
||||
#define SCNiLEAST16 "i"
|
||||
#define SCNoLEAST16 "o"
|
||||
#define SCNuLEAST16 "u"
|
||||
#define SCNxLEAST16 "x"
|
||||
#define SCNdLEAST32 "d"
|
||||
#define SCNiLEAST32 "i"
|
||||
#define SCNoLEAST32 "o"
|
||||
#define SCNuLEAST32 "u"
|
||||
#define SCNxLEAST32 "x"
|
||||
#define SCNdLEAST64 "d"
|
||||
#define SCNiLEAST64 "i"
|
||||
#define SCNoLEAST64 "o"
|
||||
#define SCNuLEAST64 "u"
|
||||
#define SCNxLEAST64 "x"
|
||||
#define SCNdFAST8 "d"
|
||||
#define SCNiFAST8 "i"
|
||||
#define SCNoFAST8 "o"
|
||||
#define SCNuFAST8 "u"
|
||||
#define SCNxFAST8 "x"
|
||||
#define SCNdFAST16 "d"
|
||||
#define SCNiFAST16 "i"
|
||||
#define SCNoFAST16 "o"
|
||||
#define SCNuFAST16 "u"
|
||||
#define SCNxFAST16 "x"
|
||||
#define SCNdFAST32 "d"
|
||||
#define SCNiFAST32 "i"
|
||||
#define SCNoFAST32 "o"
|
||||
#define SCNuFAST32 "u"
|
||||
#define SCNxFAST32 "x"
|
||||
#define SCNdFAST64 "d"
|
||||
#define SCNiFAST64 "i"
|
||||
#define SCNoFAST64 "o"
|
||||
#define SCNuFAST64 "u"
|
||||
#define SCNxFAST64 "x"
|
||||
#define SCNdPTR "d"
|
||||
#define SCNiPTR "i"
|
||||
#define SCNoPTR "o"
|
||||
#define SCNuPTR "u"
|
||||
#define SCNxPTR "x"
|
||||
#define SCNdMAX "d"
|
||||
#define SCNiMAX "i"
|
||||
#define SCNoMAX "o"
|
||||
#define SCNuMAX "u"
|
||||
#define SCNxMAX "x"
|
||||
|
||||
/* C99 stdbool.h defines */
|
||||
#define __bool_true_false_are_defined 1
|
||||
#define false 0
|
||||
#define true 1
|
||||
|
||||
/* va_arg macros and type*/
|
||||
#define va_start(_ap, _type) __builtin_va_start((_ap))
|
||||
#define va_arg(_ap, _type) __builtin_va_arg((_ap))
|
||||
#define va_end(_list)
|
||||
|
||||
/* Vectors */
|
||||
#define __m128 int
|
||||
#define __m128_u int
|
||||
#define __m128d int
|
||||
#define __m128d_u int
|
||||
#define __m128i int
|
||||
#define __m128i_u int
|
||||
#define __m256 int
|
||||
#define __m256_u int
|
||||
#define __m256d int
|
||||
#define __m256d_u int
|
||||
#define __m256i int
|
||||
#define __m256i_u int
|
||||
#define __m512 int
|
||||
#define __m512_u int
|
||||
#define __m512d int
|
||||
#define __m512d_u int
|
||||
#define __m512i int
|
||||
#define __m512i_u int
|
||||
|
||||
/* C11 stdnoreturn.h defines */
|
||||
#define __noreturn_is_defined 1
|
||||
#define noreturn _Noreturn
|
||||
|
||||
/* C11 threads.h defines */
|
||||
#define thread_local _Thread_local
|
||||
|
||||
/* C11 assert.h defines */
|
||||
#define static_assert _Static_assert
|
||||
|
||||
/* C11 stdatomic.h defines */
|
||||
#define ATOMIC_BOOL_LOCK_FREE 0
|
||||
#define ATOMIC_CHAR_LOCK_FREE 0
|
||||
#define ATOMIC_CHAR16_T_LOCK_FREE 0
|
||||
#define ATOMIC_CHAR32_T_LOCK_FREE 0
|
||||
#define ATOMIC_WCHAR_T_LOCK_FREE 0
|
||||
#define ATOMIC_SHORT_LOCK_FREE 0
|
||||
#define ATOMIC_INT_LOCK_FREE 0
|
||||
#define ATOMIC_LONG_LOCK_FREE 0
|
||||
#define ATOMIC_LLONG_LOCK_FREE 0
|
||||
#define ATOMIC_POINTER_LOCK_FREE 0
|
||||
#define ATOMIC_VAR_INIT(value) (value)
|
||||
#define ATOMIC_FLAG_INIT { 0 }
|
||||
#define kill_dependency(y) (y)
|
||||
|
||||
/* C11 stdalign.h defines */
|
||||
#define alignas _Alignas
|
||||
#define alignof _Alignof
|
||||
#define __alignas_is_defined 1
|
||||
#define __alignof_is_defined 1
|
||||
|
||||
#endif
|
222
generator/fake_libc_include/_fake_typedefs.h
Normal file
222
generator/fake_libc_include/_fake_typedefs.h
Normal file
@ -0,0 +1,222 @@
|
||||
#ifndef _FAKE_TYPEDEFS_H
|
||||
#define _FAKE_TYPEDEFS_H
|
||||
|
||||
typedef int size_t;
|
||||
typedef int __builtin_va_list;
|
||||
typedef int __gnuc_va_list;
|
||||
typedef int va_list;
|
||||
typedef int __int8_t;
|
||||
typedef int __uint8_t;
|
||||
typedef int __int16_t;
|
||||
typedef int __uint16_t;
|
||||
typedef int __int_least16_t;
|
||||
typedef int __uint_least16_t;
|
||||
typedef int __int32_t;
|
||||
typedef int __uint32_t;
|
||||
typedef int __int64_t;
|
||||
typedef int __uint64_t;
|
||||
typedef int __int_least32_t;
|
||||
typedef int __uint_least32_t;
|
||||
typedef int __s8;
|
||||
typedef int __u8;
|
||||
typedef int __s16;
|
||||
typedef int __u16;
|
||||
typedef int __s32;
|
||||
typedef int __u32;
|
||||
typedef int __s64;
|
||||
typedef int __u64;
|
||||
typedef int _LOCK_T;
|
||||
typedef int _LOCK_RECURSIVE_T;
|
||||
typedef int _off_t;
|
||||
typedef int __dev_t;
|
||||
typedef int __uid_t;
|
||||
typedef int __gid_t;
|
||||
typedef int _off64_t;
|
||||
typedef int _fpos_t;
|
||||
typedef int _ssize_t;
|
||||
typedef int wint_t;
|
||||
typedef int _mbstate_t;
|
||||
typedef int _flock_t;
|
||||
typedef int _iconv_t;
|
||||
typedef int __ULong;
|
||||
typedef int __FILE;
|
||||
typedef int ptrdiff_t;
|
||||
typedef int wchar_t;
|
||||
typedef int char16_t;
|
||||
typedef int char32_t;
|
||||
typedef int __off_t;
|
||||
typedef int __pid_t;
|
||||
typedef int __loff_t;
|
||||
typedef int u_char;
|
||||
typedef int u_short;
|
||||
typedef int u_int;
|
||||
typedef int u_long;
|
||||
typedef int ushort;
|
||||
typedef int uint;
|
||||
typedef int clock_t;
|
||||
typedef int time_t;
|
||||
typedef int daddr_t;
|
||||
typedef int caddr_t;
|
||||
typedef int ino_t;
|
||||
typedef int off_t;
|
||||
typedef int dev_t;
|
||||
typedef int uid_t;
|
||||
typedef int gid_t;
|
||||
typedef int pid_t;
|
||||
typedef int key_t;
|
||||
typedef int ssize_t;
|
||||
typedef int mode_t;
|
||||
typedef int nlink_t;
|
||||
typedef int fd_mask;
|
||||
typedef int _types_fd_set;
|
||||
typedef int clockid_t;
|
||||
typedef int timer_t;
|
||||
typedef int useconds_t;
|
||||
typedef int suseconds_t;
|
||||
typedef int FILE;
|
||||
typedef int fpos_t;
|
||||
typedef int cookie_read_function_t;
|
||||
typedef int cookie_write_function_t;
|
||||
typedef int cookie_seek_function_t;
|
||||
typedef int cookie_close_function_t;
|
||||
typedef int cookie_io_functions_t;
|
||||
typedef int div_t;
|
||||
typedef int ldiv_t;
|
||||
typedef int lldiv_t;
|
||||
typedef int sigset_t;
|
||||
typedef int __sigset_t;
|
||||
typedef int _sig_func_ptr;
|
||||
typedef int sig_atomic_t;
|
||||
typedef int __tzrule_type;
|
||||
typedef int __tzinfo_type;
|
||||
typedef int mbstate_t;
|
||||
typedef int sem_t;
|
||||
typedef int pthread_t;
|
||||
typedef int pthread_attr_t;
|
||||
typedef int pthread_mutex_t;
|
||||
typedef int pthread_mutexattr_t;
|
||||
typedef int pthread_cond_t;
|
||||
typedef int pthread_condattr_t;
|
||||
typedef int pthread_key_t;
|
||||
typedef int pthread_once_t;
|
||||
typedef int pthread_rwlock_t;
|
||||
typedef int pthread_rwlockattr_t;
|
||||
typedef int pthread_spinlock_t;
|
||||
typedef int pthread_barrier_t;
|
||||
typedef int pthread_barrierattr_t;
|
||||
typedef int jmp_buf;
|
||||
typedef int rlim_t;
|
||||
typedef int sa_family_t;
|
||||
typedef int sigjmp_buf;
|
||||
typedef int stack_t;
|
||||
typedef int siginfo_t;
|
||||
typedef int z_stream;
|
||||
|
||||
/* C99 exact-width integer types */
|
||||
typedef int int8_t;
|
||||
typedef int uint8_t;
|
||||
typedef int int16_t;
|
||||
typedef int uint16_t;
|
||||
typedef int int32_t;
|
||||
typedef int uint32_t;
|
||||
typedef int int64_t;
|
||||
typedef int uint64_t;
|
||||
|
||||
/* C99 minimum-width integer types */
|
||||
typedef int int_least8_t;
|
||||
typedef int uint_least8_t;
|
||||
typedef int int_least16_t;
|
||||
typedef int uint_least16_t;
|
||||
typedef int int_least32_t;
|
||||
typedef int uint_least32_t;
|
||||
typedef int int_least64_t;
|
||||
typedef int uint_least64_t;
|
||||
|
||||
/* C99 fastest minimum-width integer types */
|
||||
typedef int int_fast8_t;
|
||||
typedef int uint_fast8_t;
|
||||
typedef int int_fast16_t;
|
||||
typedef int uint_fast16_t;
|
||||
typedef int int_fast32_t;
|
||||
typedef int uint_fast32_t;
|
||||
typedef int int_fast64_t;
|
||||
typedef int uint_fast64_t;
|
||||
|
||||
/* C99 integer types capable of holding object pointers */
|
||||
typedef int intptr_t;
|
||||
typedef int uintptr_t;
|
||||
|
||||
/* C99 greatest-width integer types */
|
||||
typedef int intmax_t;
|
||||
typedef int uintmax_t;
|
||||
|
||||
/* C99 stdbool.h bool type. _Bool is built-in in C99 */
|
||||
typedef _Bool bool;
|
||||
|
||||
/* Mir typedefs */
|
||||
typedef void* MirEGLNativeWindowType;
|
||||
typedef void* MirEGLNativeDisplayType;
|
||||
typedef struct MirConnection MirConnection;
|
||||
typedef struct MirSurface MirSurface;
|
||||
typedef struct MirSurfaceSpec MirSurfaceSpec;
|
||||
typedef struct MirScreencast MirScreencast;
|
||||
typedef struct MirPromptSession MirPromptSession;
|
||||
typedef struct MirBufferStream MirBufferStream;
|
||||
typedef struct MirPersistentId MirPersistentId;
|
||||
typedef struct MirBlob MirBlob;
|
||||
typedef struct MirDisplayConfig MirDisplayConfig;
|
||||
|
||||
/* xcb typedefs */
|
||||
typedef struct xcb_connection_t xcb_connection_t;
|
||||
typedef uint32_t xcb_window_t;
|
||||
typedef uint32_t xcb_visualid_t;
|
||||
|
||||
/* C11 stdatomic.h types */
|
||||
typedef _Atomic(_Bool) atomic_bool;
|
||||
typedef _Atomic(char) atomic_char;
|
||||
typedef _Atomic(signed char) atomic_schar;
|
||||
typedef _Atomic(unsigned char) atomic_uchar;
|
||||
typedef _Atomic(short) atomic_short;
|
||||
typedef _Atomic(unsigned short) atomic_ushort;
|
||||
typedef _Atomic(int) atomic_int;
|
||||
typedef _Atomic(unsigned int) atomic_uint;
|
||||
typedef _Atomic(long) atomic_long;
|
||||
typedef _Atomic(unsigned long) atomic_ulong;
|
||||
typedef _Atomic(long long) atomic_llong;
|
||||
typedef _Atomic(unsigned long long) atomic_ullong;
|
||||
typedef _Atomic(uint_least16_t) atomic_char16_t;
|
||||
typedef _Atomic(uint_least32_t) atomic_char32_t;
|
||||
typedef _Atomic(wchar_t) atomic_wchar_t;
|
||||
typedef _Atomic(int_least8_t) atomic_int_least8_t;
|
||||
typedef _Atomic(uint_least8_t) atomic_uint_least8_t;
|
||||
typedef _Atomic(int_least16_t) atomic_int_least16_t;
|
||||
typedef _Atomic(uint_least16_t) atomic_uint_least16_t;
|
||||
typedef _Atomic(int_least32_t) atomic_int_least32_t;
|
||||
typedef _Atomic(uint_least32_t) atomic_uint_least32_t;
|
||||
typedef _Atomic(int_least64_t) atomic_int_least64_t;
|
||||
typedef _Atomic(uint_least64_t) atomic_uint_least64_t;
|
||||
typedef _Atomic(int_fast8_t) atomic_int_fast8_t;
|
||||
typedef _Atomic(uint_fast8_t) atomic_uint_fast8_t;
|
||||
typedef _Atomic(int_fast16_t) atomic_int_fast16_t;
|
||||
typedef _Atomic(uint_fast16_t) atomic_uint_fast16_t;
|
||||
typedef _Atomic(int_fast32_t) atomic_int_fast32_t;
|
||||
typedef _Atomic(uint_fast32_t) atomic_uint_fast32_t;
|
||||
typedef _Atomic(int_fast64_t) atomic_int_fast64_t;
|
||||
typedef _Atomic(uint_fast64_t) atomic_uint_fast64_t;
|
||||
typedef _Atomic(intptr_t) atomic_intptr_t;
|
||||
typedef _Atomic(uintptr_t) atomic_uintptr_t;
|
||||
typedef _Atomic(size_t) atomic_size_t;
|
||||
typedef _Atomic(ptrdiff_t) atomic_ptrdiff_t;
|
||||
typedef _Atomic(intmax_t) atomic_intmax_t;
|
||||
typedef _Atomic(uintmax_t) atomic_uintmax_t;
|
||||
typedef struct atomic_flag { atomic_bool _Value; } atomic_flag;
|
||||
typedef enum memory_order {
|
||||
memory_order_relaxed,
|
||||
memory_order_consume,
|
||||
memory_order_acquire,
|
||||
memory_order_release,
|
||||
memory_order_acq_rel,
|
||||
memory_order_seq_cst
|
||||
} memory_order;
|
||||
|
||||
#endif
|
2
generator/fake_libc_include/_syslist.h
Normal file
2
generator/fake_libc_include/_syslist.h
Normal file
@ -0,0 +1,2 @@
|
||||
#include "_fake_defines.h"
|
||||
#include "_fake_typedefs.h"
|
2
generator/fake_libc_include/stdbool.h
Normal file
2
generator/fake_libc_include/stdbool.h
Normal file
@ -0,0 +1,2 @@
|
||||
#include "_fake_defines.h"
|
||||
#include "_fake_typedefs.h"
|
2
generator/fake_libc_include/stddef.h
Normal file
2
generator/fake_libc_include/stddef.h
Normal file
@ -0,0 +1,2 @@
|
||||
#include "_fake_defines.h"
|
||||
#include "_fake_typedefs.h"
|
2
generator/fake_libc_include/stdint.h
Normal file
2
generator/fake_libc_include/stdint.h
Normal file
@ -0,0 +1,2 @@
|
||||
#include "_fake_defines.h"
|
||||
#include "_fake_typedefs.h"
|
5
generator/gen_repo_bindings.sh
Normal file
5
generator/gen_repo_bindings.sh
Normal file
@ -0,0 +1,5 @@
|
||||
#!/usr/bin/bash
|
||||
REPO_ROOT=$(realpath $(dirname $(dirname $0)))
|
||||
|
||||
# Generate odin bindings
|
||||
python $REPO_ROOT/generator/cli.py $REPO_ROOT/clay.h --output-dir $REPO_ROOT/bindings/odin/clay-odin --generator odin --verbose
|
0
generator/generators/__init__.py
Normal file
0
generator/generators/__init__.py
Normal file
47
generator/generators/base_generator.py
Normal file
47
generator/generators/base_generator.py
Normal file
@ -0,0 +1,47 @@
|
||||
|
||||
from parser import ExtractedSymbols, ExtractedEnum, ExtractedStruct, ExtractedFunction
|
||||
from typing import Any, Callable, DefaultDict, Literal, NotRequired, Optional, TypedDict
|
||||
from pathlib import Path
|
||||
from dataclasses import dataclass
|
||||
|
||||
SymbolType = Literal['enum', 'struct', 'function']
|
||||
|
||||
class BaseGenerator:
|
||||
def __init__(self, extracted_symbols: ExtractedSymbols):
|
||||
self.extracted_symbols = extracted_symbols
|
||||
self.output_content: dict[str, list[str]] = dict()
|
||||
|
||||
def generate(self) -> None:
|
||||
pass
|
||||
|
||||
def has_symbol(self, symbol: str) -> bool:
|
||||
return (
|
||||
symbol in self.extracted_symbols.enums or
|
||||
symbol in self.extracted_symbols.structs or
|
||||
symbol in self.extracted_symbols.functions
|
||||
)
|
||||
|
||||
def get_symbol_type(self, symbol: str) -> SymbolType:
|
||||
if symbol in self.extracted_symbols.enums:
|
||||
return 'enum'
|
||||
elif symbol in self.extracted_symbols.structs:
|
||||
return 'struct'
|
||||
elif symbol in self.extracted_symbols.functions:
|
||||
return 'function'
|
||||
raise ValueError(f'Unknown symbol: {symbol}')
|
||||
|
||||
def _write(self, file_name: str, content: str) -> None:
|
||||
if file_name not in self.output_content:
|
||||
self.output_content[file_name] = []
|
||||
self.output_content[file_name].append(content)
|
||||
|
||||
def write_outputs(self, output_dir: Path) -> None:
|
||||
for file_name, content in self.output_content.items():
|
||||
(output_dir / file_name).parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(output_dir / file_name, 'w') as f:
|
||||
f.write("\n".join(content))
|
||||
|
||||
def get_outputs(self) -> dict[str, str]:
|
||||
return {file_name: "\n".join(content) for file_name, content in self.output_content.items()}
|
||||
|
||||
|
164
generator/generators/odin/clay.template.odin
Normal file
164
generator/generators/odin/clay.template.odin
Normal file
@ -0,0 +1,164 @@
|
||||
package clay
|
||||
|
||||
import "core:c"
|
||||
import "core:strings"
|
||||
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import Clay "windows/clay.lib"
|
||||
} else when ODIN_OS == .Linux {
|
||||
foreign import Clay "linux/clay.a"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
when ODIN_ARCH == .arm64 {
|
||||
foreign import Clay "macos-arm64/clay.a"
|
||||
} else {
|
||||
foreign import Clay "macos/clay.a"
|
||||
}
|
||||
} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
|
||||
foreign import Clay "wasm/clay.o"
|
||||
}
|
||||
|
||||
when ODIN_OS == .Windows {
|
||||
EnumBackingType :: u32
|
||||
} else {
|
||||
EnumBackingType :: u8
|
||||
}
|
||||
|
||||
{{enums}}
|
||||
|
||||
Context :: struct {
|
||||
|
||||
}
|
||||
|
||||
ClayArray :: struct($type: typeid) {
|
||||
capacity: i32,
|
||||
length: i32,
|
||||
internalArray: [^]type,
|
||||
}
|
||||
|
||||
SizingConstraints :: struct #raw_union {
|
||||
sizeMinMax: SizingConstraintsMinMax,
|
||||
sizePercent: c.float,
|
||||
}
|
||||
|
||||
TypedConfig :: struct {
|
||||
type: ElementConfigType,
|
||||
config: rawptr,
|
||||
id: ElementId,
|
||||
}
|
||||
|
||||
{{structs}}
|
||||
|
||||
@(link_prefix = "Clay_", default_calling_convention = "c")
|
||||
foreign Clay {
|
||||
{{public_functions}}
|
||||
}
|
||||
|
||||
@(link_prefix = "Clay_", default_calling_convention = "c", private)
|
||||
foreign Clay {
|
||||
{{private_functions}}
|
||||
}
|
||||
|
||||
@(require_results, deferred_none = _CloseElement)
|
||||
UI :: proc(configs: ..TypedConfig) -> bool {
|
||||
_OpenElement()
|
||||
for config in configs {
|
||||
#partial switch (config.type) {
|
||||
case ElementConfigType.Id:
|
||||
_AttachId(config.id)
|
||||
case ElementConfigType.Layout:
|
||||
_AttachLayoutConfig(cast(^LayoutConfig)config.config)
|
||||
case:
|
||||
_AttachElementConfig(config.config, config.type)
|
||||
}
|
||||
}
|
||||
_ElementPostConfiguration()
|
||||
return true
|
||||
}
|
||||
|
||||
Layout :: proc(config: LayoutConfig) -> TypedConfig {
|
||||
return {type = ElementConfigType.Layout, config = _StoreLayoutConfig(config) }
|
||||
}
|
||||
|
||||
PaddingAll :: proc (padding: u16) -> Padding {
|
||||
return { padding, padding, padding, padding }
|
||||
}
|
||||
|
||||
Rectangle :: proc(config: RectangleElementConfig) -> TypedConfig {
|
||||
return {type = ElementConfigType.Rectangle, config = _StoreRectangleElementConfig(config)}
|
||||
}
|
||||
|
||||
Text :: proc(text: string, config: ^TextElementConfig) {
|
||||
_OpenTextElement(MakeString(text), config)
|
||||
}
|
||||
|
||||
TextConfig :: proc(config: TextElementConfig) -> ^TextElementConfig {
|
||||
return _StoreTextElementConfig(config)
|
||||
}
|
||||
|
||||
Image :: proc(config: ImageElementConfig) -> TypedConfig {
|
||||
return {type = ElementConfigType.Image, config = _StoreImageElementConfig(config)}
|
||||
}
|
||||
|
||||
Floating :: proc(config: FloatingElementConfig) -> TypedConfig {
|
||||
return {type = ElementConfigType.Floating, config = _StoreFloatingElementConfig(config)}
|
||||
}
|
||||
|
||||
Custom :: proc(config: CustomElementConfig) -> TypedConfig {
|
||||
return {type = ElementConfigType.Custom, config = _StoreCustomElementConfig(config)}
|
||||
}
|
||||
|
||||
Scroll :: proc(config: ScrollElementConfig) -> TypedConfig {
|
||||
return {type = ElementConfigType.Scroll, config = _StoreScrollElementConfig(config)}
|
||||
}
|
||||
|
||||
Border :: proc(config: BorderElementConfig) -> TypedConfig {
|
||||
return {type = ElementConfigType.Border, config = _StoreBorderElementConfig(config)}
|
||||
}
|
||||
|
||||
BorderOutside :: proc(outsideBorders: BorderData) -> TypedConfig {
|
||||
return { type = ElementConfigType.Border, config = _StoreBorderElementConfig((BorderElementConfig){left = outsideBorders, right = outsideBorders, top = outsideBorders, bottom = outsideBorders}) }
|
||||
}
|
||||
|
||||
BorderOutsideRadius :: proc(outsideBorders: BorderData, radius: f32) -> TypedConfig {
|
||||
return { type = ElementConfigType.Border, config = _StoreBorderElementConfig(
|
||||
(BorderElementConfig){left = outsideBorders, right = outsideBorders, top = outsideBorders, bottom = outsideBorders, cornerRadius = {radius, radius, radius, radius}},
|
||||
) }
|
||||
}
|
||||
|
||||
BorderAll :: proc(allBorders: BorderData) -> TypedConfig {
|
||||
return { type = ElementConfigType.Border, config = _StoreBorderElementConfig((BorderElementConfig){left = allBorders, right = allBorders, top = allBorders, bottom = allBorders, betweenChildren = allBorders}) }
|
||||
}
|
||||
|
||||
BorderAllRadius :: proc(allBorders: BorderData, radius: f32) -> TypedConfig {
|
||||
return { type = ElementConfigType.Border, config = _StoreBorderElementConfig(
|
||||
(BorderElementConfig){left = allBorders, right = allBorders, top = allBorders, bottom = allBorders, cornerRadius = {radius, radius, radius, radius}},
|
||||
) }
|
||||
}
|
||||
|
||||
CornerRadiusAll :: proc(radius: f32) -> CornerRadius {
|
||||
return CornerRadius{radius, radius, radius, radius}
|
||||
}
|
||||
|
||||
SizingFit :: proc(sizeMinMax: SizingConstraintsMinMax) -> SizingAxis {
|
||||
return SizingAxis{type = SizingType.FIT, constraints = {sizeMinMax = sizeMinMax}}
|
||||
}
|
||||
|
||||
SizingGrow :: proc(sizeMinMax: SizingConstraintsMinMax) -> SizingAxis {
|
||||
return SizingAxis{type = SizingType.GROW, constraints = {sizeMinMax = sizeMinMax}}
|
||||
}
|
||||
|
||||
SizingFixed :: proc(size: c.float) -> SizingAxis {
|
||||
return SizingAxis{type = SizingType.FIXED, constraints = {sizeMinMax = {size, size}}}
|
||||
}
|
||||
|
||||
SizingPercent :: proc(sizePercent: c.float) -> SizingAxis {
|
||||
return SizingAxis{type = SizingType.PERCENT, constraints = {sizePercent = sizePercent}}
|
||||
}
|
||||
|
||||
MakeString :: proc(label: string) -> String {
|
||||
return String{chars = raw_data(label), length = cast(c.int)len(label)}
|
||||
}
|
||||
|
||||
ID :: proc(label: string, index: u32 = 0) -> TypedConfig {
|
||||
return { type = ElementConfigType.Id, id = _HashString(MakeString(label), index, 0) }
|
||||
}
|
315
generator/generators/odin_generator.py
Normal file
315
generator/generators/odin_generator.py
Normal file
@ -0,0 +1,315 @@
|
||||
from pathlib import Path
|
||||
import logging
|
||||
|
||||
from parser import ExtractedSymbolType
|
||||
from generators.base_generator import BaseGenerator
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def get_common_prefix(keys: list[str]) -> str:
|
||||
# find a prefix that's shared between all keys
|
||||
prefix = ""
|
||||
for i in range(min(map(len, keys))):
|
||||
if len(set(key[i] for key in keys)) > 1:
|
||||
break
|
||||
prefix += keys[0][i]
|
||||
return prefix
|
||||
|
||||
def snake_case_to_pascal_case(snake_case: str) -> str:
|
||||
return ''.join(word.lower().capitalize() for word in snake_case.split('_'))
|
||||
|
||||
|
||||
SYMBOL_NAME_OVERRIDES = {
|
||||
'Clay_TextElementConfigWrapMode': 'TextWrapMode',
|
||||
'Clay_Border': 'BorderData',
|
||||
'Clay_SizingMinMax': 'SizingConstraintsMinMax',
|
||||
}
|
||||
SYMBOL_COMPLETE_OVERRIDES = {
|
||||
'Clay_RenderCommandArray': 'ClayArray(RenderCommand)',
|
||||
'Clay_Context': 'Context',
|
||||
'Clay_ElementConfig': None,
|
||||
# 'Clay_SetQueryScrollOffsetFunction': None,
|
||||
}
|
||||
|
||||
# These enums should have output binding members that are PascalCase instead of UPPER_SNAKE_CASE.
|
||||
ENUM_MEMBER_PASCAL = {
|
||||
'Clay_RenderCommandType',
|
||||
'Clay_TextElementConfigWrapMode',
|
||||
'Clay__ElementConfigType',
|
||||
}
|
||||
ENUM_MEMBER_OVERRIDES = {
|
||||
'Clay__ElementConfigType': {
|
||||
'CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER': 'Border',
|
||||
'CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER': 'Floating',
|
||||
'CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER': 'Scroll',
|
||||
}
|
||||
}
|
||||
ENUM_ADDITIONAL_MEMBERS = {
|
||||
'Clay__ElementConfigType': {
|
||||
'Id': 65,
|
||||
'Layout': 66,
|
||||
}
|
||||
}
|
||||
|
||||
TYPE_MAPPING = {
|
||||
'*char': '[^]c.char',
|
||||
'const *char': '[^]c.char',
|
||||
'*void': 'rawptr',
|
||||
'bool': 'bool',
|
||||
'float': 'c.float',
|
||||
'uint16_t': 'u16',
|
||||
'uint32_t': 'u32',
|
||||
'int32_t': 'c.int32_t',
|
||||
'uintptr_t': 'rawptr',
|
||||
'intptr_t': 'rawptr',
|
||||
'void': 'void',
|
||||
}
|
||||
STRUCT_TYPE_OVERRIDES = {
|
||||
'Clay_Arena': {
|
||||
'nextAllocation': 'uintptr',
|
||||
'capacity': 'uintptr',
|
||||
},
|
||||
'Clay_SizingAxis': {
|
||||
'size': 'SizingConstraints',
|
||||
},
|
||||
"Clay_RenderCommand": {
|
||||
"zIndex": 'i32',
|
||||
},
|
||||
}
|
||||
STRUCT_MEMBER_OVERRIDES = {
|
||||
'Clay_ErrorHandler': {
|
||||
'errorHandlerFunction': 'handler',
|
||||
},
|
||||
'Clay_SizingAxis': {
|
||||
'size': 'constraints',
|
||||
},
|
||||
}
|
||||
STRUCT_OVERRIDE_AS_FIXED_ARRAY = {
|
||||
'Clay_Color',
|
||||
'Clay_Vector2',
|
||||
}
|
||||
|
||||
FUNCTION_PARAM_OVERRIDES = {
|
||||
'Clay_SetCurrentContext': {
|
||||
'context': 'ctx',
|
||||
},
|
||||
}
|
||||
FUNCTION_TYPE_OVERRIDES = {
|
||||
'Clay_CreateArenaWithCapacityAndMemory': {
|
||||
'offset': '[^]u8',
|
||||
},
|
||||
'Clay_SetMeasureTextFunction': {
|
||||
'userData': 'uintptr',
|
||||
},
|
||||
'Clay_RenderCommandArray_Get': {
|
||||
'index': 'i32',
|
||||
},
|
||||
"Clay__AttachElementConfig": {
|
||||
"config": 'rawptr',
|
||||
},
|
||||
}
|
||||
|
||||
class OdinGenerator(BaseGenerator):
|
||||
|
||||
def generate(self) -> None:
|
||||
self.generate_structs()
|
||||
self.generate_enums()
|
||||
self.generate_functions()
|
||||
|
||||
odin_template_path = Path(__file__).parent / 'odin' / 'clay.template.odin'
|
||||
with open(odin_template_path, 'r') as f:
|
||||
template = f.read()
|
||||
self.output_content['clay.odin'] = (
|
||||
template
|
||||
.replace('{{structs}}', '\n'.join(self.output_content['struct']))
|
||||
.replace('{{enums}}', '\n'.join(self.output_content['enum']))
|
||||
.replace('{{public_functions}}', '\n'.join(self.output_content['public_function']))
|
||||
.replace('{{private_functions}}', '\n'.join(self.output_content['private_function']))
|
||||
.splitlines()
|
||||
)
|
||||
del self.output_content['struct']
|
||||
del self.output_content['enum']
|
||||
del self.output_content['private_function']
|
||||
del self.output_content['public_function']
|
||||
|
||||
def get_symbol_name(self, symbol: str) -> str:
|
||||
if symbol in SYMBOL_NAME_OVERRIDES:
|
||||
return SYMBOL_NAME_OVERRIDES[symbol]
|
||||
symbol_type = self.get_symbol_type(symbol)
|
||||
base_name = symbol.removeprefix('Clay_')
|
||||
if symbol_type == 'enum':
|
||||
return base_name.removeprefix('_') # Clay_ and Clay__ are exported as public types.
|
||||
elif symbol_type == 'struct':
|
||||
return base_name
|
||||
elif symbol_type == 'function':
|
||||
return base_name
|
||||
raise ValueError(f'Unknown symbol: {symbol}')
|
||||
|
||||
def format_type(self, type: ExtractedSymbolType) -> str:
|
||||
if isinstance(type, str):
|
||||
return type
|
||||
|
||||
parameter_strs = []
|
||||
for param_name, param_type in type['params']:
|
||||
parameter_strs.append(f"{param_name}: {self.format_type(param_type or 'unknown')}")
|
||||
return_type_str = ''
|
||||
if type['return_type'] is not None and type['return_type'] != 'void':
|
||||
return_type_str = ' -> ' + self.format_type(type['return_type'])
|
||||
return f"proc \"c\" ({', '.join(parameter_strs)}){return_type_str}"
|
||||
|
||||
def resolve_binding_type(self, symbol: str, member: str | None, member_type: ExtractedSymbolType | None, type_overrides: dict[str, dict[str, str]]) -> str | None:
|
||||
if isinstance(member_type, str):
|
||||
if member_type in SYMBOL_COMPLETE_OVERRIDES:
|
||||
return SYMBOL_COMPLETE_OVERRIDES[member_type]
|
||||
if symbol in type_overrides and member in type_overrides[symbol]:
|
||||
return type_overrides[symbol][member]
|
||||
if member_type in TYPE_MAPPING:
|
||||
return TYPE_MAPPING[member_type]
|
||||
if member_type and self.has_symbol(member_type):
|
||||
return self.get_symbol_name(member_type)
|
||||
if member_type and member_type.startswith('*'):
|
||||
result = self.resolve_binding_type(symbol, member, member_type[1:], type_overrides)
|
||||
if result:
|
||||
return f"^{result}"
|
||||
return None
|
||||
if member_type is None:
|
||||
return None
|
||||
|
||||
resolved_parameters = []
|
||||
for param_name, param_type in member_type['params']:
|
||||
resolved_param = self.resolve_binding_type(symbol, param_name, param_type, type_overrides)
|
||||
if resolved_param is None:
|
||||
return None
|
||||
resolved_parameters.append((param_name, resolved_param))
|
||||
resolved_return_type = self.resolve_binding_type(symbol, None, member_type['return_type'], type_overrides)
|
||||
if resolved_return_type is None:
|
||||
return None
|
||||
return self.format_type({
|
||||
"params": resolved_parameters,
|
||||
"return_type": resolved_return_type,
|
||||
})
|
||||
|
||||
def generate_structs(self) -> None:
|
||||
for struct, struct_data in sorted(self.extracted_symbols.structs.items(), key=lambda x: x[0]):
|
||||
members = struct_data['attrs']
|
||||
if not struct.startswith('Clay_'):
|
||||
continue
|
||||
if struct in SYMBOL_COMPLETE_OVERRIDES:
|
||||
continue
|
||||
|
||||
binding_name = self.get_symbol_name(struct)
|
||||
if binding_name.startswith('_'):
|
||||
continue
|
||||
|
||||
if struct in STRUCT_OVERRIDE_AS_FIXED_ARRAY:
|
||||
array_size = len(members)
|
||||
first_elem = list(members.values())[0]
|
||||
array_type = None
|
||||
if 'type' in first_elem:
|
||||
array_type = first_elem['type']
|
||||
|
||||
if array_type in TYPE_MAPPING:
|
||||
array_binding_type = TYPE_MAPPING[array_type]
|
||||
elif array_type and self.has_symbol(self.format_type(array_type)):
|
||||
array_binding_type = self.get_symbol_name(self.format_type(array_type))
|
||||
else:
|
||||
self._write('struct', f"// {struct} ({array_type}) - has no mapping")
|
||||
continue
|
||||
|
||||
self._write('struct', f"// {struct} (overridden as fixed array)")
|
||||
self._write('struct', f"{binding_name} :: [{array_size}]{array_binding_type}")
|
||||
self._write('struct', "")
|
||||
continue
|
||||
|
||||
raw_union = ' #raw_union' if struct_data.get('is_union', False) else ''
|
||||
|
||||
self._write('struct', f"// {struct}")
|
||||
self._write('struct', f"{binding_name} :: struct{raw_union} {{")
|
||||
|
||||
for member, member_info in members.items():
|
||||
if struct in STRUCT_TYPE_OVERRIDES and member in STRUCT_TYPE_OVERRIDES[struct]:
|
||||
member_type = 'unknown'
|
||||
elif not 'type' in member_info:
|
||||
self._write('struct', f" // {member} (unknown type)")
|
||||
continue
|
||||
else:
|
||||
member_type = member_info['type']
|
||||
|
||||
binding_member_name = member
|
||||
if struct in STRUCT_MEMBER_OVERRIDES and member in STRUCT_MEMBER_OVERRIDES[struct]:
|
||||
binding_member_name = STRUCT_MEMBER_OVERRIDES[struct][member]
|
||||
|
||||
member_binding_type = self.resolve_binding_type(struct, member, member_type, STRUCT_TYPE_OVERRIDES)
|
||||
if member_binding_type is None:
|
||||
self._write('struct', f" // {binding_member_name} ({member_type}) - has no mapping")
|
||||
continue
|
||||
self._write('struct', f" {binding_member_name}: {member_binding_type}, // {member} ({member_type})")
|
||||
self._write('struct', "}")
|
||||
self._write('struct', '')
|
||||
|
||||
def generate_enums(self) -> None:
|
||||
for enum, members in sorted(self.extracted_symbols.enums.items(), key=lambda x: x[0]):
|
||||
if not enum.startswith('Clay_'):
|
||||
continue
|
||||
if enum in SYMBOL_COMPLETE_OVERRIDES:
|
||||
continue
|
||||
|
||||
binding_name = self.get_symbol_name(enum)
|
||||
common_member_prefix = get_common_prefix(list(members.keys()))
|
||||
self._write('enum', f"// {enum}")
|
||||
self._write('enum', f"{binding_name} :: enum EnumBackingType {{")
|
||||
for member in members:
|
||||
if enum in ENUM_MEMBER_OVERRIDES and member in ENUM_MEMBER_OVERRIDES[enum]:
|
||||
binding_member_name = ENUM_MEMBER_OVERRIDES[enum][member]
|
||||
else:
|
||||
binding_member_name = member.removeprefix(common_member_prefix)
|
||||
if enum in ENUM_MEMBER_PASCAL:
|
||||
binding_member_name = snake_case_to_pascal_case(binding_member_name)
|
||||
|
||||
if members[member] is not None:
|
||||
self._write('enum', f" {binding_member_name} = {members[member]}, // {member}")
|
||||
else:
|
||||
self._write('enum', f" {binding_member_name}, // {member}")
|
||||
|
||||
if enum in ENUM_ADDITIONAL_MEMBERS:
|
||||
self._write('enum', ' // Odin specific enum types')
|
||||
for member, value in ENUM_ADDITIONAL_MEMBERS[enum].items():
|
||||
self._write('enum', f" {member} = {value},")
|
||||
self._write('enum', "}")
|
||||
self._write('enum', '')
|
||||
|
||||
def generate_functions(self) -> None:
|
||||
for function, function_info in sorted(self.extracted_symbols.functions.items(), key=lambda x: x[0]):
|
||||
if not function.startswith('Clay_'):
|
||||
continue
|
||||
if function in SYMBOL_COMPLETE_OVERRIDES:
|
||||
continue
|
||||
is_private = function.startswith('Clay__')
|
||||
write_to = 'private_function' if is_private else 'public_function'
|
||||
|
||||
binding_name = self.get_symbol_name(function)
|
||||
|
||||
return_type = function_info['return_type']
|
||||
binding_return_type = self.resolve_binding_type(function, None, return_type, {})
|
||||
if binding_return_type is None:
|
||||
self._write(write_to, f" // {function} ({return_type}) - has no mapping")
|
||||
continue
|
||||
|
||||
skip = False
|
||||
binding_params = []
|
||||
for param_name, param_type in function_info['params']:
|
||||
binding_param_name = param_name
|
||||
if function in FUNCTION_PARAM_OVERRIDES and param_name in FUNCTION_PARAM_OVERRIDES[function]:
|
||||
binding_param_name = FUNCTION_PARAM_OVERRIDES[function][param_name]
|
||||
binding_param_type = self.resolve_binding_type(function, param_name, param_type, FUNCTION_TYPE_OVERRIDES)
|
||||
if binding_param_type is None:
|
||||
skip = True
|
||||
binding_params.append(f"{binding_param_name}: {binding_param_type}")
|
||||
if skip:
|
||||
self._write(write_to, f" // {function} - has no mapping")
|
||||
continue
|
||||
|
||||
binding_params_str = ', '.join(binding_params)
|
||||
return_str = f" -> {binding_return_type}" if binding_return_type != 'void' else ''
|
||||
self._write(write_to, f" {binding_name} :: proc({binding_params_str}){return_str} --- // {function}")
|
||||
|
173
generator/parser.py
Normal file
173
generator/parser.py
Normal file
@ -0,0 +1,173 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional, TypedDict, NotRequired, Union
|
||||
from pycparser import c_ast, parse_file, preprocess_file
|
||||
from pathlib import Path
|
||||
import os
|
||||
import json
|
||||
import shutil
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
ExtractedSymbolType = Union[str, "ExtractedFunction"]
|
||||
|
||||
class ExtractedStructAttributeUnion(TypedDict):
|
||||
type: Optional[ExtractedSymbolType]
|
||||
|
||||
class ExtractedStructAttribute(TypedDict):
|
||||
type: NotRequired[ExtractedSymbolType]
|
||||
union: NotRequired[dict[str, Optional[ExtractedSymbolType]]]
|
||||
|
||||
class ExtractedStruct(TypedDict):
|
||||
attrs: dict[str, ExtractedStructAttribute]
|
||||
is_union: NotRequired[bool]
|
||||
|
||||
ExtractedEnum = dict[str, Optional[str]]
|
||||
ExtractedFunctionParam = tuple[str, Optional[ExtractedSymbolType]]
|
||||
|
||||
class ExtractedFunction(TypedDict):
|
||||
return_type: Optional["ExtractedSymbolType"]
|
||||
params: list[ExtractedFunctionParam]
|
||||
|
||||
@dataclass
|
||||
class ExtractedSymbols:
|
||||
structs: dict[str, ExtractedStruct]
|
||||
enums: dict[str, ExtractedEnum]
|
||||
functions: dict[str, ExtractedFunction]
|
||||
|
||||
def get_type_names(node: c_ast.Node, prefix: str="") -> Optional[ExtractedSymbolType]:
|
||||
if isinstance(node, c_ast.TypeDecl) and hasattr(node, 'quals') and node.quals:
|
||||
prefix = " ".join(node.quals) + " " + prefix
|
||||
if isinstance(node, c_ast.PtrDecl):
|
||||
prefix = "*" + prefix
|
||||
if isinstance(node, c_ast.FuncDecl):
|
||||
func: ExtractedFunction = {
|
||||
'return_type': get_type_names(node.type),
|
||||
'params': [],
|
||||
}
|
||||
for param in node.args.params:
|
||||
if param.name is None:
|
||||
continue
|
||||
func['params'].append((param.name, get_type_names(param)))
|
||||
return func
|
||||
|
||||
if hasattr(node, 'names'):
|
||||
return prefix + node.names[0] # type: ignore
|
||||
elif hasattr(node, 'type'):
|
||||
return get_type_names(node.type, prefix) # type: ignore
|
||||
return None
|
||||
|
||||
class Visitor(c_ast.NodeVisitor):
|
||||
def __init__(self):
|
||||
self.structs: dict[str, ExtractedStruct] = {}
|
||||
self.enums: dict[str, ExtractedEnum] = {}
|
||||
self.functions: dict[str, ExtractedFunction] = {}
|
||||
|
||||
def visit_FuncDecl(self, node: c_ast.FuncDecl):
|
||||
# node.show()
|
||||
# logger.debug(node)
|
||||
node_type = node.type
|
||||
is_pointer = False
|
||||
if isinstance(node.type, c_ast.PtrDecl):
|
||||
node_type = node.type.type
|
||||
is_pointer = True
|
||||
|
||||
if hasattr(node_type, "declname"):
|
||||
return_type = get_type_names(node_type.type)
|
||||
if return_type is not None and isinstance(return_type, str) and is_pointer:
|
||||
return_type = "*" + return_type
|
||||
func: ExtractedFunction = {
|
||||
'return_type': return_type,
|
||||
'params': [],
|
||||
}
|
||||
for param in node.args.params:
|
||||
if param.name is None:
|
||||
continue
|
||||
func['params'].append((param.name, get_type_names(param)))
|
||||
self.functions[node_type.declname] = func
|
||||
self.generic_visit(node)
|
||||
|
||||
def visit_Struct(self, node: c_ast.Struct):
|
||||
# node.show()
|
||||
if node.name and node.decls:
|
||||
struct = {}
|
||||
for decl in node.decls:
|
||||
struct[decl.name] = {
|
||||
"type": get_type_names(decl),
|
||||
}
|
||||
self.structs[node.name] = {
|
||||
'attrs': struct,
|
||||
}
|
||||
self.generic_visit(node)
|
||||
|
||||
def visit_Typedef(self, node: c_ast.Typedef):
|
||||
# node.show()
|
||||
if hasattr(node.type, 'type') and hasattr(node.type.type, 'decls') and node.type.type.decls:
|
||||
struct = {}
|
||||
for decl in node.type.type.decls:
|
||||
if hasattr(decl, 'type') and hasattr(decl.type, 'type') and isinstance(decl.type.type, c_ast.Union):
|
||||
union = {}
|
||||
for field in decl.type.type.decls:
|
||||
union[field.name] = get_type_names(field)
|
||||
struct[decl.name] = {
|
||||
'union': union
|
||||
}
|
||||
else:
|
||||
struct[decl.name] = {
|
||||
"type": get_type_names(decl),
|
||||
}
|
||||
|
||||
self.structs[node.name] = {
|
||||
'attrs': struct,
|
||||
'is_union': isinstance(node.type.type, c_ast.Union),
|
||||
}
|
||||
if hasattr(node.type, 'type') and isinstance(node.type.type, c_ast.Enum):
|
||||
enum = {}
|
||||
for enumerator in node.type.type.values.enumerators:
|
||||
if enumerator.value is None:
|
||||
enum[enumerator.name] = None
|
||||
else:
|
||||
enum[enumerator.name] = enumerator.value.value
|
||||
self.enums[node.name] = enum
|
||||
self.generic_visit(node)
|
||||
|
||||
|
||||
def parse_headers(input_files: list[Path], tmp_dir: Path) -> ExtractedSymbols:
|
||||
cpp_args = ["-nostdinc", "-D__attribute__(x)=", "-E"]
|
||||
|
||||
# Make a new clay.h that combines the provided input files, so that we can add bindings for customized structs
|
||||
with open(tmp_dir / 'merged_clay.h', 'w') as f:
|
||||
for input_file in input_files:
|
||||
with open(input_file, 'r') as f2:
|
||||
for line in f2:
|
||||
# Ignore includes, as they should be manually included in input_files.
|
||||
if line.startswith("#include"):
|
||||
continue
|
||||
|
||||
# Ignore the CLAY_IMPLEMENTATION define, because we only want to parse the public api code.
|
||||
# This is helpful so that the user can provide their implementation code, which will contain any custom extensions
|
||||
if "#define CLAY_IMPLEMENTATION" in line:
|
||||
continue
|
||||
|
||||
f.write(line)
|
||||
|
||||
# Preprocess the file
|
||||
logger.info("Preprocessing file")
|
||||
preprocessed = preprocess_file(tmp_dir / 'merged_clay.h', cpp_path="cpp", cpp_args=cpp_args) # type: ignore
|
||||
with open(tmp_dir / 'clay.preprocessed.h', 'w') as f:
|
||||
f.write(preprocessed)
|
||||
|
||||
# Parse the file
|
||||
logger.info("Parsing file")
|
||||
ast = parse_file(tmp_dir / 'clay.preprocessed.h', use_cpp=False) # type: ignore
|
||||
|
||||
# Extract symbols
|
||||
visitor = Visitor()
|
||||
visitor.visit(ast)
|
||||
|
||||
result = ExtractedSymbols(
|
||||
structs=visitor.structs,
|
||||
enums=visitor.enums,
|
||||
functions=visitor.functions
|
||||
)
|
||||
return result
|
1
generator/requirements.txt
Normal file
1
generator/requirements.txt
Normal file
@ -0,0 +1 @@
|
||||
pycparser==2.22
|
450
renderers/sokol/sokol_clay.h
Normal file
450
renderers/sokol/sokol_clay.h
Normal file
@ -0,0 +1,450 @@
|
||||
#ifndef SOKOL_CLAY_INCLUDED
|
||||
#define SOKOL_CLAY_INCLUDED (1)
|
||||
/*
|
||||
sokol_clay.h -- drop-in Clay renderer for sokol_gfx.h
|
||||
|
||||
Do this:
|
||||
#define SOKOL_CLAY_IMPL
|
||||
|
||||
before you include this file in *one* C file to create the
|
||||
implementation.
|
||||
|
||||
Optionally provide the following configuration define both before including the
|
||||
the declaration and implementation:
|
||||
|
||||
SOKOL_CLAY_NO_SOKOL_APP - don't depend on sokol_app.h (see below for details)
|
||||
|
||||
Include the following headers before sokol_clay.h (both before including
|
||||
the declaration and implementation):
|
||||
|
||||
sokol_gl.h
|
||||
sokol_fontstash.h
|
||||
sokol_app.h (except SOKOL_CLAY_NO_SOKOL_APP)
|
||||
clay.h
|
||||
|
||||
FEATURE OVERVIEW:
|
||||
=================
|
||||
sokol_clay.h implements the rendering and event-handling code for Clay
|
||||
(https://github.com/nicbarker/clay) on top of sokol_gl.h and (optionally)
|
||||
sokol_app.h.
|
||||
|
||||
Since sokol_fontstash.h already depends on sokol_gl.h, the rendering is
|
||||
implemented using sokol_gl calls. (TODO: make fontstash optional?)
|
||||
|
||||
The sokol_app.h dependency is optional and used for input event handling.
|
||||
If you only use sokol_gfx.h but not sokol_app.h in your application,
|
||||
define SOKOL_CLAY_NO_SOKOL_APP before including the implementation
|
||||
of sokol_clay.h, this will remove any dependency to sokol_app.h, but
|
||||
you must call sclay_set_layout_dimensions and handle input yourself.
|
||||
|
||||
sokol_clay.h is not thread-safe, all calls must be made from the
|
||||
same thread where sokol_gfx.h is running.
|
||||
|
||||
HOWTO:
|
||||
======
|
||||
|
||||
--- To initialize sokol-clay, call sclay_setup(). This can be done
|
||||
before or after Clay_Initialize.
|
||||
|
||||
--- Create an array of sclay_font_t and fill it by calling one of:
|
||||
|
||||
sclay_font_t sclay_add_font(const char *filename);
|
||||
sclay_font_t sclay_add_font_mem(unsigned char *data, int dataLen);
|
||||
|
||||
The fontId value in Clay corresponds to indices in this array. After calling
|
||||
Clay_Initialize but before calling any layout code, do this:
|
||||
|
||||
Clay_SetMeasureTextFunction(sclay_measure_text, &fonts);
|
||||
|
||||
where `fonts` is the abovementioned array.
|
||||
|
||||
--- At the start of a frame, call sclay_new_frame() if you're using sokol_app.h.
|
||||
If you're not using sokol_app.h, call:
|
||||
|
||||
void sclay_set_layout_dimensions(Clay_Dimensions size, float dpi_scale);
|
||||
|
||||
at the start of the frame (or just when the window is resized.)
|
||||
|
||||
Either way, do some layout, then at the end of the frame call sclay_render:
|
||||
|
||||
sg_begin_pass(...)
|
||||
// other rendering...
|
||||
sclay_render(renderCommands, &fonts);
|
||||
// other rendering...
|
||||
sgl_draw();
|
||||
sg_end_pass();
|
||||
sg_commit();
|
||||
|
||||
One caveat: sclay_render assumes the default gl view matrix, and handles scaling
|
||||
automatically. If you've adjusted the view matrix, remember to first call:
|
||||
|
||||
sgl_matrix_mode_modelview();
|
||||
sgl_load_identity();
|
||||
|
||||
before calling sclay_render.
|
||||
|
||||
--- if you're using sokol_app.h, from inside the sokol_app.h event callback,
|
||||
call:
|
||||
|
||||
void sclay_handle_event(const sapp_event* ev);
|
||||
|
||||
Unfortunately Clay does not currently provide feedback on whether a mouse
|
||||
click was handled or not.
|
||||
|
||||
--- finally, on application shutdown, call
|
||||
|
||||
sclay_shutdown()
|
||||
*/
|
||||
#if !defined(SOKOL_CLAY_NO_SOKOL_APP) && !defined(SOKOL_APP_INCLUDED)
|
||||
#error "Please include sokol_app.h before sokol_clay.h (or define SOKOL_CLAY_NO_SOKOL_APP)"
|
||||
#endif
|
||||
|
||||
typedef int sclay_font_t;
|
||||
|
||||
void sclay_setup();
|
||||
void sclay_shutdown();
|
||||
|
||||
sclay_font_t sclay_add_font(const char *filename);
|
||||
sclay_font_t sclay_add_font_mem(unsigned char *data, int dataLen);
|
||||
Clay_Dimensions sclay_measure_text(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData);
|
||||
|
||||
#ifndef SOKOL_CLAY_NO_SOKOL_APP
|
||||
void sclay_new_frame();
|
||||
void sclay_handle_event(const sapp_event *ev);
|
||||
#endif /* SOKOL_CLAY_NO_SOKOL_APP */
|
||||
|
||||
/* Use this if you don't call sclay_new_frame. `size` is the "virtual" size which
|
||||
* your layout is relative to (ie. the actual framebuffer size divided by dpi_scale.)
|
||||
* Set dpi_scale to 1 if you're not using high-dpi support. */
|
||||
void sclay_set_layout_dimensions(Clay_Dimensions size, float dpi_scale);
|
||||
|
||||
void sclay_render(Clay_RenderCommandArray renderCommands, sclay_font_t *fonts);
|
||||
|
||||
#endif /* SOKOL_CLAY_INCLUDED */
|
||||
|
||||
#ifdef SOKOL_CLAY_IMPL
|
||||
#define SOKOL_CLAY_IMPL_INCLUDED (1)
|
||||
#ifndef SOKOL_GL_INCLUDED
|
||||
#error "Please include sokol_gl.h before sokol_clay.h"
|
||||
#endif
|
||||
#ifndef SOKOL_FONTSTASH_INCLUDED
|
||||
#error "Please include sokol_fontstash.h before sokol_clay.h"
|
||||
#endif
|
||||
#ifndef CLAY_HEADER
|
||||
#error "Please include clay.h before sokol_clay.h"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
sgl_pipeline pip;
|
||||
#ifndef SOKOL_CLAY_NO_SOKOL_APP
|
||||
Clay_Vector2 mouse_pos, scroll;
|
||||
bool mouse_down;
|
||||
#endif
|
||||
Clay_Dimensions size;
|
||||
float dpi_scale;
|
||||
FONScontext *fonts;
|
||||
} _sclay_state_t;
|
||||
static _sclay_state_t _sclay;
|
||||
|
||||
void sclay_setup() {
|
||||
_sclay.pip = sgl_make_pipeline(&(sg_pipeline_desc){
|
||||
.colors[0] = {
|
||||
.blend = {
|
||||
.enabled = true,
|
||||
.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA,
|
||||
.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
|
||||
},
|
||||
}
|
||||
});
|
||||
#ifndef SOKOL_CLAY_NO_SOKOL_APP
|
||||
_sclay.mouse_pos = (Clay_Vector2){0, 0};
|
||||
_sclay.scroll = (Clay_Vector2){0, 0};
|
||||
_sclay.mouse_down = false;
|
||||
#endif
|
||||
_sclay.size = (Clay_Dimensions){1, 1};
|
||||
_sclay.dpi_scale = 1;
|
||||
_sclay.fonts = sfons_create(&(sfons_desc_t){ 0 });
|
||||
//TODO clay error handler?
|
||||
}
|
||||
|
||||
void sclay_shutdown() {
|
||||
sgl_destroy_pipeline(_sclay.pip);
|
||||
sfons_destroy(_sclay.fonts);
|
||||
}
|
||||
|
||||
#ifndef SOKOL_CLAY_NO_SOKOL_APP
|
||||
void sclay_handle_event(const sapp_event* ev) {
|
||||
switch(ev->type){
|
||||
case SAPP_EVENTTYPE_MOUSE_MOVE:
|
||||
_sclay.mouse_pos.x = ev->mouse_x / _sclay.dpi_scale;
|
||||
_sclay.mouse_pos.y = ev->mouse_y / _sclay.dpi_scale;
|
||||
break;
|
||||
case SAPP_EVENTTYPE_MOUSE_DOWN:
|
||||
_sclay.mouse_down = true;
|
||||
break;
|
||||
case SAPP_EVENTTYPE_MOUSE_UP:
|
||||
_sclay.mouse_down = false;
|
||||
break;
|
||||
case SAPP_EVENTTYPE_MOUSE_SCROLL:
|
||||
_sclay.scroll.x += ev->scroll_x;
|
||||
_sclay.scroll.y += ev->scroll_y;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void sclay_new_frame() {
|
||||
sclay_set_layout_dimensions((Clay_Dimensions){ (float)sapp_width(), (float)sapp_height() },
|
||||
sapp_dpi_scale());
|
||||
Clay_SetPointerState(_sclay.mouse_pos, _sclay.mouse_down);
|
||||
Clay_UpdateScrollContainers(true, _sclay.scroll, sapp_frame_duration());
|
||||
_sclay.scroll = (Clay_Vector2){0, 0};
|
||||
}
|
||||
#endif /* SOKOL_CLAY_NO_SOKOL_APP */
|
||||
|
||||
void sclay_set_layout_dimensions(Clay_Dimensions size, float dpi_scale) {
|
||||
size.width /= dpi_scale;
|
||||
size.height /= dpi_scale;
|
||||
_sclay.size = size;
|
||||
if(_sclay.dpi_scale != dpi_scale){
|
||||
_sclay.dpi_scale = dpi_scale;
|
||||
Clay_ResetMeasureTextCache();
|
||||
}
|
||||
Clay_SetLayoutDimensions(size);
|
||||
}
|
||||
|
||||
sclay_font_t sclay_add_font(const char *filename) {
|
||||
//TODO log something if we get FONS_INVALID
|
||||
return fonsAddFont(_sclay.fonts, "", filename);
|
||||
}
|
||||
|
||||
sclay_font_t sclay_add_font_mem(unsigned char *data, int dataLen) {
|
||||
//TODO log something if we get FONS_INVALID
|
||||
return fonsAddFontMem(_sclay.fonts, "", data, dataLen, false);
|
||||
}
|
||||
|
||||
Clay_Dimensions sclay_measure_text(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData) {
|
||||
sclay_font_t *fonts = (sclay_font_t *)userData;
|
||||
if(!fonts) return (Clay_Dimensions){ 0 };
|
||||
fonsSetFont(_sclay.fonts, fonts[config->fontId]);
|
||||
fonsSetSize(_sclay.fonts, config->fontSize);
|
||||
fonsSetSpacing(_sclay.fonts, config->letterSpacing);
|
||||
float ascent, descent, lineh;
|
||||
fonsVertMetrics(_sclay.fonts, &ascent, &descent, &lineh);
|
||||
return (Clay_Dimensions) {
|
||||
.width = fonsTextBounds(_sclay.fonts, 0, 0, text.chars, text.chars + text.length, NULL),
|
||||
.height = ascent - descent
|
||||
};
|
||||
}
|
||||
|
||||
static void _draw_rect(float x, float y, float w, float h){
|
||||
sgl_v2f(x, y);
|
||||
sgl_v2f(x, y);
|
||||
sgl_v2f(x+w, y);
|
||||
sgl_v2f(x, y+h);
|
||||
sgl_v2f(x+w, y+h);
|
||||
sgl_v2f(x+w, y+h);
|
||||
}
|
||||
|
||||
static float _SIN[16] = {
|
||||
0.000000f, 0.104528f, 0.207912f, 0.309017f,
|
||||
0.406737f, 0.500000f, 0.587785f, 0.669131f,
|
||||
0.743145f, 0.809017f, 0.866025f, 0.913545f,
|
||||
0.951057f, 0.978148f, 0.994522f, 1.000000f,
|
||||
};
|
||||
|
||||
/* rx,ry = radius */
|
||||
static void _draw_corner(float x, float y, float rx, float ry){
|
||||
x -= rx;
|
||||
y -= ry;
|
||||
sgl_v2f(x, y);
|
||||
for(int i = 0; i < 16; ++i){
|
||||
sgl_v2f(x, y);
|
||||
sgl_v2f(x+(rx*_SIN[15-i]), y+(ry*_SIN[i]));
|
||||
}
|
||||
sgl_v2f(x+(rx*_SIN[0]), y+(ry*_SIN[15]));
|
||||
}
|
||||
|
||||
/* rx,ry = radius ix,iy = inner radius */
|
||||
static void _draw_corner_border(float x, float y, float rx, float ry, float ix, float iy){
|
||||
x -= rx;
|
||||
y -= ry;
|
||||
sgl_v2f(x+(ix*_SIN[15]), y+(iy*_SIN[0]));
|
||||
for(int i = 0; i < 16; ++i){
|
||||
sgl_v2f(x+(ix*_SIN[15-i]), y+(iy*_SIN[i]));
|
||||
sgl_v2f(x+(rx*_SIN[15-i]), y+(ry*_SIN[i]));
|
||||
}
|
||||
sgl_v2f(x+(rx*_SIN[0]), y+(ry*_SIN[15]));
|
||||
}
|
||||
|
||||
void sclay_render(Clay_RenderCommandArray renderCommands, sclay_font_t *fonts) {
|
||||
sgl_matrix_mode_modelview();
|
||||
sgl_translate(-1.0f, 1.0f, 0.0f);
|
||||
sgl_scale(2.0f/_sclay.size.width, -2.0f/_sclay.size.height, 1.0f);
|
||||
sgl_disable_texture();
|
||||
sgl_push_pipeline();
|
||||
sgl_load_pipeline(_sclay.pip);
|
||||
for (uint32_t i = 0; i < renderCommands.length; i++) {
|
||||
Clay_RenderCommand *renderCommand = Clay_RenderCommandArray_Get(&renderCommands, i);
|
||||
Clay_BoundingBox bbox = renderCommand->boundingBox;
|
||||
switch (renderCommand->commandType) {
|
||||
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
|
||||
Clay_RectangleRenderData *config = &renderCommand->renderData.rectangle;
|
||||
sgl_c4f(config->backgroundColor.r / 255.0f,
|
||||
config->backgroundColor.g / 255.0f,
|
||||
config->backgroundColor.b / 255.0f,
|
||||
config->backgroundColor.a / 255.0f);
|
||||
Clay_CornerRadius r = config->cornerRadius;
|
||||
sgl_begin_triangle_strip();
|
||||
if(r.topLeft > 0 || r.topRight > 0){
|
||||
_draw_corner(bbox.x, bbox.y, -r.topLeft, -r.topLeft);
|
||||
_draw_corner(bbox.x+bbox.width, bbox.y, r.topRight, -r.topRight);
|
||||
_draw_rect(bbox.x+r.topLeft, bbox.y,
|
||||
bbox.width-r.topLeft-r.topRight, CLAY__MAX(r.topLeft, r.topRight));
|
||||
}
|
||||
if(r.bottomLeft > 0 || r.bottomRight > 0){
|
||||
_draw_corner(bbox.x, bbox.y+bbox.height, -r.bottomLeft, r.bottomLeft);
|
||||
_draw_corner(bbox.x+bbox.width, bbox.y+bbox.height, r.bottomRight, r.bottomRight);
|
||||
_draw_rect(bbox.x+r.bottomLeft,
|
||||
bbox.y+bbox.height-CLAY__MAX(r.bottomLeft, r.bottomRight),
|
||||
bbox.width-r.bottomLeft-r.bottomRight, CLAY__MAX(r.bottomLeft, r.bottomRight));
|
||||
}
|
||||
if(r.topLeft < r.bottomLeft){
|
||||
if(r.topLeft < r.topRight){
|
||||
_draw_rect(bbox.x, bbox.y+r.topLeft, r.topLeft, bbox.height-r.topLeft-r.bottomLeft);
|
||||
_draw_rect(bbox.x+r.topLeft, bbox.y+r.topRight,
|
||||
r.bottomLeft-r.topLeft, bbox.height-r.topRight-r.bottomLeft);
|
||||
} else {
|
||||
_draw_rect(bbox.x, bbox.y+r.topLeft, r.bottomLeft, bbox.height-r.topLeft-r.bottomLeft);
|
||||
}
|
||||
} else {
|
||||
if(r.bottomLeft < r.bottomRight){
|
||||
_draw_rect(bbox.x, bbox.y+r.topLeft, r.bottomLeft, bbox.height-r.topLeft-r.bottomLeft);
|
||||
_draw_rect(bbox.x+r.bottomLeft, bbox.y+r.topLeft,
|
||||
r.topLeft-r.bottomLeft, bbox.height-r.topLeft-r.bottomRight);
|
||||
} else {
|
||||
_draw_rect(bbox.x, bbox.y+r.topLeft, r.topLeft, bbox.height-r.topLeft-r.bottomLeft);
|
||||
}
|
||||
}
|
||||
if(r.topRight < r.bottomRight){
|
||||
if(r.topRight < r.topLeft){
|
||||
_draw_rect(bbox.x+bbox.width-r.bottomRight, bbox.y+r.topLeft,
|
||||
r.bottomRight-r.topRight, bbox.height-r.topLeft-r.bottomRight);
|
||||
_draw_rect(bbox.x+bbox.width-r.topRight, bbox.y+r.topRight,
|
||||
r.topRight, bbox.height-r.topRight-r.bottomRight);
|
||||
} else {
|
||||
_draw_rect(bbox.x+bbox.width-r.bottomRight, bbox.y+r.topRight,
|
||||
r.bottomRight, bbox.height-r.topRight-r.bottomRight);
|
||||
}
|
||||
} else {
|
||||
if(r.bottomRight < r.bottomLeft){
|
||||
_draw_rect(bbox.x+bbox.width-r.topRight, bbox.y+r.topRight,
|
||||
r.topRight-r.bottomRight, bbox.height-r.topRight-r.bottomLeft);
|
||||
_draw_rect(bbox.x+bbox.width-r.bottomRight, bbox.y+r.topRight,
|
||||
r.bottomRight, bbox.height-r.topRight-r.bottomRight);
|
||||
} else {
|
||||
_draw_rect(bbox.x+bbox.width-r.topRight, bbox.y+r.topRight,
|
||||
r.topRight, bbox.height-r.topRight-r.bottomRight);
|
||||
}
|
||||
}
|
||||
_draw_rect(bbox.x+CLAY__MAX(r.topLeft, r.bottomLeft),
|
||||
bbox.y+CLAY__MAX(r.topLeft, r.topRight),
|
||||
bbox.width-CLAY__MAX(r.topLeft, r.bottomLeft)-CLAY__MAX(r.topRight, r.bottomRight),
|
||||
bbox.height-CLAY__MAX(r.topLeft, r.topRight)-CLAY__MAX(r.bottomLeft, r.bottomRight));
|
||||
sgl_end();
|
||||
break;
|
||||
}
|
||||
case CLAY_RENDER_COMMAND_TYPE_TEXT: {
|
||||
if(!fonts) break;
|
||||
Clay_TextRenderData *config = &renderCommand->renderData.text;
|
||||
Clay_StringSlice text = config->stringContents;
|
||||
fonsSetFont(_sclay.fonts, fonts[config->fontId]);
|
||||
uint32_t color = sfons_rgba(
|
||||
config->textColor.r,
|
||||
config->textColor.g,
|
||||
config->textColor.b,
|
||||
config->textColor.a);
|
||||
fonsSetColor(_sclay.fonts, color);
|
||||
fonsSetSpacing(_sclay.fonts, config->letterSpacing * _sclay.dpi_scale);
|
||||
fonsSetAlign(_sclay.fonts, FONS_ALIGN_LEFT | FONS_ALIGN_TOP);
|
||||
fonsSetSize(_sclay.fonts, config->fontSize * _sclay.dpi_scale);
|
||||
sgl_matrix_mode_modelview();
|
||||
sgl_push_matrix();
|
||||
sgl_scale(1.0f/_sclay.dpi_scale, 1.0f/_sclay.dpi_scale, 1.0f);
|
||||
fonsDrawText(_sclay.fonts, bbox.x*_sclay.dpi_scale, bbox.y*_sclay.dpi_scale,
|
||||
text.chars, text.chars + text.length);
|
||||
sgl_pop_matrix();
|
||||
break;
|
||||
}
|
||||
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: {
|
||||
sgl_scissor_rectf(bbox.x*_sclay.dpi_scale, bbox.y*_sclay.dpi_scale,
|
||||
bbox.width*_sclay.dpi_scale, bbox.height*_sclay.dpi_scale,
|
||||
true);
|
||||
break;
|
||||
}
|
||||
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END: {
|
||||
sgl_scissor_rectf(0, 0,
|
||||
_sclay.size.width*_sclay.dpi_scale, _sclay.size.height*_sclay.dpi_scale,
|
||||
true);
|
||||
break;
|
||||
}
|
||||
case CLAY_RENDER_COMMAND_TYPE_IMAGE: {
|
||||
//TODO
|
||||
break;
|
||||
}
|
||||
case CLAY_RENDER_COMMAND_TYPE_BORDER: {
|
||||
Clay_BorderRenderData *config = &renderCommand->renderData.border;
|
||||
sgl_c4f(config->color.r / 255.0f,
|
||||
config->color.g / 255.0f,
|
||||
config->color.b / 255.0f,
|
||||
config->color.a / 255.0f);
|
||||
Clay_BorderWidth w = config->width;
|
||||
Clay_CornerRadius r = config->cornerRadius;
|
||||
sgl_begin_triangle_strip();
|
||||
if(w.left > 0){
|
||||
_draw_rect(bbox.x, bbox.y + r.topLeft,
|
||||
w.left, bbox.height - r.topLeft - r.bottomLeft);
|
||||
}
|
||||
if(w.right > 0){
|
||||
_draw_rect(bbox.x + bbox.width - w.right, bbox.y + r.topRight,
|
||||
w.right, bbox.height - r.topRight - r.bottomRight);
|
||||
}
|
||||
if(w.top > 0){
|
||||
_draw_rect(bbox.x + r.topLeft, bbox.y,
|
||||
bbox.width - r.topLeft - r.topRight, w.top);
|
||||
}
|
||||
if(w.bottom > 0){
|
||||
_draw_rect(bbox.x + r.bottomLeft, bbox.y + bbox.height - w.bottom,
|
||||
bbox.width - r.bottomLeft - r.bottomRight, w.bottom);
|
||||
}
|
||||
if(r.topLeft > 0 && (w.top > 0 || w.left > 0)){
|
||||
_draw_corner_border(bbox.x, bbox.y,
|
||||
-r.topLeft, -r.topLeft,
|
||||
-r.topLeft+w.left, -r.topLeft+w.top);
|
||||
}
|
||||
if(r.topRight > 0 && (w.top > 0 || w.right > 0)){
|
||||
_draw_corner_border(bbox.x+bbox.width, bbox.y,
|
||||
r.topRight, -r.topRight,
|
||||
r.topRight-w.right, -r.topRight+w.top);
|
||||
}
|
||||
if(r.bottomLeft > 0 && (w.bottom > 0 || w.left > 0)){
|
||||
_draw_corner_border(bbox.x, bbox.y+bbox.height,
|
||||
-r.bottomLeft, r.bottomLeft,
|
||||
-r.bottomLeft+w.left, r.bottomLeft-w.bottom);
|
||||
}
|
||||
if(r.bottomRight > 0 && (w.bottom > 0 || w.right > 0)){
|
||||
_draw_corner_border(bbox.x+bbox.width, bbox.y+bbox.height,
|
||||
r.bottomRight, r.bottomRight,
|
||||
r.bottomRight-w.right, r.bottomRight-w.bottom);
|
||||
}
|
||||
sgl_end();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
sgl_pop_pipeline();
|
||||
sfons_flush(_sclay.fonts);
|
||||
}
|
||||
#endif /* SOKOL_CLAY_IMPL */
|
@ -1,11 +1,290 @@
|
||||
#include <Windows.h>
|
||||
|
||||
#if !defined(CLAY_DISABLE_SIMD) && (defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64))
|
||||
#include <immintrin.h> // AVX intrinsincs for faster sqrtf
|
||||
#endif
|
||||
|
||||
#include "../../clay.h"
|
||||
|
||||
HDC renderer_hdcMem = {0};
|
||||
HBITMAP renderer_hbmMem = {0};
|
||||
HANDLE renderer_hOld = {0};
|
||||
DWORD g_dwGdiRenderFlags;
|
||||
|
||||
void Clay_Win32_Render(HWND hwnd, Clay_RenderCommandArray renderCommands)
|
||||
#ifndef RECTWIDTH
|
||||
#define RECTWIDTH(rc) ((rc).right - (rc).left)
|
||||
#endif
|
||||
#ifndef RECTHEIGHT
|
||||
#define RECTHEIGHT(rc) ((rc).bottom - (rc).top)
|
||||
#endif
|
||||
|
||||
// Renderer options bit flags
|
||||
// RF clearly stated in the name to avoid confusion with possible macro definitions for other purposes
|
||||
#define CLAYGDI_RF_ALPHABLEND 0x00000001
|
||||
#define CLAYGDI_RF_SMOOTHCORNERS 0x00000002
|
||||
// These are bitflags, not indexes. Next would be 0x00000004
|
||||
|
||||
inline DWORD Clay_Win32_GetRendererFlags() { return g_dwGdiRenderFlags; }
|
||||
|
||||
// Replaces the rendering flags with new ones provided
|
||||
inline void Clay_Win32_SetRendererFlags(DWORD dwFlags) { g_dwGdiRenderFlags = dwFlags; }
|
||||
|
||||
// Returns `true` if flags were modified
|
||||
inline bool Clay_Win32_ModifyRendererFlags(DWORD dwRemove, DWORD dwAdd)
|
||||
{
|
||||
DWORD dwSavedFlags = g_dwGdiRenderFlags;
|
||||
DWORD dwNewFlags = (dwSavedFlags & ~dwRemove) | dwAdd;
|
||||
|
||||
if (dwSavedFlags == dwNewFlags)
|
||||
return false;
|
||||
|
||||
Clay_Win32_SetRendererFlags(dwNewFlags);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------------+
|
||||
| Math stuff start |
|
||||
+----------------------------------------------------------------------------*/
|
||||
// Intrinsincs wrappers
|
||||
#if !defined(CLAY_DISABLE_SIMD) && (defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64))
|
||||
inline float intrin_sqrtf(const float f)
|
||||
{
|
||||
__m128 temp = _mm_set_ss(f);
|
||||
temp = _mm_sqrt_ss(temp);
|
||||
return _mm_cvtss_f32(temp);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Use fast inverse square root
|
||||
#if defined(USE_FAST_SQRT)
|
||||
float fast_inv_sqrtf(float number)
|
||||
{
|
||||
const float threehalfs = 1.5f;
|
||||
|
||||
float x2 = number * 0.5f;
|
||||
float y = number;
|
||||
|
||||
// Evil bit-level hacking
|
||||
uint32_t i = *(uint32_t*)&y;
|
||||
i = 0x5f3759df - (i >> 1); // Initial guess for Newton's method
|
||||
y = *(float*)&i;
|
||||
|
||||
// One iteration of Newton's method
|
||||
y = y * (threehalfs - (x2 * y * y)); // y = y * (1.5 - 0.5 * x * y^2)
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
// Fast square root approximation using the inverse square root
|
||||
float fast_sqrtf(float number)
|
||||
{
|
||||
if (number < 0.0f) return 0.0f; // Handle negative input
|
||||
return number * fast_inv_sqrtf(number);
|
||||
}
|
||||
#endif
|
||||
|
||||
// sqrtf_impl implementation chooser
|
||||
#if !defined(CLAY_DISABLE_SIMD) && (defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64))
|
||||
#define sqrtf_impl(x) intrin_sqrtf(x)
|
||||
#elif defined(USE_FAST_SQRT)
|
||||
#define sqrtf_impl(x) fast_sqrtf(x)
|
||||
#else
|
||||
#define sqrtf_impl(x) sqrtf(x) // Fallback to std sqrtf
|
||||
#endif
|
||||
/*----------------------------------------------------------------------------+
|
||||
| Math stuff end |
|
||||
+----------------------------------------------------------------------------*/
|
||||
|
||||
static inline Clay_Color ColorBlend(Clay_Color base, Clay_Color overlay, float factor)
|
||||
{
|
||||
Clay_Color blended;
|
||||
|
||||
// Normalize alpha values for multiplications
|
||||
float base_a = base.a / 255.0f;
|
||||
float overlay_a = overlay.a / 255.0f;
|
||||
|
||||
overlay_a *= factor;
|
||||
|
||||
float out_a = overlay_a + base_a * (1.0f - overlay_a);
|
||||
|
||||
// Avoid division by zero and fully transparent cases
|
||||
if (out_a <= 0.0f)
|
||||
{
|
||||
return (Clay_Color) { .a = 0, .r = 0, .g = 0, .b = 0 };
|
||||
}
|
||||
|
||||
blended.r = (overlay.r * overlay_a + base.r * base_a * (1.0f - overlay_a)) / out_a;
|
||||
blended.g = (overlay.g * overlay_a + base.g * base_a * (1.0f - overlay_a)) / out_a;
|
||||
blended.b = (overlay.b * overlay_a + base.b * base_a * (1.0f - overlay_a)) / out_a;
|
||||
blended.a = out_a * 255.0f; // Denormalize alpha back
|
||||
|
||||
return blended;
|
||||
}
|
||||
|
||||
static float RoundedRectPixelCoverage(int x, int y, const Clay_CornerRadius radius, int width, int height) {
|
||||
// Check if the pixel is in one of the four rounded corners
|
||||
if (x < radius.topLeft && y < radius.topLeft) {
|
||||
// Top-left corner
|
||||
float dx = radius.topLeft - x - 1;
|
||||
float dy = radius.topLeft - y - 1;
|
||||
float distance = sqrtf_impl(dx * dx + dy * dy);
|
||||
if (distance > radius.topLeft)
|
||||
return 0.0f;
|
||||
if (distance <= radius.topLeft - 1)
|
||||
return 1.0f;
|
||||
return radius.topLeft - distance;
|
||||
}
|
||||
else if (x >= width - radius.topRight && y < radius.topRight) {
|
||||
// Top-right corner
|
||||
float dx = x - (width - radius.topRight);
|
||||
float dy = radius.topRight - y - 1;
|
||||
float distance = sqrtf_impl(dx * dx + dy * dy);
|
||||
if (distance > radius.topRight)
|
||||
return 0.0f;
|
||||
if (distance <= radius.topRight - 1)
|
||||
return 1.0f;
|
||||
return radius.topRight - distance;
|
||||
}
|
||||
else if (x < radius.bottomLeft && y >= height - radius.bottomLeft) {
|
||||
// Bottom-left corner
|
||||
float dx = radius.bottomLeft - x - 1;
|
||||
float dy = y - (height - radius.bottomLeft);
|
||||
float distance = sqrtf_impl(dx * dx + dy * dy);
|
||||
if (distance > radius.bottomLeft)
|
||||
return 0.0f;
|
||||
if (distance <= radius.bottomLeft - 1)
|
||||
return 1.0f;
|
||||
return radius.bottomLeft - distance;
|
||||
}
|
||||
else if (x >= width - radius.bottomRight && y >= height - radius.bottomRight) {
|
||||
// Bottom-right corner
|
||||
float dx = x - (width - radius.bottomRight);
|
||||
float dy = y - (height - radius.bottomRight);
|
||||
float distance = sqrtf_impl(dx * dx + dy * dy);
|
||||
if (distance > radius.bottomRight)
|
||||
return 0.0f;
|
||||
if (distance <= radius.bottomRight - 1)
|
||||
return 1.0f;
|
||||
return radius.bottomRight - distance;
|
||||
}
|
||||
else {
|
||||
// Not in a corner, full coverage
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
HDC hdcMem;
|
||||
HBITMAP hbmMem;
|
||||
HBITMAP hbmMemPrev;
|
||||
void* pBits;
|
||||
SIZE size;
|
||||
} HDCSubstitute;
|
||||
|
||||
static void CreateHDCSubstitute(HDCSubstitute* phdcs, HDC hdcSrc, PRECT prc)
|
||||
{
|
||||
if (prc == NULL)
|
||||
return;
|
||||
|
||||
phdcs->size = (SIZE){ RECTWIDTH(*prc), RECTHEIGHT(*prc) };
|
||||
if (phdcs->size.cx <= 0 || phdcs->size.cy <= 0)
|
||||
return;
|
||||
|
||||
phdcs->hdcMem = CreateCompatibleDC(hdcSrc);
|
||||
if (phdcs->hdcMem == NULL)
|
||||
return;
|
||||
|
||||
// Create a 32-bit DIB section for the memory DC
|
||||
BITMAPINFO bmi = { 0 };
|
||||
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bmi.bmiHeader.biWidth = phdcs->size.cx;
|
||||
bmi.bmiHeader.biHeight = -phdcs->size.cy; // I think it's faster? Probably
|
||||
bmi.bmiHeader.biPlanes = 1;
|
||||
bmi.bmiHeader.biBitCount = 32;
|
||||
bmi.bmiHeader.biCompression = BI_RGB;
|
||||
|
||||
phdcs->pBits = NULL;
|
||||
|
||||
phdcs->hbmMem = CreateDIBSection(phdcs->hdcMem, &bmi, DIB_RGB_COLORS, &phdcs->pBits, NULL, 0);
|
||||
if (phdcs->hbmMem == NULL)
|
||||
{
|
||||
DeleteDC(phdcs->hdcMem);
|
||||
return;
|
||||
}
|
||||
|
||||
// Select the DIB section into the memory DC
|
||||
phdcs->hbmMemPrev = SelectObject(phdcs->hdcMem, phdcs->hbmMem);
|
||||
|
||||
// Copy the content of the target DC to the memory DC
|
||||
BitBlt(phdcs->hdcMem, 0, 0, phdcs->size.cx, phdcs->size.cy, hdcSrc, prc->left, prc->top, SRCCOPY);
|
||||
}
|
||||
|
||||
static void DestroyHDCSubstitute(HDCSubstitute* phdcs)
|
||||
{
|
||||
if (phdcs == NULL)
|
||||
return;
|
||||
|
||||
// Clean up
|
||||
SelectObject(phdcs->hdcMem, phdcs->hbmMemPrev);
|
||||
DeleteObject(phdcs->hbmMem);
|
||||
DeleteDC(phdcs->hdcMem);
|
||||
|
||||
ZeroMemory(phdcs, sizeof(HDCSubstitute));
|
||||
}
|
||||
|
||||
static void __Clay_Win32_FillRoundRect(HDC hdc, PRECT prc, Clay_Color color, Clay_CornerRadius radius)
|
||||
{
|
||||
HDCSubstitute substitute = { 0 };
|
||||
CreateHDCSubstitute(&substitute, hdc, prc);
|
||||
|
||||
bool has_corner_radius = radius.topLeft || radius.topRight || radius.bottomLeft || radius.bottomRight;
|
||||
|
||||
if (has_corner_radius)
|
||||
{
|
||||
// Limit the corner radius to the minimum of half the width and half the height
|
||||
float max_radius = (float)fmin(substitute.size.cx / 2.0f, substitute.size.cy / 2.0f);
|
||||
if (radius.topLeft > max_radius) radius.topLeft = max_radius;
|
||||
if (radius.topRight > max_radius) radius.topRight = max_radius;
|
||||
if (radius.bottomLeft > max_radius) radius.bottomLeft = max_radius;
|
||||
if (radius.bottomRight > max_radius) radius.bottomRight = max_radius;
|
||||
}
|
||||
|
||||
// Iterate over each pixel in the DIB section
|
||||
uint32_t* pixels = (uint32_t*)substitute.pBits;
|
||||
for (int y = 0; y < substitute.size.cy; ++y)
|
||||
{
|
||||
for (int x = 0; x < substitute.size.cx; ++x)
|
||||
{
|
||||
float coverage = 1.0f;
|
||||
if (has_corner_radius)
|
||||
coverage = RoundedRectPixelCoverage(x, y, radius, substitute.size.cx, substitute.size.cy);
|
||||
|
||||
if (coverage > 0.0f)
|
||||
{
|
||||
uint32_t pixel = pixels[y * substitute.size.cx + x];
|
||||
Clay_Color dst_color = {
|
||||
.r = (float)((pixel >> 16) & 0xFF), // Red
|
||||
.g = (float)((pixel >> 8) & 0xFF), // Green
|
||||
.b = (float)(pixel & 0xFF), // Blue
|
||||
.a = 255.0f // Fully opaque
|
||||
};
|
||||
Clay_Color blended = ColorBlend(dst_color, color, coverage);
|
||||
|
||||
pixels[y * substitute.size.cx + x] =
|
||||
((uint32_t)(blended.b) << 0) |
|
||||
((uint32_t)(blended.g) << 8) |
|
||||
((uint32_t)(blended.r) << 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the blended content back to the target DC
|
||||
BitBlt(hdc, prc->left, prc->top, substitute.size.cx, substitute.size.cy, substitute.hdcMem, 0, 0, SRCCOPY);
|
||||
DestroyHDCSubstitute(&substitute);
|
||||
}
|
||||
|
||||
void Clay_Win32_Render(HWND hwnd, Clay_RenderCommandArray renderCommands, HFONT* fonts)
|
||||
{
|
||||
bool is_clipping = false;
|
||||
HRGN clipping_region = {0};
|
||||
@ -48,14 +327,22 @@ void Clay_Win32_Render(HWND hwnd, Clay_RenderCommandArray renderCommands)
|
||||
r.right = boundingBox.x + boundingBox.width + r.right;
|
||||
r.bottom = boundingBox.y + boundingBox.height + r.bottom;
|
||||
|
||||
uint16_t font_id = renderCommand->renderData.text.fontId;
|
||||
HFONT hFont = fonts[font_id];
|
||||
HFONT hPrevFont = SelectObject(renderer_hdcMem, hFont);
|
||||
|
||||
// Actually draw text
|
||||
DrawTextA(renderer_hdcMem, renderCommand->renderData.text.stringContents.chars,
|
||||
renderCommand->renderData.text.stringContents.length,
|
||||
&r, DT_TOP | DT_LEFT);
|
||||
|
||||
SelectObject(renderer_hdcMem, hPrevFont);
|
||||
|
||||
break;
|
||||
}
|
||||
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE:
|
||||
{
|
||||
DWORD dwFlags = Clay_Win32_GetRendererFlags();
|
||||
Clay_RectangleRenderData rrd = renderCommand->renderData.rectangle;
|
||||
RECT r = rc;
|
||||
|
||||
@ -64,23 +351,44 @@ void Clay_Win32_Render(HWND hwnd, Clay_RenderCommandArray renderCommands)
|
||||
r.right = boundingBox.x + boundingBox.width;
|
||||
r.bottom = boundingBox.y + boundingBox.height;
|
||||
|
||||
HBRUSH recColor = CreateSolidBrush(RGB(rrd.backgroundColor.r, rrd.backgroundColor.g, rrd.backgroundColor.b));
|
||||
bool translucid = false;
|
||||
// There is need to check that only if alphablending is enabled.
|
||||
// In other case the blending will be always opaque and we can jump to simpler FillRgn/Rect
|
||||
if (dwFlags & CLAYGDI_RF_ALPHABLEND)
|
||||
translucid = rrd.backgroundColor.a > 0.0f && rrd.backgroundColor.a < 255.0f;
|
||||
|
||||
bool has_rounded_corners = rrd.cornerRadius.topLeft > 0.0f
|
||||
|| rrd.cornerRadius.topRight > 0.0f
|
||||
|| rrd.cornerRadius.bottomLeft > 0.0f
|
||||
|| rrd.cornerRadius.bottomRight > 0.0f;
|
||||
|
||||
if (rrd.cornerRadius.topLeft > 0)
|
||||
// We go here if CLAYGDI_RF_SMOOTHCORNERS flag is set and one of the corners is rounded
|
||||
// Also we go here if GLAYGDI_RF_ALPHABLEND flag is set and the fill color is translucid
|
||||
if ((dwFlags & CLAYGDI_RF_ALPHABLEND) && translucid || (dwFlags & CLAYGDI_RF_SMOOTHCORNERS) && has_rounded_corners)
|
||||
{
|
||||
HRGN roundedRectRgn = CreateRoundRectRgn(
|
||||
r.left, r.top, r.right + 1, r.bottom + 1,
|
||||
rrd.cornerRadius.topLeft * 2, rrd.cornerRadius.topLeft * 2);
|
||||
|
||||
FillRgn(renderer_hdcMem, roundedRectRgn, recColor);
|
||||
DeleteObject(roundedRectRgn);
|
||||
__Clay_Win32_FillRoundRect(renderer_hdcMem, &r, rrd.backgroundColor, rrd.cornerRadius);
|
||||
}
|
||||
else
|
||||
{
|
||||
FillRect(renderer_hdcMem, &r, recColor);
|
||||
HBRUSH recColor = CreateSolidBrush(RGB(rrd.backgroundColor.r, rrd.backgroundColor.g, rrd.backgroundColor.b));
|
||||
|
||||
if (has_rounded_corners)
|
||||
{
|
||||
HRGN roundedRectRgn = CreateRoundRectRgn(
|
||||
r.left, r.top, r.right + 1, r.bottom + 1,
|
||||
rrd.cornerRadius.topLeft * 2, rrd.cornerRadius.topLeft * 2);
|
||||
|
||||
FillRgn(renderer_hdcMem, roundedRectRgn, recColor);
|
||||
DeleteObject(roundedRectRgn);
|
||||
}
|
||||
else
|
||||
{
|
||||
FillRect(renderer_hdcMem, &r, recColor);
|
||||
}
|
||||
|
||||
DeleteObject(recColor);
|
||||
}
|
||||
|
||||
DeleteObject(recColor);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -216,6 +524,37 @@ static inline Clay_Dimensions Clay_Win32_MeasureText(Clay_StringSlice text, Clay
|
||||
{
|
||||
Clay_Dimensions textSize = {0};
|
||||
|
||||
if (userData != NULL)
|
||||
{
|
||||
HFONT* fonts = (HFONT*)userData;
|
||||
HFONT hFont = fonts[config->fontId];
|
||||
|
||||
if (hFont != NULL)
|
||||
{
|
||||
HDC hScreenDC = GetDC(NULL);
|
||||
HDC hTempDC = CreateCompatibleDC(hScreenDC);
|
||||
|
||||
if (hTempDC != NULL)
|
||||
{
|
||||
HFONT hPrevFont = SelectObject(hTempDC, hFont);
|
||||
|
||||
SIZE size;
|
||||
GetTextExtentPoint32(hTempDC, text.chars, text.length, &size);
|
||||
|
||||
textSize.width = size.cx;
|
||||
textSize.height = size.cy;
|
||||
|
||||
SelectObject(hScreenDC, hPrevFont);
|
||||
DeleteDC(hTempDC);
|
||||
|
||||
return textSize;
|
||||
}
|
||||
|
||||
ReleaseDC(HWND_DESKTOP, hScreenDC);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback for system bitmap font
|
||||
float maxTextWidth = 0.0f;
|
||||
float lineTextWidth = 0;
|
||||
float textHeight = WIN32_FONT_HEIGHT;
|
||||
@ -238,4 +577,33 @@ static inline Clay_Dimensions Clay_Win32_MeasureText(Clay_StringSlice text, Clay
|
||||
textSize.height = textHeight;
|
||||
|
||||
return textSize;
|
||||
}
|
||||
}
|
||||
|
||||
HFONT Clay_Win32_SimpleCreateFont(const char* filePath, const char* family, int height, int weight)
|
||||
{
|
||||
// Add the font resource to the application instance
|
||||
int fontAdded = AddFontResourceEx(filePath, FR_PRIVATE, NULL);
|
||||
if (fontAdded == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fontHeight = height;
|
||||
|
||||
// If negative, treat height as Pt rather than pixels
|
||||
if (height < 0) {
|
||||
// Get the screen DPI
|
||||
HDC hScreenDC = GetDC(NULL);
|
||||
int iScreenDPI = GetDeviceCaps(hScreenDC, LOGPIXELSY);
|
||||
ReleaseDC(HWND_DESKTOP, hScreenDC);
|
||||
|
||||
// Convert font height from points to pixels
|
||||
fontHeight = MulDiv(height, iScreenDPI, 72);
|
||||
}
|
||||
|
||||
// Create the font using the calculated height and the font name
|
||||
HFONT hFont = CreateFont(fontHeight, 0, 0, 0, weight, FALSE, FALSE, FALSE,
|
||||
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
|
||||
DEFAULT_PITCH, family);
|
||||
|
||||
return hFont;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user