imgui-bindings 0.1.10-arm64-darwin

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
Binary file