891 lines
52 KiB
Python
891 lines
52 KiB
Python
# Dear Bindings Version v0.17
|
|
# Generates C-language headers for Dear ImGui
|
|
# Developed by Ben Carter (e-mail: ben AT shironekolabs.com, github: @ShironekoBen)
|
|
|
|
# Example command-line:
|
|
# python dear_bindings.py --output dcimgui ../imgui/imgui.h
|
|
# python dear_bindings.py --output dcimgui_internal --include ../imgui/imgui.h ../imgui/imgui_internal.h
|
|
|
|
# Example Input:
|
|
# imgui.h : a C++ header file (aiming to also support imgui_internal.h, implot.h etc.: support is not complete yet).
|
|
|
|
# Example output:
|
|
# dcimgui.h : a C header file for compilation by a modern C compiler, including full comments from original header file.
|
|
# dcimgui.cpp : a CPP implementation file which can to be linked into a C program.
|
|
# dcimgui.json : full metadata to reconstruct bindings for other programming languages, including full comments.
|
|
|
|
import os
|
|
from pathlib import Path
|
|
from src import code_dom
|
|
from src import c_lexer
|
|
from src import utils
|
|
import argparse
|
|
import sys
|
|
import traceback
|
|
from src.modifiers import *
|
|
from src.generators import *
|
|
from src.type_comprehension import *
|
|
|
|
|
|
# Insert a single header template file, complaining if it does not exist
|
|
# Replaces any expansions in the expansions dictionary with the given result
|
|
def insert_single_template(dest_file, template_file, expansions):
|
|
if not os.path.isfile(template_file):
|
|
print("Template file " + template_file + " could not be found (note that template file names are "
|
|
"expected to match source file names, so if you have "
|
|
"renamed imgui.h you will need to rename the template as "
|
|
"well). The common template file is included regardless of source "
|
|
"file name.")
|
|
sys.exit(2)
|
|
|
|
with open(template_file, "r") as src_file:
|
|
for line in src_file.readlines():
|
|
for before, after in expansions.items():
|
|
line = line.replace(before, after)
|
|
dest_file.write(line)
|
|
|
|
|
|
# Insert the contents of the appropriate header template file(s)
|
|
def insert_header_templates(dest_file, template_dir, src_file_name, dest_file_ext, expansions):
|
|
# Include the common template file
|
|
insert_single_template(dest_file,
|
|
os.path.join(template_dir, "common-header-template" + dest_file_ext),
|
|
expansions)
|
|
|
|
# Include the specific template for the file we are generating
|
|
insert_single_template(dest_file,
|
|
os.path.join(template_dir, src_file_name + "-header-template" + dest_file_ext),
|
|
expansions)
|
|
|
|
|
|
def parse_single_header(src_file, context):
|
|
print("Parsing " + src_file)
|
|
|
|
with open(src_file, "r") as f:
|
|
file_content = f.read()
|
|
|
|
# Tokenize file and then convert into a DOM
|
|
|
|
stream = c_lexer.tokenize(file_content)
|
|
|
|
if False: # Debug dump tokens
|
|
while True:
|
|
tok = stream.get_token()
|
|
if not tok:
|
|
break # No more input
|
|
print(tok)
|
|
return
|
|
|
|
return code_dom.DOMHeaderFile.parse(context, stream, os.path.split(src_file)[1])
|
|
|
|
|
|
# Parse the C++ header found in src_file, and write a C header to dest_file_no_ext.h, with binding implementation in
|
|
# dest_file_no_ext.cpp. Metadata will be written to dest_file_no_ext.json. implementation_header should point to a file
|
|
# containing the initial header block for the implementation (provided in the templates/ directory).
|
|
def convert_header(
|
|
src_file,
|
|
include_files,
|
|
dest_file_no_ext,
|
|
template_dir,
|
|
no_struct_by_value_arguments,
|
|
no_generate_default_arg_functions,
|
|
generate_unformatted_functions,
|
|
is_backend,
|
|
imgui_include_dir,
|
|
backend_include_dir,
|
|
emit_combined_json_metadata,
|
|
prefix_replacements
|
|
):
|
|
# Set up context and DOM root
|
|
context = code_dom.ParseContext()
|
|
dom_root = code_dom.DOMHeaderFileSet()
|
|
|
|
# Check if we'll do some special treatment for imgui_internal.h
|
|
is_imgui_internal = os.path.basename(src_file) == "imgui_internal.h"
|
|
|
|
# Parse any configuration include files and add them to the DOM
|
|
for include_file in include_files:
|
|
dom_root.add_child(parse_single_header(include_file, context))
|
|
|
|
# Parse and add the main header
|
|
main_src_root = parse_single_header(src_file, context)
|
|
dom_root.add_child(main_src_root)
|
|
|
|
# Check if the version of ImGui we are dealing with has docking support
|
|
have_docking_support = False
|
|
for define in dom_root.list_all_children_of_type(code_dom.DOMDefine):
|
|
if define.name == 'IMGUI_HAS_DOCK':
|
|
have_docking_support = True
|
|
|
|
# Assign a destination filename based on the output file
|
|
dest_file_name_only = os.path.basename(dest_file_no_ext)
|
|
_, main_src_root.dest_filename = os.path.split(dest_file_no_ext)
|
|
main_src_root.dest_filename += ".h" # Presume the primary output file is the .h
|
|
|
|
dom_root.validate_hierarchy()
|
|
# dom_root.dump()
|
|
|
|
print("Storing unmodified DOM")
|
|
|
|
dom_root.save_unmodified_clones()
|
|
|
|
print("Applying modifiers")
|
|
|
|
# Apply modifiers
|
|
|
|
# Add headers we need and remove those we don't
|
|
if not is_backend:
|
|
mod_add_includes.apply(dom_root, ["<stdbool.h>"]) # We need stdbool.h to get bool defined
|
|
mod_add_includes.apply(dom_root, ["<stdint.h>"]) # We need stdint.h to get int32_t
|
|
mod_remove_includes.apply(dom_root, ["<float.h>",
|
|
"<string.h>"])
|
|
|
|
if is_backend:
|
|
# Backends need to reference dcimgui.h, not imgui.h
|
|
mod_change_includes.apply(dom_root, {"\"imgui.h\"": "\"dcimgui.h\""})
|
|
|
|
# Backends need a forward-declaration for ImDrawData so that the code generator understands
|
|
# that it is an ImGui type and needs conversion
|
|
mod_add_forward_declarations.apply(main_src_root, ["struct ImDrawData;"])
|
|
|
|
# Look for "ImGui_ImplWin32_WndProcHandler" and rewrite the #if on it (this is a bit of a hack)
|
|
mod_rewrite_containing_preprocessor_conditional.apply(dom_root, "ImGui_ImplWin32_WndProcHandler",
|
|
"0",
|
|
"IMGUI_BACKEND_HAS_WINDOWS_H",
|
|
True)
|
|
|
|
mod_attach_preceding_comments.apply(dom_root)
|
|
mod_remove_function_bodies.apply(dom_root)
|
|
mod_assign_anonymous_type_names.apply(dom_root)
|
|
# Remove ImGuiOnceUponAFrame for now as it needs custom fiddling to make it usable from C
|
|
# Remove ImNewDummy/ImNewWrapper as it's a helper for C++ new (and C dislikes empty structs)
|
|
mod_remove_structs.apply(dom_root, ["ImGuiOnceUponAFrame",
|
|
"ImNewDummy", # ImGui <1.82
|
|
"ImNewWrapper", # ImGui >=1.82
|
|
# Templated stuff in imgui_internal.h
|
|
"ImBitArray", # template with two parameters, not supported
|
|
"ImSpanAllocator",
|
|
# These appear in the DX11 backend header and conflict with the official
|
|
# definitions
|
|
"ID3D11Device",
|
|
"ID3D11DeviceContext",
|
|
"ID3D11SamplerState",
|
|
"ID3D11Buffer"
|
|
])
|
|
# Remove all functions from certain types, as they're not really useful
|
|
mod_remove_all_functions_from_classes.apply(dom_root, ["ImVector", "ImSpan", "ImChunkStream"])
|
|
# Remove all functions from ImPool, since we can't handle nested template functions yet
|
|
mod_remove_all_functions_from_classes.apply(dom_root, ["ImPool"])
|
|
|
|
# Remove Value() functions which are dumb helpers over Text(), would need custom names otherwise
|
|
mod_remove_functions.apply(dom_root, ["ImGui::Value"])
|
|
# Remove ImQsort() functions as modifiers on function pointers seem to emit a "anachronism used: modifiers on data
|
|
# are ignored" warning.
|
|
mod_remove_functions.apply(dom_root, ["ImQsort"])
|
|
# FIXME: Remove incorrectly parsed constructor due to "explicit" keyword.
|
|
mod_remove_functions.apply(dom_root, ["ImVec2ih::ImVec2ih"])
|
|
# Remove ErrorLogCallbackToDebugLog() from imgui_internal.h as there isn't a ErrorLogCallbackToDebugLogV() version
|
|
# for the bindings to call right now
|
|
mod_remove_functions.apply(dom_root, ["ImGui::ErrorLogCallbackToDebugLog"])
|
|
# Remove ImRect::AsVec4() since it breaks things by returning a reference (which is something we should fix, but
|
|
# not a priority right now), as isn't really hugely necessary as it's just syntactic sugar over a cast anyway.
|
|
mod_remove_functions.apply(dom_root, ["ImRect::AsVec4"])
|
|
# Remove some templated functions from imgui_internal.h that we don't want and cause trouble
|
|
mod_remove_functions.apply(dom_root, ["ImGui::ScaleRatioFromValueT",
|
|
"ImGui::ScaleValueFromRatioT",
|
|
"ImGui::DragBehaviorT",
|
|
"ImGui::SliderBehaviorT",
|
|
"ImGui::RoundScalarWithFormatT",
|
|
"ImGui::CheckboxFlagsT",
|
|
# This is a slightly awkward one - there are two problems here. One is that
|
|
# the function stub generator goes back to the original parsed code when
|
|
# trying to generate casts and ends up using the un-instantiated template
|
|
# parameter T as the target for a cast. That is fixable, but the other issue
|
|
# is that it takes a const& to the element to push, and in the sole existing
|
|
# usage right now that's a ImFontBaked, which isn't an easy thing for the
|
|
# structure marshalling to handle. Really it should probably be fudged to
|
|
# take a pointer instead of const& or similar, as passing ImFontBaked by
|
|
# value isn't really a sensible thing to be doing in the first place, but
|
|
# for the moment I don't think there's actually any use-case for wanting to
|
|
# add things to BakedPool from outside of ImGui itself, so I'm going to
|
|
# adopt a wait-and-see stance on this for now.
|
|
"ImStableVector::push_back"])
|
|
|
|
mod_remove_functions.apply(dom_root, ["ImGui::GetInputTextState",
|
|
"ImGui::DebugNodeInputTextState"])
|
|
|
|
mod_add_prefix_to_loose_functions.apply(dom_root, "c")
|
|
|
|
if not is_backend:
|
|
# Add helper functions to create/destroy ImVectors
|
|
# Implementation code for these can be found in templates/imgui-header.cpp
|
|
mod_add_manual_helper_functions.apply(dom_root,
|
|
[
|
|
"void ImVector_Construct(void* vector); // Construct a "
|
|
"zero-size ImVector<> (of any type). This is primarily "
|
|
"useful when calling "
|
|
"ImFontGlyphRangesBuilder_BuildRanges()",
|
|
|
|
"void ImVector_Destruct(void* vector); // Destruct an "
|
|
"ImVector<> (of any type). Important: Frees the vector "
|
|
"memory but does not call destructors on contained objects "
|
|
"(if they have them)",
|
|
])
|
|
# ImStr conversion helper, only enabled if IMGUI_HAS_IMSTR is on
|
|
mod_add_manual_helper_functions.apply(dom_root,
|
|
[
|
|
"ImStr ImStr_FromCharStr(const char* b); // Build an ImStr "
|
|
"from a regular const char* (no data is copied, so you need to make "
|
|
"sure the original char* isn't altered as long as you are using the "
|
|
"ImStr)."
|
|
],
|
|
# This weirdness is because we want this to compile cleanly even if
|
|
# IMGUI_HAS_IMSTR wasn't defined
|
|
["defined(IMGUI_HAS_IMSTR)", "IMGUI_HAS_IMSTR"])
|
|
|
|
# Add a note to ImFontGlyphRangesBuilder_BuildRanges() pointing people at the helpers
|
|
mod_add_function_comment.apply(dom_root,
|
|
"ImFontGlyphRangesBuilder::BuildRanges",
|
|
"(ImVector_Construct()/ImVector_Destruct() can be used to safely "
|
|
"construct out_ranges)")
|
|
|
|
# If we have docking support, add some functions to allow overriding platform IO functions that return structures
|
|
if have_docking_support:
|
|
# Check if we have GetWindowFramebufferScale and GetWindowWorkAreaInsets, as those are relatively recent additions
|
|
# and they may not exist
|
|
|
|
have_getWindowFramebufferScale = False
|
|
have_getWindowWorkAreaInsets = False
|
|
|
|
for field in dom_root.list_all_children_of_type(code_dom.DOMFieldDeclaration):
|
|
if field.get_fully_qualified_name() == "ImGuiPlatformIO::Platform_GetWindowFramebufferScale":
|
|
have_getWindowFramebufferScale = True
|
|
if field.get_fully_qualified_name() == "ImGuiPlatformIO::Platform_GetWindowWorkAreaInsets":
|
|
have_getWindowWorkAreaInsets = True
|
|
|
|
# Implementation for these is in templates/imgui-header-template.cpp
|
|
mod_add_manual_helper_functions.apply(dom_root,
|
|
[
|
|
"void ImGuiPlatformIO_SetPlatform_GetWindowPos(void(*getWindowPosFunc)(ImGuiViewport* vp, ImVec2* result)); "
|
|
"// Set ImGuiPlatformIO::Platform_GetWindowPos in a C-compatible mannner",
|
|
"void ImGuiPlatformIO_SetPlatform_GetWindowSize(void(*getWindowSizeFunc)(ImGuiViewport* vp, ImVec2* result)); "
|
|
"// Set ImGuiPlatformIO::Platform_GetWindowSize in a C-compatible mannner"
|
|
])
|
|
|
|
if have_getWindowFramebufferScale:
|
|
mod_add_manual_helper_functions.apply(dom_root,
|
|
[
|
|
"void ImGuiPlatformIO_SetPlatform_GetWindowFramebufferScale(void(*getWindowFramebufferScaleFunc)(ImGuiViewport* vp, ImVec2* result)); "
|
|
"// Set ImGuiPlatformIO::Platform_GetWindowFramebufferScale in a C-compatible mannner",
|
|
])
|
|
mod_add_defines.apply(dom_root, [ "#define IMGUI_DEAR_BINDINGS_HAS_GETWINDOWFRAMEBUFFERSCALE" ])
|
|
|
|
if have_getWindowWorkAreaInsets:
|
|
mod_add_manual_helper_functions.apply(dom_root,
|
|
[
|
|
"void ImGuiPlatformIO_SetPlatform_GetWindowWorkAreaInsets(void(*getWindowWorkAreaInsetsFunc)(ImGuiViewport* vp, ImVec4* result)); "
|
|
"// Set ImGuiPlatformIO::Platform_GetWindowWorkAreaInsets in a C-compatible mannner"
|
|
])
|
|
mod_add_defines.apply(dom_root, ["#define IMGUI_DEAR_BINDINGS_HAS_GETWINDOWWORKAREAINSETS"])
|
|
|
|
# Add comments to try and point people at the helpers
|
|
mod_add_field_comment.apply(dom_root,
|
|
"ImGuiPlatformIO::Platform_GetWindowPos",
|
|
"(Use ImGuiPlatformIO_SetPlatform_GetWindowPos() to set this from C, otherwise you will likely encounter stack corruption)")
|
|
|
|
mod_add_field_comment.apply(dom_root,
|
|
"ImGuiPlatformIO::Platform_GetWindowSize",
|
|
"(Use ImGuiPlatformIO_SetPlatform_GetWindowSize() to set this from C, otherwise you will likely encounter stack corruption)")
|
|
|
|
if have_getWindowFramebufferScale:
|
|
mod_add_field_comment.apply(dom_root,
|
|
"ImGuiPlatformIO::Platform_GetWindowFramebufferScale",
|
|
"(Use ImGuiPlatformIO_SetPlatform_GetWindowFramebufferScale() to set this from C, otherwise you will likely encounter stack corruption)")
|
|
|
|
if have_getWindowWorkAreaInsets:
|
|
mod_add_field_comment.apply(dom_root,
|
|
"ImGuiPlatformIO::Platform_GetWindowWorkAreaInsets",
|
|
"(Use ImGuiPlatformIO_SetPlatform_GetWindowWorkAreaInsets() to set this from C, otherwise you will likely encounter stack corruption)")
|
|
|
|
mod_set_arguments_as_nullable.apply(dom_root, ["fmt"], False) # All arguments called "fmt" are non-nullable
|
|
mod_remove_operators.apply(dom_root)
|
|
mod_remove_heap_constructors_and_destructors.apply(dom_root)
|
|
mod_convert_references_to_pointers.apply(dom_root)
|
|
if no_struct_by_value_arguments:
|
|
mod_convert_by_value_struct_args_to_pointers.apply(dom_root)
|
|
# Assume IM_VEC2_CLASS_EXTRA and IM_VEC4_CLASS_EXTRA are never defined as they are likely to just cause problems
|
|
# if anyone tries to use it
|
|
mod_flatten_conditionals.apply(dom_root, "IM_VEC2_CLASS_EXTRA", False)
|
|
mod_flatten_conditionals.apply(dom_root, "IM_VEC4_CLASS_EXTRA", False)
|
|
mod_flatten_namespaces.apply(dom_root, {'ImGui': 'ImGui_', 'ImStb': 'ImStb_'})
|
|
mod_flatten_nested_classes.apply(dom_root)
|
|
# The custom type fudge here is a workaround for how template parameters are expanded
|
|
mod_flatten_templates.apply(dom_root, custom_type_fudges={'const ImFont**': 'ImFont* const*'})
|
|
# Remove dangling unspecialized template that flattening didn't handle
|
|
mod_remove_structs.apply(dom_root, ["ImVector_T"])
|
|
# Mark all the ImVector_ instantiations as single-line definitions
|
|
mod_mark_structs_as_single_line_definition.apply(dom_root, ["ImVector_"])
|
|
|
|
# We treat certain types as by-value types
|
|
mod_mark_by_value_structs.apply(dom_root, by_value_structs=[
|
|
'ImVec1',
|
|
'ImVec2',
|
|
'ImVec2i',
|
|
'ImVec2ih',
|
|
'ImVec4',
|
|
'ImColor',
|
|
'ImStr',
|
|
'ImRect',
|
|
'ImGuiListClipperRange',
|
|
'ImTextureRef'
|
|
])
|
|
mod_mark_internal_members.apply(dom_root)
|
|
mod_flatten_class_functions.apply(dom_root)
|
|
mod_flatten_inheritance.apply(dom_root)
|
|
mod_remove_nested_typedefs.apply(dom_root)
|
|
mod_remove_static_fields.apply(dom_root)
|
|
mod_remove_extern_fields.apply(dom_root)
|
|
mod_remove_constexpr.apply(dom_root)
|
|
mod_generate_imstr_helpers.apply(dom_root)
|
|
mod_remove_enum_forward_declarations.apply(dom_root)
|
|
mod_calculate_enum_values.apply(dom_root)
|
|
# Treat enum values ending with _ as internal, and _COUNT as being count values
|
|
mod_mark_special_enum_values.apply(dom_root, internal_suffixes=["_"], count_suffixes=["_COUNT"])
|
|
# Mark enums that end with Flags (or Flags_ for the internal ones) as being flag enums
|
|
mod_mark_flags_enums.apply(dom_root, ["Flags", "Flags_"])
|
|
|
|
# These two are special cases because there are now (deprecated) overloads that differ from the main functions
|
|
# only in the type of the callback function. The normal disambiguation system can't handle that, so instead we
|
|
# manually rename the older versions of those functions here.
|
|
mod_rename_function_by_signature.apply(dom_root,
|
|
'ImGui_Combo', # Function name
|
|
'old_callback', # Argument to look for to identify this function
|
|
'ImGui_ComboObsolete' # New name
|
|
)
|
|
mod_rename_function_by_signature.apply(dom_root,
|
|
'ImGui_ListBox', # Function name
|
|
'old_callback', # Argument to look for to identify this function
|
|
'ImGui_ListBoxObsolete' # New name
|
|
)
|
|
|
|
# The DirectX backends declare some DirectX types that need to not have _t appended to their typedef names
|
|
mod_mark_structs_as_using_unmodified_name_for_typedef.apply(dom_root,
|
|
["ID3D11Device",
|
|
"ID3D11DeviceContext",
|
|
"ID3D12Device",
|
|
"ID3D12DescriptorHeap",
|
|
"ID3D12GraphicsCommandList",
|
|
"D3D12_CPU_DESCRIPTOR_HANDLE",
|
|
"D3D12_GPU_DESCRIPTOR_HANDLE",
|
|
"IDirect3DDevice9",
|
|
"GLFWwindow",
|
|
"GLFWmonitor"
|
|
])
|
|
|
|
# These DirectX types are awkward and we need to use a pointer-based cast when converting them
|
|
mod_mark_types_for_pointer_cast.apply(dom_root, ["D3D12_CPU_DESCRIPTOR_HANDLE",
|
|
"D3D12_GPU_DESCRIPTOR_HANDLE"])
|
|
|
|
# SDL backend forward-declared types
|
|
mod_mark_structs_as_using_unmodified_name_for_typedef.apply(dom_root,
|
|
["SDL_Window",
|
|
"SDL_Renderer",
|
|
"SDL_Gamepad",
|
|
"_SDL_GameController"
|
|
])
|
|
|
|
if is_imgui_internal:
|
|
# Some functions in imgui_internal already have the Ex suffix,
|
|
# which wreaks havok on disambiguation
|
|
mod_rename_functions.apply(main_src_root, {
|
|
'ImGui_BeginMenuEx': 'ImGui_BeginMenuWithIcon',
|
|
'ImGui_MenuItemEx': 'ImGui_MenuItemWithIcon',
|
|
'ImGui_BeginTableEx': 'ImGui_BeginTableWithID',
|
|
'ImGui_ButtonEx': 'ImGui_ButtonWithFlags',
|
|
'ImGui_ImageButtonEx': 'ImGui_ImageButtonWithFlags',
|
|
'ImGui_InputTextEx': 'ImGui_InputTextWithHintAndSize',
|
|
'ImGui_RenderTextClippedEx': 'ImGui_RenderTextClippedWithDrawList',
|
|
})
|
|
|
|
mod_disambiguate_functions.apply(dom_root,
|
|
name_suffix_remaps={
|
|
# Some more user-friendly suffixes for certain types
|
|
'const char*': 'Str',
|
|
'char*': 'Str',
|
|
'unsigned int': 'Uint',
|
|
'unsigned int*': 'UintPtr',
|
|
'ImGuiID': 'ID',
|
|
'const void*': 'Ptr',
|
|
'void*': 'Ptr'},
|
|
# Functions that look like they have name clashes but actually don't
|
|
# thanks to preprocessor conditionals
|
|
functions_to_ignore=[
|
|
"cImFileOpen",
|
|
"cImFileClose",
|
|
"cImFileGetSize",
|
|
"cImFileRead",
|
|
"cImFileWrite"],
|
|
functions_to_rename_everything=[
|
|
"ImGui_CheckboxFlags" # This makes more sense as IntPtr/UIntPtr variants
|
|
],
|
|
type_priorities={
|
|
})
|
|
|
|
if not no_generate_default_arg_functions:
|
|
mod_generate_default_argument_functions.apply(dom_root,
|
|
# We ignore functions that don't get called often because in those
|
|
# cases the default helper doesn't add much value but does clutter
|
|
# up the header file
|
|
functions_to_ignore=[
|
|
# Main
|
|
'ImGui_CreateContext',
|
|
'ImGui_DestroyContext',
|
|
# Demo, Debug, Information
|
|
'ImGui_ShowDemoWindow',
|
|
'ImGui_ShowMetricsWindow',
|
|
'ImGui_ShowDebugLogWindow',
|
|
'ImGui_ShowStackToolWindow',
|
|
'ImGui_ShowAboutWindow',
|
|
'ImGui_ShowStyleEditor',
|
|
# Styles
|
|
'ImGui_StyleColorsDark',
|
|
'ImGui_StyleColorsLight',
|
|
'ImGui_StyleColorsClassic',
|
|
# Windows
|
|
'ImGui_Begin',
|
|
'ImGui_BeginChild',
|
|
'ImGui_BeginChildID',
|
|
'ImGui_SetNextWindowSizeConstraints',
|
|
# Scrolling
|
|
'ImGui_SetScrollHereX',
|
|
'ImGui_SetScrollHereY',
|
|
'ImGui_SetScrollFromPosX',
|
|
'ImGui_SetScrollFromPosY',
|
|
# Parameters stacks
|
|
'ImGui_PushTextWrapPos',
|
|
# Widgets
|
|
'ImGui_ProgressBar',
|
|
'ImGui_ColorPicker4',
|
|
'ImGui_TreePushPtr',
|
|
# Ensure why core lib has this default to NULL?
|
|
'ImGui_BeginListBox',
|
|
'ImGui_ListBox',
|
|
'ImGui_MenuItemBoolPtr',
|
|
'ImGui_BeginPopupModal',
|
|
'ImGui_OpenPopupOnItemClick',
|
|
'ImGui_TableGetColumnName',
|
|
'ImGui_TableGetColumnFlags',
|
|
'ImGui_TableSetBgColor',
|
|
'ImGui_GetColumnWidth',
|
|
'ImGui_GetColumnOffset',
|
|
'ImGui_BeginTabItem',
|
|
# Misc
|
|
'ImGui_LogToTTY',
|
|
'ImGui_LogToFile',
|
|
'ImGui_LogToClipboard',
|
|
'ImGui_BeginDisabled',
|
|
# Inputs
|
|
'ImGui_IsMousePosValid',
|
|
'ImGui_IsMouseDragging',
|
|
'ImGui_GetMouseDragDelta',
|
|
'ImGui_CaptureKeyboardFromApp',
|
|
'ImGui_CaptureMouseFromApp',
|
|
# Settings
|
|
'ImGui_LoadIniSettingsFromDisk',
|
|
'ImGui_LoadIniSettingsFromMemory',
|
|
'ImGui_SaveIniSettingsToMemory',
|
|
'ImGui_SaveIniSettingsToMemory',
|
|
# Memory Allcators
|
|
'ImGui_SetAllocatorFunctions',
|
|
# Other types
|
|
'ImGuiIO_SetKeyEventNativeDataEx',
|
|
'ImGuiTextFilter_Draw',
|
|
'ImGuiTextFilter_PassFilter',
|
|
'ImGuiTextBuffer_append',
|
|
'ImGuiInputTextCallbackData_InsertChars',
|
|
'ImColor_SetHSV',
|
|
'ImColor_HSV',
|
|
'ImGuiListClipper_Begin',
|
|
# ImDrawList
|
|
# - all 'int num_segments = 0' made explicit
|
|
'ImDrawList_AddCircleFilled',
|
|
'ImDrawList_AddBezierCubic',
|
|
'ImDrawList_AddBezierQuadratic',
|
|
'ImDrawList_PathStroke',
|
|
'ImDrawList_PathArcTo',
|
|
'ImDrawList_PathBezierCubicCurveTo',
|
|
'ImDrawList_PathBezierQuadraticCurveTo',
|
|
'ImDrawList_PathRect',
|
|
'ImDrawList_AddBezierCurve',
|
|
'ImDrawList_PathBezierCurveTo',
|
|
'ImDrawList_PushClipRect',
|
|
# ImFont, ImFontGlyphRangesBuilder
|
|
'ImFontGlyphRangesBuilder_AddText',
|
|
'ImFont_AddRemapChar',
|
|
'ImFont_RenderText',
|
|
# Obsolete functions
|
|
'ImGui_ImageButtonImTextureID',
|
|
'ImGui_ListBoxHeaderInt',
|
|
'ImGui_ListBoxHeader',
|
|
'ImGui_OpenPopupContextItem',
|
|
],
|
|
function_prefixes_to_ignore=[
|
|
'ImGuiStorage_',
|
|
'ImFontAtlas_'
|
|
],
|
|
trivial_argument_types=[
|
|
'ImGuiCond'
|
|
],
|
|
trivial_argument_names=[
|
|
'flags',
|
|
'popup_flags'
|
|
])
|
|
|
|
# Do some special-case renaming of functions
|
|
mod_rename_functions.apply(dom_root, {
|
|
# We want the ImGuiCol version of GetColorU32 to be the primary one, but we can't use type_priorities on
|
|
# mod_disambiguate_functions to achieve that because it also has more arguments and thus naturally gets passed
|
|
# over. Rather than introducing yet another layer of knobs to try and control _that_, we just do some
|
|
# after-the-fact renaming here.
|
|
'ImGui_GetColorU32': 'ImGui_GetColorU32ImVec4',
|
|
'ImGui_GetColorU32ImGuiCol': 'ImGui_GetColorU32',
|
|
'ImGui_GetColorU32ImGuiColEx': 'ImGui_GetColorU32Ex',
|
|
# ImGui_IsRectVisible is kinda inobvious as it stands, since the two overrides take the exact same type but
|
|
# interpret it differently. Hence do some renaming to make it clearer.
|
|
'ImGui_IsRectVisible': 'ImGui_IsRectVisibleBySize',
|
|
'ImGui_IsRectVisibleImVec2': 'ImGui_IsRectVisible'
|
|
})
|
|
|
|
if generate_unformatted_functions:
|
|
mod_add_unformatted_functions.apply(dom_root,
|
|
functions_to_ignore=[
|
|
'ImGui_Text',
|
|
'ImGuiTextBuffer_appendf'
|
|
])
|
|
|
|
if is_imgui_internal:
|
|
mod_move_elements.apply(dom_root,
|
|
main_src_root,
|
|
[
|
|
# This terribleness is because those few type definitions needs to appear
|
|
# below the definitions of ImVector_ImGuiTable and ImVector_ImGuiTabBar
|
|
(code_dom.DOMClassStructUnion, 'ImGuiTextIndex'),
|
|
(code_dom.DOMClassStructUnion, 'ImPool_', True),
|
|
#
|
|
(code_dom.DOMClassStructUnion, 'ImVector_int'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_const_charPtr'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiColorMod'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiContextHook'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiDockNodeSettings'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiDockRequest'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiFocusScopeData'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiGroupData'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiID'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiInputEvent'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiItemFlags'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiKeyRoutingData'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiListClipperData'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiListClipperRange'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiNavTreeNodeData'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiMultiSelectState'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiMultiSelectTempData'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiOldColumnData'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiOldColumns'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiPopupData'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiPtrOrIndex'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiSettingsHandler'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiShrinkWidthItem'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiStackLevelInfo'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiStyleMod'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiTabBar'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiTabItem'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiTable'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiTableColumnSortSpecs'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiTableHeaderData'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiTableInstanceData'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiTableTempData'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiTreeNodeStackData'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiViewportPPtr'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiWindowPtr'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_ImGuiWindowStackData'),
|
|
(code_dom.DOMClassStructUnion, 'ImVector_unsigned_char'),
|
|
#
|
|
(code_dom.DOMClassStructUnion, 'ImChunkStream_ImGuiWindowSettings'),
|
|
(code_dom.DOMClassStructUnion, 'ImChunkStream_ImGuiTableSettings'),
|
|
# Fudge those typedefs to be at the top
|
|
(code_dom.DOMTypedef, 'ImGuiTableColumnIdx', False, True),
|
|
])
|
|
else:
|
|
mod_move_elements.apply(dom_root,
|
|
main_src_root,
|
|
[
|
|
# This is currently defined after the point where the ImVector expansions appear,
|
|
# so we need to move it up to avoid declaration issues. We move the #ifndef
|
|
# rather than the typedef itself because we want the whole block.
|
|
(code_dom.DOMPreprocessorIf, 'ImDrawIdx', False, True),
|
|
])
|
|
|
|
# Make all functions use CIMGUI_API/CIMGUI_IMPL_API
|
|
mod_make_all_functions_use_imgui_api.apply(dom_root)
|
|
# Rename the API defines
|
|
mod_rename_defines.apply(dom_root, {'IMGUI_API': 'CIMGUI_API', 'IMGUI_IMPL_API': 'CIMGUI_IMPL_API'})
|
|
# Remove these #defines as they don't make sense in C
|
|
mod_remove_defines.apply(dom_root, ["IM_PLACEMENT_NEW(_PTR)", "IM_NEW(_TYPE)"])
|
|
# Rewrite these defines to reference the new function names
|
|
# This could be done more generically but there are only three at present and there's a limit to how generic
|
|
# we can get (as there's all sorts of #define trickery that could break the general case), so for now we'll
|
|
# just do it the easy way
|
|
mod_rewrite_defines.apply(dom_root, [
|
|
"IM_ALLOC(_SIZE)",
|
|
"IM_FREE(_PTR)",
|
|
"IMGUI_CHECKVERSION()"
|
|
], {"ImGui::": "ImGui_"})
|
|
# Rename these to stop them generating compile warnings as they clash with those in imgui.h
|
|
mod_rename_defines.apply(dom_root, {
|
|
'IM_ALLOC(_SIZE)': 'CIM_ALLOC(_SIZE)',
|
|
'IM_FREE(_PTR)': 'CIM_FREE(_PTR)',
|
|
'IMGUI_CHECKVERSION()': 'CIMGUI_CHECKVERSION()'
|
|
})
|
|
|
|
mod_forward_declare_structs.apply(dom_root)
|
|
mod_wrap_with_extern_c.apply(main_src_root) # main_src_root here to avoid wrapping the config headers
|
|
# For now we leave #pragma once intact on the assumption that modern compilers all support it, but if necessary
|
|
# it can be replaced with a traditional #include guard by uncommenting the line below. If you find yourself needing
|
|
# this functionality in a significant way please let me know!
|
|
# mod_remove_pragma_once.apply(dom_root)
|
|
mod_remove_empty_conditionals.apply(dom_root)
|
|
mod_merge_blank_lines.apply(dom_root)
|
|
mod_remove_blank_lines.apply(dom_root)
|
|
mod_align_enum_values.apply(dom_root)
|
|
mod_align_function_names.apply(dom_root)
|
|
mod_align_structure_field_names.apply(dom_root)
|
|
mod_align_comments.apply(dom_root)
|
|
|
|
# Exclude some defines that aren't really useful from the metadata
|
|
mod_exclude_defines_from_metadata.apply(dom_root, [
|
|
"CIMGUI_IMPL_API",
|
|
"IM_COL32_WHITE",
|
|
"IM_COL32_BLACK",
|
|
"IM_COL32_BLACK_TRANS",
|
|
"ImDrawCallback_ResetRenderState"
|
|
])
|
|
|
|
mod_replace_typedef_with_opaque_buffer.apply(dom_root, [
|
|
("ImBitArrayForNamedKeys", 20) # template with two parameters, not supported
|
|
])
|
|
|
|
# Remove namespaced define
|
|
mod_remove_typedefs.apply(dom_root, [
|
|
"ImStbTexteditState"
|
|
])
|
|
# Replace the stb_textedit type reference with an opaque pointer
|
|
mod_change_class_field_type.apply(dom_root, "ImGuiInputTextState", "Stb", "void*")
|
|
|
|
# If the user requested a custom prefix, then rename everything here
|
|
|
|
if len(prefix_replacements) > 0:
|
|
mod_rename_prefix.apply(dom_root, prefix_replacements)
|
|
|
|
dom_root.validate_hierarchy()
|
|
|
|
# Test code
|
|
# dom_root.dump()
|
|
|
|
# Cases where the varargs list version of a function does not simply have a V added to the name and needs a
|
|
# custom suffix instead
|
|
custom_varargs_list_suffixes = {'appendf': 'v'}
|
|
|
|
# Get just the name portion of the source file, to use as the template name
|
|
src_file_name_only = os.path.splitext(os.path.basename(src_file))[0]
|
|
|
|
print("Writing output to " + dest_file_no_ext + "[.h/.cpp/.json]")
|
|
|
|
# If our output name ends with _internal, then generate a version of it without that on the assumption that
|
|
# this is probably imgui_internal.h and thus we need to know what imgui.h is (likely) called as well.
|
|
if is_imgui_internal:
|
|
dest_file_name_only_no_internal = dest_file_name_only[:-9]
|
|
else:
|
|
dest_file_name_only_no_internal = dest_file_name_only
|
|
|
|
# Expansions to be used when processing templates, to insert variables as required
|
|
expansions = {"%IMGUI_INCLUDE_DIR%": imgui_include_dir,
|
|
"%BACKEND_INCLUDE_DIR%": backend_include_dir,
|
|
"%OUTPUT_HEADER_NAME%": dest_file_name_only + ".h",
|
|
"%OUTPUT_HEADER_NAME_NO_INTERNAL%": dest_file_name_only_no_internal + ".h"}
|
|
|
|
with open(dest_file_no_ext + ".h", "w") as file:
|
|
insert_header_templates(file, template_dir, src_file_name_only, ".h", expansions)
|
|
|
|
write_context = code_dom.WriteContext()
|
|
write_context.for_c = True
|
|
write_context.for_backend = is_backend
|
|
main_src_root.write_to_c(file, context=write_context)
|
|
|
|
# Generate implementations
|
|
with open(dest_file_no_ext + ".cpp", "w") as file:
|
|
insert_header_templates(file, template_dir, src_file_name_only, ".cpp", expansions)
|
|
|
|
gen_struct_converters.generate(dom_root, file, indent=0)
|
|
|
|
# Extract custom types from everything we parsed,
|
|
# but generate only for the main header
|
|
imgui_custom_types = utils.get_imgui_custom_types(dom_root)
|
|
gen_function_stubs.generate(main_src_root, file, imgui_custom_types,
|
|
indent=0,
|
|
custom_varargs_list_suffixes=custom_varargs_list_suffixes,
|
|
is_backend=is_backend)
|
|
|
|
# Generate metadata
|
|
if emit_combined_json_metadata:
|
|
metadata_file_name = dest_file_no_ext + ".json"
|
|
with open(metadata_file_name, "w") as file:
|
|
# We intentionally generate JSON starting from the root here so that we emit metadata from all dependencies
|
|
gen_metadata.generate(dom_root, file)
|
|
else:
|
|
# Emit separate metadata files for each header
|
|
headers = dom_root.list_directly_contained_children_of_type(code_dom.DOMHeaderFile)
|
|
for header in headers:
|
|
if (header == main_src_root):
|
|
metadata_file_name = dest_file_no_ext
|
|
else:
|
|
metadata_file_name = dest_file_no_ext + "_" + str(Path(header.source_filename).with_suffix(""))
|
|
|
|
metadata_file_name = metadata_file_name + ".json"
|
|
with open(metadata_file_name, "w") as file:
|
|
gen_metadata.generate(header, file)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
# Parse the C++ header found in src_file, and write a C header to dest_file_no_ext.h, with binding implementation in
|
|
# dest_file_no_ext.cpp. Metadata will be written to dest_file_no_ext.json. implementation_header should point to a
|
|
# file containing the initial header block for the implementation (provided in the templates/ directory).
|
|
|
|
print("Dear Bindings: parse Dear ImGui headers, convert to C and output metadata.")
|
|
|
|
# Debug code
|
|
# type_comprehender.get_type_description("void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* cmd)").dump(0)
|
|
|
|
default_template_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "src", "templates")
|
|
|
|
parser = argparse.ArgumentParser(
|
|
add_help=True,
|
|
epilog='Result code 0 is returned on success, 1 on conversion failure and 2 on '
|
|
'parameter errors')
|
|
parser.add_argument('src',
|
|
help='Path to source header file to process (generally imgui.h)')
|
|
parser.add_argument('-o', '--output',
|
|
required=True,
|
|
help='Path to output files (generally dcimgui). This should have no extension, '
|
|
'as <output>.h, <output>.cpp and <output>.json will be written.')
|
|
parser.add_argument('-t', '--templatedir',
|
|
default=default_template_dir,
|
|
help='Path to the implementation template directory (default: ./src/templates)')
|
|
parser.add_argument('--nopassingstructsbyvalue',
|
|
action='store_true',
|
|
help='Convert any by-value struct arguments to pointers (for other language bindings)')
|
|
parser.add_argument('--nogeneratedefaultargfunctions',
|
|
action='store_true',
|
|
help='Do not generate function variants with implied default values')
|
|
parser.add_argument('--generateunformattedfunctions',
|
|
action='store_true',
|
|
help='Generate unformatted variants of format string supporting functions')
|
|
parser.add_argument('--backend',
|
|
action='store_true',
|
|
help='Indicates that the header being processed is a backend header (experimental)')
|
|
parser.add_argument('--imgui-include-dir',
|
|
default='',
|
|
help="Path to ImGui headers to use in emitted include files. Should include a trailing slash "
|
|
"(eg \"Imgui/\"). (default: blank)")
|
|
parser.add_argument('--backend-include-dir',
|
|
default='',
|
|
help="Path to ImGui backend headers to use in emitted files. Should include a trailing slash "
|
|
"(eg \"Imgui/Backends/\"). (default: same as --imgui-include-dir)")
|
|
parser.add_argument('--include',
|
|
help="Path to additional .h files to include (e.g. imgui.h if converting imgui_internal.h, "
|
|
"and/or the file you set IMGUI_USER_CONFIG to, if any)",
|
|
default=[],
|
|
action='append')
|
|
parser.add_argument('--imconfig-path',
|
|
help="Path to imconfig.h. If not specified, imconfig.h will be assumed to be in the same "
|
|
"directory as the source file, or the directory immediately above it if --backend "
|
|
"is specified")
|
|
parser.add_argument('--emit-combined-json-metadata',
|
|
action='store_true',
|
|
help="Emit a single combined metadata JSON file instead of emitting "
|
|
"separate metadata JSON files for each header",
|
|
default=False)
|
|
parser.add_argument('--custom-namespace-prefix',
|
|
help="Specify a custom prefix to use on emitted functions/etc in place of the usual "
|
|
"namespace-derived ImGui_")
|
|
parser.add_argument('--replace-prefix',
|
|
help="Specify a name prefix and something to replace it with as a pair of arguments of "
|
|
"the form <old prefix>=<new prefix>. For example, \"--replace-prefix ImFont_=if will\" "
|
|
"result in ImFont_FindGlyph() becoming ifFontGlyph() (and all other ImFont_ names "
|
|
"following suit)",
|
|
default=[],
|
|
action='append')
|
|
|
|
if len(sys.argv) == 1:
|
|
parser.print_help(sys.stderr)
|
|
sys.exit(0)
|
|
|
|
args = parser.parse_args()
|
|
|
|
include_files = []
|
|
|
|
default_imconfig_path = os.path.dirname(os.path.realpath(args.src))
|
|
|
|
# If --backend was specified, assume imconfig.h is in the directory above (as that is where it will be in the
|
|
# standard layout
|
|
if args.backend:
|
|
default_imconfig_path = os.path.dirname(default_imconfig_path)
|
|
|
|
# Add imconfig.h to the include list to get any #defines set in that
|
|
imconfig_path = args.imconfig_path if args.imconfig_path is not None else (
|
|
os.path.join(default_imconfig_path, "imconfig.h"))
|
|
|
|
include_files.append(imconfig_path)
|
|
|
|
# Build a map from all the requested prefix replacements
|
|
prefix_replacements = {}
|
|
for replacement_str in args.replace_prefix:
|
|
if '=' not in replacement_str:
|
|
print("--replace-prefix \"" + replacement_str + "\" is not of the form <old prefix>=<new prefix>")
|
|
sys.exit(1)
|
|
index = replacement_str.index('=')
|
|
old_prefix = replacement_str[:index]
|
|
new_prefix = replacement_str[(index + 1):]
|
|
prefix_replacements[old_prefix] = new_prefix
|
|
|
|
# --custom-namespace-prefix is just handled as a handy short form for --replace-prefix ImGui_=<something>
|
|
if args.custom_namespace_prefix is not None:
|
|
prefix_replacements["ImGui_"] = args.custom_namespace_prefix
|
|
|
|
# Add any user-supplied config file as well
|
|
for include in args.include:
|
|
include_files.append(os.path.realpath(include))
|
|
|
|
# Perform conversion
|
|
try:
|
|
convert_header(
|
|
os.path.realpath(args.src),
|
|
include_files,
|
|
args.output,
|
|
args.templatedir,
|
|
args.nopassingstructsbyvalue,
|
|
args.nogeneratedefaultargfunctions,
|
|
args.generateunformattedfunctions,
|
|
args.backend,
|
|
args.imgui_include_dir,
|
|
args.backend_include_dir if args.backend_include_dir is not None else args.imgui_include_dir,
|
|
args.emit_combined_json_metadata,
|
|
prefix_replacements
|
|
)
|
|
except: # noqa - suppress warning about broad exception clause as it's intentionally broad
|
|
print("Exception during conversion:")
|
|
traceback.print_exc()
|
|
sys.exit(1)
|
|
|
|
print("Done")
|
|
sys.exit(0)
|