1bf215546Sopenharmony_ci// ImGui GLFW binding with OpenGL3 + shaders 2bf215546Sopenharmony_ci// In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. 3bf215546Sopenharmony_ci 4bf215546Sopenharmony_ci// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. 5bf215546Sopenharmony_ci// If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). 6bf215546Sopenharmony_ci// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. 7bf215546Sopenharmony_ci// https://github.com/ocornut/imgui 8bf215546Sopenharmony_ci 9bf215546Sopenharmony_ci#include <stdio.h> 10bf215546Sopenharmony_ci 11bf215546Sopenharmony_ci#include "imgui/imgui.h" 12bf215546Sopenharmony_ci#include "imgui_impl_gtk3.h" 13bf215546Sopenharmony_ci 14bf215546Sopenharmony_ci#include <gtk/gtk.h> 15bf215546Sopenharmony_ci 16bf215546Sopenharmony_ci#define ARRAY_SIZE(arg) (sizeof(arg) / sizeof((arg)[0])) 17bf215546Sopenharmony_ci 18bf215546Sopenharmony_ci#define EVENT_MASK \ 19bf215546Sopenharmony_ci ((GdkEventMask) \ 20bf215546Sopenharmony_ci (GDK_STRUCTURE_MASK | \ 21bf215546Sopenharmony_ci GDK_FOCUS_CHANGE_MASK | \ 22bf215546Sopenharmony_ci GDK_EXPOSURE_MASK | \ 23bf215546Sopenharmony_ci GDK_PROPERTY_CHANGE_MASK | \ 24bf215546Sopenharmony_ci GDK_ENTER_NOTIFY_MASK | \ 25bf215546Sopenharmony_ci GDK_LEAVE_NOTIFY_MASK | \ 26bf215546Sopenharmony_ci GDK_KEY_PRESS_MASK | \ 27bf215546Sopenharmony_ci GDK_KEY_RELEASE_MASK | \ 28bf215546Sopenharmony_ci GDK_BUTTON_PRESS_MASK | \ 29bf215546Sopenharmony_ci GDK_BUTTON_RELEASE_MASK | \ 30bf215546Sopenharmony_ci GDK_POINTER_MOTION_MASK | \ 31bf215546Sopenharmony_ci GDK_SMOOTH_SCROLL_MASK | \ 32bf215546Sopenharmony_ci GDK_SCROLL_MASK)) 33bf215546Sopenharmony_ci 34bf215546Sopenharmony_ci// Data 35bf215546Sopenharmony_cistatic GtkWidget* g_GtkGlArea = NULL; 36bf215546Sopenharmony_cistatic guint64 g_Time = 0; 37bf215546Sopenharmony_cistatic bool g_MousePressed[5] = { false, false, false, false, false }; 38bf215546Sopenharmony_cistatic ImVec2 g_MousePosition = ImVec2(-1, -1); 39bf215546Sopenharmony_cistatic float g_MouseWheel = 0.0f; 40bf215546Sopenharmony_cistatic int g_NumRedraws = 0; 41bf215546Sopenharmony_cistatic guint g_RedrawTimeout = 0; 42bf215546Sopenharmony_ci 43bf215546Sopenharmony_cistatic const char* ImGui_ImplGtk3_GetClipboardText(void* user_data) 44bf215546Sopenharmony_ci{ 45bf215546Sopenharmony_ci static char *last_clipboard = NULL; 46bf215546Sopenharmony_ci 47bf215546Sopenharmony_ci g_clear_pointer(&last_clipboard, g_free); 48bf215546Sopenharmony_ci last_clipboard = gtk_clipboard_wait_for_text(GTK_CLIPBOARD(user_data)); 49bf215546Sopenharmony_ci return last_clipboard; 50bf215546Sopenharmony_ci} 51bf215546Sopenharmony_ci 52bf215546Sopenharmony_cistatic void ImGui_ImplGtk3_SetClipboardText(void* user_data, const char* text) 53bf215546Sopenharmony_ci{ 54bf215546Sopenharmony_ci gtk_clipboard_set_text(GTK_CLIPBOARD(user_data), text, -1); 55bf215546Sopenharmony_ci} 56bf215546Sopenharmony_ci 57bf215546Sopenharmony_civoid ImGui_ImplGtk3_HandleEvent(GdkEvent *event) 58bf215546Sopenharmony_ci{ 59bf215546Sopenharmony_ci ImGuiIO& io = ImGui::GetIO(); 60bf215546Sopenharmony_ci 61bf215546Sopenharmony_ci GdkEventType type = gdk_event_get_event_type(event); 62bf215546Sopenharmony_ci switch (type) 63bf215546Sopenharmony_ci { 64bf215546Sopenharmony_ci case GDK_MOTION_NOTIFY: 65bf215546Sopenharmony_ci { 66bf215546Sopenharmony_ci gdouble x = 0.0f, y = 0.0f; 67bf215546Sopenharmony_ci if (gdk_event_get_coords(event, &x, &y)) 68bf215546Sopenharmony_ci g_MousePosition = ImVec2(x, y); 69bf215546Sopenharmony_ci break; 70bf215546Sopenharmony_ci } 71bf215546Sopenharmony_ci case GDK_BUTTON_PRESS: 72bf215546Sopenharmony_ci case GDK_BUTTON_RELEASE: 73bf215546Sopenharmony_ci { 74bf215546Sopenharmony_ci guint button = 0; 75bf215546Sopenharmony_ci if (gdk_event_get_button(event, &button) && button > 0 && button <= 5) 76bf215546Sopenharmony_ci { 77bf215546Sopenharmony_ci if (type == GDK_BUTTON_PRESS) 78bf215546Sopenharmony_ci g_MousePressed[button - 1] = true; 79bf215546Sopenharmony_ci } 80bf215546Sopenharmony_ci break; 81bf215546Sopenharmony_ci } 82bf215546Sopenharmony_ci case GDK_SCROLL: 83bf215546Sopenharmony_ci { 84bf215546Sopenharmony_ci gdouble x, y; 85bf215546Sopenharmony_ci if (gdk_event_get_scroll_deltas(event, &x, &y)) 86bf215546Sopenharmony_ci g_MouseWheel = -y; 87bf215546Sopenharmony_ci break; 88bf215546Sopenharmony_ci } 89bf215546Sopenharmony_ci case GDK_KEY_PRESS: 90bf215546Sopenharmony_ci case GDK_KEY_RELEASE: 91bf215546Sopenharmony_ci { 92bf215546Sopenharmony_ci GdkEventKey *e = (GdkEventKey *) event; 93bf215546Sopenharmony_ci 94bf215546Sopenharmony_ci static const struct 95bf215546Sopenharmony_ci { 96bf215546Sopenharmony_ci enum ImGuiKey_ imgui; 97bf215546Sopenharmony_ci guint gdk; 98bf215546Sopenharmony_ci } gdk_key_to_imgui_key[] = 99bf215546Sopenharmony_ci { 100bf215546Sopenharmony_ci { ImGuiKey_Tab, GDK_KEY_Tab }, 101bf215546Sopenharmony_ci { ImGuiKey_Tab, GDK_KEY_ISO_Left_Tab }, 102bf215546Sopenharmony_ci { ImGuiKey_LeftArrow, GDK_KEY_Left }, 103bf215546Sopenharmony_ci { ImGuiKey_RightArrow, GDK_KEY_Right }, 104bf215546Sopenharmony_ci { ImGuiKey_UpArrow, GDK_KEY_Up }, 105bf215546Sopenharmony_ci { ImGuiKey_DownArrow, GDK_KEY_Down }, 106bf215546Sopenharmony_ci { ImGuiKey_PageUp, GDK_KEY_Page_Up }, 107bf215546Sopenharmony_ci { ImGuiKey_PageDown, GDK_KEY_Page_Down }, 108bf215546Sopenharmony_ci { ImGuiKey_Home, GDK_KEY_Home }, 109bf215546Sopenharmony_ci { ImGuiKey_End, GDK_KEY_End }, 110bf215546Sopenharmony_ci { ImGuiKey_Delete, GDK_KEY_Delete }, 111bf215546Sopenharmony_ci { ImGuiKey_Backspace, GDK_KEY_BackSpace }, 112bf215546Sopenharmony_ci { ImGuiKey_Enter, GDK_KEY_Return }, 113bf215546Sopenharmony_ci { ImGuiKey_Escape, GDK_KEY_Escape }, 114bf215546Sopenharmony_ci { ImGuiKey_A, GDK_KEY_a }, 115bf215546Sopenharmony_ci { ImGuiKey_C, GDK_KEY_c }, 116bf215546Sopenharmony_ci { ImGuiKey_V, GDK_KEY_v }, 117bf215546Sopenharmony_ci { ImGuiKey_X, GDK_KEY_x }, 118bf215546Sopenharmony_ci { ImGuiKey_Y, GDK_KEY_y }, 119bf215546Sopenharmony_ci { ImGuiKey_Z, GDK_KEY_z }, 120bf215546Sopenharmony_ci }; 121bf215546Sopenharmony_ci for (unsigned i = 0; i < ARRAY_SIZE(gdk_key_to_imgui_key); i++) 122bf215546Sopenharmony_ci { 123bf215546Sopenharmony_ci if (e->keyval == gdk_key_to_imgui_key[i].gdk) 124bf215546Sopenharmony_ci io.KeysDown[gdk_key_to_imgui_key[i].imgui] = type == GDK_KEY_PRESS; 125bf215546Sopenharmony_ci } 126bf215546Sopenharmony_ci gunichar c = gdk_keyval_to_unicode(e->keyval); 127bf215546Sopenharmony_ci if (g_unichar_isprint(c) && ImGuiKey_COUNT + c < ARRAY_SIZE(io.KeysDown)) 128bf215546Sopenharmony_ci io.KeysDown[ImGuiKey_COUNT + c] = type == GDK_KEY_PRESS; 129bf215546Sopenharmony_ci 130bf215546Sopenharmony_ci if (type == GDK_KEY_PRESS && e->string) 131bf215546Sopenharmony_ci io.AddInputCharactersUTF8(e->string); 132bf215546Sopenharmony_ci 133bf215546Sopenharmony_ci struct { 134bf215546Sopenharmony_ci bool *var; 135bf215546Sopenharmony_ci GdkModifierType modifier; 136bf215546Sopenharmony_ci guint keyvals[3]; 137bf215546Sopenharmony_ci } mods[] = { 138bf215546Sopenharmony_ci { &io.KeyCtrl, GDK_CONTROL_MASK, 139bf215546Sopenharmony_ci { GDK_KEY_Control_L, GDK_KEY_Control_R, 0 }, }, 140bf215546Sopenharmony_ci { &io.KeyShift, GDK_SHIFT_MASK, 141bf215546Sopenharmony_ci { GDK_KEY_Shift_L, GDK_KEY_Shift_R, 0 }, }, 142bf215546Sopenharmony_ci { &io.KeyAlt, GDK_MOD1_MASK, 143bf215546Sopenharmony_ci { GDK_KEY_Alt_L, GDK_KEY_Alt_R, 0 }, }, 144bf215546Sopenharmony_ci { &io.KeySuper, GDK_SUPER_MASK, 145bf215546Sopenharmony_ci { GDK_KEY_Super_L, GDK_KEY_Super_R, 0 }, } 146bf215546Sopenharmony_ci }; 147bf215546Sopenharmony_ci for (unsigned i = 0; i < ARRAY_SIZE(mods); i++) 148bf215546Sopenharmony_ci { 149bf215546Sopenharmony_ci *mods[i].var = (mods[i].modifier & e->state); 150bf215546Sopenharmony_ci 151bf215546Sopenharmony_ci bool match = false; 152bf215546Sopenharmony_ci for (int j = 0; mods[i].keyvals[j] != 0; j++) 153bf215546Sopenharmony_ci if (e->keyval == mods[i].keyvals[j]) 154bf215546Sopenharmony_ci match = true; 155bf215546Sopenharmony_ci 156bf215546Sopenharmony_ci if (match) 157bf215546Sopenharmony_ci *mods[i].var = type == GDK_KEY_PRESS; 158bf215546Sopenharmony_ci } 159bf215546Sopenharmony_ci break; 160bf215546Sopenharmony_ci } 161bf215546Sopenharmony_ci default: 162bf215546Sopenharmony_ci break; 163bf215546Sopenharmony_ci } 164bf215546Sopenharmony_ci 165bf215546Sopenharmony_ci // We trigger 2 subsequent redraws for each event because of the 166bf215546Sopenharmony_ci // way some ImGui widgets work. For example a Popup menu will only 167bf215546Sopenharmony_ci // appear a frame after a click happened. 168bf215546Sopenharmony_ci g_NumRedraws = 2; 169bf215546Sopenharmony_ci 170bf215546Sopenharmony_ci gtk_widget_queue_draw(g_GtkGlArea); 171bf215546Sopenharmony_ci} 172bf215546Sopenharmony_ci 173bf215546Sopenharmony_cistatic gboolean handle_gdk_event(GtkWidget *widget, GdkEvent *event, void *data) 174bf215546Sopenharmony_ci{ 175bf215546Sopenharmony_ci ImGui_ImplGtk3_HandleEvent(event); 176bf215546Sopenharmony_ci return TRUE; 177bf215546Sopenharmony_ci} 178bf215546Sopenharmony_ci 179bf215546Sopenharmony_cibool ImGui_ImplGtk3_Init(GtkWidget* gl_area, bool install_callbacks) 180bf215546Sopenharmony_ci{ 181bf215546Sopenharmony_ci g_clear_pointer(&g_GtkGlArea, g_object_unref); 182bf215546Sopenharmony_ci 183bf215546Sopenharmony_ci g_GtkGlArea = GTK_WIDGET(g_object_ref(gl_area)); 184bf215546Sopenharmony_ci gtk_widget_realize(g_GtkGlArea); 185bf215546Sopenharmony_ci gtk_widget_set_can_focus(g_GtkGlArea, TRUE); 186bf215546Sopenharmony_ci gtk_widget_grab_focus(g_GtkGlArea); 187bf215546Sopenharmony_ci 188bf215546Sopenharmony_ci if (install_callbacks) { 189bf215546Sopenharmony_ci gtk_widget_add_events(g_GtkGlArea, EVENT_MASK); 190bf215546Sopenharmony_ci g_signal_connect(g_GtkGlArea, "event", G_CALLBACK(handle_gdk_event), NULL); 191bf215546Sopenharmony_ci } 192bf215546Sopenharmony_ci 193bf215546Sopenharmony_ci ImGuiIO& io = ImGui::GetIO(); 194bf215546Sopenharmony_ci for (int i = 0; i < ImGuiKey_COUNT; i++) 195bf215546Sopenharmony_ci { 196bf215546Sopenharmony_ci io.KeyMap[i] = i; 197bf215546Sopenharmony_ci } 198bf215546Sopenharmony_ci 199bf215546Sopenharmony_ci io.SetClipboardTextFn = ImGui_ImplGtk3_SetClipboardText; 200bf215546Sopenharmony_ci io.GetClipboardTextFn = ImGui_ImplGtk3_GetClipboardText; 201bf215546Sopenharmony_ci io.ClipboardUserData = gtk_widget_get_clipboard(g_GtkGlArea, 202bf215546Sopenharmony_ci GDK_SELECTION_CLIPBOARD); 203bf215546Sopenharmony_ci 204bf215546Sopenharmony_ci return true; 205bf215546Sopenharmony_ci} 206bf215546Sopenharmony_ci 207bf215546Sopenharmony_civoid ImGui_ImplGtk3_Shutdown() 208bf215546Sopenharmony_ci{ 209bf215546Sopenharmony_ci g_clear_pointer(&g_GtkGlArea, g_object_unref); 210bf215546Sopenharmony_ci if (g_RedrawTimeout) 211bf215546Sopenharmony_ci g_source_remove(g_RedrawTimeout); 212bf215546Sopenharmony_ci} 213bf215546Sopenharmony_ci 214bf215546Sopenharmony_cistatic gboolean timeout_callback(gpointer data) 215bf215546Sopenharmony_ci{ 216bf215546Sopenharmony_ci gtk_widget_queue_draw(g_GtkGlArea); 217bf215546Sopenharmony_ci g_RedrawTimeout = 0; 218bf215546Sopenharmony_ci return FALSE; 219bf215546Sopenharmony_ci} 220bf215546Sopenharmony_ci 221bf215546Sopenharmony_cistatic void kick_timeout_redraw(float timeout) 222bf215546Sopenharmony_ci{ 223bf215546Sopenharmony_ci if (g_RedrawTimeout) 224bf215546Sopenharmony_ci return; 225bf215546Sopenharmony_ci g_RedrawTimeout = g_timeout_add(timeout * 1000, timeout_callback, NULL); 226bf215546Sopenharmony_ci} 227bf215546Sopenharmony_ci 228bf215546Sopenharmony_civoid ImGui_ImplGtk3_NewFrame() 229bf215546Sopenharmony_ci{ 230bf215546Sopenharmony_ci bool next_redraw = false; 231bf215546Sopenharmony_ci if (g_NumRedraws > 0) 232bf215546Sopenharmony_ci { 233bf215546Sopenharmony_ci gtk_widget_queue_draw(g_GtkGlArea); 234bf215546Sopenharmony_ci g_NumRedraws--; 235bf215546Sopenharmony_ci next_redraw = true; 236bf215546Sopenharmony_ci } 237bf215546Sopenharmony_ci 238bf215546Sopenharmony_ci ImGuiIO& io = ImGui::GetIO(); 239bf215546Sopenharmony_ci 240bf215546Sopenharmony_ci // Setup display size (every frame to accommodate for window resizing) 241bf215546Sopenharmony_ci io.DisplaySize = ImVec2((float)gtk_widget_get_allocated_width(g_GtkGlArea), 242bf215546Sopenharmony_ci (float)gtk_widget_get_allocated_height(g_GtkGlArea)); 243bf215546Sopenharmony_ci int scale_factor = gtk_widget_get_scale_factor(g_GtkGlArea); 244bf215546Sopenharmony_ci io.DisplayFramebufferScale = ImVec2(scale_factor, scale_factor); 245bf215546Sopenharmony_ci 246bf215546Sopenharmony_ci // Setup time step 247bf215546Sopenharmony_ci guint64 current_time = g_get_monotonic_time(); 248bf215546Sopenharmony_ci io.DeltaTime = g_Time > 0 ? ((float)(current_time - g_Time) / 1000000) : (float)(1.0f/60.0f); 249bf215546Sopenharmony_ci g_Time = current_time; 250bf215546Sopenharmony_ci 251bf215546Sopenharmony_ci // Setup inputs 252bf215546Sopenharmony_ci if (gtk_widget_has_focus(g_GtkGlArea)) 253bf215546Sopenharmony_ci { 254bf215546Sopenharmony_ci io.MousePos = g_MousePosition; // Mouse position in screen coordinates (set to -1,-1 if no mouse / on another screen, etc.) 255bf215546Sopenharmony_ci } 256bf215546Sopenharmony_ci else 257bf215546Sopenharmony_ci { 258bf215546Sopenharmony_ci io.MousePos = ImVec2(-1,-1); 259bf215546Sopenharmony_ci } 260bf215546Sopenharmony_ci 261bf215546Sopenharmony_ci GdkWindow *window = gtk_widget_get_window(g_GtkGlArea); 262bf215546Sopenharmony_ci GdkDevice *pointer = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default())); 263bf215546Sopenharmony_ci GdkModifierType modifiers; 264bf215546Sopenharmony_ci gdk_device_get_state(pointer, window, NULL, &modifiers); 265bf215546Sopenharmony_ci 266bf215546Sopenharmony_ci for (int i = 0; i < 3; i++) 267bf215546Sopenharmony_ci { 268bf215546Sopenharmony_ci io.MouseDown[i] = g_MousePressed[i] || (modifiers & (GDK_BUTTON1_MASK << i)) != 0; 269bf215546Sopenharmony_ci g_MousePressed[i] = false; 270bf215546Sopenharmony_ci } 271bf215546Sopenharmony_ci 272bf215546Sopenharmony_ci io.MouseWheel = g_MouseWheel; 273bf215546Sopenharmony_ci g_MouseWheel = 0.0f; 274bf215546Sopenharmony_ci 275bf215546Sopenharmony_ci // Hide OS mouse cursor if ImGui is drawing it 276bf215546Sopenharmony_ci GdkDisplay *display = gdk_window_get_display(window); 277bf215546Sopenharmony_ci GdkCursor *cursor = 278bf215546Sopenharmony_ci gdk_cursor_new_from_name(display, io.MouseDrawCursor ? "none" : "default"); 279bf215546Sopenharmony_ci gdk_window_set_cursor(window, cursor); 280bf215546Sopenharmony_ci g_object_unref(cursor); 281bf215546Sopenharmony_ci 282bf215546Sopenharmony_ci if (!next_redraw && io.WantTextInput) 283bf215546Sopenharmony_ci kick_timeout_redraw(0.2); 284bf215546Sopenharmony_ci} 285