Skip to content

Commit b2ebc4d

Browse files
committed
Don't generate thunks for functions that might not exist in slightly older versions of ImGui (#94)
1 parent 1afd129 commit b2ebc4d

File tree

7 files changed

+178
-40
lines changed

7 files changed

+178
-40
lines changed

dear_bindings.py

Lines changed: 58 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Dear Bindings Version v0.13 WIP
1+
# Dear Bindings Version v0.13
22
# Generates C-language headers for Dear ImGui
33
# Developed by Ben Carter (e-mail: ben AT shironekolabs.com, github: @ShironekoBen)
44

@@ -95,8 +95,7 @@ def convert_header(
9595
backend_include_dir,
9696
emit_combined_json_metadata,
9797
prefix_replacements
98-
):
99-
98+
):
10099
# Set up context and DOM root
101100
context = code_dom.ParseContext()
102101
dom_root = code_dom.DOMHeaderFileSet()
@@ -164,7 +163,7 @@ def convert_header(
164163
"ImNewDummy", # ImGui <1.82
165164
"ImNewWrapper", # ImGui >=1.82
166165
# Templated stuff in imgui_internal.h
167-
"ImBitArray", # template with two parameters, not supported
166+
"ImBitArray", # template with two parameters, not supported
168167
"ImSpanAllocator",
169168
])
170169
# Remove all functions from certain types, as they're not really useful
@@ -187,10 +186,9 @@ def convert_header(
187186
"ImGui::SliderBehaviorT",
188187
"ImGui::RoundScalarWithFormatT",
189188
"ImGui::CheckboxFlagsT"])
190-
189+
191190
mod_remove_functions.apply(dom_root, ["ImGui::GetInputTextState",
192191
"ImGui::DebugNodeInputTextState"])
193-
194192

195193
mod_add_prefix_to_loose_functions.apply(dom_root, "c")
196194

@@ -229,32 +227,59 @@ def convert_header(
229227

230228
# If we have docking support, add some functions to allow overriding platform IO functions that return structures
231229
if have_docking_support:
230+
# Check if we have GetWindowFramebufferScale and GetWindowWorkAreaInsets, as those are relatively recent additions
231+
# and they may not exist
232+
233+
have_getWindowFramebufferScale = False
234+
have_getWindowWorkAreaInsets = False
235+
236+
for field in dom_root.list_all_children_of_type(code_dom.DOMFieldDeclaration):
237+
if field.get_fully_qualified_name() == "ImGuiPlatformIO::Platform_GetWindowFramebufferScale":
238+
have_getWindowFramebufferScale = True
239+
if field.get_fully_qualified_name() == "ImGuiPlatformIO::Platform_GetWindowWorkAreaInsets":
240+
have_getWindowWorkAreaInsets = True
241+
232242
# Implementation for these is in templates/imgui-header-template.cpp
233243
mod_add_manual_helper_functions.apply(dom_root,
234244
[
235245
"void ImGuiPlatformIO_SetPlatform_GetWindowPos(void(*getWindowPosFunc)(ImGuiViewport* vp, ImVec2* result)); "
236246
"// Set ImGuiPlatformIO::Platform_GetWindowPos in a C-compatible mannner",
237247
"void ImGuiPlatformIO_SetPlatform_GetWindowSize(void(*getWindowSizeFunc)(ImGuiViewport* vp, ImVec2* result)); "
238-
"// Set ImGuiPlatformIO::Platform_GetWindowSize in a C-compatible mannner",
239-
"void ImGuiPlatformIO_SetPlatform_GetWindowFramebufferScale(void(*getWindowFramebufferScaleFunc)(ImGuiViewport* vp, ImVec2* result)); "
240-
"// Set ImGuiPlatformIO::Platform_GetWindowFramebufferScale in a C-compatible mannner",
241-
"void ImGuiPlatformIO_SetPlatform_GetWindowWorkAreaInsets(void(*getWindowWorkAreaInsetsFunc)(ImGuiViewport* vp, ImVec4* result)); "
242-
"// Set ImGuiPlatformIO::Platform_GetWindowWorkAreaInsets in a C-compatible mannner"
248+
"// Set ImGuiPlatformIO::Platform_GetWindowSize in a C-compatible mannner"
243249
])
250+
251+
if have_getWindowFramebufferScale:
252+
mod_add_manual_helper_functions.apply(dom_root,
253+
[
254+
"void ImGuiPlatformIO_SetPlatform_GetWindowFramebufferScale(void(*getWindowFramebufferScaleFunc)(ImGuiViewport* vp, ImVec2* result)); "
255+
"// Set ImGuiPlatformIO::Platform_GetWindowFramebufferScale in a C-compatible mannner",
256+
])
257+
mod_add_defines.apply(dom_root, [ "#define IMGUI_DEAR_BINDINGS_HAS_GETWINDOWFRAMEBUFFERSCALE" ])
258+
259+
if have_getWindowWorkAreaInsets:
260+
mod_add_manual_helper_functions.apply(dom_root,
261+
[
262+
"void ImGuiPlatformIO_SetPlatform_GetWindowWorkAreaInsets(void(*getWindowWorkAreaInsetsFunc)(ImGuiViewport* vp, ImVec4* result)); "
263+
"// Set ImGuiPlatformIO::Platform_GetWindowWorkAreaInsets in a C-compatible mannner"
264+
])
265+
mod_add_defines.apply(dom_root, ["#define IMGUI_DEAR_BINDINGS_HAS_GETWINDOWWORKAREAINSETS"])
266+
244267
# Add comments to try and point people at the helpers
245268
mod_add_field_comment.apply(dom_root,
246-
"ImGuiPlatformIO::Platform_GetWindowPos",
247-
"(Use ImGuiPlatformIO_SetPlatform_GetWindowPos() to set this from C, otherwise you will likely encounter stack corruption)")
269+
"ImGuiPlatformIO::Platform_GetWindowPos",
270+
"(Use ImGuiPlatformIO_SetPlatform_GetWindowPos() to set this from C, otherwise you will likely encounter stack corruption)")
248271

