imgui 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +273 -0
  4. data/Rakefile +97 -0
  5. data/api/v1.json +53 -0
  6. data/ext/CMakeLists.txt +128 -0
  7. data/ext/Rakefile +31 -0
  8. data/ext/backend_function_bridge.cpp +180 -0
  9. data/ext/backend_function_bridge.h +40 -0
  10. data/ext/build_cimgui.rb +59 -0
  11. data/ext/extconf.rb +25 -0
  12. data/ext/glfw_vulkan_bridge.cpp +14 -0
  13. data/ext/imgui_ruby_build_config.h +17 -0
  14. data/ext/imgui_ruby_glfw.cpp +73 -0
  15. data/ext/imgui_ruby_sdl3.cpp +70 -0
  16. data/ext/imgui_ruby_wgpu.cpp +75 -0
  17. data/ext/install_cimgui.rb +25 -0
  18. data/ext/webgpu_function_bridge.cpp +245 -0
  19. data/generator/api_emitter.rb +164 -0
  20. data/generator/emitter.rb +317 -0
  21. data/generator/generate.rb +47 -0
  22. data/generator/native-dependencies.yml +88 -0
  23. data/generator/native_dependency_lock.rb +78 -0
  24. data/generator/native_dependency_snapshot.rb +159 -0
  25. data/generator/overrides.yml +11 -0
  26. data/generator/type_mapper.rb +112 -0
  27. data/generator/update_vendor.rb +30 -0
  28. data/lib/imgui/api.rb +88 -0
  29. data/lib/imgui/api_generated.rb +1633 -0
  30. data/lib/imgui/api_support.rb +144 -0
  31. data/lib/imgui/backends/glfw.rb +75 -0
  32. data/lib/imgui/backends/opengl3.rb +44 -0
  33. data/lib/imgui/backends/sdl3.rb +133 -0
  34. data/lib/imgui/backends/wgpu.rb +176 -0
  35. data/lib/imgui/backends.rb +60 -0
  36. data/lib/imgui/draw_data.rb +43 -0
  37. data/lib/imgui/dsl.rb +311 -0
  38. data/lib/imgui/dsl_support.rb +107 -0
  39. data/lib/imgui/easy_loop.rb +25 -0
  40. data/lib/imgui/errors.rb +11 -0
  41. data/lib/imgui/fonts.rb +46 -0
  42. data/lib/imgui/io.rb +91 -0
  43. data/lib/imgui/layout.rb +138 -0
  44. data/lib/imgui/memory_pool.rb +18 -0
  45. data/lib/imgui/native/enums.rb +2251 -0
  46. data/lib/imgui/native/functions.rb +1470 -0
  47. data/lib/imgui/native/structs.rb +1926 -0
  48. data/lib/imgui/native/typedefs.rb +93 -0
  49. data/lib/imgui/native.rb +130 -0
  50. data/lib/imgui/plot/api.rb +273 -0
  51. data/lib/imgui/plot/native/enums.rb +593 -0
  52. data/lib/imgui/plot/native/functions.rb +749 -0
  53. data/lib/imgui/plot/native/structs.rb +363 -0
  54. data/lib/imgui/plot/native/typedefs.rb +79 -0
  55. data/lib/imgui/plot.rb +13 -0
  56. data/lib/imgui/struct_value.rb +33 -0
  57. data/lib/imgui/style.rb +44 -0
  58. data/lib/imgui/value.rb +104 -0
  59. data/lib/imgui/version.rb +5 -0
  60. data/lib/imgui/widgets.rb +187 -0
  61. data/lib/imgui.rb +30 -0
  62. data/rakelib/platform_gem.rake +54 -0
  63. data/rakelib/source_gem.rake +75 -0
  64. metadata +129 -0
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImGui
4
+ class << self
5
+ private
6
+
7
+ def guarded_native_call(function, *arguments)
8
+ guard_context!
9
+ Native.public_send(function, *arguments)
10
+ end
11
+
12
+ def define_generated_method(ruby_name, native_name, argument_types, defaults)
13
+ singleton_class.define_method(ruby_name) do |*arguments, **keywords|
14
+ if arguments.length > argument_types.length
15
+ raise ArgumentError, "wrong number of arguments (given #{arguments.length}, expected #{argument_types.length})"
16
+ end
17
+
18
+ values = argument_types.each_with_index.map do |(name, c_type), index|
19
+ value = if index < arguments.length
20
+ arguments[index]
21
+ elsif keywords.key?(name.to_sym)
22
+ keywords.delete(name.to_sym)
23
+ elsif defaults.key?(name)
24
+ defaults.fetch(name)
25
+ else
26
+ raise ArgumentError, "missing argument: #{name}"
27
+ end
28
+ convert_generated_argument(c_type, value)
29
+ end
30
+ raise ArgumentError, "unknown keywords: #{keywords.keys.join(", ")}" unless keywords.empty?
31
+
32
+ guarded_native_call(native_name, *values)
33
+ end
34
+ end
35
+
36
+ def convert_generated_argument(c_type, value)
37
+ type = c_type.gsub(/\bconst\b/, "").strip
38
+ return StructValue.vec2(value) if type == "ImVec2"
39
+ return StructValue.vec4(value) if type == "ImVec4"
40
+ return !!value if type == "bool"
41
+ return Float(value) if %w[float double].include?(type)
42
+ return Integer(value) if type.match?(/\A(?:unsigned )?(?:char|short|int|long|long long)\z/)
43
+ return value&.to_s if type == "char*"
44
+
45
+ value
46
+ end
47
+
48
+ def guard_context!
49
+ context = Native.igGetCurrentContext
50
+ raise NoContextError, "create an ImGui context before calling this method" if null_pointer?(context)
51
+
52
+ key = context_key(context)
53
+ context_threads[key] ||= Thread.current
54
+ check_context_thread!(context)
55
+ context
56
+ end
57
+
58
+ def check_context_thread!(context)
59
+ return unless thread_checks_enabled?
60
+
61
+ owner = context_threads[context_key(context)]
62
+ return unless owner && owner != Thread.current
63
+
64
+ raise ThreadError, "ImGui context belongs to thread #{owner.object_id}, not #{Thread.current.object_id}"
65
+ end
66
+
67
+ def null_pointer?(pointer)
68
+ pointer.nil? || pointer == 0 || pointer.respond_to?(:null?) && pointer.null?
69
+ end
70
+
71
+ def context_key(context)
72
+ context.respond_to?(:address) ? context.address : context.object_id
73
+ end
74
+
75
+ def context_threads
76
+ @context_threads ||= {}
77
+ end
78
+
79
+ def io_views
80
+ @io_views ||= {}
81
+ end
82
+
83
+ def style_views
84
+ @style_views ||= {}
85
+ end
86
+
87
+ def clear_context_views(context)
88
+ key = context_key(context)
89
+ io_views.delete(key)
90
+ style_views.delete(key)
91
+ end
92
+
93
+ def edit_scalar(function, label, value, type:, arguments:)
94
+ guard_context!
95
+ if value.is_a?(Value)
96
+ require_value_type!(value, type)
97
+ return Native.public_send(function, label, value.pointer, *arguments)
98
+ end
99
+
100
+ pointer = MemoryPool.pointer(type)
101
+ pointer.put(type, 0, coerce_scalar(type, value))
102
+ changed = Native.public_send(function, label, pointer, *arguments)
103
+ [changed, pointer.get(type, 0)]
104
+ end
105
+
106
+ def edit_vector(function, label, value, count:, arguments:)
107
+ guard_context!
108
+ expected_type = "vec#{count}".to_sym
109
+ if value.is_a?(Value)
110
+ require_value_type!(value, expected_type)
111
+ return Native.public_send(function, label, value.pointer, *arguments)
112
+ end
113
+
114
+ values = Array(value)
115
+ raise ArgumentError, "expected #{count} values" unless values.length == count
116
+
117
+ pointer = MemoryPool.pointer(:float, count)
118
+ pointer.write_array_of_float(values.map { |item| Float(item) })
119
+ changed = Native.public_send(function, label, pointer, *arguments)
120
+ [changed, pointer.read_array_of_float(count)]
121
+ end
122
+
123
+ def vector_result(function, *arguments, count: 2)
124
+ guard_context!
125
+ pointer = MemoryPool.pointer(:float, count)
126
+ Native.public_send(function, pointer, *arguments)
127
+ pointer.read_array_of_float(count)
128
+ end
129
+
130
+ def require_value_type!(value, expected)
131
+ return if value.type == expected
132
+
133
+ raise TypeError, "expected ImGui::Value.#{expected}, got #{value.type}"
134
+ end
135
+
136
+ def coerce_scalar(type, value)
137
+ case type
138
+ when :bool then !!value
139
+ when :int then Integer(value)
140
+ else Float(value)
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImGui
4
+ module Backends
5
+ module Glfw
6
+ module_function
7
+
8
+ def init_for_opengl(window, install_callbacks: true)
9
+ prepare_runtime!
10
+ Backends.invoke(
11
+ "GLFW",
12
+ :ImGui_ImplGlfw_InitForOpenGL,
13
+ Backends.pointer(window),
14
+ !!install_callbacks
15
+ )
16
+ end
17
+
18
+ def init_for_vulkan(window, install_callbacks: true)
19
+ prepare_runtime!
20
+ Backends.invoke(
21
+ "GLFW",
22
+ :ImGui_ImplGlfw_InitForVulkan,
23
+ Backends.pointer(window),
24
+ !!install_callbacks
25
+ )
26
+ end
27
+
28
+ def init_for_other(window, install_callbacks: true)
29
+ prepare_runtime!
30
+ Backends.invoke(
31
+ "GLFW",
32
+ :ImGui_ImplGlfw_InitForOther,
33
+ Backends.pointer(window),
34
+ !!install_callbacks
35
+ )
36
+ end
37
+
38
+ def install_callbacks(window)
39
+ Backends.invoke("GLFW", :ImGui_ImplGlfw_InstallCallbacks, Backends.pointer(window))
40
+ end
41
+
42
+ def restore_callbacks(window)
43
+ Backends.invoke("GLFW", :ImGui_ImplGlfw_RestoreCallbacks, Backends.pointer(window))
44
+ end
45
+
46
+ def chain_callbacks_for_all_windows=(enabled)
47
+ Backends.invoke("GLFW", :ImGui_ImplGlfw_SetCallbacksChainForAllWindows, !!enabled)
48
+ end
49
+
50
+ def prepare_runtime!
51
+ candidates = [ENV["IMGUI_RUBY_GLFW_LIB"]]
52
+ candidates.concat(::GLFW::API.ffi_libraries.map(&:name)) if defined?(::GLFW::API)
53
+ candidates.concat(
54
+ if FFI::Platform.windows?
55
+ %w[glfw3.dll glfw3_64.dll]
56
+ elsif FFI::Platform.mac?
57
+ %w[libglfw.3.dylib libglfw.dylib /opt/homebrew/lib/libglfw.3.dylib /usr/local/lib/libglfw.3.dylib]
58
+ else
59
+ %w[libglfw.so.3 libglfw.so]
60
+ end
61
+ )
62
+ Backends.load_runtime_library("GLFW", candidates)
63
+ end
64
+ private_class_method :prepare_runtime!
65
+
66
+ def new_frame
67
+ Backends.invoke("GLFW", :ImGui_ImplGlfw_NewFrame)
68
+ end
69
+
70
+ def shutdown
71
+ Backends.invoke("GLFW", :ImGui_ImplGlfw_Shutdown)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImGui
4
+ module Backends
5
+ module OpenGL3
6
+ module_function
7
+
8
+ def init(glsl_version = nil)
9
+ Backends.invoke("OpenGL3", :ImGui_ImplOpenGL3_Init, glsl_version)
10
+ end
11
+
12
+ def new_frame
13
+ Backends.invoke("OpenGL3", :ImGui_ImplOpenGL3_NewFrame)
14
+ end
15
+
16
+ def render_draw_data(draw_data = ImGui.draw_data)
17
+ return if draw_data.nil?
18
+
19
+ pointer = draw_data.respond_to?(:pointer) ? draw_data.pointer : Backends.pointer(draw_data)
20
+ Backends.invoke("OpenGL3", :ImGui_ImplOpenGL3_RenderDrawData, pointer)
21
+ end
22
+
23
+ def create_device_objects
24
+ Backends.invoke("OpenGL3", :ImGui_ImplOpenGL3_CreateDeviceObjects)
25
+ end
26
+
27
+ def destroy_device_objects
28
+ Backends.invoke("OpenGL3", :ImGui_ImplOpenGL3_DestroyDeviceObjects)
29
+ end
30
+
31
+ def create_fonts_texture
32
+ Backends.invoke("OpenGL3", :ImGui_ImplOpenGL3_CreateFontsTexture)
33
+ end
34
+
35
+ def destroy_fonts_texture
36
+ Backends.invoke("OpenGL3", :ImGui_ImplOpenGL3_DestroyFontsTexture)
37
+ end
38
+
39
+ def shutdown
40
+ Backends.invoke("OpenGL3", :ImGui_ImplOpenGL3_Shutdown)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImGui
4
+ module Backends
5
+ module SDL3
6
+ module GamepadMode
7
+ AutoFirst = 0
8
+ AutoAll = 1
9
+ Manual = 2
10
+ end
11
+
12
+ module_function
13
+
14
+ def init_for_opengl(window, context)
15
+ prepare_runtime!
16
+ Backends.invoke(
17
+ "SDL3",
18
+ :ImGui_ImplSDL3_InitForOpenGL,
19
+ Backends.pointer(window),
20
+ Backends.pointer(context)
21
+ )
22
+ end
23
+
24
+ def init_for_other(window)
25
+ prepare_runtime!
26
+ Backends.invoke("SDL3", :ImGui_ImplSDL3_InitForOther, Backends.pointer(window))
27
+ end
28
+
29
+ def init_for_vulkan(window)
30
+ prepare_runtime!
31
+ Backends.invoke("SDL3", :ImGui_ImplSDL3_InitForVulkan, Backends.pointer(window))
32
+ end
33
+
34
+ def init_for_d3d(window)
35
+ prepare_runtime!
36
+ Backends.invoke("SDL3", :ImGui_ImplSDL3_InitForD3D, Backends.pointer(window))
37
+ end
38
+
39
+ def init_for_metal(window)
40
+ prepare_runtime!
41
+ Backends.invoke("SDL3", :ImGui_ImplSDL3_InitForMetal, Backends.pointer(window))
42
+ end
43
+
44
+ def init_for_sdl_renderer(window, renderer)
45
+ prepare_runtime!
46
+ Backends.invoke(
47
+ "SDL3",
48
+ :ImGui_ImplSDL3_InitForSDLRenderer,
49
+ Backends.pointer(window),
50
+ Backends.pointer(renderer)
51
+ )
52
+ end
53
+
54
+ def init_for_sdl_gpu(window)
55
+ prepare_runtime!
56
+ Backends.invoke("SDL3", :ImGui_ImplSDL3_InitForSDLGPU, Backends.pointer(window))
57
+ end
58
+
59
+ def process_event(event)
60
+ Backends.invoke("SDL3", :ImGui_ImplSDL3_ProcessEvent, Backends.pointer(event))
61
+ end
62
+
63
+ def new_frame
64
+ Backends.invoke("SDL3", :ImGui_ImplSDL3_NewFrame)
65
+ end
66
+
67
+ def gamepad_mode=(mode)
68
+ set_gamepad_mode(mode)
69
+ end
70
+
71
+ def set_gamepad_mode(mode, gamepads: nil)
72
+ native_mode = normalize_gamepad_mode(mode)
73
+ pointers = Array(gamepads).map { |gamepad| Backends.pointer(gamepad) }
74
+ if native_mode == GamepadMode::Manual
75
+ @manual_gamepads = if pointers.empty?
76
+ FFI::Pointer::NULL
77
+ else
78
+ FFI::MemoryPointer.new(:pointer, pointers.length).tap do |memory|
79
+ memory.write_array_of_pointer(pointers)
80
+ end
81
+ end
82
+ count = pointers.length
83
+ else
84
+ @manual_gamepads = nil
85
+ count = -1
86
+ end
87
+
88
+ Backends.invoke(
89
+ "SDL3",
90
+ :ImGui_ImplSDL3_SetGamepadMode,
91
+ native_mode,
92
+ @manual_gamepads || FFI::Pointer::NULL,
93
+ count
94
+ )
95
+ end
96
+
97
+ def shutdown
98
+ Backends.invoke("SDL3", :ImGui_ImplSDL3_Shutdown)
99
+ ensure
100
+ @manual_gamepads = nil
101
+ end
102
+
103
+ def normalize_gamepad_mode(mode)
104
+ return Integer(mode) unless mode.is_a?(Symbol) || mode.is_a?(String)
105
+
106
+ {
107
+ auto_first: GamepadMode::AutoFirst,
108
+ auto_all: GamepadMode::AutoAll,
109
+ manual: GamepadMode::Manual
110
+ }.fetch(mode.to_sym) do
111
+ raise ArgumentError, "unknown SDL3 gamepad mode: #{mode.inspect}"
112
+ end
113
+ end
114
+ private_class_method :normalize_gamepad_mode
115
+
116
+ def prepare_runtime!
117
+ candidates = [ENV["IMGUI_RUBY_SDL3_LIB"]]
118
+ candidates.concat(::SDL3::Raw.ffi_libraries.map(&:name)) if defined?(::SDL3::Raw)
119
+ candidates.concat(
120
+ if FFI::Platform.windows?
121
+ %w[SDL3.dll]
122
+ elsif FFI::Platform.mac?
123
+ %w[libSDL3.dylib SDL3.framework/SDL3 /opt/homebrew/lib/libSDL3.dylib /usr/local/lib/libSDL3.dylib]
124
+ else
125
+ %w[libSDL3.so.0 libSDL3.so]
126
+ end
127
+ )
128
+ Backends.load_runtime_library("SDL3", candidates)
129
+ end
130
+ private_class_method :prepare_runtime!
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImGui
4
+ module Native
5
+ attach_function :imgui_ruby_wgpu_required_function_count, [], :size_t
6
+ attach_function :imgui_ruby_wgpu_required_function_name, [:size_t], :string
7
+ attach_function :imgui_ruby_wgpu_set_function, [:string, :pointer], :bool
8
+ attach_function :imgui_ruby_wgpu_bridge_ready, [], :bool
9
+ attach_function :imgui_ruby_wgpu_bridge_error, [], :string
10
+ attach_function :imgui_ruby_wgpu_init, [:pointer, :int, :int, :int, :uint, :uint, :bool], :bool
11
+ attach_function :imgui_ruby_wgpu_shutdown, [], :void
12
+ attach_function :imgui_ruby_wgpu_new_frame, [], :void
13
+ attach_function :imgui_ruby_wgpu_render_draw_data, [:pointer, :pointer], :void
14
+ attach_function :imgui_ruby_wgpu_render_draw_data_to_view, [:pointer, :pointer, :pointer], :bool
15
+ end
16
+
17
+ module Backends
18
+ module WGPU
19
+ UNDEFINED_TEXTURE_FORMAT = 0
20
+ DEFAULT_SAMPLE_MASK = 0xffff_ffff
21
+
22
+ module_function
23
+
24
+ def init(
25
+ device:,
26
+ queue: nil,
27
+ render_target_format:,
28
+ depth_format: nil,
29
+ frames_in_flight: 3,
30
+ sample_count: 1,
31
+ sample_mask: DEFAULT_SAMPLE_MASK,
32
+ alpha_to_coverage: false,
33
+ function_table: nil,
34
+ library_path: nil
35
+ )
36
+ validate_init_options!(frames_in_flight, sample_count)
37
+ source = resolve_function_source(device, function_table, library_path)
38
+ configure_function_table!(source)
39
+
40
+ # The official backend obtains the default queue from the device. Accepting
41
+ # queue keeps the stagecraft integration signature stable and validates it
42
+ # when supplied, without retaining an unnecessary second queue reference.
43
+ Backends.pointer(queue) if queue
44
+ Backends.invoke(
45
+ "WGPU",
46
+ :imgui_ruby_wgpu_init,
47
+ Backends.pointer(device),
48
+ Integer(frames_in_flight),
49
+ Integer(render_target_format),
50
+ depth_format.nil? ? UNDEFINED_TEXTURE_FORMAT : Integer(depth_format),
51
+ Integer(sample_count),
52
+ Integer(sample_mask),
53
+ !!alpha_to_coverage
54
+ )
55
+ end
56
+
57
+ def new_frame
58
+ Backends.invoke("WGPU", :imgui_ruby_wgpu_new_frame)
59
+ end
60
+
61
+ def render_draw_data(draw_data, encoder, target_view = nil)
62
+ return if draw_data.nil?
63
+
64
+ data_pointer = draw_data.respond_to?(:pointer) ? draw_data.pointer : Backends.pointer(draw_data)
65
+ encoder_pointer = Backends.pointer(encoder)
66
+ return Backends.invoke(
67
+ "WGPU",
68
+ :imgui_ruby_wgpu_render_draw_data,
69
+ data_pointer,
70
+ encoder_pointer
71
+ ) unless target_view
72
+
73
+ Backends.invoke(
74
+ "WGPU",
75
+ :imgui_ruby_wgpu_render_draw_data_to_view,
76
+ data_pointer,
77
+ encoder_pointer,
78
+ Backends.pointer(target_view)
79
+ )
80
+ end
81
+
82
+ def shutdown
83
+ Backends.invoke("WGPU", :imgui_ruby_wgpu_shutdown)
84
+ end
85
+
86
+ def available?
87
+ Native.load!
88
+ Native.imgui_ruby_wgpu_bridge_error
89
+ true
90
+ rescue LibraryLoadError, MissingSymbolError
91
+ false
92
+ end
93
+
94
+ def resolve_function_source(device, explicit, library_path)
95
+ return explicit if explicit
96
+
97
+ return device.function_table if device.respond_to?(:function_table)
98
+ return device.native_library if device.respond_to?(:native_library)
99
+
100
+ if library_path
101
+ library = FFI::DynamicLibrary.open(
102
+ File.expand_path(library_path),
103
+ FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_LOCAL
104
+ )
105
+ retained_libraries << library
106
+ return library
107
+ end
108
+
109
+ if defined?(::WGPU::Native) && ::WGPU::Native.respond_to?(:ffi_libraries)
110
+ library = ::WGPU::Native.ffi_libraries.find { |candidate| function_pointer(candidate, "wgpuDeviceCreateBuffer") }
111
+ return library if library
112
+ end
113
+
114
+ raise BackendUnavailableError, <<~MESSAGE.strip
115
+ WGPU needs a native function table. Load the wgpu/stagecraft runtime
116
+ first, pass function_table:, or pass library_path: to its native library.
117
+ MESSAGE
118
+ end
119
+ private_class_method :resolve_function_source
120
+
121
+ def configure_function_table!(source)
122
+ required_function_names.each do |name|
123
+ pointer = function_pointer(source, name)
124
+ raise BackendUnavailableError, "WebGPU function is unavailable: #{name}" unless pointer
125
+
126
+ configured = Backends.invoke("WGPU", :imgui_ruby_wgpu_set_function, name, pointer)
127
+ next if configured
128
+
129
+ raise BackendUnavailableError, "WGPU function-table bridge rejected #{name}"
130
+ end
131
+ return true if Backends.invoke("WGPU", :imgui_ruby_wgpu_bridge_ready)
132
+
133
+ raise BackendUnavailableError, "WGPU function-table bridge failed: #{Native.imgui_ruby_wgpu_bridge_error}"
134
+ end
135
+ private_class_method :configure_function_table!
136
+
137
+ def required_function_names
138
+ count = Backends.invoke("WGPU", :imgui_ruby_wgpu_required_function_count)
139
+ Array.new(count) do |index|
140
+ Backends.invoke("WGPU", :imgui_ruby_wgpu_required_function_name, index)
141
+ end
142
+ end
143
+ private_class_method :required_function_names
144
+
145
+ def function_pointer(source, name)
146
+ value = if source.respond_to?(:fetch)
147
+ source.fetch(name) { source.fetch(name.to_sym, nil) }
148
+ elsif source.respond_to?(:find_function)
149
+ source.find_function(name)
150
+ elsif source.respond_to?(:function_address)
151
+ source.function_address(name)
152
+ elsif source.respond_to?(name)
153
+ source.method(name)
154
+ end
155
+ return if value.nil?
156
+ return FFI::Pointer.new(value.address) if value.respond_to?(:address)
157
+
158
+ Backends.pointer(value)
159
+ rescue FFI::NotFoundError
160
+ nil
161
+ end
162
+ private_class_method :function_pointer
163
+
164
+ def retained_libraries
165
+ @retained_libraries ||= []
166
+ end
167
+ private_class_method :retained_libraries
168
+
169
+ def validate_init_options!(frames_in_flight, sample_count)
170
+ raise ArgumentError, "frames_in_flight must be positive" unless Integer(frames_in_flight).positive?
171
+ raise ArgumentError, "sample_count must be positive" unless Integer(sample_count).positive?
172
+ end
173
+ private_class_method :validate_init_options!
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImGui
4
+ module Native
5
+ attach_function :imgui_ruby_backend_load_library, %i[string string], :bool
6
+ attach_function :imgui_ruby_backend_library_ready, [:string], :bool
7
+ attach_function :imgui_ruby_backend_has_function, %i[string string], :bool
8
+ attach_function :imgui_ruby_backend_required_function_count, [:string], :size_t
9
+ attach_function :imgui_ruby_backend_required_function_name, %i[string size_t], :string
10
+ attach_function :imgui_ruby_backend_library_error, [], :string
11
+ end
12
+
13
+ module Backends
14
+ module_function
15
+
16
+ def pointer(value)
17
+ candidate = value.respond_to?(:handle) ? value.handle : value
18
+ candidate = candidate.to_ptr if candidate.respond_to?(:to_ptr)
19
+ return candidate if candidate.is_a?(FFI::Pointer)
20
+ return FFI::Pointer.new(candidate) if candidate.is_a?(Integer)
21
+
22
+ raise TypeError, "expected a native pointer or an object exposing #handle/#to_ptr"
23
+ end
24
+
25
+ def invoke(backend, function, *arguments)
26
+ ImGui.__send__(:guard_context!)
27
+ Native.public_send(function, *arguments)
28
+ rescue MissingSymbolError => error
29
+ raise BackendUnavailableError, "#{backend} backend is not included in the loaded native library: #{error.message}"
30
+ end
31
+
32
+ def load_runtime_library(backend, candidates)
33
+ ImGui.__send__(:guard_context!)
34
+ unless Native.imgui_ruby_backend_library_ready(backend)
35
+ Array(candidates).compact.uniq.each do |path|
36
+ break if Native.imgui_ruby_backend_load_library(backend, path.to_s)
37
+ end
38
+ end
39
+
40
+ unless Native.imgui_ruby_backend_library_ready(backend)
41
+ detail = Native.imgui_ruby_backend_library_error
42
+ raise BackendUnavailableError,
43
+ "unable to load the #{backend} runtime library#{detail.to_s.empty? ? "" : ": #{detail}"}"
44
+ end
45
+
46
+ count = Native.imgui_ruby_backend_required_function_count(backend)
47
+ missing = count.times.filter_map do |index|
48
+ name = Native.imgui_ruby_backend_required_function_name(backend, index)
49
+ name unless Native.imgui_ruby_backend_has_function(backend, name)
50
+ end
51
+ return true if missing.empty?
52
+
53
+ raise BackendUnavailableError,
54
+ "#{backend} runtime library is incompatible; missing: #{missing.join(", ")}"
55
+ rescue MissingSymbolError => error
56
+ raise BackendUnavailableError,
57
+ "#{backend} runtime loading is not included in the loaded native library: #{error.message}"
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImGui
4
+ class DrawData
5
+ attr_reader :pointer
6
+
7
+ def initialize(pointer)
8
+ @pointer = pointer
9
+ @native = Native::ImDrawData.new(pointer)
10
+ end
11
+
12
+ def valid?
13
+ @native[:Valid]
14
+ end
15
+
16
+ def command_lists_count
17
+ @native[:CmdListsCount]
18
+ end
19
+
20
+ def total_index_count
21
+ @native[:TotalIdxCount]
22
+ end
23
+
24
+ def total_vertex_count
25
+ @native[:TotalVtxCount]
26
+ end
27
+
28
+ alias total_idx_count total_index_count
29
+ alias total_vtx_count total_vertex_count
30
+
31
+ def display_pos
32
+ StructValue.to_a(@native[:DisplayPos], 2)
33
+ end
34
+
35
+ def display_size
36
+ StructValue.to_a(@native[:DisplaySize], 2)
37
+ end
38
+
39
+ def framebuffer_scale
40
+ StructValue.to_a(@native[:FramebufferScale], 2)
41
+ end
42
+ end
43
+ end