aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/gen_bindings.yml3
-rw-r--r--CHANGELOG.md8
-rw-r--r--bindgen/gen_all.py13
-rw-r--r--bindgen/gen_d.py48
-rw-r--r--bindgen/gen_zig.py32
-rw-r--r--sokol_app.h21
-rw-r--r--sokol_gfx.h4
-rw-r--r--util/sokol_debugtext.h13
-rw-r--r--util/sokol_imgui.h13
-rw-r--r--util/sokol_spine.h2
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();