imgui-bindings 0.1.10-x86_64-darwin

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.
@@ -0,0 +1,409 @@
1
+ require 'ffi'
2
+ require 'sdl2'
3
+ require_relative 'imgui'
4
+
5
+ module ImGui
6
+
7
+ @@g_BackendPlatformName = FFI::MemoryPointer.from_string("imgui_impl_sdl")
8
+
9
+ # ImGui::GetCurrentContext().address => ImGui_ImplSDL2_Data
10
+ @@g_BackendData = Hash.new
11
+
12
+ # [INTERNAL]
13
+ class ImGui_ImplSDL2_Data
14
+ attr_accessor :window, :renderer, :time, :mouseButtonsDown, :mouseCursors, :clipboardTextData, :mouseCanUseGlobalState
15
+
16
+ def initialize
17
+ @window = nil # SDL_Window*
18
+ @renderer = nil # SDL_Renderer*
19
+ @time = 0.0 # Uint64
20
+ @mouseButtonsDown = 0 # int
21
+ @mouseCursors = Array.new(ImGuiMouseCursor_COUNT) { 0 } # SDL_Cursor*
22
+ @clipboardTextData = nil # char*
23
+ @mouseCanUseGlobalState = false # bool
24
+ end
25
+ end
26
+
27
+ # Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
28
+ # It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
29
+ # FIXME: multi-context support is not well tested and probably dysfunctional in this backend.
30
+ # FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context.
31
+ # [INTERNAL]
32
+ def self.ImGui_ImplSDL2_GetBackendData()
33
+ if ImGui::GetCurrentContext() != nil
34
+ @@g_BackendData[ImGui::GetCurrentContext().address]
35
+ else
36
+ nil
37
+ end
38
+ end
39
+
40
+ # [TODO] Support ClipboardText
41
+ # g_ClipboardTextData
42
+ # ImplSDL2_GetClipboardText
43
+ # ImplSDL2_SetClipboardText
44
+
45
+ # [INTERNAL]
46
+ def self.ImGui_ImplSDL2_GetClipboardText(user_data)
47
+ bd = ImGui_ImplSDL2_GetBackendData()
48
+ SDL.free(bd.clipboardTextData) if bd.clipboardTextData
49
+ bd.clipboardTextData = SDL.GetClipboardText()
50
+ return bd.clipboardTextData
51
+ end
52
+
53
+ # [INTERNAL]
54
+ def self.ImGui_ImplSDL2_SetClipboardText(user_data, text)
55
+ SDL.SetClipboardText(text)
56
+ end
57
+
58
+ # [INTERNAL]
59
+ def self.ImGui_ImplSDL2_KeyToImGuiKey(key)
60
+ case key
61
+ when SDL::SDLK_TAB then ImGuiKey_Tab
62
+ when SDL::SDLK_LEFT then ImGuiKey_LeftArrow
63
+ when SDL::SDLK_RIGHT then ImGuiKey_RightArrow
64
+ when SDL::SDLK_UP then ImGuiKey_UpArrow
65
+ when SDL::SDLK_DOWN then ImGuiKey_DownArrow
66
+ when SDL::SDLK_PAGEUP then ImGuiKey_PageUp
67
+ when SDL::SDLK_PAGEDOWN then ImGuiKey_PageDown
68
+ when SDL::SDLK_HOME then ImGuiKey_Home
69
+ when SDL::SDLK_END then ImGuiKey_End
70
+ when SDL::SDLK_INSERT then ImGuiKey_Insert
71
+ when SDL::SDLK_DELETE then ImGuiKey_Delete
72
+ when SDL::SDLK_BACKSPACE then ImGuiKey_Backspace
73
+ when SDL::SDLK_SPACE then ImGuiKey_Space
74
+ when SDL::SDLK_RETURN then ImGuiKey_Enter
75
+ when SDL::SDLK_ESCAPE then ImGuiKey_Escape
76
+ when SDL::SDLK_QUOTE then ImGuiKey_Apostrophe
77
+ when SDL::SDLK_COMMA then ImGuiKey_Comma
78
+ when SDL::SDLK_MINUS then ImGuiKey_Minus
79
+ when SDL::SDLK_PERIOD then ImGuiKey_Period
80
+ when SDL::SDLK_SLASH then ImGuiKey_Slash
81
+ when SDL::SDLK_SEMICOLON then ImGuiKey_Semicolon
82
+ when SDL::SDLK_EQUALS then ImGuiKey_Equal
83
+ when SDL::SDLK_LEFTBRACKET then ImGuiKey_LeftBracket
84
+ when SDL::SDLK_BACKSLASH then ImGuiKey_Backslash
85
+ when SDL::SDLK_RIGHTBRACKET then ImGuiKey_RightBracket
86
+ when SDL::SDLK_BACKQUOTE then ImGuiKey_GraveAccent
87
+ when SDL::SDLK_CAPSLOCK then ImGuiKey_CapsLock
88
+ when SDL::SDLK_SCROLLLOCK then ImGuiKey_ScrollLock
89
+ when SDL::SDLK_NUMLOCKCLEAR then ImGuiKey_NumLock
90
+ when SDL::SDLK_PRINTSCREEN then ImGuiKey_PrintScreen
91
+ when SDL::SDLK_PAUSE then ImGuiKey_Pause
92
+ when SDL::SDLK_KP_0 then ImGuiKey_Keypad0
93
+ when SDL::SDLK_KP_1 then ImGuiKey_Keypad1
94
+ when SDL::SDLK_KP_2 then ImGuiKey_Keypad2
95
+ when SDL::SDLK_KP_3 then ImGuiKey_Keypad3
96
+ when SDL::SDLK_KP_4 then ImGuiKey_Keypad4
97
+ when SDL::SDLK_KP_5 then ImGuiKey_Keypad5
98
+ when SDL::SDLK_KP_6 then ImGuiKey_Keypad6
99
+ when SDL::SDLK_KP_7 then ImGuiKey_Keypad7
100
+ when SDL::SDLK_KP_8 then ImGuiKey_Keypad8
101
+ when SDL::SDLK_KP_9 then ImGuiKey_Keypad9
102
+ when SDL::SDLK_KP_PERIOD then ImGuiKey_KeypadDecimal
103
+ when SDL::SDLK_KP_DIVIDE then ImGuiKey_KeypadDivide
104
+ when SDL::SDLK_KP_MULTIPLY then ImGuiKey_KeypadMultiply
105
+ when SDL::SDLK_KP_MINUS then ImGuiKey_KeypadSubtract
106
+ when SDL::SDLK_KP_PLUS then ImGuiKey_KeypadAdd
107
+ when SDL::SDLK_KP_ENTER then ImGuiKey_KeypadEnter
108
+ when SDL::SDLK_KP_EQUALS then ImGuiKey_KeypadEqual
109
+ when SDL::SDLK_LCTRL then ImGuiKey_LeftCtrl
110
+ when SDL::SDLK_LSHIFT then ImGuiKey_LeftShift
111
+ when SDL::SDLK_LALT then ImGuiKey_LeftAlt
112
+ when SDL::SDLK_LGUI then ImGuiKey_LeftSuper
113
+ when SDL::SDLK_RCTRL then ImGuiKey_RightCtrl
114
+ when SDL::SDLK_RSHIFT then ImGuiKey_RightShift
115
+ when SDL::SDLK_RALT then ImGuiKey_RightAlt
116
+ when SDL::SDLK_RGUI then ImGuiKey_RightSuper
117
+ when SDL::SDLK_APPLICATION then ImGuiKey_Menu
118
+ when SDL::SDLK_0 then ImGuiKey_0
119
+ when SDL::SDLK_1 then ImGuiKey_1
120
+ when SDL::SDLK_2 then ImGuiKey_2
121
+ when SDL::SDLK_3 then ImGuiKey_3
122
+ when SDL::SDLK_4 then ImGuiKey_4
123
+ when SDL::SDLK_5 then ImGuiKey_5
124
+ when SDL::SDLK_6 then ImGuiKey_6
125
+ when SDL::SDLK_7 then ImGuiKey_7
126
+ when SDL::SDLK_8 then ImGuiKey_8
127
+ when SDL::SDLK_9 then ImGuiKey_9
128
+ when SDL::SDLK_a then ImGuiKey_A
129
+ when SDL::SDLK_b then ImGuiKey_B
130
+ when SDL::SDLK_c then ImGuiKey_C
131
+ when SDL::SDLK_d then ImGuiKey_D
132
+ when SDL::SDLK_e then ImGuiKey_E
133
+ when SDL::SDLK_f then ImGuiKey_F
134
+ when SDL::SDLK_g then ImGuiKey_G
135
+ when SDL::SDLK_h then ImGuiKey_H
136
+ when SDL::SDLK_i then ImGuiKey_I
137
+ when SDL::SDLK_j then ImGuiKey_J
138
+ when SDL::SDLK_k then ImGuiKey_K
139
+ when SDL::SDLK_l then ImGuiKey_L
140
+ when SDL::SDLK_m then ImGuiKey_M
141
+ when SDL::SDLK_n then ImGuiKey_N
142
+ when SDL::SDLK_o then ImGuiKey_O
143
+ when SDL::SDLK_p then ImGuiKey_P
144
+ when SDL::SDLK_q then ImGuiKey_Q
145
+ when SDL::SDLK_r then ImGuiKey_R
146
+ when SDL::SDLK_s then ImGuiKey_S
147
+ when SDL::SDLK_t then ImGuiKey_T
148
+ when SDL::SDLK_u then ImGuiKey_U
149
+ when SDL::SDLK_v then ImGuiKey_V
150
+ when SDL::SDLK_w then ImGuiKey_W
151
+ when SDL::SDLK_x then ImGuiKey_X
152
+ when SDL::SDLK_y then ImGuiKey_Y
153
+ when SDL::SDLK_z then ImGuiKey_Z
154
+ when SDL::SDLK_F1 then ImGuiKey_F1
155
+ when SDL::SDLK_F2 then ImGuiKey_F2
156
+ when SDL::SDLK_F3 then ImGuiKey_F3
157
+ when SDL::SDLK_F4 then ImGuiKey_F4
158
+ when SDL::SDLK_F5 then ImGuiKey_F5
159
+ when SDL::SDLK_F6 then ImGuiKey_F6
160
+ when SDL::SDLK_F7 then ImGuiKey_F7
161
+ when SDL::SDLK_F8 then ImGuiKey_F8
162
+ when SDL::SDLK_F9 then ImGuiKey_F9
163
+ when SDL::SDLK_F10 then ImGuiKey_F10
164
+ when SDL::SDLK_F11 then ImGuiKey_F11
165
+ when SDL::SDLK_F12 then ImGuiKey_F12
166
+ else ImGuiKey_None
167
+ end
168
+ end
169
+
170
+ # [INTERNAL]
171
+ def self.ImGui_ImplSDL2_UpdateKeyModifiers(sdl_key_mods)
172
+ io = ImGuiIO.new(ImGui::GetIO())
173
+ io.AddKeyEvent(ImGuiMod_Ctrl, (sdl_key_mods & SDL::KMOD_CTRL) != 0)
174
+ io.AddKeyEvent(ImGuiMod_Shift, (sdl_key_mods & SDL::KMOD_SHIFT) != 0)
175
+ io.AddKeyEvent(ImGuiMod_Alt, (sdl_key_mods & SDL::KMOD_ALT) != 0)
176
+ io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & SDL::KMOD_GUI) != 0)
177
+ end
178
+
179
+ # You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
180
+ # - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
181
+ # - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
182
+ # Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
183
+ # If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field.
184
+ def self.ImplSDL2_ProcessEvent(event)
185
+ io = ImGuiIO.new(ImGui::GetIO())
186
+ bd = ImGui_ImplSDL2_GetBackendData()
187
+
188
+ case event[:type]
189
+
190
+ when SDL::MOUSEMOTION
191
+ io.AddMousePosEvent(event[:motion][:x].to_f, event[:motion][:y].to_f)
192
+ return true
193
+
194
+ when SDL::MOUSEWHEEL
195
+ wheel_x = (event[:wheel][:x] > 0) ? 1.0 : (event[:wheel][:x] < 0) ? -1.0 : 0.0
196
+ wheel_y = (event[:wheel][:y] > 0) ? 1.0 : (event[:wheel][:y] < 0) ? -1.0 : 0.0
197
+ io.AddMouseWheelEvent(wheel_x, wheel_y)
198
+ return true
199
+
200
+ when SDL::MOUSEBUTTONDOWN, SDL::MOUSEBUTTONUP
201
+ mouse_button = -1
202
+ mouse_button = 0 if event[:button][:button] == SDL::BUTTON_LEFT
203
+ mouse_button = 1 if event[:button][:button] == SDL::BUTTON_RIGHT
204
+ mouse_button = 2 if event[:button][:button] == SDL::BUTTON_MIDDLE
205
+ if mouse_button != -1
206
+ io.AddMouseButtonEvent(mouse_button, event[:type] == SDL::MOUSEBUTTONDOWN)
207
+ bd.mouseButtonsDown = (event[:type] == SDL::MOUSEBUTTONDOWN) ? (bd.mouseButtonsDown | (1 << mouse_button)) : (bd.mouseButtonsDown & ~(1 << mouse_button))
208
+ return true
209
+ end
210
+
211
+ when SDL::TEXTINPUT
212
+ io.AddInputCharactersUTF8(event[:text][:text])
213
+ return true
214
+
215
+ when SDL::KEYDOWN, SDL::KEYUP
216
+ ImGui_ImplSDL2_UpdateKeyModifiers(event[:key][:keysym][:mod])
217
+ key = ImGui_ImplSDL2_KeyToImGuiKey(event[:key][:keysym][:sym])
218
+ io.AddKeyEvent(key, (event[:type] == SDL::KEYDOWN))
219
+ io.SetKeyEventNativeData(key, event[:key][:keysym][:sym], event[:key][:keysym][:scancode], event[:key][:keysym][:scancode]) # To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions.
220
+ return true
221
+
222
+ when SDL::WINDOWEVENT
223
+ io.AddMousePosEvent(-Float::MAX, -Float::MAX) if event[:window][:event] == SDL::WINDOWEVENT_LEAVE
224
+
225
+ if event[:window][:event] == SDL::WINDOWEVENT_FOCUS_GAINED
226
+ io.AddFocusEvent(true)
227
+ elsif event[:window][:event] == SDL::WINDOWEVENT_FOCUS_LOST
228
+ io.AddFocusEvent(false)
229
+ end
230
+ return true
231
+
232
+ end
233
+
234
+ return false
235
+ end
236
+
237
+ def self.ImplSDL2_Init(window, renderer)
238
+
239
+ # Check and store if we are on a SDL backend that supports global mouse position
240
+ # ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list)
241
+ mouse_can_use_global_state = false
242
+ sdl_backend = SDL.GetCurrentVideoDriver().read_string
243
+ global_mouse_whitelist = ["windows", "cocoa", "x11", "DIVE", "VMAN"]
244
+ global_mouse_whitelist.each do |platform|
245
+ mouse_can_use_global_state = true if sdl_backend == platform
246
+ end
247
+
248
+ # Setup backend capabilities flags
249
+ bd = ImGui_ImplSDL2_Data.new
250
+ @@g_BackendData[ImGui::GetCurrentContext().address] = bd
251
+
252
+ io = ImGuiIO.new(ImGui::GetIO())
253
+ io[:BackendPlatformName] = @@g_BackendPlatformName
254
+ io[:BackendFlags] |= ImGuiBackendFlags_HasMouseCursors # We can honor GetMouseCursor() values (optional)
255
+ io[:BackendFlags] |= ImGuiBackendFlags_HasSetMousePos # We can honor io.WantSetMousePos requests (optional, rarely used)
256
+
257
+ bd.window = window
258
+ bd.renderer = renderer
259
+ bd.mouseCanUseGlobalState = mouse_can_use_global_state
260
+
261
+ # [TODO] Support ClipboardText : pass callbacks as Proc or something
262
+ # io[:SetClipboardTextFn] = ImGui_ImplSDL2_SetClipboardText
263
+ # io[:GetClipboardTextFn] = ImGui_ImplSDL2_GetClipboardText
264
+ io[:ClipboardUserData] = nil
265
+
266
+ # Load mouse cursors
267
+ bd.mouseCursors[ImGuiMouseCursor_Arrow] = SDL.CreateSystemCursor(SDL::SYSTEM_CURSOR_ARROW)
268
+ bd.mouseCursors[ImGuiMouseCursor_TextInput] = SDL.CreateSystemCursor(SDL::SYSTEM_CURSOR_IBEAM)
269
+ bd.mouseCursors[ImGuiMouseCursor_ResizeAll] = SDL.CreateSystemCursor(SDL::SYSTEM_CURSOR_SIZEALL)
270
+ bd.mouseCursors[ImGuiMouseCursor_ResizeNS] = SDL.CreateSystemCursor(SDL::SYSTEM_CURSOR_SIZENS)
271
+ bd.mouseCursors[ImGuiMouseCursor_ResizeEW] = SDL.CreateSystemCursor(SDL::SYSTEM_CURSOR_SIZEWE)
272
+ bd.mouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL.CreateSystemCursor(SDL::SYSTEM_CURSOR_SIZENESW)
273
+ bd.mouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL.CreateSystemCursor(SDL::SYSTEM_CURSOR_SIZENWSE)
274
+ bd.mouseCursors[ImGuiMouseCursor_Hand] = SDL.CreateSystemCursor(SDL::SYSTEM_CURSOR_HAND)
275
+ bd.mouseCursors[ImGuiMouseCursor_NotAllowed] = SDL.CreateSystemCursor(SDL::SYSTEM_CURSOR_NO)
276
+
277
+ # Set platform dependent data in viewport
278
+ case RbConfig::CONFIG['host_os']
279
+ when /mswin|msys|mingw|cygwin/
280
+ info = SDL::SysWMinfo_win.new
281
+ SDL.GetVersion(info[:version])
282
+ if SDL.GetWindowWMInfo(window, info) == SDL::TRUE
283
+ viewport = ImGuiViewport.new(ImGui::GetMainViewport())
284
+ viewport[:PlatformHandleRaw] = info[:info][:win][:window]
285
+ end
286
+ end
287
+
288
+ # Set SDL hint to receive mouse click events on window focus, otherwise SDL doesn't emit the event.
289
+ # Without this, when clicking to gain focus, our widgets wouldn't activate even though they showed as hovered.
290
+ # (This is unfortunately a global SDL setting, so enabling it might have a side-effect on your application.
291
+ # It is unlikely to make a difference, but if your app absolutely needs to ignore the initial on-focus click:
292
+ # you can ignore SDL_MOUSEBUTTONDOWN events coming right after a SDL_WINDOWEVENT_FOCUS_GAINED)
293
+ if defined?(SDL::HINT_MOUSE_FOCUS_CLICKTHROUGH)
294
+ SDL.SetHint(SDL::HINT_MOUSE_FOCUS_CLICKTHROUGH, "1")
295
+ end
296
+
297
+ return true
298
+ end
299
+
300
+ def self.ImplSDL2_Shutdown()
301
+ io = ImGuiIO.new(ImGui::GetIO())
302
+ bd = ImGui_ImplSDL2_GetBackendData()
303
+
304
+ SDL.free(bd.clipboardTextData) if bd.clipboardTextData
305
+
306
+ ImGuiMouseCursor_COUNT.times do |cursor_n|
307
+ SDL.FreeCursor(bd.mouseCursors[cursor_n])
308
+ bd.mouseCursors[cursor_n] = nil
309
+ end
310
+
311
+ io[:BackendPlatformName] = nil
312
+ io[:BackendPlatformUserData] = nil
313
+ @@g_BackendData[ImGui::GetCurrentContext()] = nil
314
+ end
315
+
316
+ # [INTERNAL]
317
+ def self.ImplSDL2_UpdateMouseData()
318
+ bd = ImGui_ImplSDL2_GetBackendData()
319
+ io = ImGuiIO.new(ImGui::GetIO())
320
+
321
+ SDL.CaptureMouse(bd.mouseButtonsDown != 0 ? SDL::TRUE : SDL::FALSE)
322
+ focused_window = SDL.GetKeyboardFocus()
323
+ is_app_focused = (bd.window == focused_window)
324
+ if is_app_focused
325
+ # (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
326
+ if io[:WantSetMousePos]
327
+ SDL.WarpMouseInWindow(bd.window, io[:MousePos][:x].to_i, io[:MousePos][:y].to_i)
328
+ end
329
+
330
+ # (Optional) Fallback to provide mouse position when focused (SDL_MOUSEMOTION already provides this when hovered or captured)
331
+ if bd.mouseCanUseGlobalState && bd.mouseButtonsDown == 0
332
+ wx = FFI::MemoryPointer.new(:int)
333
+ wy = FFI::MemoryPointer.new(:int)
334
+ mx = FFI::MemoryPointer.new(:int)
335
+ my = FFI::MemoryPointer.new(:int)
336
+ SDL.GetGlobalMouseState(mx, my)
337
+ SDL.GetWindowPosition(bd.window, wx, wy)
338
+ io.AddMousePosEvent(mx.read(:int).to_f - wx.read(:int).to_f, my.read(:int).to_f - wy.read(:int).to_f)
339
+ end
340
+ end
341
+ end
342
+
343
+ # [INTERNAL]
344
+ def self.ImplSDL2_UpdateMouseCursor()
345
+ io = ImGuiIO.new(ImGui::GetIO())
346
+ return if (io[:ConfigFlags] & ImGuiConfigFlags_NoMouseCursorChange)
347
+ bd = ImGui_ImplSDL2_GetBackendData()
348
+
349
+ imgui_cursor = ImGui::GetMouseCursor()
350
+ if io[:MouseDrawCursor] || imgui_cursor == ImGuiMouseCursor_None
351
+ # Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
352
+ SDL.ShowCursor(SDL::FALSE)
353
+ else
354
+ # Show OS mouse cursor
355
+ SDL.SetCursor(bd.mouseCursors[imgui_cursor] ? bd.mouseCursors[imgui_cursor] : bd.mouseCursors[ImGuiMouseCursor_Arrow])
356
+ SDL.ShowCursor(SDL::TRUE)
357
+ end
358
+ end
359
+
360
+ #
361
+ # [TODO] Support ImplSDL2_UpdateGamepads
362
+ #
363
+
364
+ def self.ImplSDL2_NewFrame()
365
+ bd = ImGui_ImplSDL2_GetBackendData()
366
+ io = ImGuiIO.new(ImGui::GetIO())
367
+
368
+ # Setup display size (every frame to accommodate for window resizing)
369
+ w = ' ' * 4
370
+ h = ' ' * 4
371
+ display_w = ' ' * 4
372
+ display_h = ' ' * 4
373
+ SDL.GetWindowSize(bd.window, w, h)
374
+ w = w.unpack1('L')
375
+ h = h.unpack1('L')
376
+
377
+ if (SDL.GetWindowFlags(bd.window) & SDL::WINDOW_MINIMIZED) != 0
378
+ w = h = 0
379
+ end
380
+ if bd.renderer != nil
381
+ SDL.GetRendererOutputSize(bd.renderer, display_w, display_h)
382
+ else
383
+ SDL.GL_GetDrawableSize(bd.window, display_w, display_h)
384
+ end
385
+ display_w = display_w.unpack1('L')
386
+ display_h = display_h.unpack1('L')
387
+
388
+ io[:DisplaySize] = ImVec2.create(w.to_f, h.to_f)
389
+
390
+ if w > 0 && h > 0
391
+ io[:DisplayFramebufferScale][:x] = display_w.to_f / w
392
+ io[:DisplayFramebufferScale][:y] = display_h.to_f / h
393
+ end
394
+
395
+ # Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution)
396
+ frequency = SDL.GetPerformanceFrequency()
397
+ current_time = SDL.GetPerformanceCounter()
398
+
399
+ io[:DeltaTime] = bd.time > 0 ? ((current_time - bd.time).to_f / frequency) : (1.0/60.0)
400
+ bd.time = current_time
401
+
402
+ ImplSDL2_UpdateMouseData()
403
+ ImplSDL2_UpdateMouseCursor()
404
+
405
+ # [TODO] update gamepads
406
+ # ImGui_ImplSDL2_UpdateGamepads()
407
+ end
408
+
409
+ end
@@ -0,0 +1,200 @@
1
+ require 'ffi'
2
+ require 'sdl2'
3
+ require_relative 'imgui'
4
+
5
+ module ImGui
6
+
7
+ class ImGui_ImplSDLRenderer_Data < FFI::Struct
8
+ layout(
9
+ :SDLRenderer, :pointer,
10
+ :FontTexture, :pointer
11
+ )
12
+ end
13
+
14
+ def self.ImGui_ImplSDLRenderer_GetBackendData()
15
+ if ImGui::GetCurrentContext() != nil
16
+ io = ImGuiIO.new(ImGui::GetIO())
17
+ instance = ImGui_ImplSDLRenderer_Data.new(io[:BackendRendererUserData])
18
+ return instance
19
+ else
20
+ return nil
21
+ end
22
+ end
23
+
24
+ @@g_BackendRendererName = FFI::MemoryPointer.from_string("imgui_impl_sdlrenderer")
25
+ @@g_BackendRendererUserData = nil
26
+
27
+ def self.ImplSDLRenderer_Init(renderer)
28
+ io = ImGuiIO.new(ImGui::GetIO())
29
+
30
+ # Setup backend capabilities flags
31
+
32
+ io[:BackendRendererName] = @@g_BackendRendererName
33
+
34
+ @@g_BackendRendererUserData = ImGui_ImplSDLRenderer_Data.new
35
+ @@g_BackendRendererUserData[:SDLRenderer] = renderer
36
+ @@g_BackendRendererUserData[:FontTexture] = nil
37
+ io[:BackendRendererUserData] = @@g_BackendRendererUserData
38
+
39
+ io[:BackendFlags] |= ImGuiBackendFlags_RendererHasVtxOffset # We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
40
+
41
+ return true
42
+ end
43
+
44
+ def self.ImplSDLRenderer_Shutdown()
45
+ ImplSDLRenderer_DestroyDeviceObjects()
46
+ io = ImGuiIO.new(ImGui::GetIO())
47
+ io[:BackendRendererName] = nil
48
+ io[:BackendRendererUserData] = nil
49
+ @@g_BackendRendererUserData = nil
50
+ end
51
+
52
+ # [Internal]
53
+ def self.ImplSDLRenderer_SetupRenderState()
54
+ bd = ImGui_ImplSDLRenderer_GetBackendData()
55
+
56
+ # Clear out any viewports and cliprect set by the user
57
+ # FIXME: Technically speaking there are lots of other things we could backup/setup/restore during our render process.
58
+ SDL.RenderSetViewport(bd[:SDLRenderer], nil)
59
+ SDL.RenderSetClipRect(bd[:SDLRenderer], nil)
60
+ end
61
+
62
+ def self.ImplSDLRenderer_NewFrame()
63
+ bd = ImGui_ImplSDLRenderer_GetBackendData()
64
+ ImGui::ImplSDLRenderer_CreateDeviceObjects() if bd[:FontTexture] == nil
65
+ end
66
+
67
+ def self.ImplSDLRenderer_RenderDrawData(draw_data_raw)
68
+ draw_data = ImDrawData.new(draw_data_raw)
69
+ bd = ImGui_ImplSDLRenderer_GetBackendData()
70
+
71
+ # If there's a scale factor set by the user, use that instead
72
+ # If the user has specified a scale factor to SDL_Renderer already via SDL_RenderSetScale(), SDL will scale whatever we pass
73
+ # to SDL_RenderGeometryRaw() by that scale factor. In that case we don't want to be also scaling it ourselves here.
74
+ rsx = FFI::MemoryPointer.new :float
75
+ rsy = FFI::MemoryPointer.new :float
76
+ SDL.RenderGetScale(bd[:SDLRenderer], rsx, rsy)
77
+ render_scale = ImVec2.create(0, 0)
78
+ render_scale[:x] = (rsx.read_float() == 1.0) ? draw_data[:FramebufferScale][:x] : 1.0
79
+ render_scale[:y] = (rsy.read_float() == 1.0) ? draw_data[:FramebufferScale][:y] : 1.0
80
+
81
+ # Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
82
+ fb_width = (draw_data[:DisplaySize][:x] * render_scale[:x]).to_i
83
+ fb_height = (draw_data[:DisplaySize][:y] * render_scale[:y]).to_i
84
+ return if fb_width == 0 || fb_height == 0
85
+
86
+ # Backup SDL_Renderer state that will be modified to restore it afterwards
87
+ oldViewport = SDL::Rect.new
88
+ oldClipEnabled = FFI::MemoryPointer.new :bool
89
+ oldClipRect = SDL::Rect.new
90
+
91
+ oldClipEnabled = (SDL.RenderIsClipEnabled(bd[:SDLRenderer]) == SDL::TRUE)
92
+ SDL.RenderGetViewport(bd[:SDLRenderer], oldViewport)
93
+ SDL.RenderGetClipRect(bd[:SDLRenderer], oldClipRect)
94
+
95
+ # Will project scissor/clipping rectangles into framebuffer space
96
+ clip_off = draw_data[:DisplayPos] # (0,0) unless using multi-viewports
97
+ clip_scale = render_scale
98
+
99
+ # Render command lists
100
+ ImplSDLRenderer_SetupRenderState()
101
+ draw_data[:CmdListsCount].times do |n|
102
+ cmd_list = ImDrawList.new((draw_data[:CmdLists][:Data] + FFI.type_size(:pointer) * n).read_pointer)
103
+ vtx_buffer = cmd_list[:VtxBuffer][:Data] # const ImDrawVert*
104
+ idx_buffer = cmd_list[:IdxBuffer][:Data] # const ImDrawIdx*
105
+
106
+ cmd_list[:CmdBuffer][:Size].times do |cmd_i|
107
+ pcmd = ImDrawCmd.new(cmd_list[:CmdBuffer][:Data] + ImDrawCmd.size * cmd_i) # const ImDrawCmd*
108
+ if pcmd[:UserCallback] != nil
109
+ # [TODO] Handle user callback (Ref.: https://github.com/ffi/ffi/wiki/Callbacks )
110
+
111
+ # User callback, registered via ImDrawList::AddCallback()
112
+ # (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
113
+ # if pcmd[:UserCallback] == :ImDrawCallback_ResetRenderState
114
+ ImGui_ImplSDLRenderer_SetupRenderState()
115
+ # else
116
+ # pcmd[:UserCallback](cmd_list, pcmd)
117
+ # end
118
+ else
119
+ clip_min = ImVec2.create((pcmd[:ClipRect][:x] - clip_off[:x]) * clip_scale[:x], (pcmd[:ClipRect][:y] - clip_off[:y]) * clip_scale[:y])
120
+ clip_max = ImVec2.create((pcmd[:ClipRect][:z] - clip_off[:x]) * clip_scale[:x], (pcmd[:ClipRect][:w] - clip_off[:y]) * clip_scale[:y])
121
+
122
+ clip_min[:x] = 0.0 if clip_min[:x] < 0.0
123
+ clip_min[:y] = 0.0 if clip_min[:y] < 0.0
124
+ clip_max[:x] = fb_width.to_f if clip_max[:x] > fb_width
125
+ clip_max[:y] = fb_height.to_f if clip_max[:y] > fb_height
126
+ next if (clip_max[:x] <= clip_min[:x] || clip_max[:y] <= clip_min[:y])
127
+
128
+ r = SDL::Rect.new
129
+ r[:x] = clip_min[:x].to_i
130
+ r[:y] = clip_min[:y].to_i
131
+ r[:w] = (clip_max[:x] - clip_min[:x]).to_i
132
+ r[:h] = (clip_max[:y] - clip_min[:y]).to_i
133
+
134
+ SDL.RenderSetClipRect(bd[:SDLRenderer], r.to_ptr)
135
+
136
+ xy = vtx_buffer + (pcmd[:VtxOffset] + ImDrawVert.offset_of(:pos))
137
+ uv = vtx_buffer + (pcmd[:VtxOffset] + ImDrawVert.offset_of(:uv))
138
+ color = vtx_buffer + (pcmd[:VtxOffset] + ImDrawVert.offset_of(:col))
139
+
140
+ SDL.RenderGeometryRaw(bd[:SDLRenderer], pcmd[:TextureId],
141
+ xy, ImDrawVert.size,
142
+ color, ImDrawVert.size,
143
+ uv, ImDrawVert.size,
144
+ cmd_list[:VtxBuffer][:Size] - pcmd[:VtxOffset],
145
+ idx_buffer + FFI.type_size(:ImDrawIdx) * pcmd[:IdxOffset], pcmd[:ElemCount], FFI.type_size(:ImDrawIdx)) # FFI.type_size(:ImDrawIdx) == FFI::Type::UINT16.size
146
+
147
+ # Restore modified SDL_Renderer state
148
+ SDL.RenderSetViewport(bd[:SDLRenderer], oldViewport)
149
+ SDL.RenderSetClipRect(bd[:SDLRenderer], oldClipEnabled ? oldClipRect : nil)
150
+ end
151
+ end
152
+ end
153
+ end
154
+
155
+ # Called by Init/NewFrame/Shutdown
156
+ def self.ImplSDLRenderer_CreateFontsTexture()
157
+ io = ImGuiIO.new(ImGui::GetIO())
158
+ bd = ImGui_ImplSDLRenderer_GetBackendData()
159
+
160
+ # Build texture atlas
161
+ pixels = FFI::MemoryPointer.new :pointer
162
+ width = FFI::MemoryPointer.new :int
163
+ height = FFI::MemoryPointer.new :int
164
+ io[:Fonts].GetTexDataAsRGBA32(pixels, width, height, nil) # Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
165
+
166
+ # Upload texture to graphics system
167
+ bd[:FontTexture] = SDL.CreateTexture(bd[:SDLRenderer], SDL::PIXELFORMAT_ABGR8888, SDL::TEXTUREACCESS_STATIC, width.read_int, height.read_int)
168
+ if bd[:FontTexture] == nil
169
+ SDL.Log("error creating texture")
170
+ return false
171
+ end
172
+
173
+ SDL.UpdateTexture(bd[:FontTexture], nil, pixels.read_pointer, 4 * width.read_int)
174
+ SDL.SetTextureBlendMode(bd[:FontTexture], SDL::BLENDMODE_BLEND)
175
+
176
+ # Store our identifier
177
+ io[:Fonts].SetTexID(bd[:FontTexture])
178
+
179
+ return true
180
+ end
181
+
182
+ def self.ImplSDLRenderer_DestroyFontsTexture()
183
+ io = ImGuiIO.new(ImGui::GetIO())
184
+ bd = ImGui_ImplSDLRenderer_GetBackendData()
185
+ if bd[:FontTexture] != nil
186
+ io[:Fonts].SetTexID(nil)
187
+ SDL.DestroyTexture(bd[:FontTexture])
188
+ bd[:FontTexture] = nil
189
+ end
190
+ end
191
+
192
+ def self.ImplSDLRenderer_CreateDeviceObjects()
193
+ return ImGui::ImplSDLRenderer_CreateFontsTexture()
194
+ end
195
+
196
+ def self.ImplSDLRenderer_DestroyDeviceObjects()
197
+ ImGui::ImplSDLRenderer_DestroyFontsTexture()
198
+ end
199
+
200
+ end
@@ -0,0 +1,49 @@
1
+ # imgui-bindings : Yet another ImGui wrapper for Ruby
2
+ #
3
+ # * https://github.com/vaiorabbit/ruby-imgui
4
+
5
+ require 'ffi'
6
+
7
+ module ImGui
8
+
9
+ extend FFI::Library
10
+
11
+ @@imgui_import_internal_done = false
12
+
13
+ def self.import_internal_symbols(output_error = false)
14
+
15
+ symbols = [
16
+ :igFocusWindow,
17
+ :igGetCurrentWindow,
18
+ ]
19
+
20
+ args = {
21
+ :igFocusWindow => [:pointer],
22
+ :igGetCurrentWindow => [],
23
+ }
24
+
25
+ retvals = {
26
+ :igFocusWindow => :void,
27
+ :igGetCurrentWindow => :pointer,
28
+ }
29
+
30
+ symbols.each do |sym|
31
+ begin
32
+ attach_function sym, args[sym], retvals[sym]
33
+ rescue FFI::NotFoundError
34
+ $stderr.puts("[Warning] Failed to import #{sym}.\n") if output_error
35
+ end
36
+ end
37
+
38
+ @@imgui_import_internal_done = true
39
+ end # self.import_internal_symbols
40
+
41
+ def self.GetCurrentWindow()
42
+ igGetCurrentWindow()
43
+ end
44
+
45
+ def self.FocusWindow(window)
46
+ igFocusWindow(window)
47
+ end
48
+
49
+ end # module ImGui