#include <sstream>
#include <cstdint>
#include <cstdio>
#include <chrono>
#include <thread>
#include <imgui.h>
#include <imgui_internal.h>
#include <GL/gl3w.h>
#include <GLFW/glfw3.h>
#include "../cr.h"
struct HostData {
int w, h;
int display_w, display_h;
ImGuiContext *imgui_context = nullptr;
void *wndh = nullptr;
double timestep = 0.0;
bool mousePressed[3] = {false, false, false};
float mouseWheel = 0.0f;
unsigned short inputCharacters[16 + 1] = {};
GLFWwindow *window = nullptr;
const char* (*get_clipboard_fn)(void* user_data);
void(*set_clipboard_fn)(void* user_data, const char* text);
void(*set_cursor_pos_fn)(GLFWwindow* handle, double xpos, double ypos);
void(*get_cursor_pos_fn)(GLFWwindow* handle, double* xpos, double* ypos);
int(*get_window_attrib_fn)(GLFWwindow* handle, int attrib);
int(*get_mouse_button_fn)(GLFWwindow* handle, int button);
void(*set_input_mode_fn)(GLFWwindow* handle, int mode, int value);
};
static uint32_t g_failure = 0;
static HostData *g_data = nullptr;
static auto CR_STATE g_clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
static unsigned int CR_STATE g_version = 0;
#if defined(IMGUI_GUEST_ONLY)
static ImGuiContext CR_STATE *g_imgui_context = nullptr;
static ImFontAtlas CR_STATE *g_default_font_atlas = nullptr;
#endif
static double CR_STATE g_Time = 0.0f;
static GLuint CR_STATE g_FontTexture = 0;
static int CR_STATE g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
static int CR_STATE g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0;
static int CR_STATE g_AttribLocationPosition = 0, g_AttribLocationUV = 0, g_AttribLocationColor = 0;
static unsigned int CR_STATE g_VboHandle = 0, g_VaoHandle = 0, g_ElementsHandle = 0;
void ImGui_ImplGlfwGL3_InvalidateDeviceObjects();
bool ImGui_ImplGlfwGL3_CreateDeviceObjects();
void ImGui_ImplGlfwGL3_RenderDrawLists(ImDrawData* draw_data) {
ImGuiIO& io = ImGui::GetIO();
int fb_width = (int)(io.DisplaySize.x * io.DisplayFramebufferScale.x);
int fb_height = (int)(io.DisplaySize.y * io.DisplayFramebufferScale.y);
if (fb_width == 0 || fb_height == 0)
return;
draw_data->ScaleClipRects(io.DisplayFramebufferScale);
GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
glActiveTexture(GL_TEXTURE0);
GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler);
GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
GLint last_element_array_buffer; glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_element_array_buffer);
GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);
GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb);
GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb);
GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha);
GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha);
GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb);
GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha);
GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glEnable(GL_SCISSOR_TEST);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
const float ortho_projection[4][4] =
{
{2.0f / io.DisplaySize.x, 0.0f, 0.0f, 0.0f},
{0.0f, 2.0f / -io.DisplaySize.y, 0.0f, 0.0f},
{0.0f, 0.0f, -1.0f, 0.0f},
{-1.0f, 1.0f, 0.0f, 1.0f},
};
glUseProgram(g_ShaderHandle);
glUniform1i(g_AttribLocationTex, 0);
glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
glBindVertexArray(g_VaoHandle);
glBindSampler(0, 0);
for (int n = 0; n < draw_data->CmdListsCount; n++) {
const ImDrawList* cmd_list = draw_data->CmdLists[n];
const ImDrawIdx* idx_buffer_offset = 0;
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW);
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) {
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback) {
pcmd->UserCallback(cmd_list, pcmd);
} else {
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
glScissor((int)pcmd->ClipRect.x, (int)(fb_height - pcmd->ClipRect.w), (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y));
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
}
idx_buffer_offset += pcmd->ElemCount;
}
}
glUseProgram(last_program);
glBindTexture(GL_TEXTURE_2D, last_texture);
glBindSampler(0, last_sampler);
glActiveTexture(last_active_texture);
glBindVertexArray(last_vertex_array);
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, last_element_array_buffer);
glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);
if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
glPolygonMode(GL_FRONT_AND_BACK, last_polygon_mode[0]);
glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
}
bool ImGui_ImplGlfwGL3_CreateFontsTexture() {
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); GLint last_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGenTextures(1, &g_FontTexture);
glBindTexture(GL_TEXTURE_2D, g_FontTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
io.Fonts->TexID = (void *)(intptr_t)g_FontTexture;
glBindTexture(GL_TEXTURE_2D, last_texture);
return true;
}
bool ImGui_ImplGlfwGL3_CreateDeviceObjects() {
GLint last_texture, last_array_buffer, last_vertex_array;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
const GLchar *vertex_shader =
"#version 330\n"
"uniform mat4 ProjMtx;\n"
"in vec2 Position;\n"
"in vec2 UV;\n"
"in vec4 Color;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* fragment_shader =
"#version 330\n"
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture( Texture, Frag_UV.st);\n"
"}\n";
g_ShaderHandle = glCreateProgram();
g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(g_VertHandle, 1, &vertex_shader, 0);
glShaderSource(g_FragHandle, 1, &fragment_shader, 0);
glCompileShader(g_VertHandle);
glCompileShader(g_FragHandle);
glAttachShader(g_ShaderHandle, g_VertHandle);
glAttachShader(g_ShaderHandle, g_FragHandle);
glLinkProgram(g_ShaderHandle);
g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
g_AttribLocationPosition = glGetAttribLocation(g_ShaderHandle, "Position");
g_AttribLocationUV = glGetAttribLocation(g_ShaderHandle, "UV");
g_AttribLocationColor = glGetAttribLocation(g_ShaderHandle, "Color");
glGenBuffers(1, &g_VboHandle);
glGenBuffers(1, &g_ElementsHandle);
glGenVertexArrays(1, &g_VaoHandle);
glBindVertexArray(g_VaoHandle);
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
glEnableVertexAttribArray(g_AttribLocationPosition);
glEnableVertexAttribArray(g_AttribLocationUV);
glEnableVertexAttribArray(g_AttribLocationColor);
#define OFFSETOF(TYPE, ELEMENT) ((size_t)&(((TYPE *)0)->ELEMENT))
glVertexAttribPointer(g_AttribLocationPosition, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)OFFSETOF(ImDrawVert, pos));
glVertexAttribPointer(g_AttribLocationUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)OFFSETOF(ImDrawVert, uv));
glVertexAttribPointer(g_AttribLocationColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)OFFSETOF(ImDrawVert, col));
#undef OFFSETOF
ImGui_ImplGlfwGL3_CreateFontsTexture();
glBindTexture(GL_TEXTURE_2D, last_texture);
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
glBindVertexArray(last_vertex_array);
return true;
}
void ImGui_ImplGlfwGL3_InvalidateDeviceObjects() {
if (g_VaoHandle) glDeleteVertexArrays(1, &g_VaoHandle);
if (g_VboHandle) glDeleteBuffers(1, &g_VboHandle);
if (g_ElementsHandle) glDeleteBuffers(1, &g_ElementsHandle);
g_VaoHandle = g_VboHandle = g_ElementsHandle = 0;
if (g_ShaderHandle && g_VertHandle) glDetachShader(g_ShaderHandle, g_VertHandle);
if (g_VertHandle) glDeleteShader(g_VertHandle);
g_VertHandle = 0;
if (g_ShaderHandle && g_FragHandle) glDetachShader(g_ShaderHandle, g_FragHandle);
if (g_FragHandle) glDeleteShader(g_FragHandle);
g_FragHandle = 0;
if (g_ShaderHandle) glDeleteProgram(g_ShaderHandle);
g_ShaderHandle = 0;
if (g_FontTexture) {
glDeleteTextures(1, &g_FontTexture);
ImGui::GetIO().Fonts->TexID = 0;
g_FontTexture = 0;
}
}
bool imui_init() {
gl3wInit();
#if defined(IMGUI_GUEST_ONLY)
if (!g_imgui_context) {
g_imgui_context = new ImGuiContext;
g_default_font_atlas = new ImFontAtlas;
}
ImGui::SetCurrentContext(g_imgui_context);
ImGui::GetIO().Fonts = g_default_font_atlas;
#else
ImGui::SetCurrentContext(g_data->imgui_context);
#endif
ImGuiIO& io = ImGui::GetIO();
io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP;
io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN;
io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP;
io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN;
io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME;
io.KeyMap[ImGuiKey_End] = GLFW_KEY_END;
io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE;
io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE;
io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER;
io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE;
io.KeyMap[ImGuiKey_A] = GLFW_KEY_A;
io.KeyMap[ImGuiKey_C] = GLFW_KEY_C;
io.KeyMap[ImGuiKey_V] = GLFW_KEY_V;
io.KeyMap[ImGuiKey_X] = GLFW_KEY_X;
io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y;
io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z;
io.RenderDrawListsFn = ImGui_ImplGlfwGL3_RenderDrawLists; io.SetClipboardTextFn = g_data->set_clipboard_fn;
io.GetClipboardTextFn = g_data->get_clipboard_fn;
io.ClipboardUserData = g_data->window;
io.ImeWindowHandle = g_data->wndh;
return true;
}
void imui_shutdown() {
ImGui_ImplGlfwGL3_InvalidateDeviceObjects();
#if !defined(IMGUI_GUEST_ONLY)
ImGui::Shutdown(g_data->imgui_context);
#else
ImGui::Shutdown(g_imgui_context);
delete g_default_font_atlas;
g_default_font_atlas = nullptr;
delete g_imgui_context;
g_imgui_context = nullptr;
#endif
}
void imui_frame_end() {
glViewport(0, 0, g_data->display_w, g_data->display_h);
glClearColor(g_clear_color.x, g_clear_color.y, g_clear_color.z, g_clear_color.w);
glClear(GL_COLOR_BUFFER_BIT);
ImGui::Render();
}
void imui_frame_begin() {
if (!g_FontTexture)
ImGui_ImplGlfwGL3_CreateDeviceObjects();
ImGuiIO& io = ImGui::GetIO();
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors;
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos;
for (int i = 0; g_data->inputCharacters[i]; ++i) {
io.AddInputCharacter(g_data->inputCharacters[i]);
}
io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL];
io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT];
io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT];
io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER];
int w = g_data->w;
int h = g_data->h;
int display_w = g_data->display_w;
int display_h = g_data->display_h;
io.DisplaySize = ImVec2((float)w, (float)h);
io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0);
double current_time = g_data->timestep;
io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f);
g_Time = current_time;
if (g_data->get_window_attrib_fn(g_data->window, GLFW_FOCUSED)) {
if (io.WantSetMousePos) {
g_data->set_cursor_pos_fn(g_data->window, (double)io.MousePos.x, (double)io.MousePos.y); } else {
double mouse_x, mouse_y;
g_data->get_cursor_pos_fn(g_data->window, &mouse_x, &mouse_y);
io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); }
}
for (int i = 0; i < 3; i++) {
io.MouseDown[i] = g_data->mousePressed[i] || g_data->get_mouse_button_fn(g_data->window, i) != 0; g_data->mousePressed[i] = false;
}
io.MouseWheel = g_data->mouseWheel;
g_data->mouseWheel = 0.0f;
g_data->set_input_mode_fn(g_data->window, GLFW_CURSOR, io.MouseDrawCursor ? GLFW_CURSOR_HIDDEN : GLFW_CURSOR_NORMAL);
ImGui::NewFrame();
}
void test_crash() {
ImGui::EndFrame();
int *addr = NULL; (void)addr; int i = *addr;
(void)i;
}
void imui_draw() {
static bool CR_STATE show_test_window = true;
static bool CR_STATE show_another_window = false;
{
static CR_STATE float f = 0.0f;
ImGui::Text("Hello, world!");
ImGui::SliderFloat("float", &f, 0.0f, 1.0f);
ImGui::ColorEdit3("clear color", (float*)&g_clear_color);
if (ImGui::Button("Test window"))
show_test_window ^= 1;
if (ImGui::Button("Another window"))
show_another_window ^= 1;
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
ImGui::Text("Guest Version: %d", g_version);
if (g_failure)
ImGui::Text("Guest Failure Reason: %d", g_failure);
}
if (show_another_window) {
ImGui::Begin("Another window", &show_another_window);
ImGui::Text("Hello from another window!");
ImGui::End();
}
if (show_test_window) {
ImGui::SetNextWindowPos(ImVec2(650, 20), ImGuiCond_FirstUseEver);
ImGui::ShowDemoWindow(&show_test_window);
}
}
CR_EXPORT int cr_main(cr_plugin *ctx, cr_op operation) {
assert(ctx);
g_data = (HostData *)ctx->userdata;
g_version = ctx->version;
g_failure = ctx->failure;
switch (operation) {
case CR_LOAD:
imui_init();
return 0;
case CR_UNLOAD:
return 0;
case CR_CLOSE:
imui_shutdown();
return 0;
case CR_STEP:
imui_frame_begin();
imui_draw();
imui_frame_end();
return 0;
}
return 0;
}