249272
mod_add_field_comment.apply(dom_root,
250273
"ImGuiPlatformIO::Platform_GetWindowSize",
251274
"(Use ImGuiPlatformIO_SetPlatform_GetWindowSize() to set this from C, otherwise you will likely encounter stack corruption)")
252275

253-
mod_add_field_comment.apply(dom_root,
276+
if have_getWindowFramebufferScale:
277+
mod_add_field_comment.apply(dom_root,
254278
"ImGuiPlatformIO::Platform_GetWindowFramebufferScale",
255279
"(Use ImGuiPlatformIO_SetPlatform_GetWindowFramebufferScale() to set this from C, otherwise you will likely encounter stack corruption)")
256280

257-
mod_add_field_comment.apply(dom_root,
281+
if have_getWindowWorkAreaInsets:
282+
mod_add_field_comment.apply(dom_root,
258283
"ImGuiPlatformIO::Platform_GetWindowWorkAreaInsets",
259284
"(Use ImGuiPlatformIO_SetPlatform_GetWindowWorkAreaInsets() to set this from C, otherwise you will likely encounter stack corruption)")
260285

@@ -307,15 +332,15 @@ def convert_header(
307332
# only in the type of the callback function. The normal disambiguation system can't handle that, so instead we
308333
# manually rename the older versions of those functions here.
309334
mod_rename_function_by_signature.apply(dom_root,
310-
'ImGui_Combo', # Function name
311-
'old_callback', # Argument to look for to identify this function
312-
'ImGui_ComboObsolete' # New name
313-
)
335+
'ImGui_Combo', # Function name
336+
'old_callback', # Argument to look for to identify this function
337+
'ImGui_ComboObsolete' # New name
338+
)
314339
mod_rename_function_by_signature.apply(dom_root,
315-
'ImGui_ListBox', # Function name
316-
'old_callback', # Argument to look for to identify this function
317-
'ImGui_ListBoxObsolete' # New name
318-
)
340+
'ImGui_ListBox', # Function name
341+
'old_callback', # Argument to look for to identify this function
342+
'ImGui_ListBoxObsolete' # New name
343+
)
319344

320345
# The DirectX backends declare some DirectX types that need to not have _t appended to their typedef names
321346
mod_mark_structs_as_using_unmodified_name_for_typedef.apply(dom_root,
@@ -379,7 +404,7 @@ def convert_header(
379404
],
380405
type_priorities={
381406
})
382-
407+
383408
if not no_generate_default_arg_functions:
384409
mod_generate_default_argument_functions.apply(dom_root,
385410
# We ignore functions that don't get called often because in those
@@ -415,7 +440,8 @@ def convert_header(
415440
# Widgets
416441
'ImGui_ProgressBar',
417442
'ImGui_ColorPicker4',
418-
'ImGui_TreePushPtr', # Ensure why core lib has this default to NULL?
443+
'ImGui_TreePushPtr',
444+
# Ensure why core lib has this default to NULL?
419445
'ImGui_BeginListBox',
420446
'ImGui_ListBox',
421447
'ImGui_MenuItemBoolPtr',
@@ -510,7 +536,7 @@ def convert_header(
510536
'ImGui_Text',
511537
'ImGuiTextBuffer_appendf'
512538
])
513-
539+
514540
if is_imgui_internal:
515541
mod_move_elements.apply(dom_root,
516542
main_src_root,
@@ -573,7 +599,6 @@ def convert_header(
573599
(code_dom.DOMPreprocessorIf, 'ImDrawIdx', False, True),
574600
])
575601

576-
577602
# Make all functions use CIMGUI_API/CIMGUI_IMPL_API
578603
mod_make_all_functions_use_imgui_api.apply(dom_root)
579604
# Rename the API defines
@@ -620,7 +645,7 @@ def convert_header(
620645
])
621646

622647
mod_replace_typedef_with_opaque_buffer.apply(dom_root, [
623-
("ImBitArrayForNamedKeys", 20) # template with two parameters, not supported
648+
("ImBitArrayForNamedKeys", 20) # template with two parameters, not supported
624649
])
625650

626651
# Remove namespaced define
@@ -712,14 +737,14 @@ def convert_header(
712737
print("Dear Bindings: parse Dear ImGui headers, convert to C and output metadata.")
713738

714739
# Debug code
715-
#type_comprehender.get_type_description("void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* cmd)").dump(0)
740+
# type_comprehender.get_type_description("void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* cmd)").dump(0)
716741

717742
default_template_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "src", "templates")
718743

719744
parser = argparse.ArgumentParser(
720-
add_help=True,
721-
epilog='Result code 0 is returned on success, 1 on conversion failure and 2 on '
722-
'parameter errors')
745+
add_help=True,
746+
epilog='Result code 0 is returned on success, 1 on conversion failure and 2 on '
747+
'parameter errors')
723748
parser.add_argument('src',
724749
help='Path to source header file to process (generally imgui.h)')
725750
parser.add_argument('-o', '--output',
@@ -803,7 +828,7 @@ def convert_header(
803828
sys.exit(1)
804829
index = replacement_str.index('=')
805830
old_prefix = replacement_str[:index]
806-
new_prefix = replacement_str[(index+1):]
831+
new_prefix = replacement_str[(index + 1):]
807832
prefix_replacements[old_prefix] = new_prefix
808833

809834
# --custom-namespace-prefix is just handled as a handy short form for --replace-prefix ImGui_=<something>

docs/Changelog.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
--- v0.13 WIP
1+
--- v0.13
22

33
* Cosmetic fix to the naming of the PlatformIO thunk helper functions, and made the comment more explicit about why
44
setting those callbacks directly is a bad idea.
5+
* Fixed PlatformIO thunk helpers getting generated on older versions of Dear ImGui where the relevant functions
6+
didn't exist yet. (#94)
57

68
--- v0.12
79

src/modifiers/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,4 @@
6464
from . import mod_rename_prefix
6565
from . import mod_mark_structs_as_single_line_definition
6666
from . import mod_add_field_comment
67+
from . import mod_add_defines

src/modifiers/mod_add_defines.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
from src import code_dom
2+
from src import utils
3+
4+
5+
# This modifier adds one or more new defines to the appropriate section of the file
6+
# defines should be a list of strings each containing a valid define
7+
# required_defines can contain a list of prerequisite defines which will be added around the emitted defines
8+
def apply(dom_root, defines, required_defines=None):
9+
# Generate a list of DOM elements to add
10+
11+
elements_to_append = []
12+
define_container = None
13+
14+
# Generate #if statement as required
15+
if required_defines is not None:
16+
for define in required_defines:
17+
check = code_dom.DOMPreprocessorIf()
18+
check.expression_tokens = [utils.create_token(define)]
19+
if define_container is not None:
20+
define_container.add_child(check)
21+
else:
22+
elements_to_append.append(check)
23+
define_container = check
24+
25+
# Generate defines
26+
for define in defines:
27+
define_element = utils.create_preprocessor_declaration(define)
28+
if define_container is not None:
29+
define_container.add_child(define_element)
30+
else:
31+
elements_to_append.append(define_element)
32+
33+
if len(elements_to_append) == 0:
34+
return
35+
36+
# Add an explanatory comment, if it isn't there already
37+
38+
existing_explanatory_comment = None
39+
for comment in dom_root.list_all_children_of_type(code_dom.DOMComment):
40+
if "Extra defines set by Dear Bindings for internal purposes" in comment.comment_text:
41+
existing_explanatory_comment = comment
42+
43+
if existing_explanatory_comment is None:
44+
comment = code_dom.DOMComment()
45+
comment.comment_text = "// Extra defines set by Dear Bindings for internal purposes"
46+
elements_to_append.insert(0, comment)
47+
# ...and a blank line before it to be neat
48+
elements_to_append.insert(0, code_dom.DOMBlankLines(1))
49+
50+
# Add to the file
51+
52+
insert_point = dom_root.children[0] # Default to adding at the top of the file if we can't find anywhere else
53+
54+
# Look for the right section to add these to - if we can, we want to put them under the normal ImGui defines
55+
if existing_explanatory_comment is not None:
56+
# If we already started a section, just insert at the end of that
57+
insert_point = existing_explanatory_comment
58+
else:
59+
for comment in dom_root.list_all_children_of_type(code_dom.DOMComment):
60+
if "Library Version" in comment.comment_text:
61+
insert_point = comment
62+
# No early-out here because we actually want the /last/ instance of this comment
63+
64+
# Skip down past any existing content there
65+
next_line = insert_point.parent.get_next_child(insert_point)
66+
while (next_line is not None) and (not isinstance(next_line, code_dom.DOMBlankLines)):
67+
insert_point = next_line
68+
next_line = insert_point.parent.get_next_child(insert_point)
69+
70+
if insert_point is not None:
71+
# Skip down past any other comments
72+
next_line = insert_point.parent.get_next_child(insert_point)
73+
while isinstance(next_line, code_dom.DOMComment):
74+
insert_point = next_line
75+
next_line = insert_point.parent.get_next_child(insert_point)
76+
77+
insert_point.parent.insert_after_child(insert_point, elements_to_append)

src/modifiers/mod_add_field_comment.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
# This modifier adds a comment to the field with the fully-qualified name given
66
# If a comment already exists the comment text will be appended to it with a space
77
def apply(dom_root, field_name, comment):
8-
for function in dom_root.list_all_children_of_type(code_dom.DOMFieldDeclaration):
9-
if function.get_fully_qualified_name() == field_name:
10-
utils.append_comment_text(function, comment)
8+
for field in dom_root.list_all_children_of_type(code_dom.DOMFieldDeclaration):
9+
if field.get_fully_qualified_name() == field_name:
10+
utils.append_comment_text(field, comment)

0 commit comments

Comments
 (0)