diff --git a/backends/imgui_impl_xlib.cpp b/backends/imgui_impl_xlib.cpp index 272f04723..623076f4e 100644 --- a/backends/imgui_impl_xlib.cpp +++ b/backends/imgui_impl_xlib.cpp @@ -6,6 +6,7 @@ // [X] Platform: Mouse support. // [X] Platform: Keyboard support. // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // Issues: // [ ] Platform: Missing touchscreen support. // [ ] Platform: Missing gamepad support. @@ -25,11 +26,15 @@ #include "imgui.h" #ifndef IMGUI_DISABLE #include "imgui_impl_xlib.h" +#include "stdio.h" // Xlib #include +#include #include #include +#include +#include #include #include #include @@ -43,27 +48,37 @@ #endif // Xlib Data -struct ImGui_ImplXlib_Data -{ - Display* Dpy; - Window Win; - int Xi2Opcode; - XIM IM; - XIC IC; - timespec Time; - int MouseButtonsDown; - Cursor MouseCursors[ImGuiMouseCursor_COUNT]; - Cursor LastMouseCursor; - char* ClipboardTextData; - bool SelectionWaiting; - Atom XA_CLIPBOARD; - Atom XA_SELECTION; - Atom XA_TARGETS; - Atom XA_INCR; - Atom* XA_MIME; - unsigned int MimeCount; +//struct ImGui_ImplXlib_Data +//{ +// Display* Dpy; +// Window Win; +// int Xi2Opcode; +// XIM IM; +// XIC IC; +// timespec Time; +// int MouseButtonsDown; +// Cursor MouseCursors[ImGuiMouseCursor_COUNT]; +// Cursor LastMouseCursor; +// char* ClipboardTextData; +// bool SelectionWaiting; +// bool WantUpdateMonitors; +// Atom XA_CLIPBOARD; +// Atom XA_SELECTION; +// Atom XA_TARGETS; +// Atom XA_INCR; +// Atom* XA_MIME; +// void* RendererCtx; +// unsigned int MimeCount; +// +// ImGui_ImplXlib_Data() { memset((void*)this, 0, sizeof(*this)); } +//}; - ImGui_ImplXlib_Data() { memset((void*)this, 0, sizeof(*this)); } +struct ImGui_ImplXlib_ViewportData { + Window Handle; + Window ParentHandle; + bool Owned; + + ImGui_ImplXlib_ViewportData() {memset(this, 0, sizeof(ImGui_ImplXlib_ViewportData));}; }; static const char *text_mime_types[] = { @@ -78,12 +93,18 @@ static const char *text_mime_types[] = { // It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. // FIXME: multi-context support is not well tested and probably dysfunctional in this backend. // FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context. -static ImGui_ImplXlib_Data* ImGui_ImplXlib_GetBackendData() +ImGui_ImplXlib_Data* ImGui_ImplXlib_GetBackendData() { return ImGui::GetCurrentContext() ? (ImGui_ImplXlib_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr; } // Functions + +static Window ImGui_ImplXlib_GetHandleFromViewport(ImGuiViewport* viewport) +{ + return viewport ? (Window)viewport->PlatformHandle : 0; +} + static const char* ImGui_ImplXlib_GetClipboardText(void*) { ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); @@ -318,12 +339,167 @@ static bool ImGui_ImplXlib_UpdateKeyModifiers(ImGuiKey ks, bool down) } } +static ImGuiViewport* ImGui_ImplXlib_FindViewportByPlatformHandle(void* win) +{ + ImGuiPlatformIO& io = ImGui::GetPlatformIO(); + for (ImGuiViewport* vp : io.Viewports) { + if (vp->PlatformHandle == win) + return vp; + } + return 0; +} + + +static void Imgui_ImplXlib_ShowWindow(ImGuiViewport* viewport) +{ + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + XMapWindow(bd->Dpy, (Window)viewport->PlatformHandleRaw); +} + +static void ImGui_ImplXlib_DestroyWindow(ImGuiViewport* viewport) +{ + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + ImGui_ImplXlib_ViewportData* vd = (ImGui_ImplXlib_ViewportData*)viewport->PlatformUserData; + if (!vd) return; + //TODO::There is Release Capture in win32 backend, look that up + XDestroyWindow(bd->Dpy, (Window)viewport->PlatformHandleRaw); + IM_DELETE(vd); + viewport->PlatformUserData = 0; + viewport->PlatformHandle = 0; +} + +static void ImGui_ImplXlib_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) +{ + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + XMoveWindow(bd->Dpy, (Window)viewport->PlatformHandle, pos.x, pos.y); + XFlush(bd->Dpy); +} + +static ImVec2 ImGui_ImplXlib_GetWindowPos(ImGuiViewport* viewport) { + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + XWindowAttributes attribs; + XGetWindowAttributes(bd->Dpy, (Window)viewport->PlatformHandle, &attribs); + return {(float)attribs.x, (float)(attribs.y)}; +} + +static void ImGui_ImplXlib_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + XResizeWindow(bd->Dpy, (Window)viewport->PlatformHandle, size.x, size.y); + XFlush(bd->Dpy); +} + +static ImVec2 ImGui_ImplXlib_GetWindowSize(ImGuiViewport* viewport) +{ + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + XWindowAttributes attribs; + XGetWindowAttributes(bd->Dpy, (Window)viewport->PlatformHandle, &attribs); + return {(float)attribs.width, (float)attribs.height}; +} + + +//https://stackoverflow.com/questions/2858263/how-do-i-bring-a-processes-window-to-the-foreground-on-x-windows-c +static void ImGui_ImplXlib_BringToTop(Window window) +{ + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + XEvent event = { 0 }; + event.xclient.type = ClientMessage; + event.xclient.serial = 0; + event.xclient.send_event = True; + event.xclient.message_type = XInternAtom(bd->Dpy, "_NET_ACTIVE_WINDOW", False); + event.xclient.window = window; + event.xclient.format = 32; + XSendEvent(bd->Dpy, DefaultRootWindow(bd->Dpy), False, SubstructureRedirectMask | SubstructureNotifyMask, &event ); + XMapRaised(bd->Dpy, window); +} + +static void ImGui_ImplXlib_SetWindowFocus(ImGuiViewport* viewport) +{ + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + ImGui_ImplXlib_BringToTop((Window)viewport->PlatformHandle); + XSetInputFocus(bd->Dpy, (Window)viewport->PlatformHandle, RevertToNone, CurrentTime); + XFlush(bd->Dpy); +} + +static bool ImGui_ImplXlib_GetWindowFocus(ImGuiViewport* viewport) +{ + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + int rev; + Window win; + XGetInputFocus(bd->Dpy, &win, &rev); + return win == (Window)viewport->PlatformHandle; +} + +static bool ImGui_ImplXlib_GetWindowMinimized(ImGuiViewport* viewport) +{ + ImVec2 size = ImGui_ImplXlib_GetWindowSize(viewport); + return size.x == 0 && size.y == 0; +} + +static void ImGui_ImplXlib_SetWindowTitle(ImGuiViewport* viewport, const char* title) +{ + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + XStoreName(bd->Dpy, (Window)viewport->PlatformHandle, title); +} + +static void ImGui_ImplXlib_SetWindowAlpha(ImGuiViewport* viewport, float alpha) +{ + return; +} + +static void ImGui_ImplXlib_UpdateWindow(ImGuiViewport* viewport) +{ + ImGui_ImplXlib_ViewportData* vd = (ImGui_ImplXlib_ViewportData*)viewport->PlatformUserData; + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + + Window new_parent = ImGui_ImplXlib_GetHandleFromViewport(viewport->ParentViewport); + if (new_parent != vd->ParentHandle) { + vd->ParentHandle = new_parent; + XSetTransientForHint(bd->Dpy, (Window)vd->Handle, new_parent); + } +} + +static float ImGui_ImplXlib_GetWindowDpiScale(ImGuiViewport* viewport) +{ + return 1.0; +} + +static void ImGui_ImplXlib_OnChangedViewport(ImGuiViewport* viewport) +{ + return; +} + +static void ImGui_ImplXlib_UpdateMonitors() +{ + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + int count = 0; + XRRMonitorInfo* monitors = XRRGetMonitors(bd->Dpy, DefaultRootWindow(bd->Dpy), 0, &count); + platform_io.Monitors.resize(count); + ImVec2 p = {0.0f,0.0f}; + for (int i = 0; i < count; ++i) + { + platform_io.Monitors[i].DpiScale = 1.0f; + platform_io.Monitors[i].PlatformHandle = (void*)monitors[i].name; + platform_io.Monitors[i].MainPos = p; + platform_io.Monitors[i].MainSize= ImVec2(monitors[i].width, monitors[i].height); + platform_io.Monitors[i].WorkPos = p; + platform_io.Monitors[i].WorkSize = ImVec2(monitors[i].width, monitors[i].height); + p.x += monitors[i].width; + } + XRRFreeMonitors(monitors); + bd->WantUpdateMonitors = 0; +} + + + + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. // If you have multiple 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. -bool ImGui_ImplXlib_ProcessEvent(XEvent* event) { +bool ImGui_ImplXlib_ProcessEvent(XEvent* event) +{ ImGuiIO& io = ImGui::GetIO(); ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); // Needed for Xim events @@ -385,6 +561,15 @@ bool ImGui_ImplXlib_ProcessEvent(XEvent* event) { } return true; } + case ConfigureNotify: + { + ImGuiViewport* vp = ImGui_ImplXlib_FindViewportByPlatformHandle((void*)event->xconfigure.window); + if (!vp) + return false; + vp->PlatformRequestResize = 1; + vp->PlatformRequestMove = 1; + return true; + } case FocusIn: case FocusOut: { @@ -454,6 +639,161 @@ bool ImGui_ImplXlib_ProcessEvent(XEvent* event) { return false; } +static int fbAttribs[] = { + GLX_X_RENDERABLE, + True, + GLX_DRAWABLE_TYPE, + GLX_WINDOW_BIT, + GLX_RENDER_TYPE, + GLX_RGBA_BIT, + GLX_X_VISUAL_TYPE, + GLX_TRUE_COLOR, + GLX_RED_SIZE, + 8, + GLX_GREEN_SIZE, + 8, + GLX_BLUE_SIZE, + 8, + GLX_ALPHA_SIZE, + 8, + GLX_DEPTH_SIZE, + 24, + GLX_STENCIL_SIZE, + 8, + GLX_DOUBLEBUFFER, + True, + None +}; + +static void ImGui_ImplXlib_CreateWindow(ImGuiViewport* viewport) +{ + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + ImGui_ImplXlib_ViewportData* vd = IM_NEW(ImGui_ImplXlib_ViewportData); + Window root = DefaultRootWindow(bd->Dpy); + vd->ParentHandle = ImGui_ImplXlib_GetHandleFromViewport(viewport->ParentViewport); + viewport->PlatformUserData = vd; + XSetWindowAttributes winAttribs; + winAttribs.colormap = CopyFromParent; + + winAttribs.event_mask = KeyPressMask | KeyReleaseMask | FocusChangeMask | ButtonPressMask | ButtonReleaseMask | StructureNotifyMask; + winAttribs.event_mask |= PointerMotionMask | EnterWindowMask | LeaveWindowMask | FocusChangeMask; +// winAttribs.event_mask = ExposureMask | KeyPressMask | StructureNotifyMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask; + //-------------------------- + int count = 0; + GLXFBConfig* fbConfings = glXChooseFBConfig(bd->Dpy, DefaultScreen(bd->Dpy), fbAttribs, &count); + GLXFBConfig fb_conf = fbConfings[0]; + XFree(fbConfings); + XVisualInfo* vi = glXGetVisualFromFBConfig(bd->Dpy, fb_conf); + Colormap cmap = XCreateColormap(bd->Dpy, root, vi->visual, AllocNone); + winAttribs.colormap = cmap; + //-------------------------- + + vd->Handle = XCreateWindow(bd->Dpy, root, viewport->Pos.x, viewport->Pos.y, + viewport->Size.x, viewport->Size.y, 0, vi->depth, InputOutput, vi->visual, CWColormap | CWEventMask, + &winAttribs); + if (viewport->ParentViewport) + XSetTransientForHint(bd->Dpy, (Window)vd->Handle, (Window)viewport->ParentViewport->PlatformHandle); + + XSetWindowBackground(bd->Dpy, (Window)vd->Handle, 0x0); + + Atom state = XInternAtom(bd->Dpy, "_NET_WM_STATE", False); + Atom type = XInternAtom(bd->Dpy, "_NET_WM_WINDOW_TYPE", False); + Atom taskbarBehaviour = XInternAtom(bd->Dpy, "_NET_WM_STATE_SKIP_TASKBAR", False); + Atom dock = XInternAtom(bd->Dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); + XChangeProperty(bd->Dpy, (Window)vd->Handle, type, XA_ATOM, 32, PropModeReplace, (unsigned char*)&dock, 1); + XChangeProperty(bd->Dpy, (Window)vd->Handle, state, XA_ATOM, 32, PropModeReplace, (unsigned char*)&taskbarBehaviour, 1); + + vd->Owned = 1; + viewport->PlatformHandle = (void*)vd->Handle; + viewport->PlatformHandleRaw = (void*)vd->Handle; + viewport->PlatformRequestResize = 0; +} + +void ImGui_ImplXlib_OpenGLRendererCreate(ImGuiViewport* viewport) { + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + + int count = 0; + GLXFBConfig* fbConfings = glXChooseFBConfig(bd->Dpy, DefaultScreen(bd->Dpy), fbAttribs, &count); + GLXFBConfig fb_conf = fbConfings[0]; + XFree(fbConfings); + GLXWindow* win = IM_NEW(GLXWindow); + *win = glXCreateWindow(bd->Dpy, fb_conf, (Window)viewport->PlatformHandle, 0); + viewport->RendererUserData = win; +} + +static void ImGui_ImplXlib_OpenGLRendererDestroy(ImGuiViewport* viewport) { + + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + if (!viewport->RendererUserData) + return; + GLXWindow* win = (GLXWindow*)viewport->RendererUserData; + glXDestroyWindow(bd->Dpy, *win); + viewport->RendererUserData = 0; + IM_DELETE(win); +} + +static void ImGui_ImplXlib_OpenGLRendererSwapBuffers(ImGuiViewport* viewport, void* render_arg) { + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + GLXWindow* win = (GLXWindow*)viewport->RendererUserData; + if (!win) + return; +// glViewport(0, 0, (int)200, 200); +// glClearColor(1.0,1.0,0.0,1.0); +// glClear(GL_COLOR_BUFFER_BIT); + glXSwapBuffers(bd->Dpy, *win); +} + + +void ImGui_ImplXlib_SetRendererCtx(GLXContext ctx) { + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + bd->RendererCtx = ctx; +} + +static void ImGui_ImplXlib_OpenGLRendererRender(ImGuiViewport* viewport, void*) { + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + GLXWindow* win = (GLXWindow*)viewport->RendererUserData; + glXMakeCurrent(bd->Dpy, *win, (GLXContext)bd->RendererCtx); +} + +void ImGui_ImplXlib_InitOpenGLRenderer() { + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_CreateWindow = ImGui_ImplXlib_OpenGLRendererCreate; + platform_io.Renderer_DestroyWindow = ImGui_ImplXlib_OpenGLRendererDestroy; + platform_io.Renderer_SwapBuffers = ImGui_ImplXlib_OpenGLRendererSwapBuffers; + platform_io.Platform_RenderWindow = ImGui_ImplXlib_OpenGLRendererRender; +} + +void ImGui_ImplXlib_InitMultiViewportSupport() { + ImGui_ImplXlib_UpdateMonitors(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + + platform_io.Platform_CreateWindow = ImGui_ImplXlib_CreateWindow; + platform_io.Platform_DestroyWindow = ImGui_ImplXlib_DestroyWindow; + platform_io.Platform_ShowWindow = Imgui_ImplXlib_ShowWindow; + platform_io.Platform_SetWindowPos = ImGui_ImplXlib_SetWindowPos; + platform_io.Platform_GetWindowPos = ImGui_ImplXlib_GetWindowPos; + platform_io.Platform_SetWindowSize = ImGui_ImplXlib_SetWindowSize; + platform_io.Platform_GetWindowSize = ImGui_ImplXlib_GetWindowSize; + platform_io.Platform_SetWindowFocus = ImGui_ImplXlib_SetWindowFocus; + platform_io.Platform_GetWindowFocus = ImGui_ImplXlib_GetWindowFocus; + platform_io.Platform_GetWindowMinimized = ImGui_ImplXlib_GetWindowMinimized; + platform_io.Platform_SetWindowTitle = ImGui_ImplXlib_SetWindowTitle; + platform_io.Platform_SetWindowAlpha = ImGui_ImplXlib_SetWindowAlpha; + platform_io.Platform_UpdateWindow = ImGui_ImplXlib_UpdateWindow; + platform_io.Platform_GetWindowDpiScale = ImGui_ImplXlib_GetWindowDpiScale; // FIXME-DPI + platform_io.Platform_OnChangedViewport = ImGui_ImplXlib_OnChangedViewport; // FIXME-DPI + + ImGui_ImplXlib_ViewportData* vd = IM_NEW(ImGui_ImplXlib_ViewportData); + vd->Handle = bd->Win; + vd->ParentHandle = 0; + vd->Owned = 0; + main_viewport->PlatformUserData = vd; + + ImGui_ImplXlib_InitOpenGLRenderer(); +} + bool ImGui_ImplXlib_Init(Display* display, Window window) { ImGuiIO& io = ImGui::GetIO(); @@ -466,6 +806,10 @@ bool ImGui_ImplXlib_Init(Display* display, Window window) io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) + io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional) + io.BackendFlags |= ImGuiBackendFlags_HasParentViewport; // We can honor viewport->ParentViewportId by applying the corresponding parent/child relationship at platform levle (optional) + bd->Dpy = display; bd->Win = window; @@ -487,7 +831,9 @@ bool ImGui_ImplXlib_Init(Display* display, Window window) } // Select keyboard/focus events - XSelectInput(display, window, KeyPressMask | KeyReleaseMask | FocusChangeMask | ButtonPressMask | ButtonReleaseMask | StructureNotifyMask); + long mask = KeyPressMask | KeyReleaseMask | FocusChangeMask | ButtonPressMask | ButtonReleaseMask | StructureNotifyMask; + mask |= PointerMotionMask | EnterWindowMask | LeaveWindowMask | FocusChangeMask; + XSelectInput(display, window, mask); // Setup XIM #ifdef X_HAVE_UTF8_STRING XSetLocaleModifiers(""); @@ -506,9 +852,18 @@ bool ImGui_ImplXlib_Init(Display* display, Window window) bd->MouseCursors[ImGuiMouseCursor_Hand] = XCreateFontCursor(display, XC_hand2); bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = XCreateFontCursor(display, XC_pirate); + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + main_viewport->PlatformHandle = (void*)bd->Win; + main_viewport->PlatformHandleRaw = (void*)bd->Win; + + ImGui_ImplXlib_InitMultiViewportSupport(); return true; } +static void ImGui_ImplWin32_ShutdownMultiViewportSupport() +{ + ImGui::DestroyPlatformWindows(); +} void ImGui_ImplXlib_Shutdown() { ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); @@ -523,45 +878,116 @@ void ImGui_ImplXlib_Shutdown() XFreeCursor(bd->Dpy, bd->MouseCursors[cursor_n]); bd->LastMouseCursor = 0; + ImGui_ImplWin32_ShutdownMultiViewportSupport(); + io.BackendPlatformName = nullptr; io.BackendPlatformUserData = nullptr; io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos); IM_DELETE(bd); } + +static Window ImGui_ImplXlib_FindTopMostHovered(int x, int y) +{ + Window root; + Window parent; + Window* tree; + Window ret; + XWindowAttributes attribs; + int diffx; + int diffy; + unsigned int count; + int i; + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + if (!XQueryTree(bd->Dpy, DefaultRootWindow(bd->Dpy), &root, &parent, &tree, &count) || !count) + return 0; + ret = 0; + for (i = count-1; i >= 0; --i) + { + XGetWindowAttributes(bd->Dpy, tree[i], &attribs); + diffx = x - attribs.x; + diffy = y - attribs.y; + if (attribs.map_state == IsViewable && diffx > 0 && diffx < attribs.width && diffy > 0 && diffy < attribs.height) + { + ret = tree[i]; + break; + } + } + XFree(tree); + return ret; +} + + +static Window ImGui_ImplXlib_FindFocusedWindow() +{ + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + ImGuiPlatformIO& pio = ImGui::GetPlatformIO(); + Window focused; + int revert; + XGetInputFocus(bd->Dpy, &focused, &revert); + for (ImGuiViewport* vp : pio.Viewports) { + if ((Window)vp->PlatformHandle == focused) + return focused; + } + return 0; +} + static void ImGui_ImplXlib_UpdateMouseData() { ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); ImGuiIO& io = ImGui::GetIO(); - Window focus; + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + int i; + Window focused; int revert; - XGetInputFocus(bd->Dpy, &focus, &revert); - bool is_app_focused = (focus == bd->Win); - if (!is_app_focused) - return; - // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) - if (io.WantSetMousePos) - XWarpPointer(bd->Dpy, None, bd->Win, 0, 0, 0, 0, (int)io.MousePos.x, (int)io.MousePos.y); + XGetInputFocus(bd->Dpy, &focused, &revert); + bool is_app_focused = 0; + for (i = 0; i < platform_io.Viewports.size(); ++i) { + if (focused != (Window)platform_io.Viewports[i]->PlatformHandle) + continue; + is_app_focused = 1; + break; + } int window_x, window_y, mouse_x_global, mouse_y_global; - Window root, child; + Window root, child, hovered; + ImGuiViewport* vp; unsigned int mask; - if (XQueryPointer(bd->Dpy, bd->Win, &root, &child, &mouse_x_global, &mouse_y_global, &window_x, &window_y, &mask)) - io.AddMousePosEvent((float)(window_x), (float)(window_y)); + XQueryPointer(bd->Dpy, is_app_focused ? focused : DefaultRootWindow(bd->Dpy), &root, &child, &mouse_x_global, &mouse_y_global, &window_x, &window_y, &mask); + if (is_app_focused) + { + XQueryPointer(bd->Dpy, focused, &root, &child, &mouse_x_global, &mouse_y_global, &window_x, &window_y, &mask); + // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) + if (io.WantSetMousePos) + XWarpPointer(bd->Dpy, None, focused, 0, 0, 0, 0, (int)io.MousePos.x, (int)io.MousePos.y); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + io.AddMousePosEvent(mouse_x_global, mouse_y_global); + else + io.AddMousePosEvent(window_x, window_y); + } + hovered = ImGui_ImplXlib_FindTopMostHovered(mouse_x_global, mouse_y_global); + if (hovered) + { + vp = ImGui_ImplXlib_FindViewportByPlatformHandle((void*)hovered); + if (vp) + io.AddMouseViewportEvent(vp->ID); + } } static void ImGui_ImplXlib_UpdateMouseCursor() { ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + Window focused = io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable ? ImGui_ImplXlib_FindFocusedWindow() : bd->Win; + if (!focused) + return; if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) return; - ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); - ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None) { // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor - XUndefineCursor(bd->Dpy, bd->Win); + XUndefineCursor(bd->Dpy, focused); } else { @@ -569,7 +995,7 @@ static void ImGui_ImplXlib_UpdateMouseCursor() Cursor expected_cursor = bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]; if (bd->LastMouseCursor != expected_cursor) { - XDefineCursor(bd->Dpy, bd->Win, expected_cursor); + XDefineCursor(bd->Dpy, focused, expected_cursor); bd->LastMouseCursor = expected_cursor; } } @@ -585,6 +1011,9 @@ void ImGui_ImplXlib_NewFrame() unsigned int w = 0, h = 0, border_width, depth; Window root; XGetGeometry(bd->Dpy, bd->Win, &root, &x, &y, &w, &h, &border_width, &depth); + if (bd->WantUpdateMonitors) { + ImGui_ImplXlib_UpdateMonitors(); + } io.DisplaySize = ImVec2((float)w, (float)h); diff --git a/backends/imgui_impl_xlib.h b/backends/imgui_impl_xlib.h index 8b37d1ca6..3ca9ed4ae 100644 --- a/backends/imgui_impl_xlib.h +++ b/backends/imgui_impl_xlib.h @@ -30,4 +30,32 @@ IMGUI_IMPL_API void ImGui_ImplXlib_Shutdown(); IMGUI_IMPL_API void ImGui_ImplXlib_NewFrame(); IMGUI_IMPL_API bool ImGui_ImplXlib_ProcessEvent(XEvent* event); +struct ImGui_ImplXlib_Data +{ + Display* Dpy; + Window Win; + int Xi2Opcode; + XIM IM; + XIC IC; + timespec Time; + int MouseButtonsDown; + Cursor MouseCursors[ImGuiMouseCursor_COUNT]; + Cursor LastMouseCursor; + char* ClipboardTextData; + bool SelectionWaiting; + bool WantUpdateMonitors; + Atom XA_CLIPBOARD; + Atom XA_SELECTION; + Atom XA_TARGETS; + Atom XA_INCR; + Atom* XA_MIME; + void* RendererCtx; + unsigned int MimeCount; + + ImGui_ImplXlib_Data() { memset((void*)this, 0, sizeof(*this)); } +}; + +ImGui_ImplXlib_Data* ImGui_ImplXlib_GetBackendData(); +void ImGui_ImplXlib_InitMultiViewportSupport(); + #endif // #ifndef IMGUI_DISABLE diff --git a/examples/example_xlib_opengl3/Makefile b/examples/example_xlib_opengl3/Makefile index dd1bc7acb..7a5ba5430 100644 --- a/examples/example_xlib_opengl3/Makefile +++ b/examples/example_xlib_opengl3/Makefile @@ -19,7 +19,7 @@ OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) CXXFLAGS = -std=c++11 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends CXXFLAGS += -g -Wall -Wformat -LIBS = -ldl -lGL -lX11 -lXi +LIBS = -ldl -lGL -lX11 -lXi -lXrandr ##--------------------------------------------------------------------- ## OPENGL ES diff --git a/examples/example_xlib_opengl3/main.cpp b/examples/example_xlib_opengl3/main.cpp index 5d9959b41..38489d378 100644 --- a/examples/example_xlib_opengl3/main.cpp +++ b/examples/example_xlib_opengl3/main.cpp @@ -15,6 +15,7 @@ #include // Main code +void ImGui_ImplXlib_SetRendererCtx(GLXContext ctx); int main(int, char**) { // Setup Xlib @@ -59,7 +60,7 @@ int main(int, char**) window_attribs.colormap = XCreateColormap(display, RootWindow(display, screen_id), visual->visual, AllocNone); Window window = XCreateWindow(display, RootWindow(display, screen_id), 0, 0, 1280, 720, 0, visual->depth, InputOutput, visual->visual, CWBackPixel | CWColormap | CWBorderPixel, &window_attribs); - // Handle window closing + // Handle window closing Atom wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False); XSetWMProtocols(display, window, &wmDeleteMessage, 1); @@ -92,7 +93,6 @@ int main(int, char**) #endif GLXContext glx_context = glXCreateContextAttribsARB(display, fbc[0], 0, True, context_attribs); - if (glx_context == 0) { printf("Error: Could not create GLX context\n"); XCloseDisplay(display); @@ -107,6 +107,8 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Setup Dear ImGui style ImGui::StyleColorsDark(); @@ -115,6 +117,11 @@ int main(int, char**) // Setup Platform/Renderer backends ImGui_ImplXlib_Init(display, window); ImGui_ImplOpenGL3_Init(glsl_version); + ImGui_ImplXlib_SetRendererCtx(glx_context); + + GLXWindow wctx = glXCreateWindow(ImGui_ImplXlib_GetBackendData()->Dpy, fbc[0], window, context_attribs); + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + glXMakeCurrent(bd->Dpy, wctx, (GLXContext)bd->RendererCtx); // Load Fonts // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. @@ -152,7 +159,7 @@ int main(int, char**) XEvent event; XNextEvent(display, &event); ImGui_ImplXlib_ProcessEvent(&event); - + if (event.type == ClientMessage && event.xclient.window == window && (Atom)event.xclient.data.l[0] == wmDeleteMessage) done = true; } @@ -162,6 +169,7 @@ int main(int, char**) ImGui_ImplXlib_NewFrame(); ImGui::NewFrame(); + ImGui::DockSpaceOverViewport(); // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). if (show_demo_window) ImGui::ShowDemoWindow(&show_demo_window); @@ -170,6 +178,7 @@ int main(int, char**) { static float f = 0.0f; static int counter = 0; + static char text[512] = {0}; ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. @@ -180,6 +189,8 @@ int main(int, char**) ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color + ImGui::InputText("input text", text, 512); + if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) counter++; ImGui::SameLine(); @@ -198,14 +209,16 @@ int main(int, char**) show_another_window = false; ImGui::End(); } - // Rendering ImGui::Render(); - glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); - glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); - glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - glXSwapBuffers(display, window); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + ImGui_ImplXlib_Data* bd = ImGui_ImplXlib_GetBackendData(); + glXMakeCurrent(bd->Dpy, wctx, (GLXContext)bd->RendererCtx); + } + glXSwapBuffers(display, wctx); } // Cleanup