From 0b429e06b8c4b66a9f7fe89b5504315ab4f69616 Mon Sep 17 00:00:00 2001 From: Aldrik Ramaekers Date: Fri, 8 Mar 2024 20:48:05 +0100 Subject: linux building --- .gitignore | 3 +- .vscode/settings.json | 70 +-- build_linux.sh | 11 + build_win32.bat | 4 +- fonts/GmarketSans.h | Bin 6520824 -> 32547 bytes imgui.ini | 12 + imgui/backends/imgui_impl_glfw.cpp | 854 ++++++++++++++++++++++++++++++++++ imgui/backends/imgui_impl_glfw.h | 58 +++ imgui/backends/imgui_impl_win32.cpp | 903 ++++++++++++++++++++++++++++++++++++ imgui/backends/imgui_impl_win32.h | 52 +++ imgui/imgui_impl_win32.cpp | 903 ------------------------------------ imgui/imgui_impl_win32.h | 52 --- imgui/imgui_spectrum.cpp | 4 +- src/array.cpp | 3 + src/config.cpp | 8 +- src/image.cpp | 4 +- src/image.h | 14 + src/linux/main_linux.cpp | 296 ++++++++++++ src/linux/mutex.cpp | 129 ++++++ src/main_windows.cpp | 384 --------------- src/memory_bucket.cpp | 1 + src/mutex.cpp | 102 ---- src/mutex.h | 23 +- src/platform.h | 1 - src/windows/main_windows.cpp | 384 +++++++++++++++ src/windows/mutex.cpp | 102 ++++ 26 files changed, 2855 insertions(+), 1522 deletions(-) create mode 100644 build_linux.sh create mode 100644 imgui.ini create mode 100644 imgui/backends/imgui_impl_glfw.cpp create mode 100644 imgui/backends/imgui_impl_glfw.h create mode 100644 imgui/backends/imgui_impl_win32.cpp create mode 100644 imgui/backends/imgui_impl_win32.h delete mode 100644 imgui/imgui_impl_win32.cpp delete mode 100644 imgui/imgui_impl_win32.h create mode 100644 src/linux/main_linux.cpp create mode 100644 src/linux/mutex.cpp delete mode 100644 src/main_windows.cpp delete mode 100644 src/mutex.cpp create mode 100644 src/windows/main_windows.cpp create mode 100644 src/windows/mutex.cpp diff --git a/.gitignore b/.gitignore index 1f5f859..c3d0335 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ bin/* -TODO \ No newline at end of file +TODO +.vscode \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index b276d29..e0ea277 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,74 +1,10 @@ { "files.associations": { - "xstring": "cpp", - "algorithm": "cpp", - "array": "cpp", - "atomic": "cpp", - "bit": "cpp", - "cctype": "cpp", - "charconv": "cpp", + "xhash": "cpp", "chrono": "cpp", - "clocale": "cpp", - "cmath": "cpp", - "compare": "cpp", - "concepts": "cpp", - "cstddef": "cpp", - "cstdint": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cstring": "cpp", - "ctime": "cpp", - "cwchar": "cpp", - "deque": "cpp", - "exception": "cpp", - "filesystem": "cpp", - "format": "cpp", - "forward_list": "cpp", - "fstream": "cpp", - "functional": "cpp", - "initializer_list": "cpp", - "iomanip": "cpp", - "ios": "cpp", - "iosfwd": "cpp", - "istream": "cpp", - "iterator": "cpp", - "limits": "cpp", - "list": "cpp", - "locale": "cpp", - "map": "cpp", - "memory": "cpp", - "new": "cpp", - "optional": "cpp", - "ostream": "cpp", - "ratio": "cpp", - "sstream": "cpp", - "stack": "cpp", - "stdexcept": "cpp", - "stop_token": "cpp", - "streambuf": "cpp", - "string": "cpp", "system_error": "cpp", - "thread": "cpp", - "tuple": "cpp", - "type_traits": "cpp", - "typeinfo": "cpp", - "unordered_map": "cpp", - "utility": "cpp", - "vector": "cpp", - "xfacet": "cpp", - "xhash": "cpp", - "xiosbase": "cpp", "xlocale": "cpp", - "xlocbuf": "cpp", - "xlocinfo": "cpp", - "xlocmes": "cpp", - "xlocmon": "cpp", - "xlocnum": "cpp", - "xloctime": "cpp", - "xmemory": "cpp", - "xstddef": "cpp", - "xtr1common": "cpp", - "xtree": "cpp", - "xutility": "cpp" + "stack": "cpp", + "list": "cpp" } } \ No newline at end of file diff --git a/build_linux.sh b/build_linux.sh new file mode 100644 index 0000000..fee4b3b --- /dev/null +++ b/build_linux.sh @@ -0,0 +1,11 @@ +shopt -s extglob + +OUT_DIR="bin/debug" + +mkdir -p "bin/debug" + +ld -r -b binary -o bin/debug/data.obj LICENSE misc/logo_64.png imgui/LICENSE imspinner/LICENSE imfiledialog/LICENSE misc/search.png misc/folder.png +g++ -std=c++17 -w -DUNICODE -o $OUT_DIR/text-search imgui/imgui*.cpp imgui/backends/imgui_impl_glfw.cpp src/*.cpp imfiledialog/*.cpp src/linux/*.cpp bin/debug/data.obj -Iimgui -Iimgui/backends -Isrc -Isrc/linux -pthread -ldl -lglfw -lGLU -lGL + + +./bin/debug/text-search \ No newline at end of file diff --git a/build_win32.bat b/build_win32.bat index 0fe045a..5b235f4 100644 --- a/build_win32.bat +++ b/build_win32.bat @@ -4,7 +4,7 @@ call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary @set OUT_DIR=bin\\debug @set OUT_EXE=text-search @set INCLUDES=/I..\.. /I..\..\backends -@set SOURCES=imgui/imgui*.cpp src/*.cpp imfiledialog/*.cpp +@set SOURCES=imgui/imgui*.cpp src/*.cpp imfiledialog/*.cpp src/windows/*.cpp imgui/backends/imgui_impl_win32.cpp @set LIBS=opengl32.lib Advapi32.lib Shell32.lib bin/debug/data.obj bin/debug/icon.res @set FLAGS= windres misc/icon.rc -O coff -o bin/debug/icon.res @@ -15,6 +15,6 @@ if "%1"=="-release" ( @set FLAGS=/GL /OPT:REF /O2 ) mkdir %OUT_DIR% -cl /std:c++17 /nologo /Zi /MD /EHsc /utf-8 %INCLUDES% /D UNICODE /D _UNICODE %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fd%OUT_DIR%/vc140.pdb /Fo%OUT_DIR%/ /link %LIBS% +cl /std:c++17 /nologo /Zi /MD /EHsc /Isrc/windows /Iimgui /Iimgui/backends /Isrc /utf-8 %INCLUDES% /D UNICODE /D _UNICODE %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fd%OUT_DIR%/vc140.pdb /Fo%OUT_DIR%/ /link %LIBS% if "%1"=="-r" call "bin/debug/text-search.exe" if "%1"=="-d" call devenv "bin/debug/text-search.exe" diff --git a/fonts/GmarketSans.h b/fonts/GmarketSans.h index 2b153fa..1b74483 100644 Binary files a/fonts/GmarketSans.h and b/fonts/GmarketSans.h differ diff --git a/imgui.ini b/imgui.ini new file mode 100644 index 0000000..a45230f --- /dev/null +++ b/imgui.ini @@ -0,0 +1,12 @@ +[Window][Debug##Default] +Pos=60,60 +Size=400,400 + +[Window][Dear ImGui Demo] +Pos=580,49 +Size=550,680 + +[Window][Hello, world!] +Pos=60,60 +Size=339,180 + diff --git a/imgui/backends/imgui_impl_glfw.cpp b/imgui/backends/imgui_impl_glfw.cpp new file mode 100644 index 0000000..a968fe3 --- /dev/null +++ b/imgui/backends/imgui_impl_glfw.cpp @@ -0,0 +1,854 @@ +// dear imgui: Platform Backend for GLFW +// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..) +// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) +// (Requires: GLFW 3.1+. Prefer GLFW 3.3+ or GLFW 3.4+ for full feature support.) + +// Implemented features: +// [X] Platform: Clipboard support. +// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen (Windows only). +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] +// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+). + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2023-12-19: Emscripten: Added ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback() to register canvas selector and auto-resize GLFW window. +// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys. +// 2023-07-18: Inputs: Revert ignoring mouse data on GLFW_CURSOR_DISABLED as it can be used differently. User may set ImGuiConfigFLags_NoMouse if desired. (#5625, #6609) +// 2023-06-12: Accept glfwGetTime() not returning a monotonically increasing value. This seems to happens on some Windows setup when peripherals disconnect, and is likely to also happen on browser + Emscripten. (#6491) +// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen on Windows ONLY, using a custom WndProc hook. (#2702) +// 2023-03-16: Inputs: Fixed key modifiers handling on secondary viewports (docking branch). Broken on 2023/01/04. (#6248, #6034) +// 2023-03-14: Emscripten: Avoid using glfwGetError() and glfwGetGamepadState() which are not correctly implemented in Emscripten emulation. (#6240) +// 2023-02-03: Emscripten: Registering custom low-level mouse wheel handler to get more accurate scrolling impulses on Emscripten. (#4019, #6096) +// 2023-01-04: Inputs: Fixed mods state on Linux when using Alt-GR text input (e.g. German keyboard layout), could lead to broken text input. Revert a 2022/01/17 change were we resumed using mods provided by GLFW, turns out they were faulty. +// 2022-11-22: Perform a dummy glfwGetError() read to cancel missing names with glfwGetKeyName(). (#5908) +// 2022-10-18: Perform a dummy glfwGetError() read to cancel missing mouse cursors errors. Using GLFW_VERSION_COMBINED directly. (#5785) +// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. +// 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported). +// 2022-09-01: Inputs: Honor GLFW_CURSOR_DISABLED by not setting mouse position *EDIT* Reverted 2023-07-18. +// 2022-04-30: Inputs: Fixed ImGui_ImplGlfw_TranslateUntranslatedKey() for lower case letters on OSX. +// 2022-03-23: Inputs: Fixed a regression in 1.87 which resulted in keyboard modifiers events being reported incorrectly on Linux/X11. +// 2022-02-07: Added ImGui_ImplGlfw_InstallCallbacks()/ImGui_ImplGlfw_RestoreCallbacks() helpers to facilitate user installing callbacks after initializing backend. +// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. +// 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. +// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). +// 2022-01-17: Inputs: always update key mods next and before key event (not in NewFrame) to fix input queue with very low framerates. +// 2022-01-12: *BREAKING CHANGE*: Now using glfwSetCursorPosCallback(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetCursorPosCallback() and forward it to the backend via ImGui_ImplGlfw_CursorPosCallback(). +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-05: Inputs: Converting GLFW untranslated keycodes back to translated keycodes (in the ImGui_ImplGlfw_KeyCallback() function) in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API. +// 2021-08-17: *BREAKING CHANGE*: Now using glfwSetWindowFocusCallback() to calling io.AddFocusEvent(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() and forward it to the backend via ImGui_ImplGlfw_WindowFocusCallback(). +// 2021-07-29: *BREAKING CHANGE*: Now using glfwSetCursorEnterCallback(). MousePos is correctly reported when the host platform window is hovered but not focused. If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() callback and forward it to the backend via ImGui_ImplGlfw_CursorEnterCallback(). +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). +// 2020-01-17: Inputs: Disable error callback while assigning mouse cursors because some X11 setup don't have them and it generates errors. +// 2019-12-05: Inputs: Added support for new mouse cursors added in GLFW 3.4+ (resizing cursors, not allowed cursor). +// 2019-10-18: Misc: Previously installed user callbacks are now restored on shutdown. +// 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter. +// 2019-05-11: Inputs: Don't filter value from character callback before calling AddInputCharacter(). +// 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized. +// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. +// 2018-11-07: Inputs: When installing our GLFW callbacks, we save user's previously installed ones - if any - and chain call them. +// 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls. +// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. +// 2018-06-08: Misc: Extracted imgui_impl_glfw.cpp/.h away from the old combined GLFW+OpenGL/Vulkan examples. +// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag. +// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value, passed to glfwSetCursor()). +// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. +// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. +// 2018-01-25: Inputs: Added gamepad support if ImGuiConfigFlags_NavEnableGamepad is set. +// 2018-01-25: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). +// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. +// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. +// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). +// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. + +#include "imgui.h" +#ifndef IMGUI_DISABLE +#include "imgui_impl_glfw.h" + +// Clang warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#endif + +// GLFW +#include + +#ifdef _WIN32 +#undef APIENTRY +#define GLFW_EXPOSE_NATIVE_WIN32 +#include // for glfwGetWin32Window() +#endif +#ifdef __APPLE__ +#define GLFW_EXPOSE_NATIVE_COCOA +#include // for glfwGetCocoaWindow() +#endif + +#ifdef __EMSCRIPTEN__ +#include +#include +#endif + +// We gather version tests as define in order to easily see which features are version-dependent. +#define GLFW_VERSION_COMBINED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 + GLFW_VERSION_REVISION) +#ifdef GLFW_RESIZE_NESW_CURSOR // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released? +#define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_COMBINED >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR +#else +#define GLFW_HAS_NEW_CURSORS (0) +#endif +#define GLFW_HAS_GAMEPAD_API (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetGamepadState() new api +#define GLFW_HAS_GETKEYNAME (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwGetKeyName() +#define GLFW_HAS_GETERROR (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetError() + +// GLFW data +enum GlfwClientApi +{ + GlfwClientApi_Unknown, + GlfwClientApi_OpenGL, + GlfwClientApi_Vulkan, +}; + +struct ImGui_ImplGlfw_Data +{ + GLFWwindow* Window; + GlfwClientApi ClientApi; + double Time; + GLFWwindow* MouseWindow; + GLFWcursor* MouseCursors[ImGuiMouseCursor_COUNT]; + ImVec2 LastValidMousePos; + bool InstalledCallbacks; + bool CallbacksChainForAllWindows; +#ifdef __EMSCRIPTEN__ + const char* CanvasSelector; +#endif + + // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. + GLFWwindowfocusfun PrevUserCallbackWindowFocus; + GLFWcursorposfun PrevUserCallbackCursorPos; + GLFWcursorenterfun PrevUserCallbackCursorEnter; + GLFWmousebuttonfun PrevUserCallbackMousebutton; + GLFWscrollfun PrevUserCallbackScroll; + GLFWkeyfun PrevUserCallbackKey; + GLFWcharfun PrevUserCallbackChar; + GLFWmonitorfun PrevUserCallbackMonitor; +#ifdef _WIN32 + WNDPROC GlfwWndProc; +#endif + + ImGui_ImplGlfw_Data() { memset((void*)this, 0, sizeof(*this)); } +}; + +// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts +// 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. +// - Because glfwPollEvents() process all windows and some events may be called outside of it, you will need to register your own callbacks +// (passing install_callbacks=false in ImGui_ImplGlfw_InitXXX functions), set the current dear imgui context and then call our callbacks. +// - Otherwise we may need to store a GLFWWindow* -> ImGuiContext* map and handle this in the backend, adding a little bit of extra complexity to it. +// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context. +static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplGlfw_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr; +} + +// Functions +static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data) +{ + return glfwGetClipboardString((GLFWwindow*)user_data); +} + +static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text) +{ + glfwSetClipboardString((GLFWwindow*)user_data, text); +} + +static ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int key) +{ + switch (key) + { + case GLFW_KEY_TAB: return ImGuiKey_Tab; + case GLFW_KEY_LEFT: return ImGuiKey_LeftArrow; + case GLFW_KEY_RIGHT: return ImGuiKey_RightArrow; + case GLFW_KEY_UP: return ImGuiKey_UpArrow; + case GLFW_KEY_DOWN: return ImGuiKey_DownArrow; + case GLFW_KEY_PAGE_UP: return ImGuiKey_PageUp; + case GLFW_KEY_PAGE_DOWN: return ImGuiKey_PageDown; + case GLFW_KEY_HOME: return ImGuiKey_Home; + case GLFW_KEY_END: return ImGuiKey_End; + case GLFW_KEY_INSERT: return ImGuiKey_Insert; + case GLFW_KEY_DELETE: return ImGuiKey_Delete; + case GLFW_KEY_BACKSPACE: return ImGuiKey_Backspace; + case GLFW_KEY_SPACE: return ImGuiKey_Space; + case GLFW_KEY_ENTER: return ImGuiKey_Enter; + case GLFW_KEY_ESCAPE: return ImGuiKey_Escape; + case GLFW_KEY_APOSTROPHE: return ImGuiKey_Apostrophe; + case GLFW_KEY_COMMA: return ImGuiKey_Comma; + case GLFW_KEY_MINUS: return ImGuiKey_Minus; + case GLFW_KEY_PERIOD: return ImGuiKey_Period; + case GLFW_KEY_SLASH: return ImGuiKey_Slash; + case GLFW_KEY_SEMICOLON: return ImGuiKey_Semicolon; + case GLFW_KEY_EQUAL: return ImGuiKey_Equal; + case GLFW_KEY_LEFT_BRACKET: return ImGuiKey_LeftBracket; + case GLFW_KEY_BACKSLASH: return ImGuiKey_Backslash; + case GLFW_KEY_RIGHT_BRACKET: return ImGuiKey_RightBracket; + case GLFW_KEY_GRAVE_ACCENT: return ImGuiKey_GraveAccent; + case GLFW_KEY_CAPS_LOCK: return ImGuiKey_CapsLock; + case GLFW_KEY_SCROLL_LOCK: return ImGuiKey_ScrollLock; + case GLFW_KEY_NUM_LOCK: return ImGuiKey_NumLock; + case GLFW_KEY_PRINT_SCREEN: return ImGuiKey_PrintScreen; + case GLFW_KEY_PAUSE: return ImGuiKey_Pause; + case GLFW_KEY_KP_0: return ImGuiKey_Keypad0; + case GLFW_KEY_KP_1: return ImGuiKey_Keypad1; + case GLFW_KEY_KP_2: return ImGuiKey_Keypad2; + case GLFW_KEY_KP_3: return ImGuiKey_Keypad3; + case GLFW_KEY_KP_4: return ImGuiKey_Keypad4; + case GLFW_KEY_KP_5: return ImGuiKey_Keypad5; + case GLFW_KEY_KP_6: return ImGuiKey_Keypad6; + case GLFW_KEY_KP_7: return ImGuiKey_Keypad7; + case GLFW_KEY_KP_8: return ImGuiKey_Keypad8; + case GLFW_KEY_KP_9: return ImGuiKey_Keypad9; + case GLFW_KEY_KP_DECIMAL: return ImGuiKey_KeypadDecimal; + case GLFW_KEY_KP_DIVIDE: return ImGuiKey_KeypadDivide; + case GLFW_KEY_KP_MULTIPLY: return ImGuiKey_KeypadMultiply; + case GLFW_KEY_KP_SUBTRACT: return ImGuiKey_KeypadSubtract; + case GLFW_KEY_KP_ADD: return ImGuiKey_KeypadAdd; + case GLFW_KEY_KP_ENTER: return ImGuiKey_KeypadEnter; + case GLFW_KEY_KP_EQUAL: return ImGuiKey_KeypadEqual; + case GLFW_KEY_LEFT_SHIFT: return ImGuiKey_LeftShift; + case GLFW_KEY_LEFT_CONTROL: return ImGuiKey_LeftCtrl; + case GLFW_KEY_LEFT_ALT: return ImGuiKey_LeftAlt; + case GLFW_KEY_LEFT_SUPER: return ImGuiKey_LeftSuper; + case GLFW_KEY_RIGHT_SHIFT: return ImGuiKey_RightShift; + case GLFW_KEY_RIGHT_CONTROL: return ImGuiKey_RightCtrl; + case GLFW_KEY_RIGHT_ALT: return ImGuiKey_RightAlt; + case GLFW_KEY_RIGHT_SUPER: return ImGuiKey_RightSuper; + case GLFW_KEY_MENU: return ImGuiKey_Menu; + case GLFW_KEY_0: return ImGuiKey_0; + case GLFW_KEY_1: return ImGuiKey_1; + case GLFW_KEY_2: return ImGuiKey_2; + case GLFW_KEY_3: return ImGuiKey_3; + case GLFW_KEY_4: return ImGuiKey_4; + case GLFW_KEY_5: return ImGuiKey_5; + case GLFW_KEY_6: return ImGuiKey_6; + case GLFW_KEY_7: return ImGuiKey_7; + case GLFW_KEY_8: return ImGuiKey_8; + case GLFW_KEY_9: return ImGuiKey_9; + case GLFW_KEY_A: return ImGuiKey_A; + case GLFW_KEY_B: return ImGuiKey_B; + case GLFW_KEY_C: return ImGuiKey_C; + case GLFW_KEY_D: return ImGuiKey_D; + case GLFW_KEY_E: return ImGuiKey_E; + case GLFW_KEY_F: return ImGuiKey_F; + case GLFW_KEY_G: return ImGuiKey_G; + case GLFW_KEY_H: return ImGuiKey_H; + case GLFW_KEY_I: return ImGuiKey_I; + case GLFW_KEY_J: return ImGuiKey_J; + case GLFW_KEY_K: return ImGuiKey_K; + case GLFW_KEY_L: return ImGuiKey_L; + case GLFW_KEY_M: return ImGuiKey_M; + case GLFW_KEY_N: return ImGuiKey_N; + case GLFW_KEY_O: return ImGuiKey_O; + case GLFW_KEY_P: return ImGuiKey_P; + case GLFW_KEY_Q: return ImGuiKey_Q; + case GLFW_KEY_R: return ImGuiKey_R; + case GLFW_KEY_S: return ImGuiKey_S; + case GLFW_KEY_T: return ImGuiKey_T; + case GLFW_KEY_U: return ImGuiKey_U; + case GLFW_KEY_V: return ImGuiKey_V; + case GLFW_KEY_W: return ImGuiKey_W; + case GLFW_KEY_X: return ImGuiKey_X; + case GLFW_KEY_Y: return ImGuiKey_Y; + case GLFW_KEY_Z: return ImGuiKey_Z; + case GLFW_KEY_F1: return ImGuiKey_F1; + case GLFW_KEY_F2: return ImGuiKey_F2; + case GLFW_KEY_F3: return ImGuiKey_F3; + case GLFW_KEY_F4: return ImGuiKey_F4; + case GLFW_KEY_F5: return ImGuiKey_F5; + case GLFW_KEY_F6: return ImGuiKey_F6; + case GLFW_KEY_F7: return ImGuiKey_F7; + case GLFW_KEY_F8: return ImGuiKey_F8; + case GLFW_KEY_F9: return ImGuiKey_F9; + case GLFW_KEY_F10: return ImGuiKey_F10; + case GLFW_KEY_F11: return ImGuiKey_F11; + case GLFW_KEY_F12: return ImGuiKey_F12; + case GLFW_KEY_F13: return ImGuiKey_F13; + case GLFW_KEY_F14: return ImGuiKey_F14; + case GLFW_KEY_F15: return ImGuiKey_F15; + case GLFW_KEY_F16: return ImGuiKey_F16; + case GLFW_KEY_F17: return ImGuiKey_F17; + case GLFW_KEY_F18: return ImGuiKey_F18; + case GLFW_KEY_F19: return ImGuiKey_F19; + case GLFW_KEY_F20: return ImGuiKey_F20; + case GLFW_KEY_F21: return ImGuiKey_F21; + case GLFW_KEY_F22: return ImGuiKey_F22; + case GLFW_KEY_F23: return ImGuiKey_F23; + case GLFW_KEY_F24: return ImGuiKey_F24; + default: return ImGuiKey_None; + } +} + +// X11 does not include current pressed/released modifier key in 'mods' flags submitted by GLFW +// See https://github.com/ocornut/imgui/issues/6034 and https://github.com/glfw/glfw/issues/1630 +static void ImGui_ImplGlfw_UpdateKeyModifiers(GLFWwindow* window) +{ + ImGuiIO& io = ImGui::GetIO(); + io.AddKeyEvent(ImGuiMod_Ctrl, (glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS)); + io.AddKeyEvent(ImGuiMod_Shift, (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)); + io.AddKeyEvent(ImGuiMod_Alt, (glfwGetKey(window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS)); + io.AddKeyEvent(ImGuiMod_Super, (glfwGetKey(window, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS)); +} + +static bool ImGui_ImplGlfw_ShouldChainCallback(GLFWwindow* window) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + return bd->CallbacksChainForAllWindows ? true : (window == bd->Window); +} + +void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackMousebutton != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + bd->PrevUserCallbackMousebutton(window, button, action, mods); + + ImGui_ImplGlfw_UpdateKeyModifiers(window); + + ImGuiIO& io = ImGui::GetIO(); + if (button >= 0 && button < ImGuiMouseButton_COUNT) + io.AddMouseButtonEvent(button, action == GLFW_PRESS); +} + +void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackScroll != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + bd->PrevUserCallbackScroll(window, xoffset, yoffset); + +#ifdef __EMSCRIPTEN__ + // Ignore GLFW events: will be processed in ImGui_ImplEmscripten_WheelCallback(). + return; +#endif + + ImGuiIO& io = ImGui::GetIO(); + io.AddMouseWheelEvent((float)xoffset, (float)yoffset); +} + +static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) +{ +#if GLFW_HAS_GETKEYNAME && !defined(__EMSCRIPTEN__) + // GLFW 3.1+ attempts to "untranslate" keys, which goes the opposite of what every other framework does, making using lettered shortcuts difficult. + // (It had reasons to do so: namely GLFW is/was more likely to be used for WASD-type game controls rather than lettered shortcuts, but IHMO the 3.1 change could have been done differently) + // See https://github.com/glfw/glfw/issues/1502 for details. + // Adding a workaround to undo this (so our keys are translated->untranslated->translated, likely a lossy process). + // This won't cover edge cases but this is at least going to cover common cases. + if (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_EQUAL) + return key; + GLFWerrorfun prev_error_callback = glfwSetErrorCallback(nullptr); + const char* key_name = glfwGetKeyName(key, scancode); + glfwSetErrorCallback(prev_error_callback); +#if GLFW_HAS_GETERROR && !defined(__EMSCRIPTEN__) // Eat errors (see #5908) + (void)glfwGetError(nullptr); +#endif + if (key_name && key_name[0] != 0 && key_name[1] == 0) + { + const char char_names[] = "`-=[]\\,;\'./"; + const int char_keys[] = { GLFW_KEY_GRAVE_ACCENT, GLFW_KEY_MINUS, GLFW_KEY_EQUAL, GLFW_KEY_LEFT_BRACKET, GLFW_KEY_RIGHT_BRACKET, GLFW_KEY_BACKSLASH, GLFW_KEY_COMMA, GLFW_KEY_SEMICOLON, GLFW_KEY_APOSTROPHE, GLFW_KEY_PERIOD, GLFW_KEY_SLASH, 0 }; + IM_ASSERT(IM_ARRAYSIZE(char_names) == IM_ARRAYSIZE(char_keys)); + if (key_name[0] >= '0' && key_name[0] <= '9') { key = GLFW_KEY_0 + (key_name[0] - '0'); } + else if (key_name[0] >= 'A' && key_name[0] <= 'Z') { key = GLFW_KEY_A + (key_name[0] - 'A'); } + else if (key_name[0] >= 'a' && key_name[0] <= 'z') { key = GLFW_KEY_A + (key_name[0] - 'a'); } + else if (const char* p = strchr(char_names, key_name[0])) { key = char_keys[p - char_names]; } + } + // if (action == GLFW_PRESS) printf("key %d scancode %d name '%s'\n", key, scancode, key_name); +#else + IM_UNUSED(scancode); +#endif + return key; +} + +void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, int action, int mods) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackKey != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + bd->PrevUserCallbackKey(window, keycode, scancode, action, mods); + + if (action != GLFW_PRESS && action != GLFW_RELEASE) + return; + + ImGui_ImplGlfw_UpdateKeyModifiers(window); + + keycode = ImGui_ImplGlfw_TranslateUntranslatedKey(keycode, scancode); + + ImGuiIO& io = ImGui::GetIO(); + ImGuiKey imgui_key = ImGui_ImplGlfw_KeyToImGuiKey(keycode); + io.AddKeyEvent(imgui_key, (action == GLFW_PRESS)); + io.SetKeyEventNativeData(imgui_key, keycode, scancode); // To support legacy indexing (<1.87 user code) +} + +void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackWindowFocus != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + bd->PrevUserCallbackWindowFocus(window, focused); + + ImGuiIO& io = ImGui::GetIO(); + io.AddFocusEvent(focused != 0); +} + +void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackCursorPos != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + bd->PrevUserCallbackCursorPos(window, x, y); + + ImGuiIO& io = ImGui::GetIO(); + io.AddMousePosEvent((float)x, (float)y); + bd->LastValidMousePos = ImVec2((float)x, (float)y); +} + +// Workaround: X11 seems to send spurious Leave/Enter events which would make us lose our position, +// so we back it up and restore on Leave/Enter (see https://github.com/ocornut/imgui/issues/4984) +void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackCursorEnter != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + bd->PrevUserCallbackCursorEnter(window, entered); + + ImGuiIO& io = ImGui::GetIO(); + if (entered) + { + bd->MouseWindow = window; + io.AddMousePosEvent(bd->LastValidMousePos.x, bd->LastValidMousePos.y); + } + else if (!entered && bd->MouseWindow == window) + { + bd->LastValidMousePos = io.MousePos; + bd->MouseWindow = nullptr; + io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); + } +} + +void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackChar != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + bd->PrevUserCallbackChar(window, c); + + ImGuiIO& io = ImGui::GetIO(); + io.AddInputCharacter(c); +} + +void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int) +{ + // Unused in 'master' branch but 'docking' branch will use this, so we declare it ahead of it so if you have to install callbacks you can install this one too. +} + +#ifdef __EMSCRIPTEN__ +static EM_BOOL ImGui_ImplEmscripten_WheelCallback(int, const EmscriptenWheelEvent* ev, void*) +{ + // Mimic Emscripten_HandleWheel() in SDL. + // Corresponding equivalent in GLFW JS emulation layer has incorrect quantizing preventing small values. See #6096 + float multiplier = 0.0f; + if (ev->deltaMode == DOM_DELTA_PIXEL) { multiplier = 1.0f / 100.0f; } // 100 pixels make up a step. + else if (ev->deltaMode == DOM_DELTA_LINE) { multiplier = 1.0f / 3.0f; } // 3 lines make up a step. + else if (ev->deltaMode == DOM_DELTA_PAGE) { multiplier = 80.0f; } // A page makes up 80 steps. + float wheel_x = ev->deltaX * -multiplier; + float wheel_y = ev->deltaY * -multiplier; + ImGuiIO& io = ImGui::GetIO(); + io.AddMouseWheelEvent(wheel_x, wheel_y); + //IMGUI_DEBUG_LOG("[Emsc] mode %d dx: %.2f, dy: %.2f, dz: %.2f --> feed %.2f %.2f\n", (int)ev->deltaMode, ev->deltaX, ev->deltaY, ev->deltaZ, wheel_x, wheel_y); + return EM_TRUE; +} +#endif + +#ifdef _WIN32 +// GLFW doesn't allow to distinguish Mouse vs TouchScreen vs Pen. +// Add support for Win32 (based on imgui_impl_win32), because we rely on _TouchScreen info to trickle inputs differently. +static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo() +{ + LPARAM extra_info = ::GetMessageExtraInfo(); + if ((extra_info & 0xFFFFFF80) == 0xFF515700) + return ImGuiMouseSource_Pen; + if ((extra_info & 0xFFFFFF80) == 0xFF515780) + return ImGuiMouseSource_TouchScreen; + return ImGuiMouseSource_Mouse; +} +static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + switch (msg) + { + case WM_MOUSEMOVE: case WM_NCMOUSEMOVE: + case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_LBUTTONUP: + case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: case WM_RBUTTONUP: + case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: case WM_MBUTTONUP: + case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: case WM_XBUTTONUP: + ImGui::GetIO().AddMouseSourceEvent(GetMouseSourceFromMessageExtraInfo()); + break; + } + return ::CallWindowProcW(bd->GlfwWndProc, hWnd, msg, wParam, lParam); +} +#endif + +void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd->InstalledCallbacks == false && "Callbacks already installed!"); + IM_ASSERT(bd->Window == window); + + bd->PrevUserCallbackWindowFocus = glfwSetWindowFocusCallback(window, ImGui_ImplGlfw_WindowFocusCallback); + bd->PrevUserCallbackCursorEnter = glfwSetCursorEnterCallback(window, ImGui_ImplGlfw_CursorEnterCallback); + bd->PrevUserCallbackCursorPos = glfwSetCursorPosCallback(window, ImGui_ImplGlfw_CursorPosCallback); + bd->PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); + bd->PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); + bd->PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); + bd->PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); + bd->PrevUserCallbackMonitor = glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback); + bd->InstalledCallbacks = true; +} + +void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd->InstalledCallbacks == true && "Callbacks not installed!"); + IM_ASSERT(bd->Window == window); + + glfwSetWindowFocusCallback(window, bd->PrevUserCallbackWindowFocus); + glfwSetCursorEnterCallback(window, bd->PrevUserCallbackCursorEnter); + glfwSetCursorPosCallback(window, bd->PrevUserCallbackCursorPos); + glfwSetMouseButtonCallback(window, bd->PrevUserCallbackMousebutton); + glfwSetScrollCallback(window, bd->PrevUserCallbackScroll); + glfwSetKeyCallback(window, bd->PrevUserCallbackKey); + glfwSetCharCallback(window, bd->PrevUserCallbackChar); + glfwSetMonitorCallback(bd->PrevUserCallbackMonitor); + bd->InstalledCallbacks = false; + bd->PrevUserCallbackWindowFocus = nullptr; + bd->PrevUserCallbackCursorEnter = nullptr; + bd->PrevUserCallbackCursorPos = nullptr; + bd->PrevUserCallbackMousebutton = nullptr; + bd->PrevUserCallbackScroll = nullptr; + bd->PrevUserCallbackKey = nullptr; + bd->PrevUserCallbackChar = nullptr; + bd->PrevUserCallbackMonitor = nullptr; +} + +// Set to 'true' to enable chaining installed callbacks for all windows (including secondary viewports created by backends or by user. +// This is 'false' by default meaning we only chain callbacks for the main viewport. +// We cannot set this to 'true' by default because user callbacks code may be not testing the 'window' parameter of their callback. +// If you set this to 'true' your user callback code will need to make sure you are testing the 'window' parameter. +void ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool chain_for_all_windows) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + bd->CallbacksChainForAllWindows = chain_for_all_windows; +} + +static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) +{ + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!"); + //printf("GLFW_VERSION: %d.%d.%d (%d)", GLFW_VERSION_MAJOR, GLFW_VERSION_MINOR, GLFW_VERSION_REVISION, GLFW_VERSION_COMBINED); + + // Setup backend capabilities flags + ImGui_ImplGlfw_Data* bd = IM_NEW(ImGui_ImplGlfw_Data)(); + io.BackendPlatformUserData = (void*)bd; + io.BackendPlatformName = "imgui_impl_glfw"; + io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) + io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + + bd->Window = window; + bd->Time = 0.0; + + io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; + io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; + io.ClipboardUserData = bd->Window; + + // Create mouse cursors + // (By design, on X11 cursors are user configurable and some cursors may be missing. When a cursor doesn't exist, + // GLFW will emit an error which will often be printed by the app, so we temporarily disable error reporting. + // Missing cursors will return nullptr and our _UpdateMouseCursor() function will use the Arrow cursor instead.) + GLFWerrorfun prev_error_callback = glfwSetErrorCallback(nullptr); + bd->MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR); +#if GLFW_HAS_NEW_CURSORS + bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_RESIZE_NESW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR); +#else + bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); +#endif + glfwSetErrorCallback(prev_error_callback); +#if GLFW_HAS_GETERROR && !defined(__EMSCRIPTEN__) // Eat errors (see #5908) + (void)glfwGetError(nullptr); +#endif + + // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. + if (install_callbacks) + ImGui_ImplGlfw_InstallCallbacks(window); + // Register Emscripten Wheel callback to workaround issue in Emscripten GLFW Emulation (#6096) + // We intentionally do not check 'if (install_callbacks)' here, as some users may set it to false and call GLFW callback themselves. + // FIXME: May break chaining in case user registered their own Emscripten callback? +#ifdef __EMSCRIPTEN__ + emscripten_set_wheel_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, false, ImGui_ImplEmscripten_WheelCallback); +#endif + + // Set platform dependent data in viewport + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); +#ifdef _WIN32 + main_viewport->PlatformHandleRaw = glfwGetWin32Window(bd->Window); +#elif defined(__APPLE__) + main_viewport->PlatformHandleRaw = (void*)glfwGetCocoaWindow(bd->Window); +#else + IM_UNUSED(main_viewport); +#endif + + // Windows: register a WndProc hook so we can intercept some messages. +#ifdef _WIN32 + bd->GlfwWndProc = (WNDPROC)::GetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC); + IM_ASSERT(bd->GlfwWndProc != nullptr); + ::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)ImGui_ImplGlfw_WndProc); +#endif + + bd->ClientApi = client_api; + return true; +} + +bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks) +{ + return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_OpenGL); +} + +bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks) +{ + return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Vulkan); +} + +bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks) +{ + return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Unknown); +} + +void ImGui_ImplGlfw_Shutdown() +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + + if (bd->InstalledCallbacks) + ImGui_ImplGlfw_RestoreCallbacks(bd->Window); +#ifdef __EMSCRIPTEN__ + emscripten_set_wheel_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, false, nullptr); +#endif + + for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) + glfwDestroyCursor(bd->MouseCursors[cursor_n]); + + // Windows: register a WndProc hook so we can intercept some messages. +#ifdef _WIN32 + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)bd->GlfwWndProc); + bd->GlfwWndProc = nullptr; +#endif + + io.BackendPlatformName = nullptr; + io.BackendPlatformUserData = nullptr; + io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad); + IM_DELETE(bd); +} + +static void ImGui_ImplGlfw_UpdateMouseData() +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGuiIO& io = ImGui::GetIO(); + + // (those braces are here to reduce diff with multi-viewports support in 'docking' branch) + { + GLFWwindow* window = bd->Window; +#ifdef __EMSCRIPTEN__ + const bool is_window_focused = true; +#else + const bool is_window_focused = glfwGetWindowAttrib(window, GLFW_FOCUSED) != 0; +#endif + if (is_window_focused) + { + // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) + if (io.WantSetMousePos) + glfwSetCursorPos(window, (double)io.MousePos.x, (double)io.MousePos.y); + + // (Optional) Fallback to provide mouse position when focused (ImGui_ImplGlfw_CursorPosCallback already provides this when hovered or captured) + if (bd->MouseWindow == nullptr) + { + double mouse_x, mouse_y; + glfwGetCursorPos(window, &mouse_x, &mouse_y); + bd->LastValidMousePos = ImVec2((float)mouse_x, (float)mouse_y); + io.AddMousePosEvent((float)mouse_x, (float)mouse_y); + } + } + } +} + +static void ImGui_ImplGlfw_UpdateMouseCursor() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(bd->Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) + return; + + ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); + // (those braces are here to reduce diff with multi-viewports support in 'docking' branch) + { + GLFWwindow* window = bd->Window; + if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) + { + // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + } + else + { + // Show OS mouse cursor + // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. + glfwSetCursor(window, bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]); + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + } + } +} + +// Update gamepad inputs +static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v; } +static void ImGui_ImplGlfw_UpdateGamepads() +{ + ImGuiIO& io = ImGui::GetIO(); + if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs. + return; + + io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; +#if GLFW_HAS_GAMEPAD_API && !defined(__EMSCRIPTEN__) + GLFWgamepadstate gamepad; + if (!glfwGetGamepadState(GLFW_JOYSTICK_1, &gamepad)) + return; + #define MAP_BUTTON(KEY_NO, BUTTON_NO, _UNUSED) do { io.AddKeyEvent(KEY_NO, gamepad.buttons[BUTTON_NO] != 0); } while (0) + #define MAP_ANALOG(KEY_NO, AXIS_NO, _UNUSED, V0, V1) do { float v = gamepad.axes[AXIS_NO]; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v)); } while (0) +#else + int axes_count = 0, buttons_count = 0; + const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count); + const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count); + if (axes_count == 0 || buttons_count == 0) + return; + #define MAP_BUTTON(KEY_NO, _UNUSED, BUTTON_NO) do { io.AddKeyEvent(KEY_NO, (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS)); } while (0) + #define MAP_ANALOG(KEY_NO, _UNUSED, AXIS_NO, V0, V1) do { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v)); } while (0) +#endif + io.BackendFlags |= ImGuiBackendFlags_HasGamepad; + MAP_BUTTON(ImGuiKey_GamepadStart, GLFW_GAMEPAD_BUTTON_START, 7); + MAP_BUTTON(ImGuiKey_GamepadBack, GLFW_GAMEPAD_BUTTON_BACK, 6); + MAP_BUTTON(ImGuiKey_GamepadFaceLeft, GLFW_GAMEPAD_BUTTON_X, 2); // Xbox X, PS Square + MAP_BUTTON(ImGuiKey_GamepadFaceRight, GLFW_GAMEPAD_BUTTON_B, 1); // Xbox B, PS Circle + MAP_BUTTON(ImGuiKey_GamepadFaceUp, GLFW_GAMEPAD_BUTTON_Y, 3); // Xbox Y, PS Triangle + MAP_BUTTON(ImGuiKey_GamepadFaceDown, GLFW_GAMEPAD_BUTTON_A, 0); // Xbox A, PS Cross + MAP_BUTTON(ImGuiKey_GamepadDpadLeft, GLFW_GAMEPAD_BUTTON_DPAD_LEFT, 13); + MAP_BUTTON(ImGuiKey_GamepadDpadRight, GLFW_GAMEPAD_BUTTON_DPAD_RIGHT, 11); + MAP_BUTTON(ImGuiKey_GamepadDpadUp, GLFW_GAMEPAD_BUTTON_DPAD_UP, 10); + MAP_BUTTON(ImGuiKey_GamepadDpadDown, GLFW_GAMEPAD_BUTTON_DPAD_DOWN, 12); + MAP_BUTTON(ImGuiKey_GamepadL1, GLFW_GAMEPAD_BUTTON_LEFT_BUMPER, 4); + MAP_BUTTON(ImGuiKey_GamepadR1, GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER, 5); + MAP_ANALOG(ImGuiKey_GamepadL2, GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, 4, -0.75f, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadR2, GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, 5, -0.75f, +1.0f); + MAP_BUTTON(ImGuiKey_GamepadL3, GLFW_GAMEPAD_BUTTON_LEFT_THUMB, 8); + MAP_BUTTON(ImGuiKey_GamepadR3, GLFW_GAMEPAD_BUTTON_RIGHT_THUMB, 9); + MAP_ANALOG(ImGuiKey_GamepadLStickLeft, GLFW_GAMEPAD_AXIS_LEFT_X, 0, -0.25f, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadLStickRight, GLFW_GAMEPAD_AXIS_LEFT_X, 0, +0.25f, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadLStickUp, GLFW_GAMEPAD_AXIS_LEFT_Y, 1, -0.25f, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadLStickDown, GLFW_GAMEPAD_AXIS_LEFT_Y, 1, +0.25f, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickLeft, GLFW_GAMEPAD_AXIS_RIGHT_X, 2, -0.25f, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickRight, GLFW_GAMEPAD_AXIS_RIGHT_X, 2, +0.25f, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickUp, GLFW_GAMEPAD_AXIS_RIGHT_Y, 3, -0.25f, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickDown, GLFW_GAMEPAD_AXIS_RIGHT_Y, 3, +0.25f, +1.0f); + #undef MAP_BUTTON + #undef MAP_ANALOG +} + +void ImGui_ImplGlfw_NewFrame() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplGlfw_InitForXXX()?"); + + // Setup display size (every frame to accommodate for window resizing) + int w, h; + int display_w, display_h; + glfwGetWindowSize(bd->Window, &w, &h); + glfwGetFramebufferSize(bd->Window, &display_w, &display_h); + io.DisplaySize = ImVec2((float)w, (float)h); + if (w > 0 && h > 0) + io.DisplayFramebufferScale = ImVec2((float)display_w / (float)w, (float)display_h / (float)h); + + // Setup time step + // (Accept glfwGetTime() not returning a monotonically increasing value. Seems to happens on disconnecting peripherals and probably on VMs and Emscripten, see #6491, #6189, #6114, #3644) + double current_time = glfwGetTime(); + if (current_time <= bd->Time) + current_time = bd->Time + 0.00001f; + io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f); + bd->Time = current_time; + + ImGui_ImplGlfw_UpdateMouseData(); + ImGui_ImplGlfw_UpdateMouseCursor(); + + // Update game controllers (if enabled and available) + ImGui_ImplGlfw_UpdateGamepads(); +} + +#ifdef __EMSCRIPTEN__ +static EM_BOOL ImGui_ImplGlfw_OnCanvasSizeChange(int event_type, const EmscriptenUiEvent* event, void* user_data) +{ + ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data; + double canvas_width, canvas_height; + emscripten_get_element_css_size(bd->CanvasSelector, &canvas_width, &canvas_height); + glfwSetWindowSize(bd->Window, (int)canvas_width, (int)canvas_height); + return true; +} + +static EM_BOOL ImGui_ImplEmscripten_FullscreenChangeCallback(int event_type, const EmscriptenFullscreenChangeEvent* event, void* user_data) +{ + ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data; + double canvas_width, canvas_height; + emscripten_get_element_css_size(bd->CanvasSelector, &canvas_width, &canvas_height); + glfwSetWindowSize(bd->Window, (int)canvas_width, (int)canvas_height); + return true; +} + +// 'canvas_selector' is a CSS selector. The event listener is applied to the first element that matches the query. +// STRING MUST PERSIST FOR THE APPLICATION DURATION. PLEASE USE A STRING LITERAL OR ENSURE POINTER WILL STAY VALID. +void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_selector) +{ + IM_ASSERT(canvas_selector != nullptr); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplGlfw_InitForXXX()?"); + + bd->CanvasSelector = canvas_selector; + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, bd, false, ImGui_ImplGlfw_OnCanvasSizeChange); + emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, bd, false, ImGui_ImplEmscripten_FullscreenChangeCallback); + + // Change the size of the GLFW window according to the size of the canvas + ImGui_ImplGlfw_OnCanvasSizeChange(EMSCRIPTEN_EVENT_RESIZE, {}, bd); +} +#endif + +//----------------------------------------------------------------------------- + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/imgui/backends/imgui_impl_glfw.h b/imgui/backends/imgui_impl_glfw.h new file mode 100644 index 0000000..6a9acd0 --- /dev/null +++ b/imgui/backends/imgui_impl_glfw.h @@ -0,0 +1,58 @@ +// dear imgui: Platform Backend for GLFW +// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..) +// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) + +// Implemented features: +// [X] Platform: Clipboard support. +// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen (Windows only). +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] +// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+). + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +#pragma once +#include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE + +struct GLFWwindow; +struct GLFWmonitor; + +IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks); +IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks); +IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks); +IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(); + +// Emscripten related initialization phase methods +#ifdef __EMSCRIPTEN__ +IMGUI_IMPL_API void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_selector); +#endif + +// GLFW callbacks install +// - When calling Init with 'install_callbacks=true': ImGui_ImplGlfw_InstallCallbacks() is called. GLFW callbacks will be installed for you. They will chain-call user's previously installed callbacks, if any. +// - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call individual function yourself from your own GLFW callbacks. +IMGUI_IMPL_API void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window); +IMGUI_IMPL_API void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window); + +// GFLW callbacks options: +// - Set 'chain_for_all_windows=true' to enable chaining callbacks for all windows (including secondary viewports created by backends or by user) +IMGUI_IMPL_API void ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool chain_for_all_windows); + +// GLFW callbacks (individual callbacks to call yourself if you didn't install callbacks) +IMGUI_IMPL_API void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused); // Since 1.84 +IMGUI_IMPL_API void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered); // Since 1.84 +IMGUI_IMPL_API void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y); // Since 1.87 +IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); +IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); +IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); +IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c); +IMGUI_IMPL_API void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int event); + +#endif // #ifndef IMGUI_DISABLE diff --git a/imgui/backends/imgui_impl_win32.cpp b/imgui/backends/imgui_impl_win32.cpp new file mode 100644 index 0000000..73ed700 --- /dev/null +++ b/imgui/backends/imgui_impl_win32.cpp @@ -0,0 +1,903 @@ +// dear imgui: Platform Backend for Windows (standard windows API for 32-bits AND 64-bits applications) +// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) + +// Implemented features: +// [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui) +// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen. +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] +// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +#include "imgui.h" +#ifndef IMGUI_DISABLE +#include "imgui_impl_win32.h" +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include // GET_X_LPARAM(), GET_Y_LPARAM() +#include +#include + +// Configuration flags to add in your imconfig.h file: +//#define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD // Disable gamepad support. This was meaningful before <1.81 but we now load XInput dynamically so the option is now less relevant. + +// Using XInput for gamepad (will load DLL dynamically) +#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD +#include +typedef DWORD (WINAPI *PFN_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*); +typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); +#endif + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys. +// 2023-09-25: Inputs: Synthesize key-down event on key-up for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit it (same behavior as GLFW/SDL). +// 2023-09-07: Inputs: Added support for keyboard codepage conversion for when application is compiled in MBCS mode and using a non-Unicode window. +// 2023-04-19: Added ImGui_ImplWin32_InitForOpenGL() to facilitate combining raw Win32/Winapi with OpenGL. (#3218) +// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen. (#2702) +// 2023-02-15: Inputs: Use WM_NCMOUSEMOVE / WM_NCMOUSELEAVE to track mouse position over non-client area (e.g. OS decorations) when app is not focused. (#6045, #6162) +// 2023-02-02: Inputs: Flipping WM_MOUSEHWHEEL (horizontal mouse-wheel) value to match other backends and offer consistent horizontal scrolling direction. (#4019, #6096, #1463) +// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. +// 2022-09-28: Inputs: Convert WM_CHAR values with MultiByteToWideChar() when window class was registered as MBCS (not Unicode). +// 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported). +// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. +// 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. +// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). +// 2022-01-17: Inputs: always update key mods next and before a key event (not in NewFrame) to fix input queue with very low framerates. +// 2022-01-12: Inputs: Update mouse inputs using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback to provide it when focused but not hovered/captured. More standard and will allow us to pass it to future input queue API. +// 2022-01-12: Inputs: Maintain our own copy of MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2021-12-16: Inputs: Fill VK_LCONTROL/VK_RCONTROL/VK_LSHIFT/VK_RSHIFT/VK_LMENU/VK_RMENU for completeness. +// 2021-08-17: Calling io.AddFocusEvent() on WM_SETFOCUS/WM_KILLFOCUS messages. +// 2021-08-02: Inputs: Fixed keyboard modifiers being reported when host window doesn't have focus. +// 2021-07-29: Inputs: MousePos is correctly reported when the host platform window is hovered but not focused (using TrackMouseEvent() to receive WM_MOUSELEAVE events). +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). +// 2021-06-08: Fixed ImGui_ImplWin32_EnableDpiAwareness() and ImGui_ImplWin32_GetDpiScaleForMonitor() to handle Windows 8.1/10 features without a manifest (per-monitor DPI, and properly calls SetProcessDpiAwareness() on 8.1). +// 2021-03-23: Inputs: Clearing keyboard down array when losing focus (WM_KILLFOCUS). +// 2021-02-18: Added ImGui_ImplWin32_EnableAlphaCompositing(). Non Visual Studio users will need to link with dwmapi.lib (MinGW/gcc: use -ldwmapi). +// 2021-02-17: Fixed ImGui_ImplWin32_EnableDpiAwareness() attempting to get SetProcessDpiAwareness from shcore.dll on Windows 8 whereas it is only supported on Windows 8.1. +// 2021-01-25: Inputs: Dynamically loading XInput DLL. +// 2020-12-04: Misc: Fixed setting of io.DisplaySize to invalid/uninitialized data when after hwnd has been closed. +// 2020-03-03: Inputs: Calling AddInputCharacterUTF16() to support surrogate pairs leading to codepoint >= 0x10000 (for more complete CJK inputs) +// 2020-02-17: Added ImGui_ImplWin32_EnableDpiAwareness(), ImGui_ImplWin32_GetDpiScaleForHwnd(), ImGui_ImplWin32_GetDpiScaleForMonitor() helper functions. +// 2020-01-14: Inputs: Added support for #define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD/IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT. +// 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor. +// 2019-05-11: Inputs: Don't filter value from WM_CHAR before calling AddInputCharacter(). +// 2019-01-17: Misc: Using GetForegroundWindow()+IsChild() instead of GetActiveWindow() to be compatible with windows created in a different thread or parent. +// 2019-01-17: Inputs: Added support for mouse buttons 4 and 5 via WM_XBUTTON* messages. +// 2019-01-15: Inputs: Added support for XInput gamepads (if ImGuiConfigFlags_NavEnableGamepad is set by user application). +// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. +// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. +// 2018-06-10: Inputs: Fixed handling of mouse wheel messages to support fine position messages (typically sent by track-pads). +// 2018-06-08: Misc: Extracted imgui_impl_win32.cpp/.h away from the old combined DX9/DX10/DX11/DX12 examples. +// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors and ImGuiBackendFlags_HasSetMousePos flags + honor ImGuiConfigFlags_NoMouseCursorChange flag. +// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling). +// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. +// 2018-02-06: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). +// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. +// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. +// 2018-01-08: Inputs: Added mapping for ImGuiKey_Insert. +// 2018-01-05: Inputs: Added WM_LBUTTONDBLCLK double-click handlers for window classes with the CS_DBLCLKS flag. +// 2017-10-23: Inputs: Added WM_SYSKEYDOWN / WM_SYSKEYUP handlers so e.g. the VK_MENU key can be read. +// 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging. +// 2016-11-12: Inputs: Only call Win32 ::SetCursor(nullptr) when io.MouseDrawCursor is set. + +struct ImGui_ImplWin32_Data +{ + HWND hWnd; + HWND MouseHwnd; + int MouseTrackedArea; // 0: not tracked, 1: client are, 2: non-client area + int MouseButtonsDown; + INT64 Time; + INT64 TicksPerSecond; + ImGuiMouseCursor LastMouseCursor; + UINT32 KeyboardCodePage; + +#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD + bool HasGamepad; + bool WantUpdateHasGamepad; + HMODULE XInputDLL; + PFN_XInputGetCapabilities XInputGetCapabilities; + PFN_XInputGetState XInputGetState; +#endif + + ImGui_ImplWin32_Data() { memset((void*)this, 0, sizeof(*this)); } +}; + +// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts +// 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_ImplWin32_Data* ImGui_ImplWin32_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplWin32_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr; +} + +// Functions +static void ImGui_ImplWin32_UpdateKeyboardCodePage() +{ + // Retrieve keyboard code page, required for handling of non-Unicode Windows. + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + HKL keyboard_layout = ::GetKeyboardLayout(0); + LCID keyboard_lcid = MAKELCID(HIWORD(keyboard_layout), SORT_DEFAULT); + if (::GetLocaleInfoA(keyboard_lcid, (LOCALE_RETURN_NUMBER | LOCALE_IDEFAULTANSICODEPAGE), (LPSTR)&bd->KeyboardCodePage, sizeof(bd->KeyboardCodePage)) == 0) + bd->KeyboardCodePage = CP_ACP; // Fallback to default ANSI code page when fails. +} + +static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc) +{ + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!"); + + INT64 perf_frequency, perf_counter; + if (!::QueryPerformanceFrequency((LARGE_INTEGER*)&perf_frequency)) + return false; + if (!::QueryPerformanceCounter((LARGE_INTEGER*)&perf_counter)) + return false; + + // Setup backend capabilities flags + ImGui_ImplWin32_Data* bd = IM_NEW(ImGui_ImplWin32_Data)(); + io.BackendPlatformUserData = (void*)bd; + io.BackendPlatformName = "imgui_impl_win32"; + io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) + io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + + bd->hWnd = (HWND)hwnd; + bd->TicksPerSecond = perf_frequency; + bd->Time = perf_counter; + bd->LastMouseCursor = ImGuiMouseCursor_COUNT; + ImGui_ImplWin32_UpdateKeyboardCodePage(); + + // Set platform dependent data in viewport + ImGui::GetMainViewport()->PlatformHandleRaw = (void*)hwnd; + IM_UNUSED(platform_has_own_dc); // Used in 'docking' branch + + // Dynamically load XInput library +#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD + bd->WantUpdateHasGamepad = true; + const char* xinput_dll_names[] = + { + "xinput1_4.dll", // Windows 8+ + "xinput1_3.dll", // DirectX SDK + "xinput9_1_0.dll", // Windows Vista, Windows 7 + "xinput1_2.dll", // DirectX SDK + "xinput1_1.dll" // DirectX SDK + }; + for (int n = 0; n < IM_ARRAYSIZE(xinput_dll_names); n++) + if (HMODULE dll = ::LoadLibraryA(xinput_dll_names[n])) + { + bd->XInputDLL = dll; + bd->XInputGetCapabilities = (PFN_XInputGetCapabilities)::GetProcAddress(dll, "XInputGetCapabilities"); + bd->XInputGetState = (PFN_XInputGetState)::GetProcAddress(dll, "XInputGetState"); + break; + } +#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD + + return true; +} + +IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd) +{ + return ImGui_ImplWin32_InitEx(hwnd, false); +} + +IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd) +{ + // OpenGL needs CS_OWNDC + return ImGui_ImplWin32_InitEx(hwnd, true); +} + +void ImGui_ImplWin32_Shutdown() +{ + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + + // Unload XInput library +#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD + if (bd->XInputDLL) + ::FreeLibrary(bd->XInputDLL); +#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD + + io.BackendPlatformName = nullptr; + io.BackendPlatformUserData = nullptr; + io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad); + IM_DELETE(bd); +} + +static bool ImGui_ImplWin32_UpdateMouseCursor() +{ + ImGuiIO& io = ImGui::GetIO(); + if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) + return false; + + ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); + if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) + { + // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor + ::SetCursor(nullptr); + } + else + { + // Show OS mouse cursor + LPTSTR win32_cursor = IDC_ARROW; + switch (imgui_cursor) + { + case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break; + case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break; + case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break; + case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break; + case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break; + case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break; + case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break; + case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break; + case ImGuiMouseCursor_NotAllowed: win32_cursor = IDC_NO; break; + } + ::SetCursor(::LoadCursor(nullptr, win32_cursor)); + } + return true; +} + +static bool IsVkDown(int vk) +{ + return (::GetKeyState(vk) & 0x8000) != 0; +} + +static void ImGui_ImplWin32_AddKeyEvent(ImGuiKey key, bool down, int native_keycode, int native_scancode = -1) +{ + ImGuiIO& io = ImGui::GetIO(); + io.AddKeyEvent(key, down); + io.SetKeyEventNativeData(key, native_keycode, native_scancode); // To support legacy indexing (<1.87 user code) + IM_UNUSED(native_scancode); +} + +static void ImGui_ImplWin32_ProcessKeyEventsWorkarounds() +{ + // Left & right Shift keys: when both are pressed together, Windows tend to not generate the WM_KEYUP event for the first released one. + if (ImGui::IsKeyDown(ImGuiKey_LeftShift) && !IsVkDown(VK_LSHIFT)) + ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftShift, false, VK_LSHIFT); + if (ImGui::IsKeyDown(ImGuiKey_RightShift) && !IsVkDown(VK_RSHIFT)) + ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightShift, false, VK_RSHIFT); + + // Sometimes WM_KEYUP for Win key is not passed down to the app (e.g. for Win+V on some setups, according to GLFW). + if (ImGui::IsKeyDown(ImGuiKey_LeftSuper) && !IsVkDown(VK_LWIN)) + ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftSuper, false, VK_LWIN); + if (ImGui::IsKeyDown(ImGuiKey_RightSuper) && !IsVkDown(VK_RWIN)) + ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightSuper, false, VK_RWIN); +} + +static void ImGui_ImplWin32_UpdateKeyModifiers() +{ + ImGuiIO& io = ImGui::GetIO(); + io.AddKeyEvent(ImGuiMod_Ctrl, IsVkDown(VK_CONTROL)); + io.AddKeyEvent(ImGuiMod_Shift, IsVkDown(VK_SHIFT)); + io.AddKeyEvent(ImGuiMod_Alt, IsVkDown(VK_MENU)); + io.AddKeyEvent(ImGuiMod_Super, IsVkDown(VK_APPS)); +} + +static void ImGui_ImplWin32_UpdateMouseData() +{ + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(bd->hWnd != 0); + + HWND focused_window = ::GetForegroundWindow(); + const bool is_app_focused = (focused_window == bd->hWnd); + if (is_app_focused) + { + // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) + if (io.WantSetMousePos) + { + POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; + if (::ClientToScreen(bd->hWnd, &pos)) + ::SetCursorPos(pos.x, pos.y); + } + + // (Optional) Fallback to provide mouse position when focused (WM_MOUSEMOVE already provides this when hovered or captured) + // This also fills a short gap when clicking non-client area: WM_NCMOUSELEAVE -> modal OS move -> gap -> WM_NCMOUSEMOVE + if (!io.WantSetMousePos && bd->MouseTrackedArea == 0) + { + POINT pos; + if (::GetCursorPos(&pos) && ::ScreenToClient(bd->hWnd, &pos)) + io.AddMousePosEvent((float)pos.x, (float)pos.y); + } + } +} + +// Gamepad navigation mapping +static void ImGui_ImplWin32_UpdateGamepads() +{ +#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + //if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs. + // return; + + // Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow. + // Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE. + if (bd->WantUpdateHasGamepad) + { + XINPUT_CAPABILITIES caps = {}; + bd->HasGamepad = bd->XInputGetCapabilities ? (bd->XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) : false; + bd->WantUpdateHasGamepad = false; + } + + io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; + XINPUT_STATE xinput_state; + XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad; + if (!bd->HasGamepad || bd->XInputGetState == nullptr || bd->XInputGetState(0, &xinput_state) != ERROR_SUCCESS) + return; + io.BackendFlags |= ImGuiBackendFlags_HasGamepad; + + #define IM_SATURATE(V) (V < 0.0f ? 0.0f : V > 1.0f ? 1.0f : V) + #define MAP_BUTTON(KEY_NO, BUTTON_ENUM) { io.AddKeyEvent(KEY_NO, (gamepad.wButtons & BUTTON_ENUM) != 0); } + #define MAP_ANALOG(KEY_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); io.AddKeyAnalogEvent(KEY_NO, vn > 0.10f, IM_SATURATE(vn)); } + MAP_BUTTON(ImGuiKey_GamepadStart, XINPUT_GAMEPAD_START); + MAP_BUTTON(ImGuiKey_GamepadBack, XINPUT_GAMEPAD_BACK); + MAP_BUTTON(ImGuiKey_GamepadFaceLeft, XINPUT_GAMEPAD_X); + MAP_BUTTON(ImGuiKey_GamepadFaceRight, XINPUT_GAMEPAD_B); + MAP_BUTTON(ImGuiKey_GamepadFaceUp, XINPUT_GAMEPAD_Y); + MAP_BUTTON(ImGuiKey_GamepadFaceDown, XINPUT_GAMEPAD_A); + MAP_BUTTON(ImGuiKey_GamepadDpadLeft, XINPUT_GAMEPAD_DPAD_LEFT); + MAP_BUTTON(ImGuiKey_GamepadDpadRight, XINPUT_GAMEPAD_DPAD_RIGHT); + MAP_BUTTON(ImGuiKey_GamepadDpadUp, XINPUT_GAMEPAD_DPAD_UP); + MAP_BUTTON(ImGuiKey_GamepadDpadDown, XINPUT_GAMEPAD_DPAD_DOWN); + MAP_BUTTON(ImGuiKey_GamepadL1, XINPUT_GAMEPAD_LEFT_SHOULDER); + MAP_BUTTON(ImGuiKey_GamepadR1, XINPUT_GAMEPAD_RIGHT_SHOULDER); + MAP_ANALOG(ImGuiKey_GamepadL2, gamepad.bLeftTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, 255); + MAP_ANALOG(ImGuiKey_GamepadR2, gamepad.bRightTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, 255); + MAP_BUTTON(ImGuiKey_GamepadL3, XINPUT_GAMEPAD_LEFT_THUMB); + MAP_BUTTON(ImGuiKey_GamepadR3, XINPUT_GAMEPAD_RIGHT_THUMB); + MAP_ANALOG(ImGuiKey_GamepadLStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768); + MAP_ANALOG(ImGuiKey_GamepadLStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); + MAP_ANALOG(ImGuiKey_GamepadLStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); + MAP_ANALOG(ImGuiKey_GamepadLStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768); + MAP_ANALOG(ImGuiKey_GamepadRStickLeft, gamepad.sThumbRX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768); + MAP_ANALOG(ImGuiKey_GamepadRStickRight, gamepad.sThumbRX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); + MAP_ANALOG(ImGuiKey_GamepadRStickUp, gamepad.sThumbRY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); + MAP_ANALOG(ImGuiKey_GamepadRStickDown, gamepad.sThumbRY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768); + #undef MAP_BUTTON + #undef MAP_ANALOG +#endif // #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD +} + +void ImGui_ImplWin32_NewFrame() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplWin32_Init()?"); + + // Setup display size (every frame to accommodate for window resizing) + RECT rect = { 0, 0, 0, 0 }; + ::GetClientRect(bd->hWnd, &rect); + io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); + + // Setup time step + INT64 current_time = 0; + ::QueryPerformanceCounter((LARGE_INTEGER*)¤t_time); + io.DeltaTime = (float)(current_time - bd->Time) / bd->TicksPerSecond; + bd->Time = current_time; + + // Update OS mouse position + ImGui_ImplWin32_UpdateMouseData(); + + // Process workarounds for known Windows key handling issues + ImGui_ImplWin32_ProcessKeyEventsWorkarounds(); + + // Update OS mouse cursor with the cursor requested by imgui + ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor(); + if (bd->LastMouseCursor != mouse_cursor) + { + bd->LastMouseCursor = mouse_cursor; + ImGui_ImplWin32_UpdateMouseCursor(); + } + + // Update game controllers (if enabled and available) + ImGui_ImplWin32_UpdateGamepads(); +} + +// There is no distinct VK_xxx for keypad enter, instead it is VK_RETURN + KF_EXTENDED, we assign it an arbitrary value to make code more readable (VK_ codes go up to 255) +#define IM_VK_KEYPAD_ENTER (VK_RETURN + 256) + +// Map VK_xxx to ImGuiKey_xxx. +static ImGuiKey ImGui_ImplWin32_VirtualKeyToImGuiKey(WPARAM wParam) +{ + switch (wParam) + { + case VK_TAB: return ImGuiKey_Tab; + case VK_LEFT: return ImGuiKey_LeftArrow; + case VK_RIGHT: return ImGuiKey_RightArrow; + case VK_UP: return ImGuiKey_UpArrow; + case VK_DOWN: return ImGuiKey_DownArrow; + case VK_PRIOR: return ImGuiKey_PageUp; + case VK_NEXT: return ImGuiKey_PageDown; + case VK_HOME: return ImGuiKey_Home; + case VK_END: return ImGuiKey_End; + case VK_INSERT: return ImGuiKey_Insert; + case VK_DELETE: return ImGuiKey_Delete; + case VK_BACK: return ImGuiKey_Backspace; + case VK_SPACE: return ImGuiKey_Space; + case VK_RETURN: return ImGuiKey_Enter; + case VK_ESCAPE: return ImGuiKey_Escape; + case VK_OEM_7: return ImGuiKey_Apostrophe; + case VK_OEM_COMMA: return ImGuiKey_Comma; + case VK_OEM_MINUS: return ImGuiKey_Minus; + case VK_OEM_PERIOD: return ImGuiKey_Period; + case VK_OEM_2: return ImGuiKey_Slash; + case VK_OEM_1: return ImGuiKey_Semicolon; + case VK_OEM_PLUS: return ImGuiKey_Equal; + case VK_OEM_4: return ImGuiKey_LeftBracket; + case VK_OEM_5: return ImGuiKey_Backslash; + case VK_OEM_6: return ImGuiKey_RightBracket; + case VK_OEM_3: return ImGuiKey_GraveAccent; + case VK_CAPITAL: return ImGuiKey_CapsLock; + case VK_SCROLL: return ImGuiKey_ScrollLock; + case VK_NUMLOCK: return ImGuiKey_NumLock; + case VK_SNAPSHOT: return ImGuiKey_PrintScreen; + case VK_PAUSE: return ImGuiKey_Pause; + case VK_NUMPAD0: return ImGuiKey_Keypad0; + case VK_NUMPAD1: return ImGuiKey_Keypad1; + case VK_NUMPAD2: return ImGuiKey_Keypad2; + case VK_NUMPAD3: return ImGuiKey_Keypad3; + case VK_NUMPAD4: return ImGuiKey_Keypad4; + case VK_NUMPAD5: return ImGuiKey_Keypad5; + case VK_NUMPAD6: return ImGuiKey_Keypad6; + case VK_NUMPAD7: return ImGuiKey_Keypad7; + case VK_NUMPAD8: return ImGuiKey_Keypad8; + case VK_NUMPAD9: return ImGuiKey_Keypad9; + case VK_DECIMAL: return ImGuiKey_KeypadDecimal; + case VK_DIVIDE: return ImGuiKey_KeypadDivide; + case VK_MULTIPLY: return ImGuiKey_KeypadMultiply; + case VK_SUBTRACT: return ImGuiKey_KeypadSubtract; + case VK_ADD: return ImGuiKey_KeypadAdd; + case IM_VK_KEYPAD_ENTER: return ImGuiKey_KeypadEnter; + case VK_LSHIFT: return ImGuiKey_LeftShift; + case VK_LCONTROL: return ImGuiKey_LeftCtrl; + case VK_LMENU: return ImGuiKey_LeftAlt; + case VK_LWIN: return ImGuiKey_LeftSuper; + case VK_RSHIFT: return ImGuiKey_RightShift; + case VK_RCONTROL: return ImGuiKey_RightCtrl; + case VK_RMENU: return ImGuiKey_RightAlt; + case VK_RWIN: return ImGuiKey_RightSuper; + case VK_APPS: return ImGuiKey_Menu; + case '0': return ImGuiKey_0; + case '1': return ImGuiKey_1; + case '2': return ImGuiKey_2; + case '3': return ImGuiKey_3; + case '4': return ImGuiKey_4; + case '5': return ImGuiKey_5; + case '6': return ImGuiKey_6; + case '7': return ImGuiKey_7; + case '8': return ImGuiKey_8; + case '9': return ImGuiKey_9; + case 'A': return ImGuiKey_A; + case 'B': return ImGuiKey_B; + case 'C': return ImGuiKey_C; + case 'D': return ImGuiKey_D; + case 'E': return ImGuiKey_E; + case 'F': return ImGuiKey_F; + case 'G': return ImGuiKey_G; + case 'H': return ImGuiKey_H; + case 'I': return ImGuiKey_I; + case 'J': return ImGuiKey_J; + case 'K': return ImGuiKey_K; + case 'L': return ImGuiKey_L; + case 'M': return ImGuiKey_M; + case 'N': return ImGuiKey_N; + case 'O': return ImGuiKey_O; + case 'P': return ImGuiKey_P; + case 'Q': return ImGuiKey_Q; + case 'R': return ImGuiKey_R; + case 'S': return ImGuiKey_S; + case 'T': return ImGuiKey_T; + case 'U': return ImGuiKey_U; + case 'V': return ImGuiKey_V; + case 'W': return ImGuiKey_W; + case 'X': return ImGuiKey_X; + case 'Y': return ImGuiKey_Y; + case 'Z': return ImGuiKey_Z; + case VK_F1: return ImGuiKey_F1; + case VK_F2: return ImGuiKey_F2; + case VK_F3: return ImGuiKey_F3; + case VK_F4: return ImGuiKey_F4; + case VK_F5: return ImGuiKey_F5; + case VK_F6: return ImGuiKey_F6; + case VK_F7: return ImGuiKey_F7; + case VK_F8: return ImGuiKey_F8; + case VK_F9: return ImGuiKey_F9; + case VK_F10: return ImGuiKey_F10; + case VK_F11: return ImGuiKey_F11; + case VK_F12: return ImGuiKey_F12; + case VK_F13: return ImGuiKey_F13; + case VK_F14: return ImGuiKey_F14; + case VK_F15: return ImGuiKey_F15; + case VK_F16: return ImGuiKey_F16; + case VK_F17: return ImGuiKey_F17; + case VK_F18: return ImGuiKey_F18; + case VK_F19: return ImGuiKey_F19; + case VK_F20: return ImGuiKey_F20; + case VK_F21: return ImGuiKey_F21; + case VK_F22: return ImGuiKey_F22; + case VK_F23: return ImGuiKey_F23; + case VK_F24: return ImGuiKey_F24; + case VK_BROWSER_BACK: return ImGuiKey_AppBack; + case VK_BROWSER_FORWARD: return ImGuiKey_AppForward; + default: return ImGuiKey_None; + } +} + +// Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions. +#ifndef WM_MOUSEHWHEEL +#define WM_MOUSEHWHEEL 0x020E +#endif +#ifndef DBT_DEVNODES_CHANGED +#define DBT_DEVNODES_CHANGED 0x0007 +#endif + +// Win32 message handler (process Win32 mouse/keyboard inputs, etc.) +// Call from your application's message handler. Keep calling your message handler unless this function returns TRUE. +// When implementing your own backend, 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. +// PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinates when dragging mouse outside of our window bounds. +// PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag. +#if 0 +// Copy this line into your .cpp file to forward declare the function. +extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +#endif + +// See https://learn.microsoft.com/en-us/windows/win32/tablet/system-events-and-mouse-messages +// Prefer to call this at the top of the message handler to avoid the possibility of other Win32 calls interfering with this. +static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo() +{ + LPARAM extra_info = ::GetMessageExtraInfo(); + if ((extra_info & 0xFFFFFF80) == 0xFF515700) + return ImGuiMouseSource_Pen; + if ((extra_info & 0xFFFFFF80) == 0xFF515780) + return ImGuiMouseSource_TouchScreen; + return ImGuiMouseSource_Mouse; +} + +IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (ImGui::GetCurrentContext() == nullptr) + return 0; + + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + + switch (msg) + { + case WM_MOUSEMOVE: + case WM_NCMOUSEMOVE: + { + // We need to call TrackMouseEvent in order to receive WM_MOUSELEAVE events + ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo(); + const int area = (msg == WM_MOUSEMOVE) ? 1 : 2; + bd->MouseHwnd = hwnd; + if (bd->MouseTrackedArea != area) + { + TRACKMOUSEEVENT tme_cancel = { sizeof(tme_cancel), TME_CANCEL, hwnd, 0 }; + TRACKMOUSEEVENT tme_track = { sizeof(tme_track), (DWORD)((area == 2) ? (TME_LEAVE | TME_NONCLIENT) : TME_LEAVE), hwnd, 0 }; + if (bd->MouseTrackedArea != 0) + ::TrackMouseEvent(&tme_cancel); + ::TrackMouseEvent(&tme_track); + bd->MouseTrackedArea = area; + } + POINT mouse_pos = { (LONG)GET_X_LPARAM(lParam), (LONG)GET_Y_LPARAM(lParam) }; + if (msg == WM_NCMOUSEMOVE && ::ScreenToClient(hwnd, &mouse_pos) == FALSE) // WM_NCMOUSEMOVE are provided in absolute coordinates. + break; + io.AddMouseSourceEvent(mouse_source); + io.AddMousePosEvent((float)mouse_pos.x, (float)mouse_pos.y); + break; + } + case WM_MOUSELEAVE: + case WM_NCMOUSELEAVE: + { + const int area = (msg == WM_MOUSELEAVE) ? 1 : 2; + if (bd->MouseTrackedArea == area) + { + if (bd->MouseHwnd == hwnd) + bd->MouseHwnd = nullptr; + bd->MouseTrackedArea = 0; + io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); + } + break; + } + case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: + case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: + case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: + case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: + { + ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo(); + int button = 0; + if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; } + if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; } + if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) { button = 2; } + if (msg == WM_XBUTTONDOWN || msg == WM_XBUTTONDBLCLK) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; } + if (bd->MouseButtonsDown == 0 && ::GetCapture() == nullptr) + ::SetCapture(hwnd); + bd->MouseButtonsDown |= 1 << button; + io.AddMouseSourceEvent(mouse_source); + io.AddMouseButtonEvent(button, true); + return 0; + } + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: + case WM_XBUTTONUP: + { + ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo(); + int button = 0; + if (msg == WM_LBUTTONUP) { button = 0; } + if (msg == WM_RBUTTONUP) { button = 1; } + if (msg == WM_MBUTTONUP) { button = 2; } + if (msg == WM_XBUTTONUP) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; } + bd->MouseButtonsDown &= ~(1 << button); + if (bd->MouseButtonsDown == 0 && ::GetCapture() == hwnd) + ::ReleaseCapture(); + io.AddMouseSourceEvent(mouse_source); + io.AddMouseButtonEvent(button, false); + return 0; + } + case WM_MOUSEWHEEL: + io.AddMouseWheelEvent(0.0f, (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA); + return 0; + case WM_MOUSEHWHEEL: + io.AddMouseWheelEvent(-(float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0.0f); + return 0; + case WM_KEYDOWN: + case WM_KEYUP: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + { + const bool is_key_down = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN); + if (wParam < 256) + { + // Submit modifiers + ImGui_ImplWin32_UpdateKeyModifiers(); + + // Obtain virtual key code + // (keypad enter doesn't have its own... VK_RETURN with KF_EXTENDED flag means keypad enter, see IM_VK_KEYPAD_ENTER definition for details, it is mapped to ImGuiKey_KeyPadEnter.) + int vk = (int)wParam; + if ((wParam == VK_RETURN) && (HIWORD(lParam) & KF_EXTENDED)) + vk = IM_VK_KEYPAD_ENTER; + const ImGuiKey key = ImGui_ImplWin32_VirtualKeyToImGuiKey(vk); + const int scancode = (int)LOBYTE(HIWORD(lParam)); + + // Special behavior for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit the key down event. + if (key == ImGuiKey_PrintScreen && !is_key_down) + ImGui_ImplWin32_AddKeyEvent(key, true, vk, scancode); + + // Submit key event + if (key != ImGuiKey_None) + ImGui_ImplWin32_AddKeyEvent(key, is_key_down, vk, scancode); + + // Submit individual left/right modifier events + if (vk == VK_SHIFT) + { + // Important: Shift keys tend to get stuck when pressed together, missing key-up events are corrected in ImGui_ImplWin32_ProcessKeyEventsWorkarounds() + if (IsVkDown(VK_LSHIFT) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftShift, is_key_down, VK_LSHIFT, scancode); } + if (IsVkDown(VK_RSHIFT) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightShift, is_key_down, VK_RSHIFT, scancode); } + } + else if (vk == VK_CONTROL) + { + if (IsVkDown(VK_LCONTROL) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftCtrl, is_key_down, VK_LCONTROL, scancode); } + if (IsVkDown(VK_RCONTROL) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightCtrl, is_key_down, VK_RCONTROL, scancode); } + } + else if (vk == VK_MENU) + { + if (IsVkDown(VK_LMENU) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftAlt, is_key_down, VK_LMENU, scancode); } + if (IsVkDown(VK_RMENU) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightAlt, is_key_down, VK_RMENU, scancode); } + } + } + return 0; + } + case WM_SETFOCUS: + case WM_KILLFOCUS: + io.AddFocusEvent(msg == WM_SETFOCUS); + return 0; + case WM_INPUTLANGCHANGE: + ImGui_ImplWin32_UpdateKeyboardCodePage(); + return 0; + case WM_CHAR: + if (::IsWindowUnicode(hwnd)) + { + // You can also use ToAscii()+GetKeyboardState() to retrieve characters. + if (wParam > 0 && wParam < 0x10000) + io.AddInputCharacterUTF16((unsigned short)wParam); + } + else + { + wchar_t wch = 0; + ::MultiByteToWideChar(bd->KeyboardCodePage, MB_PRECOMPOSED, (char*)&wParam, 1, &wch, 1); + io.AddInputCharacter(wch); + } + return 0; + case WM_SETCURSOR: + // This is required to restore cursor when transitioning from e.g resize borders to client area. + if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor()) + return 1; + return 0; + case WM_DEVICECHANGE: +#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD + if ((UINT)wParam == DBT_DEVNODES_CHANGED) + bd->WantUpdateHasGamepad = true; +#endif + return 0; + } + return 0; +} + + +//-------------------------------------------------------------------------------------------------------- +// DPI-related helpers (optional) +//-------------------------------------------------------------------------------------------------------- +// - Use to enable DPI awareness without having to create an application manifest. +// - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps. +// - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc. +// but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime, +// neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies. +//--------------------------------------------------------------------------------------------------------- +// This is the scheme successfully used by GLFW (from which we borrowed some of the code) and other apps aiming to be highly portable. +// ImGui_ImplWin32_EnableDpiAwareness() is just a helper called by main.cpp, we don't call it automatically. +// If you are trying to implement your own backend for your own engine, you may ignore that noise. +//--------------------------------------------------------------------------------------------------------- + +// Perform our own check with RtlVerifyVersionInfo() instead of using functions from as they +// require a manifest to be functional for checks above 8.1. See https://github.com/ocornut/imgui/issues/4200 +static BOOL _IsWindowsVersionOrGreater(WORD major, WORD minor, WORD) +{ + typedef LONG(WINAPI* PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*, ULONG, ULONGLONG); + static PFN_RtlVerifyVersionInfo RtlVerifyVersionInfoFn = nullptr; + if (RtlVerifyVersionInfoFn == nullptr) + if (HMODULE ntdllModule = ::GetModuleHandleA("ntdll.dll")) + RtlVerifyVersionInfoFn = (PFN_RtlVerifyVersionInfo)GetProcAddress(ntdllModule, "RtlVerifyVersionInfo"); + if (RtlVerifyVersionInfoFn == nullptr) + return FALSE; + + RTL_OSVERSIONINFOEXW versionInfo = { }; + ULONGLONG conditionMask = 0; + versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); + versionInfo.dwMajorVersion = major; + versionInfo.dwMinorVersion = minor; + VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL); + return (RtlVerifyVersionInfoFn(&versionInfo, VER_MAJORVERSION | VER_MINORVERSION, conditionMask) == 0) ? TRUE : FALSE; +} + +#define _IsWindowsVistaOrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0600), LOBYTE(0x0600), 0) // _WIN32_WINNT_VISTA +#define _IsWindows8OrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0602), LOBYTE(0x0602), 0) // _WIN32_WINNT_WIN8 +#define _IsWindows8Point1OrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0603), LOBYTE(0x0603), 0) // _WIN32_WINNT_WINBLUE +#define _IsWindows10OrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0A00), LOBYTE(0x0A00), 0) // _WIN32_WINNT_WINTHRESHOLD / _WIN32_WINNT_WIN10 + +#ifndef DPI_ENUMS_DECLARED +typedef enum { PROCESS_DPI_UNAWARE = 0, PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS; +typedef enum { MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE; +#endif +#ifndef _DPI_AWARENESS_CONTEXTS_ +DECLARE_HANDLE(DPI_AWARENESS_CONTEXT); +#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE (DPI_AWARENESS_CONTEXT)-3 +#endif +#ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 +#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 (DPI_AWARENESS_CONTEXT)-4 +#endif +typedef HRESULT(WINAPI* PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); // Shcore.lib + dll, Windows 8.1+ +typedef HRESULT(WINAPI* PFN_GetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); // Shcore.lib + dll, Windows 8.1+ +typedef DPI_AWARENESS_CONTEXT(WINAPI* PFN_SetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT); // User32.lib + dll, Windows 10 v1607+ (Creators Update) + +// Helper function to enable DPI awareness without setting up a manifest +void ImGui_ImplWin32_EnableDpiAwareness() +{ + if (_IsWindows10OrGreater()) + { + static HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); // Reference counted per-process + if (PFN_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContextFn = (PFN_SetThreadDpiAwarenessContext)::GetProcAddress(user32_dll, "SetThreadDpiAwarenessContext")) + { + SetThreadDpiAwarenessContextFn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + return; + } + } + if (_IsWindows8Point1OrGreater()) + { + static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process + if (PFN_SetProcessDpiAwareness SetProcessDpiAwarenessFn = (PFN_SetProcessDpiAwareness)::GetProcAddress(shcore_dll, "SetProcessDpiAwareness")) + { + SetProcessDpiAwarenessFn(PROCESS_PER_MONITOR_DPI_AWARE); + return; + } + } +#if _WIN32_WINNT >= 0x0600 + ::SetProcessDPIAware(); +#endif +} + +#if defined(_MSC_VER) && !defined(NOGDI) +#pragma comment(lib, "gdi32") // Link with gdi32.lib for GetDeviceCaps(). MinGW will require linking with '-lgdi32' +#endif + +float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor) +{ + UINT xdpi = 96, ydpi = 96; + if (_IsWindows8Point1OrGreater()) + { + static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process + static PFN_GetDpiForMonitor GetDpiForMonitorFn = nullptr; + if (GetDpiForMonitorFn == nullptr && shcore_dll != nullptr) + GetDpiForMonitorFn = (PFN_GetDpiForMonitor)::GetProcAddress(shcore_dll, "GetDpiForMonitor"); + if (GetDpiForMonitorFn != nullptr) + { + GetDpiForMonitorFn((HMONITOR)monitor, MDT_EFFECTIVE_DPI, &xdpi, &ydpi); + IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert! + return xdpi / 96.0f; + } + } +#ifndef NOGDI + const HDC dc = ::GetDC(nullptr); + xdpi = ::GetDeviceCaps(dc, LOGPIXELSX); + ydpi = ::GetDeviceCaps(dc, LOGPIXELSY); + IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert! + ::ReleaseDC(nullptr, dc); +#endif + return xdpi / 96.0f; +} + +float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd) +{ + HMONITOR monitor = ::MonitorFromWindow((HWND)hwnd, MONITOR_DEFAULTTONEAREST); + return ImGui_ImplWin32_GetDpiScaleForMonitor(monitor); +} + +//--------------------------------------------------------------------------------------------------------- +// Transparency related helpers (optional) +//-------------------------------------------------------------------------------------------------------- + +#if defined(_MSC_VER) +#pragma comment(lib, "dwmapi") // Link with dwmapi.lib. MinGW will require linking with '-ldwmapi' +#endif + +// [experimental] +// Borrowed from GLFW's function updateFramebufferTransparency() in src/win32_window.c +// (the Dwm* functions are Vista era functions but we are borrowing logic from GLFW) +void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd) +{ + if (!_IsWindowsVistaOrGreater()) + return; + + BOOL composition; + if (FAILED(::DwmIsCompositionEnabled(&composition)) || !composition) + return; + + BOOL opaque; + DWORD color; + if (_IsWindows8OrGreater() || (SUCCEEDED(::DwmGetColorizationColor(&color, &opaque)) && !opaque)) + { + HRGN region = ::CreateRectRgn(0, 0, -1, -1); + DWM_BLURBEHIND bb = {}; + bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; + bb.hRgnBlur = region; + bb.fEnable = TRUE; + ::DwmEnableBlurBehindWindow((HWND)hwnd, &bb); + ::DeleteObject(region); + } + else + { + DWM_BLURBEHIND bb = {}; + bb.dwFlags = DWM_BB_ENABLE; + ::DwmEnableBlurBehindWindow((HWND)hwnd, &bb); + } +} + +//--------------------------------------------------------------------------------------------------------- + +#endif // #ifndef IMGUI_DISABLE diff --git a/imgui/backends/imgui_impl_win32.h b/imgui/backends/imgui_impl_win32.h new file mode 100644 index 0000000..be56271 --- /dev/null +++ b/imgui/backends/imgui_impl_win32.h @@ -0,0 +1,52 @@ +// dear imgui: Platform Backend for Windows (standard windows API for 32-bits AND 64-bits applications) +// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) + +// Implemented features: +// [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui) +// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen. +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] +// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +#pragma once +#include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE + +IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd); +IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd); +IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); + +// Win32 message handler your application need to call. +// - Intentionally commented out in a '#if 0' block to avoid dragging dependencies on from this helper. +// - You should COPY the line below into your .cpp code to forward declare the function and then you can call it. +// - Call from your application's message handler. Keep calling your message handler unless this function returns TRUE. + +#if 0 +extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +#endif + +// DPI-related helpers (optional) +// - Use to enable DPI awareness without having to create an application manifest. +// - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps. +// - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc. +// but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime, +// neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies. +IMGUI_IMPL_API void ImGui_ImplWin32_EnableDpiAwareness(); +IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd); // HWND hwnd +IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // HMONITOR monitor + +// Transparency related helpers (optional) [experimental] +// - Use to enable alpha compositing transparency with the desktop. +// - Use together with e.g. clearing your framebuffer with zero-alpha. +IMGUI_IMPL_API void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd); // HWND hwnd + +#endif // #ifndef IMGUI_DISABLE diff --git a/imgui/imgui_impl_win32.cpp b/imgui/imgui_impl_win32.cpp deleted file mode 100644 index 73ed700..0000000 --- a/imgui/imgui_impl_win32.cpp +++ /dev/null @@ -1,903 +0,0 @@ -// dear imgui: Platform Backend for Windows (standard windows API for 32-bits AND 64-bits applications) -// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) - -// Implemented features: -// [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui) -// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen. -// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] -// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. - -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. -// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. -// Learn about Dear ImGui: -// - FAQ https://dearimgui.com/faq -// - Getting Started https://dearimgui.com/getting-started -// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). -// - Introduction, links and more at the top of imgui.cpp - -#include "imgui.h" -#ifndef IMGUI_DISABLE -#include "imgui_impl_win32.h" -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -#include // GET_X_LPARAM(), GET_Y_LPARAM() -#include -#include - -// Configuration flags to add in your imconfig.h file: -//#define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD // Disable gamepad support. This was meaningful before <1.81 but we now load XInput dynamically so the option is now less relevant. - -// Using XInput for gamepad (will load DLL dynamically) -#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD -#include -typedef DWORD (WINAPI *PFN_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*); -typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); -#endif - -// CHANGELOG -// (minor and older changes stripped away, please see git history for details) -// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys. -// 2023-09-25: Inputs: Synthesize key-down event on key-up for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit it (same behavior as GLFW/SDL). -// 2023-09-07: Inputs: Added support for keyboard codepage conversion for when application is compiled in MBCS mode and using a non-Unicode window. -// 2023-04-19: Added ImGui_ImplWin32_InitForOpenGL() to facilitate combining raw Win32/Winapi with OpenGL. (#3218) -// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen. (#2702) -// 2023-02-15: Inputs: Use WM_NCMOUSEMOVE / WM_NCMOUSELEAVE to track mouse position over non-client area (e.g. OS decorations) when app is not focused. (#6045, #6162) -// 2023-02-02: Inputs: Flipping WM_MOUSEHWHEEL (horizontal mouse-wheel) value to match other backends and offer consistent horizontal scrolling direction. (#4019, #6096, #1463) -// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. -// 2022-09-28: Inputs: Convert WM_CHAR values with MultiByteToWideChar() when window class was registered as MBCS (not Unicode). -// 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported). -// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. -// 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. -// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). -// 2022-01-17: Inputs: always update key mods next and before a key event (not in NewFrame) to fix input queue with very low framerates. -// 2022-01-12: Inputs: Update mouse inputs using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback to provide it when focused but not hovered/captured. More standard and will allow us to pass it to future input queue API. -// 2022-01-12: Inputs: Maintain our own copy of MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. -// 2021-12-16: Inputs: Fill VK_LCONTROL/VK_RCONTROL/VK_LSHIFT/VK_RSHIFT/VK_LMENU/VK_RMENU for completeness. -// 2021-08-17: Calling io.AddFocusEvent() on WM_SETFOCUS/WM_KILLFOCUS messages. -// 2021-08-02: Inputs: Fixed keyboard modifiers being reported when host window doesn't have focus. -// 2021-07-29: Inputs: MousePos is correctly reported when the host platform window is hovered but not focused (using TrackMouseEvent() to receive WM_MOUSELEAVE events). -// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). -// 2021-06-08: Fixed ImGui_ImplWin32_EnableDpiAwareness() and ImGui_ImplWin32_GetDpiScaleForMonitor() to handle Windows 8.1/10 features without a manifest (per-monitor DPI, and properly calls SetProcessDpiAwareness() on 8.1). -// 2021-03-23: Inputs: Clearing keyboard down array when losing focus (WM_KILLFOCUS). -// 2021-02-18: Added ImGui_ImplWin32_EnableAlphaCompositing(). Non Visual Studio users will need to link with dwmapi.lib (MinGW/gcc: use -ldwmapi). -// 2021-02-17: Fixed ImGui_ImplWin32_EnableDpiAwareness() attempting to get SetProcessDpiAwareness from shcore.dll on Windows 8 whereas it is only supported on Windows 8.1. -// 2021-01-25: Inputs: Dynamically loading XInput DLL. -// 2020-12-04: Misc: Fixed setting of io.DisplaySize to invalid/uninitialized data when after hwnd has been closed. -// 2020-03-03: Inputs: Calling AddInputCharacterUTF16() to support surrogate pairs leading to codepoint >= 0x10000 (for more complete CJK inputs) -// 2020-02-17: Added ImGui_ImplWin32_EnableDpiAwareness(), ImGui_ImplWin32_GetDpiScaleForHwnd(), ImGui_ImplWin32_GetDpiScaleForMonitor() helper functions. -// 2020-01-14: Inputs: Added support for #define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD/IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT. -// 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor. -// 2019-05-11: Inputs: Don't filter value from WM_CHAR before calling AddInputCharacter(). -// 2019-01-17: Misc: Using GetForegroundWindow()+IsChild() instead of GetActiveWindow() to be compatible with windows created in a different thread or parent. -// 2019-01-17: Inputs: Added support for mouse buttons 4 and 5 via WM_XBUTTON* messages. -// 2019-01-15: Inputs: Added support for XInput gamepads (if ImGuiConfigFlags_NavEnableGamepad is set by user application). -// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. -// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. -// 2018-06-10: Inputs: Fixed handling of mouse wheel messages to support fine position messages (typically sent by track-pads). -// 2018-06-08: Misc: Extracted imgui_impl_win32.cpp/.h away from the old combined DX9/DX10/DX11/DX12 examples. -// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors and ImGuiBackendFlags_HasSetMousePos flags + honor ImGuiConfigFlags_NoMouseCursorChange flag. -// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling). -// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. -// 2018-02-06: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). -// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. -// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. -// 2018-01-08: Inputs: Added mapping for ImGuiKey_Insert. -// 2018-01-05: Inputs: Added WM_LBUTTONDBLCLK double-click handlers for window classes with the CS_DBLCLKS flag. -// 2017-10-23: Inputs: Added WM_SYSKEYDOWN / WM_SYSKEYUP handlers so e.g. the VK_MENU key can be read. -// 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging. -// 2016-11-12: Inputs: Only call Win32 ::SetCursor(nullptr) when io.MouseDrawCursor is set. - -struct ImGui_ImplWin32_Data -{ - HWND hWnd; - HWND MouseHwnd; - int MouseTrackedArea; // 0: not tracked, 1: client are, 2: non-client area - int MouseButtonsDown; - INT64 Time; - INT64 TicksPerSecond; - ImGuiMouseCursor LastMouseCursor; - UINT32 KeyboardCodePage; - -#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD - bool HasGamepad; - bool WantUpdateHasGamepad; - HMODULE XInputDLL; - PFN_XInputGetCapabilities XInputGetCapabilities; - PFN_XInputGetState XInputGetState; -#endif - - ImGui_ImplWin32_Data() { memset((void*)this, 0, sizeof(*this)); } -}; - -// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts -// 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_ImplWin32_Data* ImGui_ImplWin32_GetBackendData() -{ - return ImGui::GetCurrentContext() ? (ImGui_ImplWin32_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr; -} - -// Functions -static void ImGui_ImplWin32_UpdateKeyboardCodePage() -{ - // Retrieve keyboard code page, required for handling of non-Unicode Windows. - ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); - HKL keyboard_layout = ::GetKeyboardLayout(0); - LCID keyboard_lcid = MAKELCID(HIWORD(keyboard_layout), SORT_DEFAULT); - if (::GetLocaleInfoA(keyboard_lcid, (LOCALE_RETURN_NUMBER | LOCALE_IDEFAULTANSICODEPAGE), (LPSTR)&bd->KeyboardCodePage, sizeof(bd->KeyboardCodePage)) == 0) - bd->KeyboardCodePage = CP_ACP; // Fallback to default ANSI code page when fails. -} - -static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc) -{ - ImGuiIO& io = ImGui::GetIO(); - IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!"); - - INT64 perf_frequency, perf_counter; - if (!::QueryPerformanceFrequency((LARGE_INTEGER*)&perf_frequency)) - return false; - if (!::QueryPerformanceCounter((LARGE_INTEGER*)&perf_counter)) - return false; - - // Setup backend capabilities flags - ImGui_ImplWin32_Data* bd = IM_NEW(ImGui_ImplWin32_Data)(); - io.BackendPlatformUserData = (void*)bd; - io.BackendPlatformName = "imgui_impl_win32"; - io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) - io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) - - bd->hWnd = (HWND)hwnd; - bd->TicksPerSecond = perf_frequency; - bd->Time = perf_counter; - bd->LastMouseCursor = ImGuiMouseCursor_COUNT; - ImGui_ImplWin32_UpdateKeyboardCodePage(); - - // Set platform dependent data in viewport - ImGui::GetMainViewport()->PlatformHandleRaw = (void*)hwnd; - IM_UNUSED(platform_has_own_dc); // Used in 'docking' branch - - // Dynamically load XInput library -#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD - bd->WantUpdateHasGamepad = true; - const char* xinput_dll_names[] = - { - "xinput1_4.dll", // Windows 8+ - "xinput1_3.dll", // DirectX SDK - "xinput9_1_0.dll", // Windows Vista, Windows 7 - "xinput1_2.dll", // DirectX SDK - "xinput1_1.dll" // DirectX SDK - }; - for (int n = 0; n < IM_ARRAYSIZE(xinput_dll_names); n++) - if (HMODULE dll = ::LoadLibraryA(xinput_dll_names[n])) - { - bd->XInputDLL = dll; - bd->XInputGetCapabilities = (PFN_XInputGetCapabilities)::GetProcAddress(dll, "XInputGetCapabilities"); - bd->XInputGetState = (PFN_XInputGetState)::GetProcAddress(dll, "XInputGetState"); - break; - } -#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD - - return true; -} - -IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd) -{ - return ImGui_ImplWin32_InitEx(hwnd, false); -} - -IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd) -{ - // OpenGL needs CS_OWNDC - return ImGui_ImplWin32_InitEx(hwnd, true); -} - -void ImGui_ImplWin32_Shutdown() -{ - ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); - IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); - ImGuiIO& io = ImGui::GetIO(); - - // Unload XInput library -#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD - if (bd->XInputDLL) - ::FreeLibrary(bd->XInputDLL); -#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD - - io.BackendPlatformName = nullptr; - io.BackendPlatformUserData = nullptr; - io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad); - IM_DELETE(bd); -} - -static bool ImGui_ImplWin32_UpdateMouseCursor() -{ - ImGuiIO& io = ImGui::GetIO(); - if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) - return false; - - ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); - if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) - { - // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor - ::SetCursor(nullptr); - } - else - { - // Show OS mouse cursor - LPTSTR win32_cursor = IDC_ARROW; - switch (imgui_cursor) - { - case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break; - case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break; - case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break; - case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break; - case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break; - case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break; - case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break; - case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break; - case ImGuiMouseCursor_NotAllowed: win32_cursor = IDC_NO; break; - } - ::SetCursor(::LoadCursor(nullptr, win32_cursor)); - } - return true; -} - -static bool IsVkDown(int vk) -{ - return (::GetKeyState(vk) & 0x8000) != 0; -} - -static void ImGui_ImplWin32_AddKeyEvent(ImGuiKey key, bool down, int native_keycode, int native_scancode = -1) -{ - ImGuiIO& io = ImGui::GetIO(); - io.AddKeyEvent(key, down); - io.SetKeyEventNativeData(key, native_keycode, native_scancode); // To support legacy indexing (<1.87 user code) - IM_UNUSED(native_scancode); -} - -static void ImGui_ImplWin32_ProcessKeyEventsWorkarounds() -{ - // Left & right Shift keys: when both are pressed together, Windows tend to not generate the WM_KEYUP event for the first released one. - if (ImGui::IsKeyDown(ImGuiKey_LeftShift) && !IsVkDown(VK_LSHIFT)) - ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftShift, false, VK_LSHIFT); - if (ImGui::IsKeyDown(ImGuiKey_RightShift) && !IsVkDown(VK_RSHIFT)) - ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightShift, false, VK_RSHIFT); - - // Sometimes WM_KEYUP for Win key is not passed down to the app (e.g. for Win+V on some setups, according to GLFW). - if (ImGui::IsKeyDown(ImGuiKey_LeftSuper) && !IsVkDown(VK_LWIN)) - ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftSuper, false, VK_LWIN); - if (ImGui::IsKeyDown(ImGuiKey_RightSuper) && !IsVkDown(VK_RWIN)) - ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightSuper, false, VK_RWIN); -} - -static void ImGui_ImplWin32_UpdateKeyModifiers() -{ - ImGuiIO& io = ImGui::GetIO(); - io.AddKeyEvent(ImGuiMod_Ctrl, IsVkDown(VK_CONTROL)); - io.AddKeyEvent(ImGuiMod_Shift, IsVkDown(VK_SHIFT)); - io.AddKeyEvent(ImGuiMod_Alt, IsVkDown(VK_MENU)); - io.AddKeyEvent(ImGuiMod_Super, IsVkDown(VK_APPS)); -} - -static void ImGui_ImplWin32_UpdateMouseData() -{ - ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); - ImGuiIO& io = ImGui::GetIO(); - IM_ASSERT(bd->hWnd != 0); - - HWND focused_window = ::GetForegroundWindow(); - const bool is_app_focused = (focused_window == bd->hWnd); - if (is_app_focused) - { - // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) - if (io.WantSetMousePos) - { - POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; - if (::ClientToScreen(bd->hWnd, &pos)) - ::SetCursorPos(pos.x, pos.y); - } - - // (Optional) Fallback to provide mouse position when focused (WM_MOUSEMOVE already provides this when hovered or captured) - // This also fills a short gap when clicking non-client area: WM_NCMOUSELEAVE -> modal OS move -> gap -> WM_NCMOUSEMOVE - if (!io.WantSetMousePos && bd->MouseTrackedArea == 0) - { - POINT pos; - if (::GetCursorPos(&pos) && ::ScreenToClient(bd->hWnd, &pos)) - io.AddMousePosEvent((float)pos.x, (float)pos.y); - } - } -} - -// Gamepad navigation mapping -static void ImGui_ImplWin32_UpdateGamepads() -{ -#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD - ImGuiIO& io = ImGui::GetIO(); - ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); - //if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs. - // return; - - // Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow. - // Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE. - if (bd->WantUpdateHasGamepad) - { - XINPUT_CAPABILITIES caps = {}; - bd->HasGamepad = bd->XInputGetCapabilities ? (bd->XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) : false; - bd->WantUpdateHasGamepad = false; - } - - io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; - XINPUT_STATE xinput_state; - XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad; - if (!bd->HasGamepad || bd->XInputGetState == nullptr || bd->XInputGetState(0, &xinput_state) != ERROR_SUCCESS) - return; - io.BackendFlags |= ImGuiBackendFlags_HasGamepad; - - #define IM_SATURATE(V) (V < 0.0f ? 0.0f : V > 1.0f ? 1.0f : V) - #define MAP_BUTTON(KEY_NO, BUTTON_ENUM) { io.AddKeyEvent(KEY_NO, (gamepad.wButtons & BUTTON_ENUM) != 0); } - #define MAP_ANALOG(KEY_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); io.AddKeyAnalogEvent(KEY_NO, vn > 0.10f, IM_SATURATE(vn)); } - MAP_BUTTON(ImGuiKey_GamepadStart, XINPUT_GAMEPAD_START); - MAP_BUTTON(ImGuiKey_GamepadBack, XINPUT_GAMEPAD_BACK); - MAP_BUTTON(ImGuiKey_GamepadFaceLeft, XINPUT_GAMEPAD_X); - MAP_BUTTON(ImGuiKey_GamepadFaceRight, XINPUT_GAMEPAD_B); - MAP_BUTTON(ImGuiKey_GamepadFaceUp, XINPUT_GAMEPAD_Y); - MAP_BUTTON(ImGuiKey_GamepadFaceDown, XINPUT_GAMEPAD_A); - MAP_BUTTON(ImGuiKey_GamepadDpadLeft, XINPUT_GAMEPAD_DPAD_LEFT); - MAP_BUTTON(ImGuiKey_GamepadDpadRight, XINPUT_GAMEPAD_DPAD_RIGHT); - MAP_BUTTON(ImGuiKey_GamepadDpadUp, XINPUT_GAMEPAD_DPAD_UP); - MAP_BUTTON(ImGuiKey_GamepadDpadDown, XINPUT_GAMEPAD_DPAD_DOWN); - MAP_BUTTON(ImGuiKey_GamepadL1, XINPUT_GAMEPAD_LEFT_SHOULDER); - MAP_BUTTON(ImGuiKey_GamepadR1, XINPUT_GAMEPAD_RIGHT_SHOULDER); - MAP_ANALOG(ImGuiKey_GamepadL2, gamepad.bLeftTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, 255); - MAP_ANALOG(ImGuiKey_GamepadR2, gamepad.bRightTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, 255); - MAP_BUTTON(ImGuiKey_GamepadL3, XINPUT_GAMEPAD_LEFT_THUMB); - MAP_BUTTON(ImGuiKey_GamepadR3, XINPUT_GAMEPAD_RIGHT_THUMB); - MAP_ANALOG(ImGuiKey_GamepadLStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768); - MAP_ANALOG(ImGuiKey_GamepadLStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); - MAP_ANALOG(ImGuiKey_GamepadLStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); - MAP_ANALOG(ImGuiKey_GamepadLStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768); - MAP_ANALOG(ImGuiKey_GamepadRStickLeft, gamepad.sThumbRX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768); - MAP_ANALOG(ImGuiKey_GamepadRStickRight, gamepad.sThumbRX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); - MAP_ANALOG(ImGuiKey_GamepadRStickUp, gamepad.sThumbRY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); - MAP_ANALOG(ImGuiKey_GamepadRStickDown, gamepad.sThumbRY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768); - #undef MAP_BUTTON - #undef MAP_ANALOG -#endif // #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD -} - -void ImGui_ImplWin32_NewFrame() -{ - ImGuiIO& io = ImGui::GetIO(); - ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); - IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplWin32_Init()?"); - - // Setup display size (every frame to accommodate for window resizing) - RECT rect = { 0, 0, 0, 0 }; - ::GetClientRect(bd->hWnd, &rect); - io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); - - // Setup time step - INT64 current_time = 0; - ::QueryPerformanceCounter((LARGE_INTEGER*)¤t_time); - io.DeltaTime = (float)(current_time - bd->Time) / bd->TicksPerSecond; - bd->Time = current_time; - - // Update OS mouse position - ImGui_ImplWin32_UpdateMouseData(); - - // Process workarounds for known Windows key handling issues - ImGui_ImplWin32_ProcessKeyEventsWorkarounds(); - - // Update OS mouse cursor with the cursor requested by imgui - ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor(); - if (bd->LastMouseCursor != mouse_cursor) - { - bd->LastMouseCursor = mouse_cursor; - ImGui_ImplWin32_UpdateMouseCursor(); - } - - // Update game controllers (if enabled and available) - ImGui_ImplWin32_UpdateGamepads(); -} - -// There is no distinct VK_xxx for keypad enter, instead it is VK_RETURN + KF_EXTENDED, we assign it an arbitrary value to make code more readable (VK_ codes go up to 255) -#define IM_VK_KEYPAD_ENTER (VK_RETURN + 256) - -// Map VK_xxx to ImGuiKey_xxx. -static ImGuiKey ImGui_ImplWin32_VirtualKeyToImGuiKey(WPARAM wParam) -{ - switch (wParam) - { - case VK_TAB: return ImGuiKey_Tab; - case VK_LEFT: return ImGuiKey_LeftArrow; - case VK_RIGHT: return ImGuiKey_RightArrow; - case VK_UP: return ImGuiKey_UpArrow; - case VK_DOWN: return ImGuiKey_DownArrow; - case VK_PRIOR: return ImGuiKey_PageUp; - case VK_NEXT: return ImGuiKey_PageDown; - case VK_HOME: return ImGuiKey_Home; - case VK_END: return ImGuiKey_End; - case VK_INSERT: return ImGuiKey_Insert; - case VK_DELETE: return ImGuiKey_Delete; - case VK_BACK: return ImGuiKey_Backspace; - case VK_SPACE: return ImGuiKey_Space; - case VK_RETURN: return ImGuiKey_Enter; - case VK_ESCAPE: return ImGuiKey_Escape; - case VK_OEM_7: return ImGuiKey_Apostrophe; - case VK_OEM_COMMA: return ImGuiKey_Comma; - case VK_OEM_MINUS: return ImGuiKey_Minus; - case VK_OEM_PERIOD: return ImGuiKey_Period; - case VK_OEM_2: return ImGuiKey_Slash; - case VK_OEM_1: return ImGuiKey_Semicolon; - case VK_OEM_PLUS: return ImGuiKey_Equal; - case VK_OEM_4: return ImGuiKey_LeftBracket; - case VK_OEM_5: return ImGuiKey_Backslash; - case VK_OEM_6: return ImGuiKey_RightBracket; - case VK_OEM_3: return ImGuiKey_GraveAccent; - case VK_CAPITAL: return ImGuiKey_CapsLock; - case VK_SCROLL: return ImGuiKey_ScrollLock; - case VK_NUMLOCK: return ImGuiKey_NumLock; - case VK_SNAPSHOT: return ImGuiKey_PrintScreen; - case VK_PAUSE: return ImGuiKey_Pause; - case VK_NUMPAD0: return ImGuiKey_Keypad0; - case VK_NUMPAD1: return ImGuiKey_Keypad1; - case VK_NUMPAD2: return ImGuiKey_Keypad2; - case VK_NUMPAD3: return ImGuiKey_Keypad3; - case VK_NUMPAD4: return ImGuiKey_Keypad4; - case VK_NUMPAD5: return ImGuiKey_Keypad5; - case VK_NUMPAD6: return ImGuiKey_Keypad6; - case VK_NUMPAD7: return ImGuiKey_Keypad7; - case VK_NUMPAD8: return ImGuiKey_Keypad8; - case VK_NUMPAD9: return ImGuiKey_Keypad9; - case VK_DECIMAL: return ImGuiKey_KeypadDecimal; - case VK_DIVIDE: return ImGuiKey_KeypadDivide; - case VK_MULTIPLY: return ImGuiKey_KeypadMultiply; - case VK_SUBTRACT: return ImGuiKey_KeypadSubtract; - case VK_ADD: return ImGuiKey_KeypadAdd; - case IM_VK_KEYPAD_ENTER: return ImGuiKey_KeypadEnter; - case VK_LSHIFT: return ImGuiKey_LeftShift; - case VK_LCONTROL: return ImGuiKey_LeftCtrl; - case VK_LMENU: return ImGuiKey_LeftAlt; - case VK_LWIN: return ImGuiKey_LeftSuper; - case VK_RSHIFT: return ImGuiKey_RightShift; - case VK_RCONTROL: return ImGuiKey_RightCtrl; - case VK_RMENU: return ImGuiKey_RightAlt; - case VK_RWIN: return ImGuiKey_RightSuper; - case VK_APPS: return ImGuiKey_Menu; - case '0': return ImGuiKey_0; - case '1': return ImGuiKey_1; - case '2': return ImGuiKey_2; - case '3': return ImGuiKey_3; - case '4': return ImGuiKey_4; - case '5': return ImGuiKey_5; - case '6': return ImGuiKey_6; - case '7': return ImGuiKey_7; - case '8': return ImGuiKey_8; - case '9': return ImGuiKey_9; - case 'A': return ImGuiKey_A; - case 'B': return ImGuiKey_B; - case 'C': return ImGuiKey_C; - case 'D': return ImGuiKey_D; - case 'E': return ImGuiKey_E; - case 'F': return ImGuiKey_F; - case 'G': return ImGuiKey_G; - case 'H': return ImGuiKey_H; - case 'I': return ImGuiKey_I; - case 'J': return ImGuiKey_J; - case 'K': return ImGuiKey_K; - case 'L': return ImGuiKey_L; - case 'M': return ImGuiKey_M; - case 'N': return ImGuiKey_N; - case 'O': return ImGuiKey_O; - case 'P': return ImGuiKey_P; - case 'Q': return ImGuiKey_Q; - case 'R': return ImGuiKey_R; - case 'S': return ImGuiKey_S; - case 'T': return ImGuiKey_T; - case 'U': return ImGuiKey_U; - case 'V': return ImGuiKey_V; - case 'W': return ImGuiKey_W; - case 'X': return ImGuiKey_X; - case 'Y': return ImGuiKey_Y; - case 'Z': return ImGuiKey_Z; - case VK_F1: return ImGuiKey_F1; - case VK_F2: return ImGuiKey_F2; - case VK_F3: return ImGuiKey_F3; - case VK_F4: return ImGuiKey_F4; - case VK_F5: return ImGuiKey_F5; - case VK_F6: return ImGuiKey_F6; - case VK_F7: return ImGuiKey_F7; - case VK_F8: return ImGuiKey_F8; - case VK_F9: return ImGuiKey_F9; - case VK_F10: return ImGuiKey_F10; - case VK_F11: return ImGuiKey_F11; - case VK_F12: return ImGuiKey_F12; - case VK_F13: return ImGuiKey_F13; - case VK_F14: return ImGuiKey_F14; - case VK_F15: return ImGuiKey_F15; - case VK_F16: return ImGuiKey_F16; - case VK_F17: return ImGuiKey_F17; - case VK_F18: return ImGuiKey_F18; - case VK_F19: return ImGuiKey_F19; - case VK_F20: return ImGuiKey_F20; - case VK_F21: return ImGuiKey_F21; - case VK_F22: return ImGuiKey_F22; - case VK_F23: return ImGuiKey_F23; - case VK_F24: return ImGuiKey_F24; - case VK_BROWSER_BACK: return ImGuiKey_AppBack; - case VK_BROWSER_FORWARD: return ImGuiKey_AppForward; - default: return ImGuiKey_None; - } -} - -// Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions. -#ifndef WM_MOUSEHWHEEL -#define WM_MOUSEHWHEEL 0x020E -#endif -#ifndef DBT_DEVNODES_CHANGED -#define DBT_DEVNODES_CHANGED 0x0007 -#endif - -// Win32 message handler (process Win32 mouse/keyboard inputs, etc.) -// Call from your application's message handler. Keep calling your message handler unless this function returns TRUE. -// When implementing your own backend, 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. -// PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinates when dragging mouse outside of our window bounds. -// PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag. -#if 0 -// Copy this line into your .cpp file to forward declare the function. -extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); -#endif - -// See https://learn.microsoft.com/en-us/windows/win32/tablet/system-events-and-mouse-messages -// Prefer to call this at the top of the message handler to avoid the possibility of other Win32 calls interfering with this. -static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo() -{ - LPARAM extra_info = ::GetMessageExtraInfo(); - if ((extra_info & 0xFFFFFF80) == 0xFF515700) - return ImGuiMouseSource_Pen; - if ((extra_info & 0xFFFFFF80) == 0xFF515780) - return ImGuiMouseSource_TouchScreen; - return ImGuiMouseSource_Mouse; -} - -IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - if (ImGui::GetCurrentContext() == nullptr) - return 0; - - ImGuiIO& io = ImGui::GetIO(); - ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); - - switch (msg) - { - case WM_MOUSEMOVE: - case WM_NCMOUSEMOVE: - { - // We need to call TrackMouseEvent in order to receive WM_MOUSELEAVE events - ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo(); - const int area = (msg == WM_MOUSEMOVE) ? 1 : 2; - bd->MouseHwnd = hwnd; - if (bd->MouseTrackedArea != area) - { - TRACKMOUSEEVENT tme_cancel = { sizeof(tme_cancel), TME_CANCEL, hwnd, 0 }; - TRACKMOUSEEVENT tme_track = { sizeof(tme_track), (DWORD)((area == 2) ? (TME_LEAVE | TME_NONCLIENT) : TME_LEAVE), hwnd, 0 }; - if (bd->MouseTrackedArea != 0) - ::TrackMouseEvent(&tme_cancel); - ::TrackMouseEvent(&tme_track); - bd->MouseTrackedArea = area; - } - POINT mouse_pos = { (LONG)GET_X_LPARAM(lParam), (LONG)GET_Y_LPARAM(lParam) }; - if (msg == WM_NCMOUSEMOVE && ::ScreenToClient(hwnd, &mouse_pos) == FALSE) // WM_NCMOUSEMOVE are provided in absolute coordinates. - break; - io.AddMouseSourceEvent(mouse_source); - io.AddMousePosEvent((float)mouse_pos.x, (float)mouse_pos.y); - break; - } - case WM_MOUSELEAVE: - case WM_NCMOUSELEAVE: - { - const int area = (msg == WM_MOUSELEAVE) ? 1 : 2; - if (bd->MouseTrackedArea == area) - { - if (bd->MouseHwnd == hwnd) - bd->MouseHwnd = nullptr; - bd->MouseTrackedArea = 0; - io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); - } - break; - } - case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: - case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: - case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: - case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: - { - ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo(); - int button = 0; - if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; } - if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; } - if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) { button = 2; } - if (msg == WM_XBUTTONDOWN || msg == WM_XBUTTONDBLCLK) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; } - if (bd->MouseButtonsDown == 0 && ::GetCapture() == nullptr) - ::SetCapture(hwnd); - bd->MouseButtonsDown |= 1 << button; - io.AddMouseSourceEvent(mouse_source); - io.AddMouseButtonEvent(button, true); - return 0; - } - case WM_LBUTTONUP: - case WM_RBUTTONUP: - case WM_MBUTTONUP: - case WM_XBUTTONUP: - { - ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo(); - int button = 0; - if (msg == WM_LBUTTONUP) { button = 0; } - if (msg == WM_RBUTTONUP) { button = 1; } - if (msg == WM_MBUTTONUP) { button = 2; } - if (msg == WM_XBUTTONUP) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; } - bd->MouseButtonsDown &= ~(1 << button); - if (bd->MouseButtonsDown == 0 && ::GetCapture() == hwnd) - ::ReleaseCapture(); - io.AddMouseSourceEvent(mouse_source); - io.AddMouseButtonEvent(button, false); - return 0; - } - case WM_MOUSEWHEEL: - io.AddMouseWheelEvent(0.0f, (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA); - return 0; - case WM_MOUSEHWHEEL: - io.AddMouseWheelEvent(-(float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0.0f); - return 0; - case WM_KEYDOWN: - case WM_KEYUP: - case WM_SYSKEYDOWN: - case WM_SYSKEYUP: - { - const bool is_key_down = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN); - if (wParam < 256) - { - // Submit modifiers - ImGui_ImplWin32_UpdateKeyModifiers(); - - // Obtain virtual key code - // (keypad enter doesn't have its own... VK_RETURN with KF_EXTENDED flag means keypad enter, see IM_VK_KEYPAD_ENTER definition for details, it is mapped to ImGuiKey_KeyPadEnter.) - int vk = (int)wParam; - if ((wParam == VK_RETURN) && (HIWORD(lParam) & KF_EXTENDED)) - vk = IM_VK_KEYPAD_ENTER; - const ImGuiKey key = ImGui_ImplWin32_VirtualKeyToImGuiKey(vk); - const int scancode = (int)LOBYTE(HIWORD(lParam)); - - // Special behavior for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit the key down event. - if (key == ImGuiKey_PrintScreen && !is_key_down) - ImGui_ImplWin32_AddKeyEvent(key, true, vk, scancode); - - // Submit key event - if (key != ImGuiKey_None) - ImGui_ImplWin32_AddKeyEvent(key, is_key_down, vk, scancode); - - // Submit individual left/right modifier events - if (vk == VK_SHIFT) - { - // Important: Shift keys tend to get stuck when pressed together, missing key-up events are corrected in ImGui_ImplWin32_ProcessKeyEventsWorkarounds() - if (IsVkDown(VK_LSHIFT) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftShift, is_key_down, VK_LSHIFT, scancode); } - if (IsVkDown(VK_RSHIFT) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightShift, is_key_down, VK_RSHIFT, scancode); } - } - else if (vk == VK_CONTROL) - { - if (IsVkDown(VK_LCONTROL) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftCtrl, is_key_down, VK_LCONTROL, scancode); } - if (IsVkDown(VK_RCONTROL) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightCtrl, is_key_down, VK_RCONTROL, scancode); } - } - else if (vk == VK_MENU) - { - if (IsVkDown(VK_LMENU) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftAlt, is_key_down, VK_LMENU, scancode); } - if (IsVkDown(VK_RMENU) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightAlt, is_key_down, VK_RMENU, scancode); } - } - } - return 0; - } - case WM_SETFOCUS: - case WM_KILLFOCUS: - io.AddFocusEvent(msg == WM_SETFOCUS); - return 0; - case WM_INPUTLANGCHANGE: - ImGui_ImplWin32_UpdateKeyboardCodePage(); - return 0; - case WM_CHAR: - if (::IsWindowUnicode(hwnd)) - { - // You can also use ToAscii()+GetKeyboardState() to retrieve characters. - if (wParam > 0 && wParam < 0x10000) - io.AddInputCharacterUTF16((unsigned short)wParam); - } - else - { - wchar_t wch = 0; - ::MultiByteToWideChar(bd->KeyboardCodePage, MB_PRECOMPOSED, (char*)&wParam, 1, &wch, 1); - io.AddInputCharacter(wch); - } - return 0; - case WM_SETCURSOR: - // This is required to restore cursor when transitioning from e.g resize borders to client area. - if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor()) - return 1; - return 0; - case WM_DEVICECHANGE: -#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD - if ((UINT)wParam == DBT_DEVNODES_CHANGED) - bd->WantUpdateHasGamepad = true; -#endif - return 0; - } - return 0; -} - - -//-------------------------------------------------------------------------------------------------------- -// DPI-related helpers (optional) -//-------------------------------------------------------------------------------------------------------- -// - Use to enable DPI awareness without having to create an application manifest. -// - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps. -// - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc. -// but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime, -// neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies. -//--------------------------------------------------------------------------------------------------------- -// This is the scheme successfully used by GLFW (from which we borrowed some of the code) and other apps aiming to be highly portable. -// ImGui_ImplWin32_EnableDpiAwareness() is just a helper called by main.cpp, we don't call it automatically. -// If you are trying to implement your own backend for your own engine, you may ignore that noise. -//--------------------------------------------------------------------------------------------------------- - -// Perform our own check with RtlVerifyVersionInfo() instead of using functions from as they -// require a manifest to be functional for checks above 8.1. See https://github.com/ocornut/imgui/issues/4200 -static BOOL _IsWindowsVersionOrGreater(WORD major, WORD minor, WORD) -{ - typedef LONG(WINAPI* PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*, ULONG, ULONGLONG); - static PFN_RtlVerifyVersionInfo RtlVerifyVersionInfoFn = nullptr; - if (RtlVerifyVersionInfoFn == nullptr) - if (HMODULE ntdllModule = ::GetModuleHandleA("ntdll.dll")) - RtlVerifyVersionInfoFn = (PFN_RtlVerifyVersionInfo)GetProcAddress(ntdllModule, "RtlVerifyVersionInfo"); - if (RtlVerifyVersionInfoFn == nullptr) - return FALSE; - - RTL_OSVERSIONINFOEXW versionInfo = { }; - ULONGLONG conditionMask = 0; - versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); - versionInfo.dwMajorVersion = major; - versionInfo.dwMinorVersion = minor; - VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); - VER_SET_CONDITION(conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL); - return (RtlVerifyVersionInfoFn(&versionInfo, VER_MAJORVERSION | VER_MINORVERSION, conditionMask) == 0) ? TRUE : FALSE; -} - -#define _IsWindowsVistaOrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0600), LOBYTE(0x0600), 0) // _WIN32_WINNT_VISTA -#define _IsWindows8OrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0602), LOBYTE(0x0602), 0) // _WIN32_WINNT_WIN8 -#define _IsWindows8Point1OrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0603), LOBYTE(0x0603), 0) // _WIN32_WINNT_WINBLUE -#define _IsWindows10OrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0A00), LOBYTE(0x0A00), 0) // _WIN32_WINNT_WINTHRESHOLD / _WIN32_WINNT_WIN10 - -#ifndef DPI_ENUMS_DECLARED -typedef enum { PROCESS_DPI_UNAWARE = 0, PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS; -typedef enum { MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE; -#endif -#ifndef _DPI_AWARENESS_CONTEXTS_ -DECLARE_HANDLE(DPI_AWARENESS_CONTEXT); -#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE (DPI_AWARENESS_CONTEXT)-3 -#endif -#ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 -#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 (DPI_AWARENESS_CONTEXT)-4 -#endif -typedef HRESULT(WINAPI* PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); // Shcore.lib + dll, Windows 8.1+ -typedef HRESULT(WINAPI* PFN_GetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); // Shcore.lib + dll, Windows 8.1+ -typedef DPI_AWARENESS_CONTEXT(WINAPI* PFN_SetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT); // User32.lib + dll, Windows 10 v1607+ (Creators Update) - -// Helper function to enable DPI awareness without setting up a manifest -void ImGui_ImplWin32_EnableDpiAwareness() -{ - if (_IsWindows10OrGreater()) - { - static HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); // Reference counted per-process - if (PFN_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContextFn = (PFN_SetThreadDpiAwarenessContext)::GetProcAddress(user32_dll, "SetThreadDpiAwarenessContext")) - { - SetThreadDpiAwarenessContextFn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); - return; - } - } - if (_IsWindows8Point1OrGreater()) - { - static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process - if (PFN_SetProcessDpiAwareness SetProcessDpiAwarenessFn = (PFN_SetProcessDpiAwareness)::GetProcAddress(shcore_dll, "SetProcessDpiAwareness")) - { - SetProcessDpiAwarenessFn(PROCESS_PER_MONITOR_DPI_AWARE); - return; - } - } -#if _WIN32_WINNT >= 0x0600 - ::SetProcessDPIAware(); -#endif -} - -#if defined(_MSC_VER) && !defined(NOGDI) -#pragma comment(lib, "gdi32") // Link with gdi32.lib for GetDeviceCaps(). MinGW will require linking with '-lgdi32' -#endif - -float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor) -{ - UINT xdpi = 96, ydpi = 96; - if (_IsWindows8Point1OrGreater()) - { - static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process - static PFN_GetDpiForMonitor GetDpiForMonitorFn = nullptr; - if (GetDpiForMonitorFn == nullptr && shcore_dll != nullptr) - GetDpiForMonitorFn = (PFN_GetDpiForMonitor)::GetProcAddress(shcore_dll, "GetDpiForMonitor"); - if (GetDpiForMonitorFn != nullptr) - { - GetDpiForMonitorFn((HMONITOR)monitor, MDT_EFFECTIVE_DPI, &xdpi, &ydpi); - IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert! - return xdpi / 96.0f; - } - } -#ifndef NOGDI - const HDC dc = ::GetDC(nullptr); - xdpi = ::GetDeviceCaps(dc, LOGPIXELSX); - ydpi = ::GetDeviceCaps(dc, LOGPIXELSY); - IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert! - ::ReleaseDC(nullptr, dc); -#endif - return xdpi / 96.0f; -} - -float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd) -{ - HMONITOR monitor = ::MonitorFromWindow((HWND)hwnd, MONITOR_DEFAULTTONEAREST); - return ImGui_ImplWin32_GetDpiScaleForMonitor(monitor); -} - -//--------------------------------------------------------------------------------------------------------- -// Transparency related helpers (optional) -//-------------------------------------------------------------------------------------------------------- - -#if defined(_MSC_VER) -#pragma comment(lib, "dwmapi") // Link with dwmapi.lib. MinGW will require linking with '-ldwmapi' -#endif - -// [experimental] -// Borrowed from GLFW's function updateFramebufferTransparency() in src/win32_window.c -// (the Dwm* functions are Vista era functions but we are borrowing logic from GLFW) -void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd) -{ - if (!_IsWindowsVistaOrGreater()) - return; - - BOOL composition; - if (FAILED(::DwmIsCompositionEnabled(&composition)) || !composition) - return; - - BOOL opaque; - DWORD color; - if (_IsWindows8OrGreater() || (SUCCEEDED(::DwmGetColorizationColor(&color, &opaque)) && !opaque)) - { - HRGN region = ::CreateRectRgn(0, 0, -1, -1); - DWM_BLURBEHIND bb = {}; - bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; - bb.hRgnBlur = region; - bb.fEnable = TRUE; - ::DwmEnableBlurBehindWindow((HWND)hwnd, &bb); - ::DeleteObject(region); - } - else - { - DWM_BLURBEHIND bb = {}; - bb.dwFlags = DWM_BB_ENABLE; - ::DwmEnableBlurBehindWindow((HWND)hwnd, &bb); - } -} - -//--------------------------------------------------------------------------------------------------------- - -#endif // #ifndef IMGUI_DISABLE diff --git a/imgui/imgui_impl_win32.h b/imgui/imgui_impl_win32.h deleted file mode 100644 index be56271..0000000 --- a/imgui/imgui_impl_win32.h +++ /dev/null @@ -1,52 +0,0 @@ -// dear imgui: Platform Backend for Windows (standard windows API for 32-bits AND 64-bits applications) -// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) - -// Implemented features: -// [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui) -// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen. -// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] -// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. - -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. -// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. -// Learn about Dear ImGui: -// - FAQ https://dearimgui.com/faq -// - Getting Started https://dearimgui.com/getting-started -// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). -// - Introduction, links and more at the top of imgui.cpp - -#pragma once -#include "imgui.h" // IMGUI_IMPL_API -#ifndef IMGUI_DISABLE - -IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd); -IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd); -IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); -IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); - -// Win32 message handler your application need to call. -// - Intentionally commented out in a '#if 0' block to avoid dragging dependencies on from this helper. -// - You should COPY the line below into your .cpp code to forward declare the function and then you can call it. -// - Call from your application's message handler. Keep calling your message handler unless this function returns TRUE. - -#if 0 -extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); -#endif - -// DPI-related helpers (optional) -// - Use to enable DPI awareness without having to create an application manifest. -// - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps. -// - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc. -// but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime, -// neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies. -IMGUI_IMPL_API void ImGui_ImplWin32_EnableDpiAwareness(); -IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd); // HWND hwnd -IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // HMONITOR monitor - -// Transparency related helpers (optional) [experimental] -// - Use to enable alpha compositing transparency with the desktop. -// - Use together with e.g. clearing your framebuffer with zero-alpha. -IMGUI_IMPL_API void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd); // HWND hwnd - -#endif // #ifndef IMGUI_DISABLE diff --git a/imgui/imgui_spectrum.cpp b/imgui/imgui_spectrum.cpp index fab9d27..4a1f02a 100644 --- a/imgui/imgui_spectrum.cpp +++ b/imgui/imgui_spectrum.cpp @@ -1,6 +1,6 @@ #include "imgui_spectrum.h" -#include "../fonts/GmarketSans.h" -#include "../fonts/NotoSansJP.h" +//#include "../fonts/GmarketSans.h" +//#include "../fonts/NotoSansJP.h" #include "../fonts/SourceSansProRegular.h" #include "imgui.h" diff --git a/src/array.cpp b/src/array.cpp index 38b7095..a0b5863 100644 --- a/src/array.cpp +++ b/src/array.cpp @@ -1,5 +1,8 @@ #include "array.h" +#include +#include + ts_array ts_array_create(int entry_size) { ts_array new_ts_array; diff --git a/src/config.cpp b/src/config.cpp index 4813c5c..216c93d 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -3,6 +3,8 @@ #include "search.h" #include +#include +#include utf8_int8_t path_buffer[MAX_INPUT_LENGTH]; utf8_int8_t filter_buffer[MAX_INPUT_LENGTH]; @@ -19,9 +21,9 @@ static void _ts_config_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entr int threads, maxSize; - if (sscanf(line, "Path=%s", &path) == 1) { strcpy_s(path_buffer, MAX_INPUT_LENGTH, (char*)path); } - else if (sscanf(line, "Filter=%s", &filter) == 1) { strcpy_s(filter_buffer, MAX_INPUT_LENGTH, (char*)filter); } - else if (sscanf(line, "Query=%s", &query) == 1) { strcpy_s(query_buffer, MAX_INPUT_LENGTH, (char*)query); } + if (sscanf(line, "Path=%s", &path) == 1) { strcpy(path_buffer, (char*)path); } + else if (sscanf(line, "Filter=%s", &filter) == 1) { strcpy(filter_buffer, (char*)filter); } + else if (sscanf(line, "Query=%s", &query) == 1) { strcpy(query_buffer, (char*)query); } else if (sscanf(line, "Threads=%d", &threads) == 1) { ts_thread_count = threads; } else if (sscanf(line, "MaxSize=%d", &maxSize) == 1) { max_file_size = maxSize; } } diff --git a/src/image.cpp b/src/image.cpp index ba67859..0755055 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -71,8 +71,8 @@ void ts_load_images() { glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + 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, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); //glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); diff --git a/src/image.h b/src/image.h index 21f3438..860c767 100644 --- a/src/image.h +++ b/src/image.h @@ -1,6 +1,7 @@ #ifndef INCLUDE_IMAGE #define INCLUDE_IMAGE +#if defined(_WIN32) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif @@ -13,6 +14,19 @@ typedef struct t_ts_image { int width; int height; } ts_image; +#elif defined(__linux__) + +//#include +#include "imgui.h" +#include "imgui_impl_opengl3_loader.h" +#include "imgui_impl_opengl3.h" + +typedef struct t_ts_image { + GLuint id; + int width; + int height; +} ts_image; +#endif extern ts_image img_logo; extern ts_image img_search; diff --git a/src/linux/main_linux.cpp b/src/linux/main_linux.cpp new file mode 100644 index 0000000..f1fc01d --- /dev/null +++ b/src/linux/main_linux.cpp @@ -0,0 +1,296 @@ +#include "imgui.h" +#include "imgui_spectrum.h" +#include "imgui_impl_glfw.h" +#include "imgui_impl_opengl3.h" +#include "../utf8.h" +#include "platform.h" +#include "mutex.h" +#include "array.h" +#include "memory_bucket.h" +#include "image.h" +#include "config.h" +#include +#include +#include +#include +#include +#include +#define GL_SILENCE_DEPRECATION +#if defined(IMGUI_IMPL_OPENGL_ES2) +#include +#endif +#include // Will drag system OpenGL headers + +void ts_create_gui(int window_w, int window_h); +void ts_load_images(); +void ts_init(); + +bool program_running = true; + +char config_path[MAX_INPUT_LENGTH]; +static const char* _ts_platform_get_config_file_path(char* buffer) { + char *env = getenv("HOME"); + snprintf(buffer, MAX_INPUT_LENGTH, "%s%s", env, "text-search/imgui.ini"); + return buffer; +} + +static void glfw_error_callback(int error, const char* description) +{ + fprintf(stderr, "GLFW Error %d: %s\n", error, description); +} + +// Main code +int main(int, char**) +{ + glfwSetErrorCallback(glfw_error_callback); + if (!glfwInit()) + return 1; + + // Decide GL+GLSL versions +#if defined(IMGUI_IMPL_OPENGL_ES2) + // GL ES 2.0 + GLSL 100 + const char* glsl_version = "#version 100"; + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); +#elif defined(__APPLE__) + // GL 3.2 + GLSL 150 + const char* glsl_version = "#version 150"; + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac +#else + // GL 3.0 + GLSL 130 + const char* glsl_version = "#version 130"; + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + //glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only + //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only +#endif + + // Create window with graphics context + GLFWwindow* window = glfwCreateWindow(1280, 720, "Text-Search", nullptr, nullptr); + if (window == nullptr) + return 1; + glfwMakeContextCurrent(window); + glfwSwapInterval(1); // Enable vsync + + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.IniFilename = _ts_platform_get_config_file_path(config_path); + + // Setup Platform/Renderer backends + ImGui_ImplGlfw_InitForOpenGL(window, true); + ImGui_ImplOpenGL3_Init(glsl_version); + + // Setup Dear ImGui style + ImGui::Spectrum::StyleColorsSpectrum(); + //ImGui::Spectrum::LoadFont(18.0f); + + ts_init(); + ts_load_images(); + ts_load_config(); + + ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + int display_w, display_h; + while (!glfwWindowShouldClose(window)) + { + glfwPollEvents(); + + // Start the Dear ImGui frame + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + + ts_create_gui(display_w, display_h); + + // Rendering + ImGui::Render(); + glfwGetFramebufferSize(window, &display_w, &display_h); + glViewport(0, 0, display_w, display_h); + 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()); + + glfwSwapBuffers(window); + } + + // Cleanup + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + ImGui::DestroyContext(); + + glfwDestroyWindow(window); + glfwTerminate(); + + return 0; +} + +bool ts_platform_dir_exists(utf8_int8_t* path) { + DIR* dir = opendir(path); + if (dir) { + /* Directory exists. */ + closedir(dir); + return true; + } else if (ENOENT == errno) { + return false; // does not exist + } else { + return false; // error opening dir + } +} + +ts_file_content ts_platform_read_file(char *path, const char *mode) { + ts_file_content result; + result.content = 0; + result.content_length = 0; + result.file_error = 0; + + FILE *file = fopen(path, mode); + + if (!file) + { + if (errno == EMFILE) + result.file_error = FILE_ERROR_TOO_MANY_OPEN_FILES_PROCESS; + else if (errno == ENFILE) + result.file_error = FILE_ERROR_TOO_MANY_OPEN_FILES_SYSTEM; + else if (errno == EACCES) + result.file_error = FILE_ERROR_NO_ACCESS; + else if (errno == EPERM) + result.file_error = FILE_ERROR_NO_ACCESS; + else if (errno == ENOENT) + result.file_error = FILE_ERROR_NOT_FOUND; + else if (errno == ECONNABORTED) + result.file_error = FILE_ERROR_CONNECTION_ABORTED; + else if (errno == ECONNREFUSED) + result.file_error = FILE_ERROR_CONNECTION_REFUSED; + else if (errno == ENETDOWN) + result.file_error = FILE_ERROR_NETWORK_DOWN; + else if (errno == EREMOTEIO) + result.file_error = FILE_ERROR_REMOTE_IO_ERROR; + else if (errno == ESTALE) + result.file_error = FILE_ERROR_STALE; + else + { + result.file_error = FILE_ERROR_GENERIC; + } + + return result; + } + + fseek(file, 0 , SEEK_END); + int length = ftell(file); + fseek(file, 0, SEEK_SET); + + int length_to_alloc = length+1; + + result.content = malloc(length_to_alloc); + if (!result.content) { + fclose(file); + return result; + } + + memset(result.content, 0, length); + int read_result = fread(result.content, 1, length, file); + if (read_result == 0 && length != 0) + { + free(result.content); + result.content = 0; + return result; + } + + result.content_length = read_result; + + ((char*)result.content)[length] = 0; + + fclose(file); + return result; +} + +void ts_platform_list_files_block(ts_search_result* result, wchar_t* start_dir) { + + utf8_int8_t* search_dir = (utf8_int8_t*)ts_memory_bucket_reserve(&result->memory, MAX_INPUT_LENGTH); + if (start_dir == nullptr) { + strcpy(search_dir, result->directory_to_search); + } + else { + strcpy(search_dir, (char*)start_dir); + } + + // Append wildcard + utf8_int8_t* search_dir_fix = (utf8_int8_t*)ts_memory_bucket_reserve(&result->memory, MAX_INPUT_LENGTH); + strcpy(search_dir_fix, search_dir); + strcat(search_dir_fix, u8"/*"); + + DIR *d; + struct dirent *dir; + d = opendir(search_dir); + if (d) { + chdir(search_dir); + while ((dir = readdir(d)) != NULL) { + if (result->cancel_search) return; + chdir(search_dir); + + if (dir->d_type == DT_DIR) + { + if ((strcmp(dir->d_name, ".") == 0) || (strcmp(dir->d_name, "..") == 0)) + continue; + + utf8_int8_t complete_file_path[MAX_INPUT_LENGTH]; + strcpy(complete_file_path, search_dir); + strcat(complete_file_path, "/"); + strcat(complete_file_path, dir->d_name); + + // do recursive search + ts_platform_list_files_block(result, (wchar_t*)complete_file_path); + } + // we handle DT_UNKNOWN for file systems that do not support type lookup. + else if (dir->d_type == DT_REG || dir->d_type == DT_UNKNOWN) + { + char *matched_filter = 0; + if (ts_filter_matches(&result->filters, dir->d_name, &matched_filter) == -1) { + continue; + } + (void)matched_filter; + + + utf8_int8_t complete_file_path[MAX_INPUT_LENGTH]; + strcpy(complete_file_path, search_dir); + strcat(complete_file_path, "/"); + strcat(complete_file_path, dir->d_name); + + ts_found_file* f = (ts_found_file*)ts_memory_bucket_reserve(&result->memory, sizeof(ts_found_file)); + f->path = (utf8_int8_t*)ts_memory_bucket_reserve(&result->memory, MAX_INPUT_LENGTH); + f->match_count = 0; + f->error = 0; + strcpy(f->path, complete_file_path); + + ts_mutex_lock(&result->files.mutex); + ts_array_push_size(&result->files, &f, sizeof(ts_found_file*)); + ts_mutex_unlock(&result->files.mutex); + } + } + closedir(d); + } +} + +uint64_t ts_platform_get_time(uint64_t compare) { + struct timespec tms; + if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID,&tms)) { + return -1; + } + long result = 0; + result = tms.tv_sec * 1000000; + result += tms.tv_nsec/1000; + if (tms.tv_nsec % 1000 >= 500) { + ++result; + } + + if (compare != 0) { + return result - compare; + } + + return result; +} diff --git a/src/linux/mutex.cpp b/src/linux/mutex.cpp new file mode 100644 index 0000000..f59a1eb --- /dev/null +++ b/src/linux/mutex.cpp @@ -0,0 +1,129 @@ +#include "mutex.h" + +extern long int syscall (long int __sysno, ...); +extern int pthread_tryjoin_np(pthread_t ts_thread, void **retval); + +ts_thread ts_thread_start(void *(*start_routine) (void *), void *arg) +{ + ts_thread result; + result.valid = false; + + pthread_attr_t attr; + int attr_init_result = pthread_attr_init(&attr); + if (attr_init_result) + return result; + + int start_thread_result = pthread_create(&result.thread, &attr, start_routine, arg); + if (start_thread_result) + { + pthread_attr_destroy(&attr); + return result; + } + + result.valid = true; + pthread_attr_destroy(&attr); + + return result; +} + +void ts_thread_detach(ts_thread *ts_thread) +{ + if (ts_thread->valid) + { + pthread_detach(ts_thread->thread); + } +} + +void ts_thread_join(ts_thread *ts_thread) +{ + if (ts_thread->valid) + { + void *retval; + pthread_join(ts_thread->thread, &retval); + } +} + +bool ts_thread_tryjoin(ts_thread *ts_thread) +{ + if (ts_thread->valid) + { + void *retval; + bool thread_joined = !pthread_tryjoin_np(ts_thread->thread, &retval); + return thread_joined; + } + return false; +} + +void ts_thread_exit() +{ + pthread_exit(0); +} + +void ts_thread_stop(ts_thread *ts_thread) +{ + if (ts_thread->valid) + { + pthread_cancel(ts_thread->thread); + } +} + +ts_mutex ts_mutex_create() +{ + ts_mutex result; + + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT); + + pthread_mutex_init(&result.mutex, &attr); + + pthread_mutexattr_destroy(&attr); + + return result; +} + +ts_mutex ts_mutex_create_recursive() +{ + ts_mutex result; + + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + + pthread_mutex_init(&result.mutex, &attr); + + pthread_mutexattr_destroy(&attr); + + return result; +} + +void ts_mutex_lock(ts_mutex *ts_mutex) +{ + pthread_mutex_lock(&ts_mutex->mutex); +} + +bool ts_mutex_trylock(ts_mutex *ts_mutex) +{ + return !pthread_mutex_trylock(&ts_mutex->mutex); +} + +void ts_mutex_unlock(ts_mutex *ts_mutex) +{ + pthread_mutex_unlock(&ts_mutex->mutex); +} + +void ts_mutex_destroy(ts_mutex *ts_mutex) +{ + ts_mutex_unlock(ts_mutex); + pthread_mutex_destroy(&ts_mutex->mutex); +} + +int ts_thread_get_id() +{ + return (int)syscall(__NR_gettid); +} + +void ts_thread_sleep(int microseconds) +{ + usleep(microseconds); +} \ No newline at end of file diff --git a/src/main_windows.cpp b/src/main_windows.cpp deleted file mode 100644 index 4410f12..0000000 --- a/src/main_windows.cpp +++ /dev/null @@ -1,384 +0,0 @@ -#include - -#include "../imgui/imgui.h" -#include "../imgui/imgui_spectrum.h" -#include "../imgui/imgui_impl_opengl3.h" -#include "../imgui/imgui_impl_win32.h" -#include "../utf8.h" -#include "platform.h" -#include "mutex.h" -#include "array.h" -#include "memory_bucket.h" -#include "image.h" -#include "config.h" -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -#include -#include -#include - -#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup") - -#define IDI_LOGO 123 - -char config_path[MAX_INPUT_LENGTH]; - -void ts_create_gui(int window_w, int window_h); -void ts_load_images(); -void ts_init(); - -struct WGL_WindowData { HDC hDC; }; - -// Data -static HGLRC g_hRC; -static WGL_WindowData g_MainWindow; -static int g_Width; -static int g_Height; -LARGE_INTEGER Frequency; -bool program_running = true; - -bool CreateDeviceWGL(HWND hWnd, WGL_WindowData* data); -void CleanupDeviceWGL(HWND hWnd, WGL_WindowData* data); -void ResetDeviceWGL(); -LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); - -static const char* _ts_platform_get_config_file_path(char* buffer) { - if(SUCCEEDED(SHGetFolderPathA(0, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, NULL, 0, buffer))) - { - strcat_s(buffer, MAX_INPUT_LENGTH, "\\text-search\\config.ini"); - return buffer; - } - - return 0; -} - -bool ts_platform_dir_exists(utf8_int8_t* dir) -{ - DWORD ftyp = GetFileAttributesA(dir); - if (ftyp == INVALID_FILE_ATTRIBUTES) - return false; - if (ftyp & FILE_ATTRIBUTE_DIRECTORY) - return true; - return false; -} - -uint64_t ts_platform_get_time(uint64_t compare) -{ - LARGE_INTEGER stamp; - QueryPerformanceCounter(&stamp); - if (compare != 0) { - return (stamp.QuadPart - compare) * 1000 / Frequency.QuadPart; - } - return stamp.QuadPart; -} - -int main(int, char**) -{ - // Create application window - //ImGui_ImplWin32_EnableDpiAwareness(); - WNDCLASSEXW wc = { sizeof(wc), CS_OWNDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, L"ImGui Example", nullptr }; - ::RegisterClassExW(&wc); - HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Text-Search", WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, nullptr, nullptr, wc.hInstance, nullptr); - - // Initialize OpenGL - if (!CreateDeviceWGL(hwnd, &g_MainWindow)) - { - CleanupDeviceWGL(hwnd, &g_MainWindow); - ::DestroyWindow(hwnd); - ::UnregisterClassW(wc.lpszClassName, wc.hInstance); - return 1; - } - wglMakeCurrent(g_MainWindow.hDC, g_hRC); - - // Show the window - ::ShowWindow(hwnd, SW_SHOWDEFAULT); - ::UpdateWindow(hwnd); - - HICON hicoCaption = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_LOGO)); - SendMessage(hwnd, WM_SETICON, ICON_BIG, - reinterpret_cast(hicoCaption)); - - // Setup Dear ImGui context - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - io.IniFilename = _ts_platform_get_config_file_path(config_path); - - // Setup Dear ImGui style - ImGui::Spectrum::StyleColorsSpectrum(); - ImGui::Spectrum::LoadFont(18.0f); - //ImGui::StyleColorsLight(); - - // Setup Platform/Renderer backends - ImGui_ImplWin32_InitForOpenGL(hwnd); - ImGui_ImplOpenGL3_Init(); - - QueryPerformanceFrequency(&Frequency); - - ts_init(); - ts_load_images(); - ts_load_config(); - - ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); - bool done = false; - while (program_running) - { - MSG msg; - while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE)) - { - ::TranslateMessage(&msg); - ::DispatchMessage(&msg); - if (msg.message == WM_QUIT) - program_running = false; - } - if (!program_running) - break; - - ImGui_ImplOpenGL3_NewFrame(); - ImGui_ImplWin32_NewFrame(); - ImGui::NewFrame(); - - ts_create_gui(g_Width, g_Height); - - // Rendering - ImGui::Render(); - glViewport(0, 0, g_Width, g_Height); - glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); - glClear(GL_COLOR_BUFFER_BIT); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - - // Present - ::SwapBuffers(g_MainWindow.hDC); - } - - ImGui_ImplOpenGL3_Shutdown(); - ImGui_ImplWin32_Shutdown(); - ImGui::DestroyContext(); - - CleanupDeviceWGL(hwnd, &g_MainWindow); - wglDeleteContext(g_hRC); - ::DestroyWindow(hwnd); - ::UnregisterClassW(wc.lpszClassName, wc.hInstance); - - return 0; -} - -bool CreateDeviceWGL(HWND hWnd, WGL_WindowData* data) -{ - HDC hDc = ::GetDC(hWnd); - PIXELFORMATDESCRIPTOR pfd = { 0 }; - pfd.nSize = sizeof(pfd); - pfd.nVersion = 1; - pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; - pfd.iPixelType = PFD_TYPE_RGBA; - pfd.cColorBits = 32; - - const int pf = ::ChoosePixelFormat(hDc, &pfd); - if (pf == 0) - return false; - if (::SetPixelFormat(hDc, pf, &pfd) == FALSE) - return false; - ::ReleaseDC(hWnd, hDc); - - data->hDC = ::GetDC(hWnd); - if (!g_hRC) - g_hRC = wglCreateContext(data->hDC); - return true; -} - -void CleanupDeviceWGL(HWND hWnd, WGL_WindowData* data) -{ - wglMakeCurrent(nullptr, nullptr); - ::ReleaseDC(hWnd, data->hDC); -} - -extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); -LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) - return true; - - switch (msg) - { - case WM_GETMINMAXINFO: - { - MINMAXINFO *minmax = (MINMAXINFO *)lParam; - minmax->ptMinTrackSize.x = 800; - minmax->ptMinTrackSize.y = 600; - break; - } - case WM_SIZE: - if (wParam != SIZE_MINIMIZED) - { - g_Width = LOWORD(lParam); - g_Height = HIWORD(lParam); - } - return 0; - case WM_SYSCOMMAND: - if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu - return 0; - break; - case WM_DESTROY: - ::PostQuitMessage(0); - return 0; - } - return ::DefWindowProcW(hWnd, msg, wParam, lParam); -} - -ts_file_content ts_platform_read_file(char *path, const char *mode) -{ - ts_file_content result; - result.content = 0; - result.content_length = 0; - result.file_error = 0; - - // convert utf8 to wchar path. - wchar_t wchar_buffer[MAX_INPUT_LENGTH]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, (wchar_t*)wchar_buffer, MAX_INPUT_LENGTH); - - const size_t cSize = strlen(mode)+1; - wchar_t* wc = new wchar_t[cSize]; - mbstowcs (wc, mode, cSize); - - FILE *file = _wfopen(wchar_buffer, wc); - if (!file) - { - if (errno == EMFILE) - result.file_error = FILE_ERROR_TOO_MANY_OPEN_FILES_PROCESS; - else if (errno == ENFILE) - result.file_error = FILE_ERROR_TOO_MANY_OPEN_FILES_SYSTEM; - else if (errno == EACCES) - result.file_error = FILE_ERROR_NO_ACCESS; - else if (errno == EPERM) - result.file_error = FILE_ERROR_NO_ACCESS; - else if (errno == ENOENT) - result.file_error = FILE_ERROR_NOT_FOUND; - else if (errno == ECONNABORTED) - result.file_error = FILE_ERROR_CONNECTION_ABORTED; - else if (errno == ECONNREFUSED) - result.file_error = FILE_ERROR_CONNECTION_REFUSED; - else if (errno == ENETDOWN) - result.file_error = FILE_ERROR_NETWORK_DOWN; - else - { - result.file_error = FILE_ERROR_GENERIC; - printf("ERROR: %d %s\n", errno, path); - } - - goto done_failure; - } - - fseek(file, 0 , SEEK_END); - int length = ftell(file); - fseek(file, 0, SEEK_SET); - - int length_to_alloc = length+1; - - result.content = malloc(length_to_alloc); - if (!result.content) goto done; - - memset(result.content, 0, length); - int read_result = fread(result.content, 1, length, file); - if (read_result == 0 && length != 0) - { - free(result.content); - result.content = 0; - return result; - } - - result.content_length = read_result; - - ((char*)result.content)[length] = 0; - - done: - fclose(file); - done_failure: - return result; -} - -void ts_platform_list_files_block(ts_search_result* result, wchar_t* start_dir) -{ - // Utf8 to wchar str - wchar_t* search_dir = (wchar_t*)ts_memory_bucket_reserve(&result->memory, MAX_INPUT_LENGTH); - if (start_dir == nullptr) { - MultiByteToWideChar(CP_UTF8, 0, result->directory_to_search, -1, search_dir, MAX_INPUT_LENGTH); - } - else { - wcscpy_s(search_dir, MAX_INPUT_LENGTH, start_dir); - } - - // Append wildcard - wchar_t* search_dir_fix = (wchar_t*)ts_memory_bucket_reserve(&result->memory, MAX_INPUT_LENGTH); - wcscpy_s(search_dir_fix, MAX_INPUT_LENGTH, search_dir); - wcscat_s(search_dir_fix, MAX_INPUT_LENGTH, L"\\*"); - - WIN32_FIND_DATAW file_info; - HANDLE handle = FindFirstFileW(search_dir_fix, &file_info); - - if (handle == INVALID_HANDLE_VALUE) - { - return; - } - - do - { - if (result->cancel_search) return; - - //if (*is_cancelled) break; - const wchar_t *name = (const wchar_t *)file_info.cFileName; - - // symbolic link is not allowed.. - if ((file_info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) - continue; - - - if ((file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) - { - if ((wcscmp(name, L".") == 0) || (wcscmp(name, L"..") == 0)) - continue; - - wchar_t* subdir_buffer_path = (wchar_t*)ts_memory_bucket_reserve(&result->memory, MAX_INPUT_LENGTH); - wcscpy_s(subdir_buffer_path, MAX_INPUT_LENGTH, search_dir); - wcscat_s(subdir_buffer_path, MAX_INPUT_LENGTH, L"\\"); - wcscat_s(subdir_buffer_path, MAX_INPUT_LENGTH, name); - ts_platform_list_files_block(result, subdir_buffer_path); - } - else if ((file_info.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) || - (file_info.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) || - (file_info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) || - (file_info.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) || - (file_info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) || - (file_info.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)) - { - char *matched_filter = 0; - utf8_int8_t uni_name[MAX_INPUT_LENGTH]; - WideCharToMultiByte(CP_UTF8,0,name,-1,(LPSTR)uni_name,MAX_INPUT_LENGTH, NULL, NULL); - if (ts_filter_matches(&result->filters, uni_name, &matched_filter) == -1) { - continue; - } - (void)matched_filter; - - wchar_t complete_file_path[MAX_INPUT_LENGTH]; - wcscpy_s(complete_file_path, MAX_INPUT_LENGTH, search_dir); - wcscat_s(complete_file_path, MAX_INPUT_LENGTH, L"\\"); - wcscat_s(complete_file_path, MAX_INPUT_LENGTH, name); - - ts_found_file* f = (ts_found_file*)ts_memory_bucket_reserve(&result->memory, sizeof(ts_found_file)); - f->path = (utf8_int8_t*)ts_memory_bucket_reserve(&result->memory, MAX_INPUT_LENGTH); - f->match_count = 0; - f->error = 0; - WideCharToMultiByte(CP_UTF8,0,complete_file_path,-1,(LPSTR)f->path,MAX_INPUT_LENGTH, NULL, NULL); - - ts_mutex_lock(&result->files.mutex); - ts_array_push_size(&result->files, &f, sizeof(ts_found_file*)); - ts_mutex_unlock(&result->files.mutex); - - } - } - while (FindNextFile(handle, (LPWIN32_FIND_DATAW)&file_info) != 0); - - FindClose(handle); -} \ No newline at end of file diff --git a/src/memory_bucket.cpp b/src/memory_bucket.cpp index 0d163d0..1a1e54d 100644 --- a/src/memory_bucket.cpp +++ b/src/memory_bucket.cpp @@ -1,4 +1,5 @@ #include "memory_bucket.h" +#include ts_memory_bucket ts_memory_bucket_init(int bucket_size) { diff --git a/src/mutex.cpp b/src/mutex.cpp deleted file mode 100644 index f224312..0000000 --- a/src/mutex.cpp +++ /dev/null @@ -1,102 +0,0 @@ -#include "mutex.h" - -ts_mutex ts_mutex_create() -{ - ts_mutex result; - result.mutex = CreateMutex( - NULL, // default security attributes - FALSE, // initially not owned - NULL); // unnamed ts_mutex - - return result; -} - -ts_mutex ts_mutex_create_recursive() -{ - return ts_mutex_create(); -} - -void ts_mutex_lock(ts_mutex *ts_mutex) -{ - WaitForSingleObject( - ts_mutex->mutex, // handle to ts_mutex - INFINITE); // no time-out interval -} - -int ts_mutex_trylock(ts_mutex *ts_mutex) -{ - return WaitForSingleObject(ts_mutex->mutex, 1) == WAIT_OBJECT_0; -} - -void ts_mutex_unlock(ts_mutex *ts_mutex) -{ - ReleaseMutex(ts_mutex->mutex); -} - -void ts_mutex_destroy(ts_mutex *ts_mutex) -{ - CloseHandle(ts_mutex->mutex); -} - - -ts_thread ts_thread_start(void *(*start_routine) (void *), void *arg) -{ - ts_thread result; - result.valid = 0; - - result.thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)start_routine, - arg, 0, NULL); - result.valid = 1; - - return result; -} - -void ts_thread_join(ts_thread *ts_thread) -{ - if (ts_thread->valid) - { - WaitForSingleObject(ts_thread->thread, INFINITE); - CloseHandle(ts_thread->thread); - } -} - -int ts_thread_tryjoin(ts_thread *ts_thread) -{ - if (ts_thread->valid) - { - int result = WaitForSingleObject(ts_thread->thread, 0); - return result == WAIT_OBJECT_0; - } - return 0; -} - -void ts_thread_detach(ts_thread *ts_thread) -{ - if (ts_thread->valid) - { - CloseHandle(ts_thread->thread); - } -} - -void ts_thread_stop(ts_thread *ts_thread) -{ - if (ts_thread->valid) - { - SuspendThread(ts_thread->thread); - } -} - -int ts_thread_get_id() -{ - return GetCurrentThreadId(); -} - -void ts_thread_exit() -{ - ExitThread(0); -} - -void ts_thread_sleep(int microseconds) -{ - Sleep(microseconds/1000); -} \ No newline at end of file diff --git a/src/mutex.h b/src/mutex.h index a745947..8499bcc 100644 --- a/src/mutex.h +++ b/src/mutex.h @@ -1,7 +1,7 @@ #ifndef INCLUDE_MUTEX #define INCLUDE_MUTEX -#ifdef _WIN32 +#if defined(_WIN32) #include #include /* _beginthread, _endthread */ #include @@ -19,11 +19,28 @@ typedef struct t_ts_thread HANDLE thread; int valid; } ts_thread; + +#elif defined(__linux__) +#include +#include +#include +#include + +typedef struct t_ts_thread +{ + pthread_t thread; + bool valid; +} ts_thread; + +typedef struct t_ts_mutex +{ + pthread_mutex_t mutex; +} ts_mutex; #endif ts_thread ts_thread_start(void *(*start_routine) (void *), void *arg); void ts_thread_join(ts_thread *ts_thread); -int ts_thread_tryjoin(ts_thread *ts_thread); +bool ts_thread_tryjoin(ts_thread *ts_thread); void ts_thread_detach(ts_thread *ts_thread); void ts_thread_stop(ts_thread *ts_thread); int ts_thread_get_id(); @@ -33,7 +50,7 @@ void ts_thread_exit(); ts_mutex ts_mutex_create_recursive(); ts_mutex ts_mutex_create(); void ts_mutex_lock(ts_mutex *ts_mutex); -int ts_mutex_trylock(ts_mutex *ts_mutex); +bool ts_mutex_trylock(ts_mutex *ts_mutex); void ts_mutex_unlock(ts_mutex *ts_mutex); void ts_mutex_destroy(ts_mutex *ts_mutex); diff --git a/src/platform.h b/src/platform.h index 7fdc15a..b9a9e9c 100644 --- a/src/platform.h +++ b/src/platform.h @@ -34,7 +34,6 @@ extern bool program_running; bool ts_platform_dir_exists(utf8_int8_t* dir); ts_file_content ts_platform_read_file(char *path, const char *mode); void ts_platform_list_files_block(ts_search_result* result, wchar_t* start_dir = nullptr); -void ts_platform_list_files(ts_search_result* result); uint64_t ts_platform_get_time(uint64_t compare = 0); // if compare is not 0, return difference between timestamp and now, in milliseconds. #endif \ No newline at end of file diff --git a/src/windows/main_windows.cpp b/src/windows/main_windows.cpp new file mode 100644 index 0000000..cb59d9d --- /dev/null +++ b/src/windows/main_windows.cpp @@ -0,0 +1,384 @@ +#include + +#include "imgui.h" +#include "imgui_spectrum.h" +#include "imgui_impl_opengl3.h" +#include "imgui_impl_win32.h" +#include "../utf8.h" +#include "platform.h" +#include "mutex.h" +#include "array.h" +#include "memory_bucket.h" +#include "image.h" +#include "config.h" +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#include +#include + +#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup") + +#define IDI_LOGO 123 + +char config_path[MAX_INPUT_LENGTH]; + +void ts_create_gui(int window_w, int window_h); +void ts_load_images(); +void ts_init(); + +struct WGL_WindowData { HDC hDC; }; + +// Data +static HGLRC g_hRC; +static WGL_WindowData g_MainWindow; +static int g_Width; +static int g_Height; +LARGE_INTEGER Frequency; +bool program_running = true; + +bool CreateDeviceWGL(HWND hWnd, WGL_WindowData* data); +void CleanupDeviceWGL(HWND hWnd, WGL_WindowData* data); +void ResetDeviceWGL(); +LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + +static const char* _ts_platform_get_config_file_path(char* buffer) { + if(SUCCEEDED(SHGetFolderPathA(0, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, NULL, 0, buffer))) + { + strcat_s(buffer, MAX_INPUT_LENGTH, "\\text-search\\config.ini"); + return buffer; + } + + return 0; +} + +bool ts_platform_dir_exists(utf8_int8_t* dir) +{ + DWORD ftyp = GetFileAttributesA(dir); + if (ftyp == INVALID_FILE_ATTRIBUTES) + return false; + if (ftyp & FILE_ATTRIBUTE_DIRECTORY) + return true; + return false; +} + +uint64_t ts_platform_get_time(uint64_t compare) +{ + LARGE_INTEGER stamp; + QueryPerformanceCounter(&stamp); + if (compare != 0) { + return (stamp.QuadPart - compare) * 1000 / Frequency.QuadPart; + } + return stamp.QuadPart; +} + +int main(int, char**) +{ + // Create application window + //ImGui_ImplWin32_EnableDpiAwareness(); + WNDCLASSEXW wc = { sizeof(wc), CS_OWNDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, L"ImGui Example", nullptr }; + ::RegisterClassExW(&wc); + HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Text-Search", WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, nullptr, nullptr, wc.hInstance, nullptr); + + // Initialize OpenGL + if (!CreateDeviceWGL(hwnd, &g_MainWindow)) + { + CleanupDeviceWGL(hwnd, &g_MainWindow); + ::DestroyWindow(hwnd); + ::UnregisterClassW(wc.lpszClassName, wc.hInstance); + return 1; + } + wglMakeCurrent(g_MainWindow.hDC, g_hRC); + + // Show the window + ::ShowWindow(hwnd, SW_SHOWDEFAULT); + ::UpdateWindow(hwnd); + + HICON hicoCaption = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_LOGO)); + SendMessage(hwnd, WM_SETICON, ICON_BIG, + reinterpret_cast(hicoCaption)); + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.IniFilename = _ts_platform_get_config_file_path(config_path); + + // Setup Dear ImGui style + ImGui::Spectrum::StyleColorsSpectrum(); + ImGui::Spectrum::LoadFont(18.0f); + //ImGui::StyleColorsLight(); + + // Setup Platform/Renderer backends + ImGui_ImplWin32_InitForOpenGL(hwnd); + ImGui_ImplOpenGL3_Init(); + + QueryPerformanceFrequency(&Frequency); + + ts_init(); + ts_load_images(); + ts_load_config(); + + ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + bool done = false; + while (program_running) + { + MSG msg; + while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE)) + { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + if (msg.message == WM_QUIT) + program_running = false; + } + if (!program_running) + break; + + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplWin32_NewFrame(); + ImGui::NewFrame(); + + ts_create_gui(g_Width, g_Height); + + // Rendering + ImGui::Render(); + glViewport(0, 0, g_Width, g_Height); + glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); + glClear(GL_COLOR_BUFFER_BIT); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + // Present + ::SwapBuffers(g_MainWindow.hDC); + } + + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplWin32_Shutdown(); + ImGui::DestroyContext(); + + CleanupDeviceWGL(hwnd, &g_MainWindow); + wglDeleteContext(g_hRC); + ::DestroyWindow(hwnd); + ::UnregisterClassW(wc.lpszClassName, wc.hInstance); + + return 0; +} + +bool CreateDeviceWGL(HWND hWnd, WGL_WindowData* data) +{ + HDC hDc = ::GetDC(hWnd); + PIXELFORMATDESCRIPTOR pfd = { 0 }; + pfd.nSize = sizeof(pfd); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 32; + + const int pf = ::ChoosePixelFormat(hDc, &pfd); + if (pf == 0) + return false; + if (::SetPixelFormat(hDc, pf, &pfd) == FALSE) + return false; + ::ReleaseDC(hWnd, hDc); + + data->hDC = ::GetDC(hWnd); + if (!g_hRC) + g_hRC = wglCreateContext(data->hDC); + return true; +} + +void CleanupDeviceWGL(HWND hWnd, WGL_WindowData* data) +{ + wglMakeCurrent(nullptr, nullptr); + ::ReleaseDC(hWnd, data->hDC); +} + +extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) + return true; + + switch (msg) + { + case WM_GETMINMAXINFO: + { + MINMAXINFO *minmax = (MINMAXINFO *)lParam; + minmax->ptMinTrackSize.x = 800; + minmax->ptMinTrackSize.y = 600; + break; + } + case WM_SIZE: + if (wParam != SIZE_MINIMIZED) + { + g_Width = LOWORD(lParam); + g_Height = HIWORD(lParam); + } + return 0; + case WM_SYSCOMMAND: + if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu + return 0; + break; + case WM_DESTROY: + ::PostQuitMessage(0); + return 0; + } + return ::DefWindowProcW(hWnd, msg, wParam, lParam); +} + +ts_file_content ts_platform_read_file(char *path, const char *mode) +{ + ts_file_content result; + result.content = 0; + result.content_length = 0; + result.file_error = 0; + + // convert utf8 to wchar path. + wchar_t wchar_buffer[MAX_INPUT_LENGTH]; + MultiByteToWideChar(CP_UTF8, 0, path, -1, (wchar_t*)wchar_buffer, MAX_INPUT_LENGTH); + + const size_t cSize = strlen(mode)+1; + wchar_t* wc = new wchar_t[cSize]; + mbstowcs (wc, mode, cSize); + + FILE *file = _wfopen(wchar_buffer, wc); + if (!file) + { + if (errno == EMFILE) + result.file_error = FILE_ERROR_TOO_MANY_OPEN_FILES_PROCESS; + else if (errno == ENFILE) + result.file_error = FILE_ERROR_TOO_MANY_OPEN_FILES_SYSTEM; + else if (errno == EACCES) + result.file_error = FILE_ERROR_NO_ACCESS; + else if (errno == EPERM) + result.file_error = FILE_ERROR_NO_ACCESS; + else if (errno == ENOENT) + result.file_error = FILE_ERROR_NOT_FOUND; + else if (errno == ECONNABORTED) + result.file_error = FILE_ERROR_CONNECTION_ABORTED; + else if (errno == ECONNREFUSED) + result.file_error = FILE_ERROR_CONNECTION_REFUSED; + else if (errno == ENETDOWN) + result.file_error = FILE_ERROR_NETWORK_DOWN; + else + { + result.file_error = FILE_ERROR_GENERIC; + printf("ERROR: %d %s\n", errno, path); + } + + goto done_failure; + } + + fseek(file, 0 , SEEK_END); + int length = ftell(file); + fseek(file, 0, SEEK_SET); + + int length_to_alloc = length+1; + + result.content = malloc(length_to_alloc); + if (!result.content) goto done; + + memset(result.content, 0, length); + int read_result = fread(result.content, 1, length, file); + if (read_result == 0 && length != 0) + { + free(result.content); + result.content = 0; + return result; + } + + result.content_length = read_result; + + ((char*)result.content)[length] = 0; + + done: + fclose(file); + done_failure: + return result; +} + +void ts_platform_list_files_block(ts_search_result* result, wchar_t* start_dir) +{ + // Utf8 to wchar str + wchar_t* search_dir = (wchar_t*)ts_memory_bucket_reserve(&result->memory, MAX_INPUT_LENGTH); + if (start_dir == nullptr) { + MultiByteToWideChar(CP_UTF8, 0, result->directory_to_search, -1, search_dir, MAX_INPUT_LENGTH); + } + else { + wcscpy_s(search_dir, MAX_INPUT_LENGTH, start_dir); + } + + // Append wildcard + wchar_t* search_dir_fix = (wchar_t*)ts_memory_bucket_reserve(&result->memory, MAX_INPUT_LENGTH); + wcscpy_s(search_dir_fix, MAX_INPUT_LENGTH, search_dir); + wcscat_s(search_dir_fix, MAX_INPUT_LENGTH, L"\\*"); + + WIN32_FIND_DATAW file_info; + HANDLE handle = FindFirstFileW(search_dir_fix, &file_info); + + if (handle == INVALID_HANDLE_VALUE) + { + return; + } + + do + { + if (result->cancel_search) return; + + //if (*is_cancelled) break; + const wchar_t *name = (const wchar_t *)file_info.cFileName; + + // symbolic link is not allowed.. + if ((file_info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) + continue; + + + if ((file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + if ((wcscmp(name, L".") == 0) || (wcscmp(name, L"..") == 0)) + continue; + + wchar_t* subdir_buffer_path = (wchar_t*)ts_memory_bucket_reserve(&result->memory, MAX_INPUT_LENGTH); + wcscpy_s(subdir_buffer_path, MAX_INPUT_LENGTH, search_dir); + wcscat_s(subdir_buffer_path, MAX_INPUT_LENGTH, L"\\"); + wcscat_s(subdir_buffer_path, MAX_INPUT_LENGTH, name); + ts_platform_list_files_block(result, subdir_buffer_path); + } + else if ((file_info.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) || + (file_info.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) || + (file_info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) || + (file_info.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) || + (file_info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) || + (file_info.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)) + { + char *matched_filter = 0; + utf8_int8_t uni_name[MAX_INPUT_LENGTH]; + WideCharToMultiByte(CP_UTF8,0,name,-1,(LPSTR)uni_name,MAX_INPUT_LENGTH, NULL, NULL); + if (ts_filter_matches(&result->filters, uni_name, &matched_filter) == -1) { + continue; + } + (void)matched_filter; + + wchar_t complete_file_path[MAX_INPUT_LENGTH]; + wcscpy_s(complete_file_path, MAX_INPUT_LENGTH, search_dir); + wcscat_s(complete_file_path, MAX_INPUT_LENGTH, L"\\"); + wcscat_s(complete_file_path, MAX_INPUT_LENGTH, name); + + ts_found_file* f = (ts_found_file*)ts_memory_bucket_reserve(&result->memory, sizeof(ts_found_file)); + f->path = (utf8_int8_t*)ts_memory_bucket_reserve(&result->memory, MAX_INPUT_LENGTH); + f->match_count = 0; + f->error = 0; + WideCharToMultiByte(CP_UTF8,0,complete_file_path,-1,(LPSTR)f->path,MAX_INPUT_LENGTH, NULL, NULL); + + ts_mutex_lock(&result->files.mutex); + ts_array_push_size(&result->files, &f, sizeof(ts_found_file*)); + ts_mutex_unlock(&result->files.mutex); + + } + } + while (FindNextFile(handle, (LPWIN32_FIND_DATAW)&file_info) != 0); + + FindClose(handle); +} \ No newline at end of file diff --git a/src/windows/mutex.cpp b/src/windows/mutex.cpp new file mode 100644 index 0000000..47663cd --- /dev/null +++ b/src/windows/mutex.cpp @@ -0,0 +1,102 @@ +#include "mutex.h" + +ts_mutex ts_mutex_create() +{ + ts_mutex result; + result.mutex = CreateMutex( + NULL, // default security attributes + FALSE, // initially not owned + NULL); // unnamed ts_mutex + + return result; +} + +ts_mutex ts_mutex_create_recursive() +{ + return ts_mutex_create(); +} + +void ts_mutex_lock(ts_mutex *ts_mutex) +{ + WaitForSingleObject( + ts_mutex->mutex, // handle to ts_mutex + INFINITE); // no time-out interval +} + +bool ts_mutex_trylock(ts_mutex *ts_mutex) +{ + return WaitForSingleObject(ts_mutex->mutex, 1) == WAIT_OBJECT_0; +} + +void ts_mutex_unlock(ts_mutex *ts_mutex) +{ + ReleaseMutex(ts_mutex->mutex); +} + +void ts_mutex_destroy(ts_mutex *ts_mutex) +{ + CloseHandle(ts_mutex->mutex); +} + + +ts_thread ts_thread_start(void *(*start_routine) (void *), void *arg) +{ + ts_thread result; + result.valid = 0; + + result.thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)start_routine, + arg, 0, NULL); + result.valid = 1; + + return result; +} + +void ts_thread_join(ts_thread *ts_thread) +{ + if (ts_thread->valid) + { + WaitForSingleObject(ts_thread->thread, INFINITE); + CloseHandle(ts_thread->thread); + } +} + +bool ts_thread_tryjoin(ts_thread *ts_thread) +{ + if (ts_thread->valid) + { + int result = WaitForSingleObject(ts_thread->thread, 0); + return result == WAIT_OBJECT_0; + } + return 0; +} + +void ts_thread_detach(ts_thread *ts_thread) +{ + if (ts_thread->valid) + { + CloseHandle(ts_thread->thread); + } +} + +void ts_thread_stop(ts_thread *ts_thread) +{ + if (ts_thread->valid) + { + SuspendThread(ts_thread->thread); + } +} + +int ts_thread_get_id() +{ + return GetCurrentThreadId(); +} + +void ts_thread_exit() +{ + ExitThread(0); +} + +void ts_thread_sleep(int microseconds) +{ + Sleep(microseconds/1000); +} \ No newline at end of file -- cgit v1.2.3-70-g09d2