# 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, [""]) # We need stdbool.h to get bool defined mod_add_includes.apply(dom_root, [""]) # We need stdint.h to get int32_t mod_remove_includes.apply(dom_root, ["", ""]) 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 .h, .cpp and .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 =. 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 =") 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_= 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)