1 // ImGui GLFW binding with OpenGL3 + shaders
2 // In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp.
4 // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
5 // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown().
6 // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp.
7 // https://github.com/ocornut/imgui
11 #include "imgui/imgui.h"
12 #include "imgui_impl_gtk3.h"
16 #define ARRAY_SIZE(arg) (sizeof(arg) / sizeof((arg)[0]))
20 (GDK_STRUCTURE_MASK | \
21 GDK_FOCUS_CHANGE_MASK | \
23 GDK_PROPERTY_CHANGE_MASK | \
24 GDK_ENTER_NOTIFY_MASK | \
25 GDK_LEAVE_NOTIFY_MASK | \
26 GDK_KEY_PRESS_MASK | \
27 GDK_KEY_RELEASE_MASK | \
28 GDK_BUTTON_PRESS_MASK | \
29 GDK_BUTTON_RELEASE_MASK | \
30 GDK_POINTER_MOTION_MASK | \
31 GDK_SMOOTH_SCROLL_MASK | \
35 static GtkWidget
* g_GtkGlArea
= NULL
;
36 static guint64 g_Time
= 0;
37 static bool g_MousePressed
[5] = { false, false, false, false, false };
38 static ImVec2 g_MousePosition
= ImVec2(-1, -1);
39 static float g_MouseWheel
= 0.0f
;
40 static int g_NumRedraws
= 0;
41 static guint g_RedrawTimeout
= 0;
43 static const char* ImGui_ImplGtk3_GetClipboardText(void* user_data
)
45 static char *last_clipboard
= NULL
;
47 g_clear_pointer(&last_clipboard
, g_free
);
48 last_clipboard
= gtk_clipboard_wait_for_text(GTK_CLIPBOARD(user_data
));
49 return last_clipboard
;
52 static void ImGui_ImplGtk3_SetClipboardText(void* user_data
, const char* text
)
54 gtk_clipboard_set_text(GTK_CLIPBOARD(user_data
), text
, -1);
57 void ImGui_ImplGtk3_HandleEvent(GdkEvent
*event
)
59 ImGuiIO
& io
= ImGui::GetIO();
61 GdkEventType type
= gdk_event_get_event_type(event
);
64 case GDK_MOTION_NOTIFY
:
66 gdouble x
= 0.0f
, y
= 0.0f
;
67 if (gdk_event_get_coords(event
, &x
, &y
))
68 g_MousePosition
= ImVec2(x
, y
);
71 case GDK_BUTTON_PRESS
:
72 case GDK_BUTTON_RELEASE
:
75 if (gdk_event_get_button(event
, &button
) && button
> 0 && button
<= 5)
77 if (type
== GDK_BUTTON_PRESS
)
78 g_MousePressed
[button
- 1] = true;
85 if (gdk_event_get_scroll_deltas(event
, &x
, &y
))
92 GdkEventKey
*e
= (GdkEventKey
*) event
;
98 } gdk_key_to_imgui_key
[] =
100 { ImGuiKey_Tab
, GDK_KEY_Tab
},
101 { ImGuiKey_Tab
, GDK_KEY_ISO_Left_Tab
},
102 { ImGuiKey_LeftArrow
, GDK_KEY_Left
},
103 { ImGuiKey_RightArrow
, GDK_KEY_Right
},
104 { ImGuiKey_UpArrow
, GDK_KEY_Up
},
105 { ImGuiKey_DownArrow
, GDK_KEY_Down
},
106 { ImGuiKey_PageUp
, GDK_KEY_Page_Up
},
107 { ImGuiKey_PageDown
, GDK_KEY_Page_Down
},
108 { ImGuiKey_Home
, GDK_KEY_Home
},
109 { ImGuiKey_End
, GDK_KEY_End
},
110 { ImGuiKey_Delete
, GDK_KEY_Delete
},
111 { ImGuiKey_Backspace
, GDK_KEY_BackSpace
},
112 { ImGuiKey_Enter
, GDK_KEY_Return
},
113 { ImGuiKey_Escape
, GDK_KEY_Escape
},
114 { ImGuiKey_A
, GDK_KEY_a
},
115 { ImGuiKey_C
, GDK_KEY_c
},
116 { ImGuiKey_V
, GDK_KEY_v
},
117 { ImGuiKey_X
, GDK_KEY_x
},
118 { ImGuiKey_Y
, GDK_KEY_y
},
119 { ImGuiKey_Z
, GDK_KEY_z
},
121 for (unsigned i
= 0; i
< ARRAY_SIZE(gdk_key_to_imgui_key
); i
++)
123 if (e
->keyval
== gdk_key_to_imgui_key
[i
].gdk
)
124 io
.KeysDown
[gdk_key_to_imgui_key
[i
].imgui
] = type
== GDK_KEY_PRESS
;
126 gunichar c
= gdk_keyval_to_unicode(e
->keyval
);
127 if (g_unichar_isprint(c
) && ImGuiKey_COUNT
+ c
< ARRAY_SIZE(io
.KeysDown
))
128 io
.KeysDown
[ImGuiKey_COUNT
+ c
] = type
== GDK_KEY_PRESS
;
130 if (type
== GDK_KEY_PRESS
&& e
->string
)
131 io
.AddInputCharactersUTF8(e
->string
);
135 GdkModifierType modifier
;
138 { &io
.KeyCtrl
, GDK_CONTROL_MASK
,
139 { GDK_KEY_Control_L
, GDK_KEY_Control_R
, 0 }, },
140 { &io
.KeyShift
, GDK_SHIFT_MASK
,
141 { GDK_KEY_Shift_L
, GDK_KEY_Shift_R
, 0 }, },
142 { &io
.KeyAlt
, GDK_MOD1_MASK
,
143 { GDK_KEY_Alt_L
, GDK_KEY_Alt_R
, 0 }, },
144 { &io
.KeySuper
, GDK_SUPER_MASK
,
145 { GDK_KEY_Super_L
, GDK_KEY_Super_R
, 0 }, }
147 for (unsigned i
= 0; i
< ARRAY_SIZE(mods
); i
++)
149 *mods
[i
].var
= (mods
[i
].modifier
& e
->state
);
152 for (int j
= 0; mods
[i
].keyvals
[j
] != 0; j
++)
153 if (e
->keyval
== mods
[i
].keyvals
[j
])
157 *mods
[i
].var
= type
== GDK_KEY_PRESS
;
165 // We trigger 2 subsequent redraws for each event because of the
166 // way some ImGui widgets work. For example a Popup menu will only
167 // appear a frame after a click happened.
170 gtk_widget_queue_draw(g_GtkGlArea
);
173 static gboolean
handle_gdk_event(GtkWidget
*widget
, GdkEvent
*event
, void *data
)
175 ImGui_ImplGtk3_HandleEvent(event
);
179 bool ImGui_ImplGtk3_Init(GtkWidget
* gl_area
, bool install_callbacks
)
181 g_clear_pointer(&g_GtkGlArea
, g_object_unref
);
183 g_GtkGlArea
= GTK_WIDGET(g_object_ref(gl_area
));
184 gtk_widget_realize(g_GtkGlArea
);
185 gtk_widget_set_can_focus(g_GtkGlArea
, TRUE
);
186 gtk_widget_grab_focus(g_GtkGlArea
);
188 if (install_callbacks
) {
189 gtk_widget_add_events(g_GtkGlArea
, EVENT_MASK
);
190 g_signal_connect(g_GtkGlArea
, "event", G_CALLBACK(handle_gdk_event
), NULL
);
193 ImGuiIO
& io
= ImGui::GetIO();
194 for (int i
= 0; i
< ImGuiKey_COUNT
; i
++)
199 io
.SetClipboardTextFn
= ImGui_ImplGtk3_SetClipboardText
;
200 io
.GetClipboardTextFn
= ImGui_ImplGtk3_GetClipboardText
;
201 io
.ClipboardUserData
= gtk_widget_get_clipboard(g_GtkGlArea
,
202 GDK_SELECTION_CLIPBOARD
);
207 void ImGui_ImplGtk3_Shutdown()
209 g_clear_pointer(&g_GtkGlArea
, g_object_unref
);
211 g_source_remove(g_RedrawTimeout
);
214 static gboolean
timeout_callback(gpointer data
)
216 gtk_widget_queue_draw(g_GtkGlArea
);
221 static void kick_timeout_redraw(float timeout
)
225 g_RedrawTimeout
= g_timeout_add(timeout
* 1000, timeout_callback
, NULL
);
228 void ImGui_ImplGtk3_NewFrame()
230 bool next_redraw
= false;
231 if (g_NumRedraws
> 0)
233 gtk_widget_queue_draw(g_GtkGlArea
);
238 ImGuiIO
& io
= ImGui::GetIO();
240 // Setup display size (every frame to accommodate for window resizing)
241 io
.DisplaySize
= ImVec2((float)gtk_widget_get_allocated_width(g_GtkGlArea
),
242 (float)gtk_widget_get_allocated_height(g_GtkGlArea
));
243 int scale_factor
= gtk_widget_get_scale_factor(g_GtkGlArea
);
244 io
.DisplayFramebufferScale
= ImVec2(scale_factor
, scale_factor
);
247 guint64 current_time
= g_get_monotonic_time();
248 io
.DeltaTime
= g_Time
> 0 ? ((float)(current_time
- g_Time
) / 1000000) : (float)(1.0f
/60.0f
);
249 g_Time
= current_time
;
252 if (gtk_widget_has_focus(g_GtkGlArea
))
254 io
.MousePos
= g_MousePosition
; // Mouse position in screen coordinates (set to -1,-1 if no mouse / on another screen, etc.)
258 io
.MousePos
= ImVec2(-1,-1);
261 GdkWindow
*window
= gtk_widget_get_window(g_GtkGlArea
);
262 GdkDevice
*pointer
= gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default()));
263 GdkModifierType modifiers
;
264 gdk_device_get_state(pointer
, window
, NULL
, &modifiers
);
266 for (int i
= 0; i
< 3; i
++)
268 io
.MouseDown
[i
] = g_MousePressed
[i
] || (modifiers
& (GDK_BUTTON1_MASK
<< i
)) != 0;
269 g_MousePressed
[i
] = false;
272 io
.MouseWheel
= g_MouseWheel
;
275 // Hide OS mouse cursor if ImGui is drawing it
276 GdkDisplay
*display
= gdk_window_get_display(window
);
278 gdk_cursor_new_from_name(display
, io
.MouseDrawCursor
? "none" : "default");
279 gdk_window_set_cursor(window
, cursor
);
280 g_object_unref(cursor
);
282 if (!next_redraw
&& io
.WantTextInput
)
283 kick_timeout_redraw(0.2);