diff options
| -rw-r--r-- | .github/workflows/gen_bindings.yml | 3 | ||||
| -rw-r--r-- | CHANGELOG.md | 8 | ||||
| -rw-r--r-- | bindgen/gen_all.py | 13 | ||||
| -rw-r--r-- | bindgen/gen_d.py | 48 | ||||
| -rw-r--r-- | bindgen/gen_zig.py | 32 | ||||
| -rw-r--r-- | sokol_app.h | 21 | ||||
| -rw-r--r-- | sokol_gfx.h | 4 | ||||
| -rw-r--r-- | util/sokol_debugtext.h | 13 | ||||
| -rw-r--r-- | util/sokol_imgui.h | 13 | ||||
| -rw-r--r-- | util/sokol_spine.h | 2 |
10 files changed, 113 insertions, 44 deletions
diff --git a/.github/workflows/gen_bindings.yml b/.github/workflows/gen_bindings.yml index 4fc2274f..5fc08df5 100644 --- a/.github/workflows/gen_bindings.yml +++ b/.github/workflows/gen_bindings.yml @@ -129,7 +129,7 @@ jobs: - uses: actions/checkout@main with: repository: floooh/sokol-zig - - uses: goto-bus-stop/setup-zig@v2 + - uses: mlugg/setup-zig@v2 - uses: actions/download-artifact@main with: name: ignore-me-zig @@ -319,6 +319,7 @@ jobs: dub build :instancingcompute dub build :mrt dub build :noninterleaved + dub build :nuklear dub build :offscreen dub build :quad dub build :shapes diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c9cd322..19089fd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ ## Updates +### 03-Aug-2025 + +sokol_app.h: character input on Windows now transparently supports surrogate pairs, +for details see PR https://github.com/floooh/sokol/pull/1304. Many thanks to @cloudwu +for the PR! + ### 29-Jun-2025 The Dear ImGui backend in sokol_imgui.h has been updated for Dear ImGui 1.92.0. @@ -23,7 +29,7 @@ If you are using Dear ImGui C bindings, you'll need to fix the places where the `igImage()` function is called. This now takes an `ImTextureRef` struct instead of an `ImTextureID` (in C++ this is not a problem since the ImTextureRef is automatically constructed on the fly from the ImTextureID, but in C you'll -need to do this by hand, e.g. change: +need to do this by hand, e.g. change): ```c igImage(my_tex_id, ...); diff --git a/bindgen/gen_all.py b/bindgen/gen_all.py index 7f34a89c..84f416bc 100644 --- a/bindgen/gen_all.py +++ b/bindgen/gen_all.py @@ -1,4 +1,4 @@ -import os, argparse, gen_nim, gen_zig, gen_odin, gen_rust, gen_d, gen_jai, gen_c3 +import os, argparse, gen_nim, gen_zig, gen_odin, gen_rust, gen_d, gen_jai, gen_c3, shutil parser = argparse.ArgumentParser() parser.add_argument("--zig-tiger-style", action="store_true", help="Enable zig tiger style mode.") @@ -48,14 +48,23 @@ for task in zig_tasks: # D d_tasks = [ *tasks, + [ '../sokol_args.h', 'sargs_', [] ], [ '../sokol_fetch.h', 'sfetch_', [] ], [ '../util/sokol_memtrack.h', 'smemtrack_', [] ], [ '../util/sokol_imgui.h', 'simgui_', ['sg_', 'sapp_'] ], ] +# check if nuklear.h is available and copy it +if os.path.exists('../tests/ext/nuklear.h'): + d_tasks.append([ '../util/sokol_nuklear.h', 'snk_', ['sg_', 'sapp_'] ]) + if os.path.exists('sokol-d'): + shutil.copy('../tests/ext/nuklear.h', 'sokol-d/src/sokol/c/nuklear.h') gen_d.prepare() for task in d_tasks: [c_header_path, main_prefix, dep_prefixes] = task gen_d.gen(c_header_path, main_prefix, dep_prefixes) +# drop nuklear.h if copied (after generated D files) +if os.path.exists('sokol-d/src/sokol/c/nuklear.h'): + os.remove('sokol-d/src/sokol/c/nuklear.h') # Rust gen_rust.prepare() @@ -67,4 +76,4 @@ for task in tasks: gen_c3.prepare() for task in tasks: [c_header_path, main_prefix, dep_prefixes] = task - gen_c3.gen(c_header_path, main_prefix, dep_prefixes) + gen_c3.gen(c_header_path, main_prefix, dep_prefixes)
\ No newline at end of file diff --git a/bindgen/gen_d.py b/bindgen/gen_d.py index d98d0385..6bcedb73 100644 --- a/bindgen/gen_d.py +++ b/bindgen/gen_d.py @@ -24,6 +24,7 @@ module_names = { 'slog_': 'log', 'sg_': 'gfx', 'sapp_': 'app', + 'sargs_': 'args', 'stm_': 'time', 'saudio_': 'audio', 'sgl_': 'gl', @@ -32,6 +33,7 @@ module_names = { 'sglue_': 'glue', 'sfetch_': 'fetch', 'simgui_': 'imgui', + 'snk_': 'nuklear', 'smemtrack_': 'memtrack', } @@ -39,6 +41,7 @@ c_source_paths = { 'slog_': 'sokol-d/src/sokol/c/sokol_log.c', 'sg_': 'sokol-d/src/sokol/c/sokol_gfx.c', 'sapp_': 'sokol-d/src/sokol/c/sokol_app.c', + 'sargs_': 'sokol-d/src/sokol/c/sokol_args.c', 'stm_': 'sokol-d/src/sokol/c/sokol_time.c', 'saudio_': 'sokol-d/src/sokol/c/sokol_audio.c', 'sgl_': 'sokol-d/src/sokol/c/sokol_gl.c', @@ -47,6 +50,7 @@ c_source_paths = { 'sglue_': 'sokol-d/src/sokol/c/sokol_glue.c', 'sfetch_': 'sokol-d/src/sokol/c/sokol_fetch.c', 'simgui_': 'sokol-d/src/sokol/c/sokol_imgui.c', + 'snk_': 'sokol-d/src/sokol/c/sokol_nuklear.c', 'smemtrack_': 'sokol-d/src/sokol/c/sokol_memtrack.c', } @@ -56,7 +60,8 @@ ignores = [ ] c_callbacks = [ - 'slog_func' + 'slog_func', + 'nk_plugin_filter', ] overrides = { @@ -77,6 +82,9 @@ overrides = { 'sdtx_font.font_index': 'uint32_t', 'SGL_NO_ERROR': 'SGL_ERROR_NO_ERROR', 'sfetch_continue': 'continue_fetching', + 'struct nk_context': 'NkContext', + 'nk_handle': 'NkHandle', + 'nk_flags': 'NkFlags', } prim_types = { @@ -123,10 +131,26 @@ class TypeConverter: self.enum_types = enum_types def as_d_type(self, c_type, is_param=False, decl_name=None): + c_type = c_type.strip() + # Check for override first + if c_type in overrides: + return overrides[c_type] + d_to_c_types = {v: k for k, v in prim_types.items()} if c_type in d_to_c_types: c_type = d_to_c_types[c_type] + # Handle struct keyword in type (e.g., "struct nk_context *") + if c_type.startswith('struct '): + c_type = c_type[7:].strip() # Remove 'struct' prefix + if c_type in overrides: + return overrides[c_type] + if c_type.endswith('*'): + # External struct pointer, treat as opaque + return 'void*' if is_param else 'void*' + # External struct by value, treat as named type if overridden + return overrides.get(c_type, c_type) + if util.is_func_ptr(c_type): return self.as_func_ptr_type(c_type, decl_name) @@ -167,6 +191,14 @@ class TypeConverter: if util.is_array_type(c_type): return self.as_array_type(c_type) + # Handle external types (e.g., nk_handle, nk_flags) not in struct_types or prim_types + if c_type not in self.struct_types and c_type not in prim_types: + if c_type.endswith('*'): + # Treat pointer to unknown type as void* + return 'void*' if is_param else 'void*' + # Treat unknown type by value as itself (assuming it's defined elsewhere) + return overrides.get(c_type, c_type) + raise ValueError(f"Unsupported C type: {c_type} in declaration: {decl_name or 'unknown'}") def as_d_struct_type(self, s): @@ -274,6 +306,17 @@ def pre_parse(inp): enum_types.append(enum_name) enum_items[enum_name] = [as_enum_item_name(item['name']) for item in decl['items']] +def gen_nuklear_types(): + """Generate type declarations for Nuklear external types.""" + l("/++ Nuklear external type declarations +/") + l("extern(C) struct NkContext;") + l("extern(C) union NkHandle {") + l(" void* ptr;") + l(" int id;") + l("}") + l("alias NkFlags = uint;") + l("alias nk_plugin_filter = extern(C) int function(const(NkContext)*, NkHandle, int*, int) @system @nogc nothrow;") + def gen_struct(decl, type_converter): struct_name = overrides.get(decl['name'], decl['name']) d_type = type_converter.as_d_struct_type(struct_name) @@ -417,6 +460,9 @@ def gen_module(inp, dep_prefixes, c_header_path): l(f'module sokol.{inp["module"]};') logging.info(f"Generating imports for module {inp['module']}") gen_imports(inp, dep_prefixes) + # Add Nuklear types for the nuklear module + if inp['module'] == 'nuklear': + gen_nuklear_types() pre_parse(inp) type_converter = TypeConverter(inp['prefix'], struct_types, enum_types) for decl in inp['decls']: diff --git a/bindgen/gen_zig.py b/bindgen/gen_zig.py index 0281e9b8..e877e6eb 100644 --- a/bindgen/gen_zig.py +++ b/bindgen/gen_zig.py @@ -352,7 +352,7 @@ def gen_struct(decl, prefix): elif is_const_prim_ptr(field_type): l(f" {field_name}: ?[*]const {as_zig_prim_type(util.extract_ptr_type(field_type))} = null,") elif util.is_func_ptr(field_type): - l(f" {field_name}: ?*const fn ({funcptr_args_c(field_type, prefix)}) callconv(.C) {funcptr_result_c(field_type)} = null,") + l(f" {field_name}: ?*const fn ({funcptr_args_c(field_type, prefix)}) callconv(.c) {funcptr_result_c(field_type)} = null,") elif util.is_1d_array_type(field_type): array_type = util.extract_array_type(field_type) array_sizes = util.extract_array_sizes(field_type) @@ -507,33 +507,13 @@ def gen_helpers(inp): l('}') l('') if inp['prefix'] == 'sdtx_': - l('// std.fmt compatible Writer') - l('pub const Writer = struct {') - l(' pub const Error = error{};') - l(' pub fn writeAll(self: Writer, bytes: []const u8) Error!void {') - l(' _ = self;') - l(' for (bytes) |byte| {') - l(' putc(byte);') - l(' }') - l(' }') - l(' pub fn writeByteNTimes(self: Writer, byte: u8, n: usize) Error!void {') - l(' _ = self;') - l(' var i: u64 = 0;') - l(' while (i < n) : (i += 1) {') - l(' putc(byte);') - l(' }') - l(' }') - l(' pub fn writeBytesNTimes(self: Writer, bytes: []const u8, n: usize) Error!void {') - l(' var i: usize = 0;') - l(' while (i < n) : (i += 1) {') - l(' try self.writeAll(bytes);') - l(' }') - l(' }') - l('};') l('// std.fmt-style formatted print') l('pub fn print(comptime fmt: anytype, args: anytype) void {') - l(' const writer: Writer = .{};') - l(' @import("std").fmt.format(writer, fmt, args) catch {};') + l(' const cbuf = getClearedFmtBuffer();') + l(' const p: [*]u8 = @constCast(@ptrCast(cbuf.ptr));') + l(' const buf = p[0..cbuf.size];') + l(' const out = @import("std").fmt.bufPrint(buf, fmt, args) catch "";') + l(' for (out) |c| putc(c);') l('}') l('') diff --git a/sokol_app.h b/sokol_app.h index d19bd6c6..ba72a0c7 100644 --- a/sokol_app.h +++ b/sokol_app.h @@ -2664,6 +2664,7 @@ typedef struct { HICON small_icon; HCURSOR cursors[_SAPP_MOUSECURSOR_NUM]; UINT orig_codepage; + WCHAR surrogate; RECT stored_window_rect; // used to restore window pos/size when toggling fullscreen => windowed bool is_win10_or_greater; bool in_create_window; @@ -7558,11 +7559,20 @@ _SOKOL_PRIVATE void _sapp_win32_key_event(sapp_event_type type, int vk, bool rep _SOKOL_PRIVATE void _sapp_win32_char_event(uint32_t c, bool repeat) { if (_sapp_events_enabled() && (c >= 32)) { - _sapp_init_event(SAPP_EVENTTYPE_CHAR); - _sapp.event.modifiers = _sapp_win32_mods(); - _sapp.event.char_code = c; - _sapp.event.key_repeat = repeat; - _sapp_call_event(&_sapp.event); + if (c >= 0xD800 && c <= 0xDBFF) { + _sapp.win32.surrogate = (WCHAR)c - 0xD800; + } else { + if (c > 0xDC00 && c <= 0xDFFF) { + c = (uint32_t)(_sapp.win32.surrogate) << 10 | (c - 0xDC00); + c += 0x10000; + _sapp.win32.surrogate = 0; + } + _sapp_init_event(SAPP_EVENTTYPE_CHAR); + _sapp.event.modifiers = _sapp_win32_mods(); + _sapp.event.char_code = c; + _sapp.event.key_repeat = repeat; + _sapp_call_event(&_sapp.event); + } } } @@ -7922,6 +7932,7 @@ _SOKOL_PRIVATE void _sapp_win32_create_window(void) { const int win_width = rect.right - rect.left; const int win_height = rect.bottom - rect.top; _sapp.win32.in_create_window = true; + _sapp.win32.surrogate = 0; _sapp.win32.hwnd = CreateWindowExW( win_ex_style, // dwExStyle L"SOKOLAPP", // lpClassName diff --git a/sokol_gfx.h b/sokol_gfx.h index 7b802cbc..fa0507db 100644 --- a/sokol_gfx.h +++ b/sokol_gfx.h @@ -254,8 +254,8 @@ Both sg_apply_viewport() and sg_apply_scissor_rect() must be called inside a rendering pass (e.g. not in a compute pass, or outside a pass) - Note that sg_begin_default_pass() and sg_begin_pass() will reset both the - viewport and scissor rectangles to cover the entire framebuffer. + Note that sg_begin_pass() will reset both the viewport and scissor + rectangles to cover the entire framebuffer. --- to update (overwrite) the content of buffer and image resources, call: diff --git a/util/sokol_debugtext.h b/util/sokol_debugtext.h index 8e990770..3cdb308c 100644 --- a/util/sokol_debugtext.h +++ b/util/sokol_debugtext.h @@ -753,6 +753,9 @@ SOKOL_DEBUGTEXT_API_DECL void sdtx_putr(const char* str, int len); // 'put ra SOKOL_DEBUGTEXT_API_DECL int sdtx_printf(const char* fmt, ...) SOKOL_DEBUGTEXT_PRINTF_ATTR; SOKOL_DEBUGTEXT_API_DECL int sdtx_vprintf(const char* fmt, va_list args); +/* language bindings helper: get the internal printf format buffer */ +SOKOL_DEBUGTEXT_API_DECL sdtx_range sdtx_get_cleared_fmt_buffer(void); + #ifdef __cplusplus } /* extern "C" */ /* C++ const-ref wrappers */ @@ -4964,6 +4967,16 @@ SOKOL_DEBUGTEXT_API_DECL int sdtx_printf(const char* fmt, ...) { return res; } +SOKOL_DEBUGTEXT_API_DECL sdtx_range sdtx_get_cleared_fmt_buffer(void) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + SOKOL_ASSERT(_sdtx.fmt_buf && (_sdtx.fmt_buf_size >= 2)); + memset(_sdtx.fmt_buf, 0, _sdtx.fmt_buf_size); + sdtx_range res; _sdtx_clear(&res, sizeof(res)); + res.ptr = _sdtx.fmt_buf; + res.size = _sdtx.fmt_buf_size - 1; + return res; +} + SOKOL_API_IMPL void sdtx_draw(void) { SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); _sdtx_context_t* ctx = _sdtx.cur_ctx; diff --git a/util/sokol_imgui.h b/util/sokol_imgui.h index 9a8f07a1..935e7708 100644 --- a/util/sokol_imgui.h +++ b/util/sokol_imgui.h @@ -231,19 +231,22 @@ ON ATTACHING YOUR OWN FONTS =========================== - Since Dear ImGui 1.92.0, using using non-default fonts has been greatly simplified: + Since Dear ImGui 1.92.0 using non-default fonts has been greatly simplified: First, call `simgui_setup()` with the `.no_default_font` so that sokol_imgui.h skips adding the default font. ...then simply call `AddFontDefault()` or `AddFontFromMemoryTTF()` on - the Dear ImGui IO object. + the Dear ImGui IO object, everything else is taken care of automatically. - Do *NOT*: + Specifically, do *NOT*: - call the deprecated `GetTexDataAsRGBA32()` function - - create a sokol-gfx image object for the font + - create a sokol-gfx image object for the font atlas - set the `Font->TexID` on the ImGui IO object + All those things are now handled inside sokol_imgui.h via a new 'texture update' + callback which is called by Dear ImGui whenever the state of the font atlas + texture changes. ON USER-PROVIDED IMAGES AND SAMPLERS ==================================== @@ -284,7 +287,7 @@ sg_image img = sg_query_view_image(tex_view); - NOTE on C bindings since 1.92.0: + NOTE on C bindings since Dear ImGui 1.92.0: Since Dear ImGui v1.92.0 the ImGui::Image function takes an ImTextureRef object instead of ImTextureID. In C++ this doesn't diff --git a/util/sokol_spine.h b/util/sokol_spine.h index e24dc47d..0a9ed690 100644 --- a/util/sokol_spine.h +++ b/util/sokol_spine.h @@ -385,7 +385,7 @@ const sspine_layer_transform tform = { ... }; - sg_begin_default_pass(...); + sg_begin_pass(...); sspine_draw_layer(0, tform); sg_end_pass(); sg_commit(); |