diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2019-10-21 22:04:19 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2019-10-24 18:55:47 +0000 |
| commit | e05b8c0ea02a1d6c230f2f7b7147d219e1543d37 (patch) | |
| tree | 71885647774888009b1d1678db7c6558549365d8 /src | |
| parent | zig-fmt everything (diff) | |
| download | zig-imgui-wip.tar.gz zig-imgui-wip.zip | |
add viewport supportwip
Diffstat (limited to 'src')
| -rw-r--r-- | src/gl3_impl.zig | 35 | ||||
| -rw-r--r-- | src/glfw_impl.zig | 268 | ||||
| -rw-r--r-- | src/main.zig | 8 |
3 files changed, 290 insertions, 21 deletions
diff --git a/src/gl3_impl.zig b/src/gl3_impl.zig index b056056..1e6ce23 100644 --- a/src/gl3_impl.zig +++ b/src/gl3_impl.zig @@ -29,24 +29,18 @@ pub fn Init() void { io.*.BackendRendererName = c"imgui_impl_gl3.zig"; if (OpenGLHasDrawWithBaseVertex) io.*.BackendFlags |= @enumToInt(c.ImGuiBackendFlags_RendererHasVtxOffset); - // @TODO: Viewports - // io.*.BackendFlags |= @enumToInt(c.ImGuiBackendFlags_RendererHasViewports); + io.*.BackendFlags |= @enumToInt(c.ImGuiBackendFlags_RendererHasViewports); // @TODO: GLSL versions? // g_GlslVersionString = g_GlslVersionString_buf[0..glsl_version.len]; // mem.copy(u8, g_GlslVersionString, glsl_version); - // @FIXME: just for testing: - var tex: c.GLint = undefined; - c.glGetIntegerv(c.GL_TEXTURE_BINDING_2D, &tex); - - // @TODO: Viewports - // if (io.*.ConfigFlags & @enumToInt(c.ImGuiConfigFlags_ViewportsEnable) != 0) - // InitPlatformInterface(); + if (io.*.ConfigFlags & @enumToInt(c.ImGuiConfigFlags_ViewportsEnable) != 0) + InitPlatformInterface(); } pub fn Shutdown() void { - // ImGui_ImplOpenGL3_ShutdownPlatformInterface(); + ShutdownPlatformInterface(); DestroyDeviceObjects(); } @@ -447,3 +441,24 @@ fn SetupRenderState(draw_data: *c.ImDrawData, fb_width: i32, fb_height: i32, ver c.glVertexAttribPointer(@intCast(c.GLuint, g_AttribLocationVtxUV), 2, c.GL_FLOAT, c.GL_FALSE, @sizeOf(c.ImDrawVert), @intToPtr(?*c.GLvoid, @byteOffsetOf(c.ImDrawVert, "uv"))); c.glVertexAttribPointer(@intCast(c.GLuint, g_AttribLocationVtxColor), 4, c.GL_UNSIGNED_BYTE, c.GL_TRUE, @sizeOf(c.ImDrawVert), @intToPtr(?*c.GLvoid, @byteOffsetOf(c.ImDrawVert, "col"))); } + +// Platform Interface (Viewport Feature) +// ------------------------------------- + +fn InitPlatformInterface() void { + const platform_io = c.igGetPlatformIO(); + platform_io.*.Renderer_RenderWindow = Platform_RenderWindow; +} + +fn ShutdownPlatformInterface() void { + c.igDestroyPlatformWindows(); +} + +extern fn Platform_RenderWindow(viewport_: ?*c.ImGuiViewport, cookie: ?*c_void) void { + const viewport = viewport_ orelse unreachable; + if (viewport.*.Flags & @enumToInt(c.ImGuiViewportFlags_NoRendererClear) != 0) { + c.glClearColor(0.0, 0.0, 0.0, 0.0); + c.glClear(c.GL_COLOR_BUFFER_BIT); + } + RenderDrawData(viewport.*.DrawData); +} diff --git a/src/glfw_impl.zig b/src/glfw_impl.zig index d2defa5..89846cd 100644 --- a/src/glfw_impl.zig +++ b/src/glfw_impl.zig @@ -3,6 +3,7 @@ const c = @import("c.zig"); const builtin = @import("builtin"); const debug = @import("std").debug; const math = @import("std").math; +const heap = @import("std").heap; pub const ClientApi = enum { Unknown, @@ -33,8 +34,8 @@ pub fn Init(window: *c.GLFWwindow, install_callbacks: bool, client_api: ClientAp const io = c.igGetIO(); io.*.BackendFlags |= @enumToInt(c.ImGuiBackendFlags_HasMouseCursors); // We can honor GetMouseCursor() values (optional) io.*.BackendFlags |= @enumToInt(c.ImGuiBackendFlags_HasSetMousePos); // We can honor io.WantSetMousePos requests (optional, rarely used) - if (false) io.*.BackendFlags |= @enumToInt(c.ImGuiBackendFlags_PlatformHasViewports); // We can create multi-viewports on the Platform side (optional) - if (false and @hasField(c, "GLFW_HAS_GLFW_HOVERED") and builtin.os == builtin.Os.windows) { + io.*.BackendFlags |= @enumToInt(c.ImGuiBackendFlags_PlatformHasViewports); // We can create multi-viewports on the Platform side (optional) + if (@hasField(c, "GLFW_HAS_GLFW_HOVERED") and builtin.os == builtin.Os.windows) { io.*.BackendFlags |= @enumToInt(ImGuiBackendFlags_HasMouseHoveredViewport); // We can set io.MouseHoveredViewport correctly (optional, not easy) } io.*.BackendPlatformName = c"imgui_impl_glfw.zig"; @@ -95,17 +96,14 @@ pub fn Init(window: *c.GLFWwindow, install_callbacks: bool, client_api: ClientAp if (builtin.os == builtin.Os.windows) main_viewport.*.PlatformHandleRaw = c.glfwGetWin32Window(g_Window); - // @TODO: Platform Interface (Viewport) if (io.*.ConfigFlags & @enumToInt(c.ImGuiConfigFlags_ViewportsEnable) != 0) - unreachable; - // ImGui_ImplGlfw_InitPlatformInterface(); + InitPlatformInterface(); g_ClientApi = client_api; } pub fn Shutdown() void { - // @TODO: Platform Interface (Viewport) - // ImGui_ImplGlfw_ShutdownPlatformInterface(); + ShutdownPlatformInterface(); for (g_MouseCursors) |*cursor| { c.glfwDestroyCursor(cursor.*); cursor.* = null; @@ -309,3 +307,259 @@ extern fn Callback_Char(window: ?*c.GLFWwindow, char: c_uint) void { const io = c.igGetIO(); c.ImGuiIO_AddInputCharacter(io, char); } + +// Platform Interface (Viewport Feature) +// ------------------------------------- +const ViewportData = struct { + Window: *c.GLFWwindow, + WindowOwned: bool, + IgnoreWindowSizeEventFrame: i32 = -1, +}; + +const allocator = heap.c_allocator; +fn InitPlatformInterface() void { + // Register platform interface (will be coupled with a renderer interface) + const platform_io = c.igGetPlatformIO(); + platform_io.*.Platform_CreateWindow = Platform_CreateWindow; + platform_io.*.Platform_DestroyWindow = Platform_DestroyWindow; + platform_io.*.Platform_ShowWindow = Platform_ShowWindow; + platform_io.*.Platform_SetWindowPos = Platform_SetWindowPos; + platform_io.*.Platform_GetWindowPos = Platform_GetWindowPos; + platform_io.*.Platform_SetWindowSize = Platform_SetWindowSize; + platform_io.*.Platform_GetWindowSize = Platform_GetWindowSize; + platform_io.*.Platform_SetWindowFocus = Platform_SetWindowFocus; + platform_io.*.Platform_GetWindowFocus = Platform_GetWindowFocus; + platform_io.*.Platform_GetWindowMinimized = Platform_GetWindowMinimized; + platform_io.*.Platform_SetWindowTitle = Platform_SetWindowTitle; + platform_io.*.Platform_RenderWindow = Platform_RenderWindow; + platform_io.*.Platform_SwapBuffers = Platform_SwapBuffers; + if (@hasField(c, "GLFW_HAS_WINDOW_ALPHA") and c.GLFW_HAS_WINDOW_ALPHA) + platform_io.*.Platform_SetWindowAlpha = Platform_SetWindowAlpha; + // if (@hasField(c, "HAS_WIN32_IME") and c.HAS_WIN32_IME) + // platform_io.*.Platform_SetImeInputPos = Platform_Win32_SetImeInputPos; + + // Note: monitor callback are broken GLFW 3.2 and earlier (see github.com/glfw/glfw/issues/784) + UpdateMonitors(); + _ = c.glfwSetMonitorCallback(Callback_Monitors); + + // Register main window handle (which is owned by the main application, not by us) + const main_viewport = c.igGetMainViewport(); + const data = &allocator.create(ViewportData); + data.* = ViewportData{ + .Window = g_Window orelse unreachable, + .WindowOwned = false, + }; + main_viewport.*.PlatformUserData = data; + main_viewport.*.PlatformHandle = @ptrCast(*c_void, g_Window); +} + +fn ShutdownPlatformInterface() void {} + +extern fn Callback_Monitors(monitors: ?*c.GLFWmonitor, num: c_int) void { + g_WantUpdateMonitors = true; +} + +extern fn Callback_WindowClose(window: ?*c.GLFWwindow) void { + if (c.igFindViewportByPlatformHandle(window)) |viewport| + viewport.*.PlatformRequestClose = true; +} + +extern fn Callback_WindowPos(window: ?*c.GLFWwindow, x: c_int, y: c_int) void { + if (c.igFindViewportByPlatformHandle(window)) |viewport| + viewport.*.PlatformRequestMove = true; +} + +extern fn Callback_WindowSize(window: ?*c.GLFWwindow, x: c_int, y: c_int) void { + if (c.igFindViewportByPlatformHandle(window)) |viewport| { + if (@ptrCast(?*ViewportData, @alignCast(@alignOf(ViewportData), viewport.*.PlatformUserData))) |*data| { + // GLFW may dispatch window size event after calling glfwSetWindowSize(). + // However depending on the platform the callback may be invoked at different time: on Windows it + // appears to be called within the glfwSetWindowSize() call whereas on Linux it is queued and invoked + // during glfwPollEvents(). + // Because the event doesn't always fire on glfwSetWindowSize() we use a frame counter tag to only + // ignore recent glfwSetWindowSize() calls. + const ignore_event = (c.igGetFrameCount() <= data.*.IgnoreWindowSizeEventFrame + 1); + data.*.IgnoreWindowSizeEventFrame = -1; + if (ignore_event) + return; + } + viewport.*.PlatformRequestResize = true; + } +} + +extern fn Platform_CreateWindow(viewport_: ?*c.ImGuiViewport) void { + const viewport = viewport_ orelse unreachable; + // GLFW 3.2 unfortunately always set focus on c.glfwCreateWindow() if c.GLFW_VISIBLE is set, regardless of c.GLFW_FOCUSED + // With GLFW 3.3, the hint c.GLFW_FOCUS_ON_SHOW fixes this problem + c.glfwWindowHint(c.GLFW_VISIBLE, c.GLFW_FALSE); + c.glfwWindowHint(c.GLFW_FOCUSED, c.GLFW_FALSE); + if (@hasField(c, "GLFW_HAS_FOCUS_ON_SHOW") and c.GLFW_HAS_FOCUS_ON_SHOW) + c.glfwWindowHint(c.GLFW_FOCUS_ON_SHOW, c.GLFW_FALSE); + + // c.glfwWindowHint(c.GLFW_DECORATED, if (viewport.*.Flags & @enumToInt(c.ImGuiViewportFlags_NoDecoration) == 0) c.GLFW_TRUE else c.GLFW_FALSE); + // if (@hasField(c, "GLFW_HAS_WINDOW_TOPMOST") and c.GLFW_HAS_WINDOW_TOPMOST) + // c.glfwWindowHint(c.GLFW_FLOATING, if (viewport.*.Flags & @enumToInt(ImGuiViewportFlags_TopMost) != 0) c.GLFW_TRUE else c.GLFW_FALSE); + const share_window = if (g_ClientApi == .OpenGL) g_Window else null; + const window = c.glfwCreateWindow(@floatToInt(c_int, viewport.*.Size.x), @floatToInt(c_int, viewport.*.Size.y), c"No Title Yet", null, share_window); + viewport.*.PlatformHandle = @ptrCast(*c_void, window); + if (builtin.os == builtin.Os.windows) + viewport.*.PlatformHandleRaw = c.glfwGetWin32Window(window); + c.glfwSetWindowPos(window, @floatToInt(c_int, viewport.*.Pos.x), @floatToInt(c_int, viewport.*.Pos.y)); + + const data = &allocator.create(ViewportData); + data.* = ViewportData{ + .Window = window orelse unreachable, + .WindowOwned = true, + }; + viewport.*.PlatformUserData = data; + + // Install callbacks for secondary viewports + _ = c.glfwSetMouseButtonCallback(window, Callback_MouseButton); + _ = c.glfwSetScrollCallback(window, Callback_Scroll); + _ = c.glfwSetKeyCallback(window, Callback_Key); + _ = c.glfwSetCharCallback(window, Callback_Char); + _ = c.glfwSetWindowCloseCallback(window, Callback_WindowClose); + _ = c.glfwSetWindowPosCallback(window, Callback_WindowPos); + _ = c.glfwSetWindowSizeCallback(window, Callback_WindowSize); + if (g_ClientApi == .OpenGL) { + c.glfwMakeContextCurrent(window); + c.glfwSwapInterval(0); + } +} + +extern fn Platform_DestroyWindow(viewport_: ?*c.ImGuiViewport) void { + const viewport = viewport_ orelse unreachable; + + if (@ptrCast(?*ViewportData, @alignCast(@alignOf(ViewportData), viewport.*.PlatformUserData))) |data| { + if (data.*.WindowOwned) { + if (@hasField(c, "GLFW_HAS_GLFW_HOVERED") and builtin.os == builtin.Os.windows) { + const hwnd = @ptrCast(c.HWND, viewport.*.PlatformHandleRaw); + c.RemovePropA(hwnd, "IMGUI_VIEWPORT"); + } + c.glfwDestroyWindow(data.*.Window); + } + allocator.destroy(data); + } + viewport.*.PlatformUserData = null; + viewport.*.PlatformHandle = null; +} + +extern fn Platform_ShowWindow(viewport_: ?*c.ImGuiViewport) void { + const viewport = viewport_ orelse unreachable; + const data = @ptrCast(*ViewportData, @alignCast(@alignOf(ViewportData), viewport.*.PlatformUserData)); + + if (builtin.os == builtin.Os.windows) { + // GLFW hack: Hide icon from task bar + // @TODO: + // HWND hwnd = (HWND)viewport.*.PlatformHandleRaw; + // if (viewport.*.Flags & ImGuiViewportFlags_NoTaskBarIcon) + // { + // LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); + // ex_style &= ~WS_EX_APPWINDOW; + // ex_style |= WS_EX_TOOLWINDOW; + // ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style); + // } + + // GLFW hack: install hook for WM_NCHITTEST message handler + if (@hasField(c, "GLFW_HAS_GLFW_HOVERED")) { + // ::SetPropA(hwnd, "IMGUI_VIEWPORT", viewport); + // if (g_GlfwWndProc == null) + // g_GlfwWndProc = (WNDPROC)::GetWindowLongPtr(hwnd, GWLP_WNDPROC); + // ::SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)WndProcNoInputs); + } + + if (!@hasField(c, "GLFW_HAS_FOCUS_ON_SHOW") or !c.GLFW_HAS_FOCUS_ON_SHOW) { + // GLFW hack: GLFW 3.2 has a bug where c.glfwShowWindow() also activates/focus the window. + // The fix was pushed to GLFW repository on 2018/01/09 and should be included in GLFW 3.3 via a c.GLFW_FOCUS_ON_SHOW window attribute. + // See https://github.com/glfw/glfw/issues/1189 + // FIXME-VIEWPORT: Implement same work-around for Linux/OSX in the meanwhile. + if (viewport.*.Flags & ImGuiViewportFlags_NoFocusOnAppearing == 0) { + c.ShowWindow(hwnd, c.SW_SHOWNA); + return; + } + } + } + + c.glfwShowWindow(data.*.Window); +} + +extern fn Platform_GetWindowPos(viewport: ?*c.ImGuiViewport) c.ImVec2 { + const data = @ptrCast(*ViewportData, @alignCast(@alignOf(ViewportData), (viewport orelse unreachable).*.PlatformUserData)); + var x: c_int = undefined; + var y: c_int = undefined; + c.glfwGetWindowPos(data.*.Window, &x, &y); + return c.ImVec2{ .x = @intToFloat(f32, x), .y = @intToFloat(f32, y) }; +} + +extern fn Platform_SetWindowPos(viewport: ?*c.ImGuiViewport, pos: c.ImVec2) void { + const data = @ptrCast(*ViewportData, @alignCast(@alignOf(ViewportData), (viewport orelse unreachable).*.PlatformUserData)); + c.glfwSetWindowPos(data.*.Window, @floatToInt(c_int, pos.x), @floatToInt(c_int, pos.y)); +} + +extern fn Platform_GetWindowSize(viewport: ?*c.ImGuiViewport) c.ImVec2 { + const data = @ptrCast(*ViewportData, @alignCast(@alignOf(ViewportData), (viewport orelse unreachable).*.PlatformUserData)); + var w: c_int = undefined; + var h: c_int = undefined; + c.glfwGetWindowSize(data.*.Window, &w, &h); + return c.ImVec2{ .x = @intToFloat(f32, w), .y = @intToFloat(f32, h) }; +} + +extern fn Platform_SetWindowSize(viewport: ?*c.ImGuiViewport, size: c.ImVec2) void { + const data = @ptrCast(*ViewportData, @alignCast(@alignOf(ViewportData), (viewport orelse unreachable).*.PlatformUserData)); + if (builtin.os == builtin.Os.macosx) { + // Native OS windows are positioned from the bottom-left corner on macOS, whereas on other platforms they are + // positioned from the upper-left corner. GLFW makes an effort to convert macOS style coordinates, however it + // doesn't handle it when changing size. We are manually moving the window in order for changes of size to be based + // on the upper-left corner. + var x: c_int = undefined; + var y: c_int = undefined; + var w: c_int = undefined; + var h: c_int = undefined; + c.glfwGetWindowPos(data.*.Window, &x, &y); + c.glfwGetWindowSize(data.*.Window, &w, &h); + c.glfwSetWindowPos(data.*.Window, x, y - height + @floatToInt(c_int, size.y)); + } + data.*.IgnoreWindowSizeEventFrame = c.igGetFrameCount(); + c.glfwSetWindowSize(data.*.Window, @floatToInt(c_int, size.x), @floatToInt(c_int, size.y)); +} + +extern fn Platform_SetWindowTitle(viewport: ?*c.ImGuiViewport, title: ?*const u8) void { + const data = @ptrCast(*ViewportData, @alignCast(@alignOf(ViewportData), (viewport orelse unreachable).*.PlatformUserData)); + c.glfwSetWindowTitle(data.*.Window, title orelse unreachable); +} + +extern fn Platform_SetWindowFocus(viewport: ?*c.ImGuiViewport) void { + if (@hasField(c, "GLFW_HAS_FOCUS_WINDOW") and c.GLFW_HAS_FOCUS_WINDOW) { + const data = @ptrCast(*ViewportData, @alignCast(@alignOf(ViewportData), (viewport orelse unreachable).*.PlatformUserData)); + c.glfwFocusWindow(data.*.Window); + } +} + +extern fn Platform_GetWindowFocus(viewport: ?*c.ImGuiViewport) bool { + const data = @ptrCast(*ViewportData, @alignCast(@alignOf(ViewportData), (viewport orelse unreachable).*.PlatformUserData)); + return c.glfwGetWindowAttrib(data.*.Window, c.GLFW_FOCUSED) != 0; +} + +extern fn Platform_GetWindowMinimized(viewport: ?*c.ImGuiViewport) bool { + const data = @ptrCast(*ViewportData, @alignCast(@alignOf(ViewportData), (viewport orelse unreachable).*.PlatformUserData)); + return c.glfwGetWindowAttrib(data.*.Window, c.GLFW_ICONIFIED) != 0; +} + +extern fn Platform_SetWindowAlpha(viewport: ?*c.ImGuiViewport, alpha: f32) void { + const data = @ptrCast(*ViewportData, @alignCast(@alignOf(ViewportData), (viewport orelse unreachable).*.PlatformUserData)); + c.glfwSetWindowOpacity(data.*.Window, alpha); +} + +extern fn Platform_RenderWindow(viewport: ?*c.ImGuiViewport, cookie: ?*c_void) void { + const data = @ptrCast(*ViewportData, @alignCast(@alignOf(ViewportData), (viewport orelse unreachable).*.PlatformUserData)); + if (g_ClientApi == .OpenGL) + c.glfwMakeContextCurrent(data.*.Window); +} + +extern fn Platform_SwapBuffers(viewport: ?*c.ImGuiViewport, cookie: ?*c_void) void { + const data = @ptrCast(*ViewportData, @alignCast(@alignOf(ViewportData), (viewport orelse unreachable).*.PlatformUserData)); + if (g_ClientApi == .OpenGL) { + c.glfwMakeContextCurrent(data.*.Window); + c.glfwSwapBuffers(data.*.Window); + } +} diff --git a/src/main.zig b/src/main.zig index 79dab91..f3d342a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -22,7 +22,7 @@ pub fn main() !void { c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MAJOR, 3); c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MINOR, 2); - c.glfwWindowHint(c.GLFW_OPENGL_FORWARD_COMPAT, c.GL_TRUE); + c.glfwWindowHint(c.GLFW_OPENGL_FORWARD_COMPAT, c.GLFW_TRUE); c.glfwWindowHint(c.GLFW_OPENGL_DEBUG_CONTEXT, debug_gl.is_on); c.glfwWindowHint(c.GLFW_OPENGL_PROFILE, c.GLFW_OPENGL_CORE_PROFILE); // c.glfwWindowHint(c.GLFW_DEPTH_BITS, 0); @@ -44,13 +44,13 @@ pub fn main() !void { const io = c.igGetIO(); io.*.ConfigFlags |= @enumToInt(c.ImGuiConfigFlags_NavEnableKeyboard); - // io.*.ConfigFlags |= @enumToInt(c.ImGuiConfigFlags_DockingEnable); - // io.*.ConfigFlags |= @enumToInt(c.ImGuiConfigFlags_ViewportsEnable); + io.*.ConfigFlags |= @enumToInt(c.ImGuiConfigFlags_DockingEnable); + io.*.ConfigFlags |= @enumToInt(c.ImGuiConfigFlags_ViewportsEnable); const style = c.igGetStyle(); c.igStyleColorsDark(style); - if (false and io.*.ConfigFlags & @enumToInt(c.ImGuiConfigFlags_ViewportsEnable) != 0) { + if (io.*.ConfigFlags & @enumToInt(c.ImGuiConfigFlags_ViewportsEnable) != 0) { style.*.WindowRounding = 0.0; style.*.Colors[@enumToInt(c.ImGuiCol_WindowBg)].w = 1.0; } |
